对于FFMPEG SDK 提供的Demuxing 为我们实现多路复用 提供了非常多方便,以下的案案例 实现的是 分离一个媒体文件的音频 视频流 而且解码输出 到 不同的文件里。
对于音频被还原回了 PCM格式 对于视频 被还原成了 YUV420等原生 格式
注意我用的FFMPEG SDK是最新版 API接口稍有改变。
每天更新 博客 记录自己学习的点点滴滴,写完了 上班去
#include "stdafx.h" /************************************************************************/ /* 利用分流器分流MP4文件音视频并进行解码输出 Programmer小卫-USher 2014/12/17 /************************************************************************/ //打开 #define __STDC_FORMAT_MACROS #ifdef _CPPRTTI extern "C" { #endif #include "libavutil/imgutils.h" //图像工具 #include "libavutil/samplefmt.h" // 音频样本格式 #include "libavutil/timestamp.h" //时间戳工具能够 被用于调试和日志目的 #include "libavformat/avformat.h" //Main libavformat public API header 包括了libavf I/O和 Demuxing 和Muxing 库 #ifdef _CPPRTTI }; #endif //音视频编码器上下文 static AVCodecContext *pVideoContext,*pAudioContext; static FILE *fVideoFile,*fAudioFile; //输出文件句柄 static AVStream *pStreamVideo,*pStreamAudio; //媒体流 static unsigned char * videoDstData[4]; //视频数据 static int videoLineSize[4]; // static int videoBufferSize; //视频缓冲区大小 static AVFormatContext *pFormatCtx=NULL; //格式上下文 static AVFrame*pFrame=NULL ; // static AVPacket pkt; //解码媒体包 static int ret=0; //状态 static int gotFrame; //获取到的视频流 //音视频流的索引 static int videoStreamIndex,audioStreamIndex; //解码媒体包 int indexFrameVideo=0; static int decode_packet(int* gotFrame, int param2) { int ret = 0 ; //解码数据大小 int decodedSize=pkt.size ; //初始化获取的数据帧为0 *gotFrame=0; //假设是视频流那么 解包视频流 if(pkt.stream_index==videoStreamIndex) { if((ret=avcodec_decode_video2(pVideoContext,pFrame,gotFrame,&pkt))<0) { //解码视频帧失败 return ret ; } indexFrameVideo++; //copy 解压后的数据到我们分配的空间中 if(*gotFrame) { av_image_copy(videoDstData,videoLineSize, (const uint8_t **)(pFrame->data), pFrame->linesize,pVideoContext->pix_fmt, pVideoContext->width, pVideoContext->height); //写入数据到缓冲区 fwrite(videoDstData[0], 1, videoBufferSize, fVideoFile); printf("输出当前第%d帧,大小:%d\n",indexFrameVideo,videoBufferSize); }else { printf("第%d帧,丢失\n",indexFrameVideo); } } //音频流解包 else if(pkt.stream_index==audioStreamIndex) { //解码音频信息 if((ret=avcodec_decode_audio4(pAudioContext,pFrame,gotFrame,&pkt))<0) return ret ; decodedSize = FFMIN(ret, pkt.size); //算出当前帧的大小 size_t unpadded_linesize = pFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)pFrame->format); ///写入数据到音频文件 fwrite(pFrame->extended_data[0], 1, unpadded_linesize, fAudioFile); } //取消全部引用 而且重置frame字段 av_frame_unref(pFrame); return decodedSize ; } ///依据样本格式 提示样本信息 static int get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt) { int i; struct sample_fmt_entry { enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; } sample_fmt_entries[] = { { AV_SAMPLE_FMT_U8, "u8", "u8" }, { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, }; *fmt = NULL; for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { struct sample_fmt_entry *entry = &sample_fmt_entries[i]; if (sample_fmt == entry->sample_fmt) { *fmt = AV_NE(entry->fmt_be, entry->fmt_le); return 0; } } fprintf(stderr,"sample format %s is not supported as output format\n",av_get_sample_fmt_name(sample_fmt)); return -1; } int _tmain(int argc,char*argv[]) { if(argc<4) { printf("Parameter Error!\n"); return 0; } //注冊全部混流器 过滤器 av_register_all(); //注冊全部编码器 avcodec_register_all(); //媒体输入源头 char*pInputFile=argv[1]; //视频输出文件 char*pOutputVideoFile=argv[2]; //音频输出文件 char*pOutputAudioFile=argv[3]; //分配环境上下文 pFormatCtx=avformat_alloc_context() ; //打开输入源 而且读取输入源的头部 if(avformat_open_input(&pFormatCtx,pInputFile,NULL,NULL)<0) { printf("Open Input Error!\n"); return 0 ; } //获取流媒体信息 if(avformat_find_stream_info(pFormatCtx,NULL)<0) { printf("获取流媒体信息失败!\n"); return 0; } //打印媒体信息 av_dump_format(pFormatCtx,0,pInputFile,0); for(unsigned i=0;i<pFormatCtx->nb_streams;i++) { AVStream *pStream=pFormatCtx->streams[i]; AVMediaType mediaType=pStream->codec->codec_type; //提取不同的编解码器 if(mediaType==AVMEDIA_TYPE_VIDEO) { videoStreamIndex=i ; pVideoContext=pStream->codec; pStreamVideo=pStream; fVideoFile=fopen(pOutputVideoFile,"wb"); if(!fVideoFile) { printf("con't open file!\n"); goto end; } int ret=av_image_alloc(videoDstData,videoLineSize,pVideoContext->width,pVideoContext->height,pVideoContext->pix_fmt,1); if(ret<0) { printf("Alloc video buffer error!\n"); goto end ; } videoBufferSize=ret ; } else if(mediaType==AVMEDIA_TYPE_AUDIO) { audioStreamIndex=i; pAudioContext=pStream->codec ; pStreamAudio=pStream; fAudioFile=fopen(pOutputAudioFile,"wb"); if(!fAudioFile) { printf("con't open file!\n"); goto end; } //分配视频帧 pFrame=av_frame_alloc(); if(pFrame==NULL) { av_freep(&videoDstData[0]); printf("alloc audio frame error\n"); goto end ; } } AVCodec *dec; //依据编码器id查找编码器 dec=avcodec_find_decoder(pStream->codec->codec_id); if(dec==NULL) { printf("查找编码器失败!\n"); goto end; } if(avcodec_open2(pStream->codec, dec, nullptr)!=0) { printf("打开编码器失败!\n"); goto end; } } av_init_packet(&pkt); pkt.data=NULL; pkt.size=0; //读取媒体数据包 数据要大于等于0 while(av_read_frame(pFormatCtx,&pkt)>=0) { AVPacket oriPkt=pkt ; do { //返回每一个包解码的数据 ret=decode_packet(&gotFrame,0); if(ret<0) break; //指针后移 空暇内存降低 pkt.data+=ret ; pkt.size-=ret ; // }while(pkt.size>0); //释放之前分配的空间 读取完成必须释放包 av_free_packet(&oriPkt); } end: //关闭视频编码器 avcodec_close(pVideoContext); //关闭音频编码器 avcodec_close(pAudioContext); avformat_close_input(&pFormatCtx); fclose(fVideoFile); fclose(fAudioFile); //释放编码帧 avcodec_free_frame(&pFrame); //释放视频数据区 av_free(videoDstData[0]); return 0; } 程序执行例如以下图所看到的
我们发现 MP4文件 被分离开了。
。。