https://www.cnblogs.com/lidabo/p/6932134.html本demo演示了利用ffmpeg从服务器拉流或本地文件读取流,更改流url或文件类型名称发送回服务器或存到本地的作用。由于本程序只写了3个小时,还要忙别的,所以会有一些bug和优化的地方。不过demo的意义已经达到了。 [cpp] view plain copy //info.h #ifndef __INFO_H__ #define __INFO_H__ #include #include #endif [cpp] view plain copy //ffmpeg.h #ifndef __FFMPEG_H__ #define __FFMPEG_H__ #include "info.h" extern "C" { #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavcodec/avcodec.h" #include "libswscale/swscale.h" #include "libavutil/avutil.h" #include "libavutil/mathematics.h" #include "libswresample/swresample.h" #include "libavutil/opt.h" #include "libavutil/channel_layout.h" #include "libavutil/samplefmt.h" #include "libavdevice/avdevice.h" //摄像头所用 #include "libavfilter/avfilter.h" #include "libavutil/error.h" #include "libavutil/mathematics.h" #include "libavutil/time.h" #include "inttypes.h" #include "stdint.h" }; #pragma comment(lib,"avformat.lib") #pragma comment(lib,"avcodec.lib") #pragma comment(lib,"avdevice.lib") #pragma comment(lib,"avfilter.lib") #pragma comment(lib,"avutil.lib") #pragma comment(lib,"postproc.lib") #pragma comment(lib,"swresample.lib") #pragma comment(lib,"swscale.lib") //#define INPUTURL "test.flv" #define INPUTURL "rtmp://test1.com:1935/myapp/teststream1" //#define OUTPUTURL "testnew.flv"; #define OUTPUTURL "rtmp://test1.com:1935/myapp/teststream1new" //video param extern int m_dwWidth; extern int m_dwHeight; extern double m_dbFrameRate; //帧率 extern AVCodecID video_codecID; extern AVPixelFormat video_pixelfromat; extern char spspps[100]; extern int spspps_size; //audio param extern int m_dwChannelCount; //声道 extern int m_dwBitsPerSample; //样本 extern int m_dwFrequency; //采样率 extern AVCodecID audio_codecID; extern int audio_frame_size; #define AUDIO_ID 0 //packet 中的ID ,如果先加入音频 pocket 则音频是 0 视频是1,否则相反 #define VEDIO_ID 1 extern int nRet; //状态标志 extern AVFormatContext* icodec; extern AVInputFormat* ifmt; extern char szError[256]; //错误字符串 extern AVFormatContext* oc ; //输出流context extern AVOutputFormat* ofmt; extern AVStream* video_st; extern AVStream* audio_st; extern AVCodec *audio_codec; extern AVCodec *video_codec; extern int video_stream_idx; extern int audio_stream_idx; extern AVPacket pkt; extern AVBitStreamFilterContext * vbsf_aac_adtstoasc; //aac->adts to asc过滤器 static int m_nVideoTimeStamp = 0; static int m_nAudioTimeStamp = 0; int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe); int InitOutput(); AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec); int OpenCodec(AVStream * istream, AVCodec * icodec); //打开编解码器 void read_frame(AVFormatContext *oc); //从文件读取一帧数据根据ID读取不同的帧 void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t); //这个是根据传过来的buf 和size 写入文件 int Es2Mux_2(); //通过时间戳控制写入音视频帧顺序 int UintInput(); int UintOutput(); #endif [cpp] view plain copy //ffmpeg.cpp #include "ffmpeg.h" int nRet = 0; AVFormatContext* icodec = NULL; AVInputFormat* ifmt = NULL; char szError[256]; AVFormatContext* oc = NULL; AVOutputFormat* ofmt = NULL; AVStream * video_st = NULL; AVStream * audio_st = NULL; AVCodec *audio_codec; AVCodec *video_codec; double audio_pts = 0.0; double video_pts = 0.0; int video_stream_idx = -1; int audio_stream_idx = -1; AVPacket pkt; AVBitStreamFilterContext * vbsf_aac_adtstoasc = NULL; //video param int m_dwWidth = 0; int m_dwHeight = 0; double m_dbFrameRate = 25.0; //帧率 AVCodecID video_codecID = AV_CODEC_ID_H264; AVPixelFormat video_pixelfromat = AV_PIX_FMT_YUV420P; char spspps[100]; int spspps_size = 0; //audio param int m_dwChannelCount = 2; //声道 int m_dwBitsPerSample = 16; //样本 int m_dwFrequency = 44100; //采样率 AVCodecID audio_codecID = AV_CODEC_ID_AAC; int audio_frame_size = 1024; int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe) { int i = 0; nRet = avformat_open_input(iframe_c, Filename,(*iinputframe), NULL); if (nRet != 0) { av_strerror(nRet, szError, 256); printf(szError); printf("\n"); printf("Call avformat_open_input function failed!\n"); return 0; } if (av_find_stream_info(*iframe_c) < 0) { printf("Call av_find_stream_info function failed!\n"); return 0; } //输出视频信息 av_dump_format(*iframe_c, -1, Filename, 0); //添加音频信息到输出context for (i = 0; i < (*iframe_c)->nb_streams; i++) { if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_idx = i; m_dwWidth = (*iframe_c)->streams[i]->codec->width; m_dwHeight = (*iframe_c)->streams[i]->codec->height; m_dbFrameRate = av_q2d((*iframe_c)->streams[i]->r_frame_rate); video_codecID = (*iframe_c)->streams[i]->codec->codec_id; video_pixelfromat = (*iframe_c)->streams[i]->codec->pix_fmt; spspps_size = (*iframe_c)->streams[i]->codec->extradata_size; memcpy(spspps,(*iframe_c)->streams[i]->codec->extradata,spspps_size); } else if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { audio_stream_idx = i; m_dwChannelCount = (*iframe_c)->streams[i]->codec->channels; switch ((*iframe_c)->streams[i]->codec->sample_fmt) { case AV_SAMPLE_FMT_U8: m_dwBitsPerSample = 8; break; case AV_SAMPLE_FMT_S16: m_dwBitsPerSample = 16; break; case AV_SAMPLE_FMT_S32: m_dwBitsPerSample = 32; break; default: break; } m_dwFrequency = (*iframe_c)->streams[i]->codec->sample_rate; audio_codecID = (*iframe_c)->streams[i]->codec->codec_id; audio_frame_size = (*iframe_c)->streams[i]->codec->frame_size; } } return 1; } int InitOutput() { int i = 0; /* allocate the output media context */ avformat_alloc_output_context2(&oc, NULL, "flv", OUTPUTURL); if (!oc) { return getchar(); } ofmt = oc->oformat; /* open the output file, if needed */ if (!(ofmt->flags & AVFMT_NOFILE)) { if (avio_open(&oc->pb, OUTPUTURL, AVIO_FLAG_WRITE) < 0) { printf("Could not open '%s'\n", OUTPUTURL); return getchar(); } } //添加音频信息到输出context if(audio_stream_idx != -1) { ofmt->audio_codec = audio_codecID; audio_st = Add_output_stream_2(oc, AVMEDIA_TYPE_AUDIO,audio_codecID,&audio_codec); } //添加视频信息到输出context ofmt->video_codec = video_codecID; video_st = Add_output_stream_2(oc, AVMEDIA_TYPE_VIDEO,video_codecID,&video_codec); if (OpenCodec(video_st,video_codec) < 0) //打开视频编码器 { printf("can not open video codec\n"); return getchar(); } if(audio_stream_idx != -1) { if (OpenCodec(audio_st,audio_codec) < 0) //打开音频编码器 { printf("can not open audio codec\n"); return getchar(); } } av_dump_format(oc, 0, OUTPUTURL, 1); if (avformat_write_header(oc, NULL)) { printf("Call avformat_write_header function failed.\n"); return 0; } if(audio_stream_idx != -1) { if ((strstr(oc->oformat->name, "flv") != NULL) || (strstr(oc->oformat->name, "mp4") != NULL) || (strstr(oc->oformat->name, "mov") != NULL) || (strstr(oc->oformat->name, "3gp") != NULL)) { if (audio_st->codec->codec_id == AV_CODEC_ID_AAC) //AV_CODEC_ID_AAC { vbsf_aac_adtstoasc = av_bitstream_filter_init("aac_adtstoasc"); } } } if(vbsf_aac_adtstoasc == NULL) { return -1; } return 1; } AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec) { AVCodecContext* output_codec_context = NULL; AVStream * output_stream = NULL; /* find the encoder */ *codec = avcodec_find_encoder(codecID); if (!(*codec)) { return NULL; } output_stream = avformat_new_stream(output_format_context, *codec); if (!output_stream) { return NULL; } output_stream->id = output_format_context->nb_streams - 1; output_codec_context = output_stream->codec; output_codec_context->codec_id = codecID; output_codec_context->codec_type = codec_type_t; switch (codec_type_t) { case AVMEDIA_TYPE_AUDIO: AVRational CodecContext_time_base; CodecContext_time_base.num = 1; CodecContext_time_base.den = m_dwFrequency; output_stream->time_base = CodecContext_time_base; output_codec_context->time_base = CodecContext_time_base; output_stream->start_time = 0; output_codec_context->sample_rate = m_dwFrequency; output_codec_context->channels = m_dwChannelCount; output_codec_context->frame_size = audio_frame_size; switch (m_dwBitsPerSample) { case 8: output_codec_context->sample_fmt = AV_SAMPLE_FMT_U8; break; case 16: output_codec_context->sample_fmt = AV_SAMPLE_FMT_S16; break; case 32: output_codec_context->sample_fmt = AV_SAMPLE_FMT_S32; break; default: break; } output_codec_context->block_align = 0; if(! strcmp( output_format_context-> oformat-> name, "mp4" ) || !strcmp (output_format_context ->oformat ->name , "mov" ) || !strcmp (output_format_context ->oformat ->name , "3gp" ) || !strcmp (output_format_context ->oformat ->name , "flv" )) { output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; } break; case AVMEDIA_TYPE_VIDEO: AVRational r_frame_rate_t; r_frame_rate_t.num = 1000; r_frame_rate_t.den = (int)(m_dbFrameRate * 1000); output_stream->time_base = r_frame_rate_t; output_codec_context->time_base = r_frame_rate_t; AVRational r_frame_rate_s; r_frame_rate_s.num = (int)(m_dbFrameRate * 1000); r_frame_rate_s.den = 1000; output_stream->r_frame_rate = r_frame_rate_s; output_stream->start_time = 0; output_codec_context->pix_fmt = video_pixelfromat; output_codec_context->width = m_dwWidth; output_codec_context->height = m_dwHeight; output_codec_context->extradata = (uint8_t *)spspps; output_codec_context->extradata_size = spspps_size; //这里注意不要加头,demux的时候 h264filter过滤器会改变文件本身信息 //这里用output_codec_context->extradata 来显示缩略图 //if(! strcmp( output_format_context-> oformat-> name, "mp4" ) || // !strcmp (output_format_context ->oformat ->name , "mov" ) || // !strcmp (output_format_context ->oformat ->name , "3gp" ) || // !strcmp (output_format_context ->oformat ->name , "flv" )) //{ // output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; //} break; default: break; } return output_stream; } int OpenCodec(AVStream * istream, AVCodec * icodec) { AVCodecContext *c = istream->codec; nRet = avcodec_open2(c, icodec, NULL); return nRet; } int UintInput() { /* free the stream */ av_free(icodec); return 1; } int UintOutput() { int i = 0; nRet = av_write_trailer(oc); if (nRet < 0) { av_strerror(nRet, szError, 256); printf(szError); printf("\n"); printf("Call av_write_trailer function failed\n"); } if (vbsf_aac_adtstoasc !=NULL) { av_bitstream_filter_close(vbsf_aac_adtstoasc); vbsf_aac_adtstoasc = NULL; } av_dump_format(oc, -1, OUTPUTURL, 1); /* Free the streams. */ for (i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } if (!(ofmt->flags & AVFMT_NOFILE)) { /* Close the output file. */ avio_close(oc->pb); } av_free(oc); return 1; } void read_frame(AVFormatContext *oc) { int ret = 0; ret = av_read_frame(icodec, &pkt); if (pkt.stream_index == video_stream_idx) { write_frame_3(oc,VEDIO_ID,pkt); } else if (pkt.stream_index == audio_stream_idx) { write_frame_3(oc,AUDIO_ID,pkt); } } void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t) { int64_t pts = 0, dts = 0; int nRet = -1; AVRational time_base_t; time_base_t.num = 1; time_base_t.den = 1000; if(ID == VEDIO_ID) { AVPacket videopacket_t; av_init_packet(&videopacket_t); if (av_dup_packet(&videopacket_t) < 0) { av_free_packet(&videopacket_t); } videopacket_t.pts = pkt_t.pts; videopacket_t.dts = pkt_t.dts; videopacket_t.pos = 0; videopacket_t.priv = 0; videopacket_t.flags = 1; videopacket_t.convergence_duration = 0; videopacket_t.side_data_elems = 0; videopacket_t.stream_index = VEDIO_ID; videopacket_t.duration = 0; videopacket_t.data = pkt_t.data; videopacket_t.size = pkt_t.size; nRet = av_interleaved_write_frame(oc, &videopacket_t); if (nRet != 0) { printf("error av_interleaved_write_frame _ video\n"); } av_free_packet(&videopacket_t); } else if(ID == AUDIO_ID) { AVPacket audiopacket_t; av_init_packet(&audiopacket_t); if (av_dup_packet(&audiopacket_t) < 0) { av_free_packet(&audiopacket_t); } audiopacket_t.pts = pkt_t.pts; audiopacket_t.dts = pkt_t.dts; audiopacket_t.pos = 0; audiopacket_t.priv = 0; audiopacket_t.flags = 1; audiopacket_t.duration = 0; audiopacket_t.convergence_duration = 0; audiopacket_t.side_data_elems = 0; audiopacket_t.stream_index = AUDIO_ID; audiopacket_t.duration = 0; audiopacket_t.data = pkt_t.data; audiopacket_t.size = pkt_t.size; //添加过滤器 if(! strcmp( oc-> oformat-> name, "mp4" ) || !strcmp (oc ->oformat ->name , "mov" ) || !strcmp (oc ->oformat ->name , "3gp" ) || !strcmp (oc ->oformat ->name , "flv" )) { if (audio_st->codec->codec_id == AV_CODEC_ID_AAC) { if (vbsf_aac_adtstoasc != NULL) { AVPacket filteredPacket = audiopacket_t; int a = av_bitstream_filter_filter(vbsf_aac_adtstoasc, audio_st->codec, NULL,&filteredPacket.data, &filteredPacket.size, audiopacket_t.data, audiopacket_t.size, audiopacket_t.flags & AV_PKT_FLAG_KEY); if (a > 0) { av_free_packet(&audiopacket_t); filteredPacket.destruct = av_destruct_packet; audiopacket_t = filteredPacket; } else if (a == 0) { audiopacket_t = filteredPacket; } else if (a < 0) { fprintf(stderr, "%s failed for stream %d, codec %s", vbsf_aac_adtstoasc->filter->name,audiopacket_t.stream_index,audio_st->codec->codec ? audio_st->codec->codec->name : "copy"); av_free_packet(&audiopacket_t); } } } } nRet = av_interleaved_write_frame(oc, &audiopacket_t); if (nRet != 0) { printf("error av_interleaved_write_frame _ audio\n"); } av_free_packet(&audiopacket_t); } } int Es2Mux_2() { for (;;) { read_frame(oc); } return 1; } [cpp] view plain copy //main.cpp #include "ffmpeg.h" int main(int argc ,char ** argv) { av_register_all(); avformat_network_init(); InitInput(INPUTURL,&icodec,&ifmt); InitOutput(); printf("--------程序运行开始----------\n"); ////////////////////////////////////////////////////////////////////////// Es2Mux_2(); ////////////////////////////////////////////////////////////////////////// UintOutput(); UintInput(); printf("--------程序运行结束----------\n"); printf("-------请按任意键退出---------\n"); return getchar(); }