FFmpeg音频的编码流程详解及demo

Posted 周末的音视频

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg音频的编码流程详解及demo相关的知识,希望对你有一定的参考价值。

        本文主要讲解FFmpeg的音频编码具体流程,API使用。最后再以一个非常简单的demo演示将一个音频原始数据pcm文件编码为AAC格式的音频文件。 本文主要基于FFmpeg音频编码新接口。

一、FFmpeg音频编码API调用流程图

        音频编码的API调用流程图如下:

          API接口简单大体讲解如下:

av_register_all():注册FFmpeg所有编解码器。
 
avformat_alloc_context():初始化输出码流的AVFormatContext。
 
avio_open():打开输出文件。
 
av_new_stream():创建输出码流的AVStream。
 
avcodec_find_encoder():查找编码器。
 
avcodec_open2():打开编码器。
 
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
 
avcodec_send_frame():编码核心接口新接口,发送一帧音频给编码器。即是AVFrame(存储PCM数据)。
 
avcodec_receive_packet():编码核心接口新接口,接收编码器编码后的一帧音频,AVPacket(存储AAC等格式的数据)。
 
av_write_frame():将编码后的音频数据写入文件。
 
flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。
 
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。

        从上面可以看到,其实音频的编码流程基本与视频的编码流程一致。

二、音频编码过程API调用流程

1、注册各大组件

        这一步是ffmpeg的任何程序的第一步都是需要先注册ffmpeg相关的各大组件的:

    //注册各大组件
    av_register_all();

2、打开pcm原始数据文件

        这里除了打开pcm原始数据文件外,还定义了一个framenum变量,这个是为了简单验证需要,我们只编码framenum帧,不然若pcm太大,编码太久,可以指定自己想要的多少帧来做验证即可,赋值framenum。

    FILE *in_file=NULL;	                        //Raw PCM data
    in_file= fopen(inputPath, "rb");

    int framenum=10000;                          //Audio frame number

3、初始化输出码流的AVFormatContext

        与视频一样,有两种方式,这里我们用的方式一。

        方式一:

    //方式1
    pFormatCtx = avformat_alloc_context();
    //Guess Format
    fmt = av_guess_format(NULL, outputPath, NULL);
    pFormatCtx->oformat = fmt;

        方式二:

    //方式2
    avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
    fmt = pFormatCtx->oformat;

4、打开输出文件

    //Open output
    if (avio_open(&pFormatCtx->pb,outputPath, AVIO_FLAG_READ_WRITE) < 0)
        LOGE("Failed to open output file!");
        return false;
    

5、创建输出码流的AVStream

    audio_st = avformat_new_stream(pFormatCtx, 0);
    if (audio_st==NULL)
        return false;
    

6、查找编码器并打开

        查找编码器之前,需要先指定配置编码器的一些参数。像音频的话,比如它的采样率,声道数,编码格式等,需要配置输入的PCM的原始数据的参数,也需要配置编码后输出的数据的参数。如下,我们这里参数的设定是输入的PCM是44100,FLT,STEREO,输出AAC是44100,FLTP,STEREO:

    // 这个参数的设定是输入的PCM是44100,FLT,STEREO,输出AAC是44100,FLTP,STEREO
    pCodecCtx = audio_st->codec;
    pCodecCtx->codec_id = fmt->audio_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
    pCodecCtx->sample_rate= 44100;
    pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO;
    pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
    pCodecCtx->bit_rate = 64000;
    pCodecCtx->profile=FF_PROFILE_AAC_MAIN;
    pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
    //Show some information
    av_dump_format(pFormatCtx, 0, outputPath, 1);

    swr = swr_alloc();
    av_opt_set_int(swr, "in_channel_layout",  AV_CH_LAYOUT_STEREO, 0);
    av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO,  0);
    av_opt_set_int(swr, "in_sample_rate",     44100, 0);
    av_opt_set_int(swr, "out_sample_rate",    44100, 0);
    av_opt_set_sample_fmt(swr, "in_sample_fmt",  AV_SAMPLE_FMT_FLT, 0);
    av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP,  0);
    swr_init(swr);
    //

        这里中间我们也使用了av_dump_format()这个API,打印出配置后的参数格式,方便调试查看。

    //Show some information
    av_dump_format(pFormatCtx, 0, outputPath, 1);

        参数配置好后调用avcodec_find_encoder寻找编码器

    pCodec = avcodec_find_encoder(fmt->audio_codec);
    if (!pCodec)
        LOGE("Can not find encoder!");
        return false;
    

        最后打开编码器

    ret = avcodec_open2(pCodecCtx, pCodec,NULL);
    if (ret < 0)
        LOGE("Failed to open encoder!");
        return false;
    

7、写文件头

        对于某些没有文件头的封装格式,不需要此函数。

    //Write Header
    avformat_write_header(pFormatCtx,NULL);

8、开始音频编码

        准备就绪后,我们就可以开始编码。音频编码的新接口同样是通过发送给编码器,然后接收的方式进行编码。编码的核心代码如下:

        ret = avcodec_send_frame(pCodecCtx, pFrame);
        if (ret < 0)
            LOGE("Error sending a frame for encoding");
            return false;
        

        while (ret >= 0)
            ret = avcodec_receive_packet(pCodecCtx, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            
            else if (ret < 0)
                LOGE("Error during encoding\\n");
                break;
            

            ret = av_write_frame(pFormatCtx, pkt);
            av_packet_unref(pkt);
        

9、读取PCM原数据文件,编码,写入文件

        我们一帧一帧地读取PCM原数据文件,然后进行编码,拿到编码后的数据通过av_write_frame写入文件。

ret = av_write_frame(pFormatCtx, pkt);

        同时为了确保数据齐全,获取到最后数据的时候,需要将缓冲区中的数据清理出来

    //Flush Encoder
    ret = audio_flush_encoder(pFormatCtx,0);
    if (ret < 0) 
        LOGE("Flushing encoder failed");
        return false;
    

        完整的编码,写入文件流程如下:

for (i=0; i<framenum; i++)
        //Read PCM data
        if ((ret = fread(frame_buf, 1, size, in_file)) <= 0)
            LOGE("fread pcm raw data failed\\n");
            getc(in_file);
            if(feof(in_file)) 
                LOGE(" -> Because this is the file feof!");
                break;
            
            return false;
        

        int count=swr_convert(swr, outs,len*4,(const uint8_t **)&frame_buf,len/4);//len 为4096
        pFrame->data[0] =(uint8_t*)outs[0];//audioFrame 是VFrame
        pFrame->data[1] =(uint8_t*)outs[1];

        //pFrame->data[0] = frame_buf;  //PCM Data
        pFrame->pts=i*100;

        ret = avcodec_send_frame(pCodecCtx, pFrame);
        if (ret < 0)
            LOGE("Error sending a frame for encoding");
            return false;
        

        while (ret >= 0)
            ret = avcodec_receive_packet(pCodecCtx, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                break;
            
            else if (ret < 0)
                LOGE("Error during encoding\\n");
                break;
            

            ret = av_write_frame(pFormatCtx, pkt);
            av_packet_unref(pkt);
        
    

    //Flush Encoder
    ret = audio_flush_encoder(pFormatCtx,0);
    if (ret < 0) 
        LOGE("Flushing encoder failed");
        return false;
    

        其中audio_flush_encoder其实与视频编码的flush_encoder一样的作用:

int audio_flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
          AV_CODEC_CAP_DELAY))
        return 0;
    while (1) 
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
                                     NULL, &got_frame);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame)
            ret=0;
            break;
        
        LOGE("Flush Encoder: Succeed to encode 1 frame!\\tsize:%5d\\n",enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    
    return ret;

10、写文件尾

         对于某些没有文件头的封装格式,不需要此步。

    //Write file trailer
    av_write_trailer(pFormatCtx);

11、收尾,释放相关资源

    //Clean
    if (audio_st)
        avcodec_close(audio_st->codec);
        av_free(pFrame);
        av_free(frame_buf);
    
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);

    fclose(in_file);

        以上便是pcm编码为AAC格式的整个流程,可以看到与视频非常相似。若是输出其他编码文件,步骤类似,但是需要调整打开编码器那一步的相关参数。若要做成通用性demo,可以在这一步加上对编码格式的判断,从而进行参数的选择。

三、demo运行

        demo中指定了编码的输入文件和输出文件,输入文件是/sdcar/audioIn.pcm,输出文件是/sdcard/audioOut.aac,需要改的话,需要在这里改:

@Override
public void onClick(View view) 
	runOnUiThread(new Runnable() 
		@Override
		public void run() 
			String PATH = Environment.getExternalStorageDirectory().getPath();
			//音频编码
			String input = PATH + File.separator + "audioIn.pcm";
			String output = PATH + File.separator + "audioOut.aac";
			encode_audio(input,output);
			Toast.makeText(MainActivity.this, "音频编码完成,请自行从手机中拉取aac文件", Toast.LENGTH_SHORT).show();
		
	);

        测试用的pcm数据文件,可以通过一些设备进行一些录音获取原始数据。或者通过上篇解码的demo先把一个mp3文件给解码为pcm,拿来用。

        运行demo,截图如下:

        点击“START AUDIO ENCODE”按钮,开始编码。编码过程可能比较久,界面不会有变化,可以通过看log看过程:

        编码结束后,界面上会有提示词:“音频编码完成,请自行从手机中拉取aac文件”,同时log也能看到"--- audio encode finished ---"

        然后我们看到/sdcard/下已经有编码后的文件,看到编码后的文件比原始pcm文件就要小很多:

        我们把它拉取到电脑上,可以看到aac格式的文件,是可以直接用普通音乐播放器播放的,如下,可以打开播放,若音乐播放正常,说明编码过程没有问题。

        完整例子已经放到github上,如下

https://github.com/weekend-y/FFmpeg_Android_Demo/tree/master/demo7_audioEncode

FFmpeg转码流程详解

前言:

音视频转码主要指这样的概念:

容器格式的转换,比如MP4转换为MOV

容器中音视频数据编码方式转换,比如H264编码转换成MPEG4编码,MP3换为AAC

音视频码率的转换,比如4M的视频码率降为2M

视频分辨率的转换,比如1080P视频变为720P,音频重采样等等

转码技术点分析

转码流程图:

流拷贝是指源文件音/视频编码方式也被目标文件支持,那么此情况下音/视频数据拷贝就可以直接拷贝到目标文件下。

转码类型分析:

容器格式的转换:

有两种情况,第一种情况源容器格式的音/视频编码方式在目标容器格式也支持,这样只需要进行流拷贝;第二种情况源容器格式的音/视频编码昂视在目标容器不被支持,那么就需要先解码再编码。

1、流拷贝方式的流程图:

1、解封装实现相关函数

AVFormatContext:代表解封装相关上下文

avformat_open_input():对源文件进行解封装

avformat_find_streaminfo():查找流信息

av_frame_read():从源文件中读取音频或者视频AVPacket

2、封装实现相关函数

AVFormatContext:代表封装相关上下文

avformat_alloc_output_context2():创建基于目标容器格式的上下文对象

avformat_new_stream():向封装上下文添加音/视频流

avio_open2():打开输出流,用于封装时写入数据

avformat_write_header():封装时写入头文件

av_write_frame():写入音/视数据

av_write_trailer():封装时写入尾部信息

3、完整代码

/** 实现MP4转换成MOV,FLV,TS,AVI文件,不改变编码方式
 */
void Transcode::doExtensionTranscode()

    string curFile(__FILE__);
    unsigned long pos = curFile.find("2-video_audio_advanced");
    if (pos == string::npos) 
        LOGD("not find file");
        return;
    
    string srcDic = curFile.substr(0,pos)+"filesources/";
    string srcPath = srcDic + "test_1280x720_3.mp4";
    string dstPath = srcDic + "2-test_1280x720_3.mov";   // 这里后缀可以改成flv,ts,avi mov则生成对应的文件
    
    AVFormatContext *in_fmtCtx = NULL,*ou_fmtCtx = NULL;
    int video_in_stream_index = -1,audio_in_stream_index = -1;
    int video_ou_stream_index = -1,audio_ou_stream_index = -1;
    
    int ret = 0;
    if ((ret = avformat_open_input(&in_fmtCtx,srcPath.c_str(),NULL,NULL)) < 0) 
        LOGD("avformat_open_input fail %s",av_err2str(ret));
        return;
    
    if (avformat_find_stream_info(in_fmtCtx,NULL) < 0) 
        LOGD("avformat_find_stream_info fail %s",av_err2str(ret));
        return;
    
    
    av_dump_format(in_fmtCtx, 0, srcPath.c_str(), 0);
    
    
    if ((ret = avformat_alloc_output_context2(&ou_fmtCtx,NULL,NULL,dstPath.c_str())) < 0) 
        LOGD("avformat_alloc_output_context2 fail %s",av_err2str(ret));
        return;
    
    
    for (int i=0; i<in_fmtCtx->nb_streams; i++) 
        AVStream *stream = in_fmtCtx->streams[i];
        
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_in_stream_index == -1) 
            video_in_stream_index = i;
            
            // 添加流信息
            AVStream *newStream = avformat_new_stream(ou_fmtCtx,NULL);
            video_ou_stream_index = newStream->index;
            // 将编码信息拷贝过来
            LOGD("fourcc %s",av_fourcc2str(newStream->codecpar->codec_tag));
            if ((ret = avcodec_parameters_copy(newStream->codecpar, stream->codecpar)) <0) 
                LOGD("avcodec_parameters_copy fail %s",av_err2str(ret));
                return;
            
            /** 遇到问题:mp4转换为avi时,播放提示“reference count 1 overflow,chroma_log2_weight_denom 1696 is out of range”
             *  分析原因:之前的写法是没有加下面的判断条件直接为newStream->codecpar->codec_tag = 0;这样avi将选择默认的h264作为码流格式(源mp4中为avc1),导致两边不一样
             *  解决方案:加如下判断
            */
            uint32_t src_codec_tag = stream->codecpar->codec_tag;
            if (av_codec_get_id(ou_fmtCtx->oformat->codec_tag, src_codec_tag) != newStream->codecpar->codec_id) 
                newStream->codecpar->codec_tag = 0;
            
        
        
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_in_stream_index == -1) 
            audio_in_stream_index = i;
            AVStream *newStream = avformat_new_stream(ou_fmtCtx,NULL);
            audio_ou_stream_index = newStream->index;
            if ((ret = avcodec_parameters_copy(newStream->codecpar, stream->codecpar)) <0) 
                LOGD("avcodec_parameters_copy2 fail %s",av_err2str(ret));
                return;
            
            /** 遇到问题:avformat_write_header()函数提示"Tag mp4a incompatible with output codec id '86018'"
             *  分析原因:code_tag代表了音视频数据采用的码流格式。拿aac举例,AVI和MP4都支持aac编码的音频数据存储,MP4支持MKTAG('m', 'p', '4',
             *  'a')码流格式的aac流,avi支持(0x00ff,0x1600,0x706d等),显然两者是不一样的,上面avcodec_parameters_copy()就相当于让封装和解封装的
             *  code_tag标签一模一样,所以造成了不一致的问题
             *  解决方案:将codecpar->codec_tag=0,系统会默认选择第一个匹配编码方式的codec_tag值
             */
            uint32_t src_codec_tag = stream->codecpar->codec_tag;
            if (av_codec_get_id(ou_fmtCtx->oformat->codec_tag, src_codec_tag) != newStream->codecpar->codec_id) 
                newStream->codecpar->codec_tag = 0;
            
        
    
    
    // 当flags没有AVFMT_NOFILE标记的时候才能调用avio_open2()函数进行初始化
    if (!(ou_fmtCtx->flags & AVFMT_NOFILE)) 
        if ((ret = avio_open2(&ou_fmtCtx->pb,dstPath.c_str(),AVIO_FLAG_WRITE,NULL,NULL)) < 0) 
            LOGD("avio_open2 fail %d",ret);
            return;
        
    
    
    // 写入文件头信息
    if ((ret = avformat_write_header(ou_fmtCtx, NULL)) < 0) 
        LOGD("avformat_write_header fail %s",av_err2str(ret));
        return;
    
    av_dump_format(ou_fmtCtx, 0, dstPath.c_str(), 1);
    
    AVPacket *in_packet = av_packet_alloc();
    while ((av_read_frame(in_fmtCtx, in_packet)) == 0) 
        if (in_packet->stream_index != video_in_stream_index && in_packet->stream_index != audio_in_stream_index) 
            continue;
        
//        LOGD("write packet index %d size %d",in_packet->stream_index,in_packet->size);
        AVStream *in_stream = in_fmtCtx->streams[in_packet->stream_index];
        AVStream *ou_stream = NULL;
        if (in_stream->index == video_in_stream_index) 
            ou_stream = ou_fmtCtx->streams[video_ou_stream_index];
         else 
            ou_stream = ou_fmtCtx->streams[audio_ou_stream_index];
        
        
        /** 每个packet中pts,dts,duration 转换成浮点数时间的公式(以pts为例):pts * timebase.num/timebase.den
         */
        in_packet->pts = av_rescale_q_rnd(in_packet->pts,in_stream->time_base,ou_stream->time_base,AV_ROUND_UP);
        in_packet->dts = av_rescale_q_rnd(in_packet->dts,in_stream->time_base,ou_stream->time_base,AV_ROUND_UP);
        in_packet->duration = av_rescale_q_rnd(in_packet->duration, in_stream->time_base, ou_stream->time_base, AV_ROUND_UP);
        in_packet->stream_index = ou_stream->index;
        /** 遇到问题:提示"H.264 bitstream malformed, no startcode found, use the video bitstream filter 'h264_mp4toannexb' to fix it ('-bsf:v
         *  h264_mp4toannexb' option with ffmpeg)"
         *  分析原因:MP4中h264编码的视频码流格式为avcc(即每个NALU的前面都加了四个大端序的字节,表示每个NALU的长度),而avi中h264
         *  编码的视频码流格式为annexb(即每个NALU的前面是0001或者001开头的开始码),两者不一致。
         *  解决方案:将开头的四个字节替换掉
         */
//        if (in_packet->stream_index == video_ou_stream_index) 
//            uint8_t *orgData = in_packet->data;
//            orgData[0] = 0;
//            orgData[1] = 0;
//            orgData[2] = 0;
//            orgData[3] = 1;
//        
//        printBuffertoHex(in_packet->data,in_packet->size);
        av_write_frame(ou_fmtCtx, in_packet);
        
        av_packet_unref(in_packet);
    
    
    av_write_trailer(ou_fmtCtx);
    avformat_close_input(&in_fmtCtx);
    avformat_free_context(ou_fmtCtx);

函数详解:

enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned int tag);

用于获取编码器支持的tags对应tag的codeid

结构体:

struct AVCodecTag;

该结构体唯一代表了一种编码方式的码流格式,由编码方式(AVCodecId)和码流标签共同决定。比如h264,它有avcc和annexb两种码流格式,对于avi,它采用annexb格式存储h264的视频数据,而对于mp4,它则采用avcc格式存储h264视频数据,所以就算是流拷贝,再进行不同容器格式的封装时也要考虑码流格式是否一致。

遇到问题:

1、mp4转换为avi时,播放提示“reference count 1 overflow,chroma_log2_weight_denom 1696 is out of range”

分析原因:

之前的写法是没有加下面的判断条件直接为newStream->codecpar->codec_tag = 0;这样avi将选择默认的h264作为码流格式(源mp4中为avc1),导致两边不一样

解决方案:

如上代码添加判断,详情上面代码。

2、提示"H.264 bitstream malformed, no startcode found, use the video bitstream filter 'h264_mp4toannexb' to fix it ('-bsf:v

* h264_mp4toannexb' option with ffmpeg)"

分析原因:

MP4中h264编码的视频码流格式为avcc(即每个NALU的前面都加了四个大端序的字节,表示每个NALU的长度),而avi中h264

* 编码的视频码流格式为annexb(即每个NALU的前面是0001或者001开头的开始码),两者不一致。

解决方案:

将开头的四个字节替换掉。

2、重新编解码方式

采用重新编解码方式意味着解封装之后得先对源音视频数据进行解码,然后将得到的未压缩音视频数据按照目标编码方式再次编码,最后将得到的压缩数据按照指定的容器格式进行封装。

流程图:

/** 做转码(包括编码方式,码率,分辨率,采样率,采样格式等等的转换)
 */
void Transcode::doTranscode()

    string curFile(__FILE__);
    unsigned long pos = curFile.find("2-video_audio_advanced");
    if (pos == string::npos) 
        LOGD("cannot find file");
        return;
    
    int ret = 0;
    string srcDic = curFile.substr(0,pos) + "filesources/";
    srcPath = srcDic + "test_1280x720_3.mp4";
    dstPath = srcDic + "1-test_1280x720_3.mov";
    // 做编码方式的转码,目标封装格式要支持 第一个参数代表是否引用源文件的参数,如果为true则忽略第二个参数,否则使用第二个参数
    // 视频
    dst_video_id = Par_CodeId(false,AV_CODEC_ID_MPEG4);
    dst_width =  Par_Int32t(false,1280);
    dst_height = Par_Int32t(false,720);
    dst_video_bit_rate = Par_Int64t(true,9000000);       // 0.9Mb/s
    dst_pix_fmt = Par_PixFmt(false,AV_PIX_FMT_NV12);
    dst_fps = Par_Int32t(true,50);  //fps
    video_need_transcode = true;
    
    // 音频
    dst_audio_id =  Par_CodeId(false,AV_CODEC_ID_MP3);
    dst_audio_bit_rate = Par_Int64t(true,128*1000);
    dst_sample_rate = Par_Int32t(true,44100);    // 44.1khz
    dst_sample_fmt = Par_SmpFmt(false,AV_SAMPLE_FMT_FLTP);
    dst_channel_layout = Par_Int64t(false,AV_CH_LAYOUT_STEREO);   // 双声道
    audio_need_transcode = true;
    
    // 打开输入文件及输出文件的上下文
    if (!openFile()) 
        LOGD("openFile()");
        return;
    
    
    // 为输出文件添加视频流
    if (video_in_stream_index != -1 && video_need_transcode) 
        if (!add_video_stream()) 
            LOGD("add_video_stream fai");
            return;
        
    
    
    // 为输出文件添加音频流
    if (audio_in_stream_index != -1 && audio_need_transcode) 
        if (!add_audio_stream()) 
            LOGD("add_audio_stream()");
            return;
        
    
    
    av_dump_format(ouFmtCtx, 0, dstPath.c_str(), 1);
    
    // 打开解封装的上下文
    if (!(ouFmtCtx->oformat->flags & AVFMT_NOFILE)) 
        if (avio_open(&ouFmtCtx->pb, dstPath.c_str(), AVIO_FLAG_WRITE) < 0) 
            LOGD("avio_open fail");
            releaseSources();
            return;
        
    
    
    
    // 写入头信息
    /** 遇到问题:avformat_write_header()崩溃
     *  分析原因:没有调用avio_open()函数
     *  解决方案:写成 !(ouFmtCtx->flags & AVFMT_NOFILE)即可
     */
    if ((ret = avformat_write_header(ouFmtCtx, NULL)) < 0) 
        LOGD("avformat_write_header fail %d",ret);
        releaseSources();
        return;
    
    
    // 读取源文件中的音视频数据进行解码
    AVPacket *inPacket = av_packet_alloc();
    while (av_read_frame(inFmtCtx, inPacket) == 0) 
        // 说明读取的视频数据
        if (inPacket->stream_index == video_in_stream_index && video_need_transcode) 
            doDecodeVideo(inPacket);
        

        // 说明读取的音频数据
        if (inPacket->stream_index == audio_in_stream_index && audio_need_transcode) 
            doDecodeAudio(inPacket);
        
        
        // 因为每一次读取的AVpacket的数据大小不一样,所以用完之后要释放
        av_packet_unref(inPacket);
    
    LOGD("文件读取完毕");
    
    // 刷新解码缓冲区,获取缓冲区中数据
    if (video_in_stream_index != -1 && video_need_transcode) 
        doDecodeVideo(NULL);
    
    if (audio_in_stream_index != -1 && audio_need_transcode) 
        doDecodeAudio(NULL);
    
    
    // 写入尾部信息
    if (ouFmtCtx) 
        av_write_trailer(ouFmtCtx);
    
    LOGD("结束");
    
    // 释放资源
    releaseSources();


bool Transcode::openFile()

    if (!srcPath.length()) 
        return false;
    
    int ret = 0;
    // 打开输入文件
    if ((ret = avformat_open_input(&inFmtCtx, srcPath.c_str(), NULL, NULL)) < 0) 
        LOGD("avformat_open_input() fail");
        releaseSources();
        return false;
    
    if ((ret = avformat_find_stream_info(inFmtCtx, NULL)) < 0) 
        LOGD("avformat_find_stream_info fail %d",ret);
        releaseSources();
        return false;
    
    // 输出输入文件信息
    av_dump_format(inFmtCtx,0,srcPath.c_str(),0);
    
    for (int i=0; i<inFmtCtx->nb_streams; i++) 
        AVCodecParameters *codecpar = inFmtCtx->streams[i]->codecpar;
        if(codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_in_stream_index == -1) 
            src_video_id = codecpar->codec_id;
            video_in_stream_index = i;
        
        if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_in_stream_index == -1) 
            src_audio_id = codecpar->codec_id;
            audio_in_stream_index = i;
        
    
    
    // 打开输出流
    if ((ret = avformat_alloc_output_context2(&ouFmtCtx, NULL, NULL, dstPath.c_str())) < 0) 
        LOGD("avformat_alloc_context fail");
        releaseSources();
        return false;
    
    // 检查目标编码方式是否被支持
    if (video_in_stream_index != -1 && av_codec_get_tag(ouFmtCtx->oformat->codec_tag, dst_video_id.par_val.codecId) == 0) 
        LOGD("video tag not found");
        releaseSources();
        return false;
    
    if (audio_in_stream_index != -1 && av_codec_get_tag(ouFmtCtx->oformat->codec_tag,dst_audio_id.par_val.codecId) == 0) 
        LOGD("audio tag not found");
        releaseSources();
        return false;
    
    
    return true;


static int select_sample_rate(AVCodec *codec,int rate)

    int best_rate = 0;
    int deft_rate = 44100;
    bool surport = false;
    const int* p = codec->supported_samplerates;
    while (*p) 
        best_rate = *p;
        if (*p == rate) 
            surport = true;
            break;
        
        p++;
    
    
    if (best_rate != rate && best_rate != 0 && best_rate != deft_rate) 
        return deft_rate;
    
    
    return best_rate;


static enum AVSampleFormat select_sample_format(AVCodec *codec,enum AVSampleFormat fmt)

    enum AVSampleFormat retfmt = AV_SAMPLE_FMT_NONE;
    enum AVSampleFormat deffmt = AV_SAMPLE_FMT_FLTP;
    const enum AVSampleFormat * fmts = codec->sample_fmts;
    while (*fmts != AV_SAMPLE_FMT_NONE) 
        retfmt = *fmts;
        if (retfmt == fmt) 
            break;
        
        fmts++;
    
    
    if (retfmt != fmt && retfmt != AV_SAMPLE_FMT_NONE && retfmt != deffmt) 
        return deffmt;
    
    
    return retfmt;


static int64_t select_channel_layout(AVCodec *codec,int64_t ch_layout)

    int64_t retch = 0;
    int64_t defch = AV_CH_LAYOUT_STEREO;
    const uint64_t * chs = codec->channel_layouts;
    while (*chs) 
        retch = *chs;
        if (retch == ch_layout) 
            break;
        
        chs++;
    
    
    if (retch != ch_layout && retch != AV_SAMPLE_FMT_NONE && retch != defch) 
        return defch;
    
    
    return retch;


static enum AVPixelFormat select_pixel_format(AVCodec *codec,enum AVPixelFormat fmt) 
    enum AVPixelFormat retpixfmt = AV_PIX_FMT_NONE;
    enum AVPixelFormat defaltfmt = AV_PIX_FMT_YUV420P;
    const enum AVPixelFormat *fmts = codec->pix_fmts;
    while (*fmts != AV_PIX_FMT_NONE) 
        retpixfmt = *fmts;
        if (retpixfmt == fmt) 
            break;
        
        fmts++;
    
    
    if (retpixfmt != fmt && retpixfmt != AV_PIX_FMT_NONE && retpixfmt != defaltfmt) 
        return defaltfmt;
    
    
    return retpixfmt;


bool Transcode::add_video_stream()

    AVCodec *codec = avcodec_find_encoder(dst_video_id.par_val.codecId);
    if (codec == NULL) 
        LOGD("video code find fail");
        releaseSources();
        return false;
    
    
    AVStream *stream = avformat_new_stream(ouFmtCtx, NULL);
    if (!stream) 
        LOGD("avformat_new_stream fail");
        return false;
    
    video_ou_stream_index = stream->index;
    
    video_en_ctx = avcodec_alloc_context3(codec);
    if (video_en_ctx == NULL) 
        releaseSources();
        LOGD("video codec not found");
        return false;
    
    
    AVCodecParameters *inpar = inFmtCtx->streams[video_in_stream_index]->codecpar;
    // 设置编码相关参数
    video_en_ctx->codec_id = codec->id;
    // 设置码率
    video_en_ctx->bit_rate = dst_video_bit_rate.copy?inpar->bit_rate:dst_video_bit_rate.par_val.i64_val;
    // 设置视频宽
    video_en_ctx->width = dst_width.copy?inpar->width:dst_width.par_val.i32_val;
    // 设置视频高
    video_en_ctx->height = dst_height.copy?inpar->height:dst_height.par_val.i32_val;
    // 设置帧率
    int fps = dst_fps.copy?inFmtCtx->streams[video_in_stream_index]->r_frame_rate.num:dst_fps.par_val.i32_val;
    video_en_ctx->framerate = (AVRational)fps,1;
    // 设置时间基;
    stream->time_base = (AVRational)1,video_en_ctx->framerate.num;
    video_en_ctx->time_base = stream->time_base;
    // I帧间隔,决定了压缩率
    video_en_ctx->gop_size = 12;
    // 设置视频像素格式
    enum AVPixelFormat want_pix_fmt = dst_pix_fmt.copy?(AVPixelFormat)inpar->format:dst_pix_fmt.par_val.pix_fmt;
    enum AVPixelFormat result_pix_fmt = select_pixel_format(codec, want_pix_fmt);
    if (result_pix_fmt == AV_PIX_FMT_NONE) 
        LOGD("can not surport pix fmt");
        releaseSources();
        return false;
    
    video_en_ctx->pix_fmt = result_pix_fmt;
    // 每组I帧间B帧的最大个数
    if (codec->id == AV_CODEC_ID_MPEG2VIDEO) 
        video_en_ctx->max_b_frames = 2;
    
    /* Needed to avoid using macroblocks in which some coeffs overflow.
    * This does not happen with normal video, it just happens here as
    * the motion of the chroma plane does not match the luma plane. */
    if (codec->id == AV_CODEC_ID_MPEG1VIDEO) 
        video_en_ctx->mb_decision = 2;
    
    
    // 判断是否需要进行格式转换
    if (result_pix_fmt != (enum AVPixelFormat)inpar->format || video_en_ctx->width != inpar->width  || video_en_ctx->height != inpar->height) 
        video_need_convert = true;
    
    
    // 对应一些封装器需要添加这个标记
    /** 遇到问题:生成的mp4或者mov文件没有显示用于预览的小图片
     *  分析原因:之前代编码器器flags标记设置的为AVFMT_GLOBALHEADER(错了),正确的值应该是AV_CODEC_FLAG_GLOBAL_HEADER
     *  解决思路:使用如下代码设置AV_CODEC_FLAG_GLOBAL_HEADER
     */
    if (ouFmtCtx->oformat->flags & AVFMT_GLOBALHEADER) 
//        video_en_ctx->flags |= AVFMT_GLOBALHEADER;
        video_en_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    
    
    // x264编码特有参数
    if (video_en_ctx->codec_id == AV_CODEC_ID_H264) 
        av_opt_set(video_en_ctx->priv_data,"preset","slow",0);
        video_en_ctx->flags |= AV_CODEC_FLAG2_LOCAL_HEADER;
    
    
    int ret = 0;
    if ((ret = avcodec_open2(video_en_ctx, video_en_ctx->codec, NULL)) < 0) 
        LOGD("video encode open2() fail %d",ret);
        releaseSources();
        return false;
    
    
    if((ret = avcodec_parameters_from_context(stream->codecpar, video_en_ctx)) < 0) 
        releaseSources();
        LOGD("video avcodec_parameters_from_context fail");
        return false;
    
    
    return true;


bool Transcode::add_audio_stream()

    if (ouFmtCtx == NULL) 
        LOGD("audio outformat NULL");
        releaseSources();
        return false;
    
    
    // 添加一个音频流
    AVStream *stream = avformat_new_stream(ouFmtCtx, NULL);
    if (!stream) 
        LOGD("avformat_new_stream fail");
        return false;
    
    audio_ou_stream_index = stream->index;
    
    AVCodecParameters *incodecpar = inFmtCtx->streams[audio_in_stream_index]->codecpar;
    AVCodec *codec = avcodec_find_encoder(dst_audio_id.par_val.codecId);
    AVCodecContext *ctx = avcodec_alloc_context3(codec);
    if (ctx == NULL) 
        LOGD("audio codec ctx NULL");
        releaseSources();
        return false;
    
    // 设置音频编码参数
    // 采样率
    int want_sample_rate = dst_sample_rate.copy?incodecpar->sample_rate:dst_sample_rate.par_val.i32_val;
    int relt_sample_rate = select_sample_rate(codec, want_sample_rate);
    if (relt_sample_rate == 0) 
        LOGD("cannot surpot sample_rate");
        releaseSources();
        return false;
    
    ctx->sample_rate = relt_sample_rate;
    // 采样格式
    enum AVSampleFormat want_sample_fmt = dst_sample_fmt.copy?(enum AVSampleFormat)incodecpar->format:dst_sample_fmt.par_val.smp_fmt;
    enum AVSampleFormat relt_sample_fmt = select_sample_format(codec, want_sample_fmt);
    if (relt_sample_fmt == AV_SAMPLE_FMT_NONE) 
        LOGD("cannot surpot sample_fmt");
        releaseSources();
        return false;
    
    ctx->sample_fmt  = relt_sample_fmt;
    // 声道格式
    int64_t want_ch = dst_channel_layout.copy?incodecpar->channel_layout:dst_channel_layout.par_val.i64_val;
    int64_t relt_ch = select_channel_layout(codec, want_ch);
    if (!relt_ch) 
        LOGD("cannot surpot channel_layout");
        releaseSources();
        return false;
    
    ctx->channel_layout = relt_ch;
    // 声道数
    ctx->channels = av_get_channel_layout_nb_channels(ctx->channel_layout);
    // 编码后的码率
    ctx->bit_rate = dst_audio_bit_rate.copy?incodecpar->bit_rate:dst_audio_bit_rate.par_val.i32_val;
    // frame_size 这里不用设置,调用avcodec_open2()函数后会根据编码类型自动设置
//    ctx->frame_size = incodecpar->frame_size;
    // 设置时间基
    ctx->time_base = (AVRational)1,ctx->sample_rate;
    stream->time_base = ctx->time_base;
    
    // 保存
    audio_en_ctx = ctx;
    
    // 设置编码的相关标记,这样进行封装的时候不会漏掉一些元信息
    if (ouFmtCtx->oformat->flags & AVFMT_GLOBALHEADER) 
        audio_en_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    
    
    // 初始化编码器
    if (avcodec_open2(ctx, codec, NULL) < 0) 
        LOGD("audio avcodec_open2() fail");
        releaseSources();
        return false;
    
    
    if (audio_en_ctx->frame_size != incodecpar->frame_size || relt_sample_fmt != incodecpar->format || relt_ch != incodecpar->channel_layout || relt_sample_rate != incodecpar->sample_rate) 
        audio_need_convert = true;
    
    
    int ret = 0;
    // 将编码信息设置到音频流中
    if ((ret = avcodec_parameters_from_context(stream->codecpar, ctx)) < 0) 
        LOGD("audio copy audio stream fail");
        releaseSources();
        return false;
    
    
    // 初始化重采样
    if (audio_need_convert) 
        swr_ctx = swr_alloc_set_opts(NULL, ctx->channel_layout, ctx->sample_fmt, ctx->sample_rate, incodecpar->channel_layout, (enum AVSampleFormat)incodecpar->format, incodecpar->sample_rate, 0, NULL);
        if ((ret = swr_init(swr_ctx)) < 0) 
            LOGD("swr_alloc_set_opts() fail %d",ret);
            releaseSources();
            return false;
        
    
    
    return true;


void Transcode::doDecodeVideo(AVPacket *inpacket)

    if (src_video_id == AV_CODEC_ID_NONE) 
        LOGD("src has not video");
        return;
    
    
    int ret = 0;
    if (video_de_ctx == NULL) 
        AVCodec *codec = avcodec_find_decoder(src_video_id);
        video_de_ctx = avcodec_alloc_context3(codec);
        if (video_de_ctx == NULL) 
            LOGD("video decodec context create fail");
            releaseSources();
            return;
        
        
        // 设置解码参数;这里是直接从AVCodecParameters中拷贝
        AVCodecParameters *codecpar = inFmtCtx->streams[video_in_stream_index]->codecpar;
        if ((ret = avcodec_parameters_to_context(video_de_ctx, codecpar)) < 0) 
           LOGD("avcodec_parameters_to_context fail %d",ret);
           releaseSources();
           return;
        
        // 初始化解码器
        if (avcodec_open2(video_de_ctx, codec, NULL) < 0) 
           releaseSources();
           LOGD("video avcodec_open2 fail");
           return;
        
    
    
    if (video_de_frame == NULL) 
        video_de_frame = av_frame_alloc();
    
    
    if ((ret = avcodec_send_packet(video_de_ctx,inpacket)) < 0) 
        LOGD("video avcodec_send_packet fail %s",av_err2str(ret));
        releaseSources();
        return;
    
    while (true) 
        // 从解码缓冲区接收解码后的数据
        if((ret = avcodec_receive_frame(video_de_ctx, video_de_frame)) < 0) 
            if (ret == AVERROR_EOF) 
                // 解码缓冲区结束了,那么也要flush编码缓冲区
                doEncodeVideo(NULL);
            
            break;
        
        
        // 如果需要格式转换 则在这里进行格式转换
        if (video_en_frame == NULL) 
            video_en_frame = get_video_frame(video_en_ctx->pix_fmt, video_en_ctx->width, video_en_ctx->height);
            if (video_en_frame == NULL) 
                LOGD("cannot create vdioe fram");
                releaseSources();
                return;
            
        
        
        if (video_need_convert) 
            // 分配像素转换上下文
            if (sws_ctx == NULL) 
                AVCodecParameters *inpar = inFmtCtx->streams[video_in_stream_index]->codecpar;
                int dst_width = video_en_ctx->width;
                int dst_height = video_en_ctx->height;
                enum AVPixelFormat dst_pix_fmt = video_en_ctx->pix_fmt;
                sws_ctx = sws_getContext(inpar->width, inpar->height, (enum AVPixelFormat)inpar->format,
                                         dst_width, dst_height, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
                if (!sws_ctx) 
                    LOGD("sws_getContext fail");
                    releaseSources();
                    return;
                
            
            
            // 进行转换;返回目标height
            ret = sws_scale(sws_ctx, video_de_frame->data, video_de_frame->linesize, 0, video_de_frame->height, video_en_frame->data, video_en_frame->linesize);
            if (ret < 0) 
                LOGD("sws_scale fail");
                releaseSources();
                return;
            
         else 
            // 直接拷贝即可
            av_frame_copy(video_en_frame, video_de_frame);
        
        
        
        // 数据拷贝到用于编码的
        /** 遇到问题:warning, too many B-frames in a row
         *  分析原因:之前下面doEncodeVideo()函数传递的参数为video_de_frame,这个是解码之后直接得到的AVFrame,它本身就包含了
         *  相关和编码不符合的参数
         *  解决方案:重新创建一个AVFrame,将解码后得到的AVFrame的data数据拷贝过去。然后用这个作为编码的AVFrame
         */
        video_en_frame->pts = video_pts;
        video_pts++;
        doEncodeVideo(video_en_frame);
    


/** 遇到问题:生成的MOV和MP4文件可以播放,但是看不到小的预览图
 */
void Transcode::doEncodeVideo(AVFrame *frame)

    int ret = 0;
    // 获得了解码后的数据,然后进行重新编码
    if ((ret = avcodec_send_frame(video_en_ctx, frame)) < 0) 
        LOGD("video encode avcodec_send_frame fail");
        return;
    
    
    while (true) 
        AVPacket *pkt = av_packet_alloc();
        if((ret = avcodec_receive_packet(video_en_ctx, pkt)) < 0) 
            break;
        

        // 将编码后的数据写入文件
        /** 遇到问题:生成的文件帧率不对
         *  分析原因:当调用avformat_write_header()后,内部会改变输出流的time_base参数,而这里没有重新调整packet的pts,dts,duration的值
         *  解决方案:使用av_packet_rescale_ts重新调整packet的pts,dts,duration的值
         */
        AVStream *stream = ouFmtCtx->streams[video_ou_stream_index];
        av_packet_rescale_ts(pkt, video_en_ctx->time_base, stream->time_base);
        pkt->stream_index = video_ou_stream_index;

        doWrite(pkt, true);
    


void Transcode::doDecodeAudio(AVPacket *packet)

    int ret = 0;
    if (audio_de_ctx == NULL) 
        AVCodec *codec = avcodec_find_decoder(src_audio_id);
        if (!codec) 
            LOGD("audio decoder not found");
            releaseSources();
            return;
        
        audio_de_ctx = avcodec_alloc_context3(codec);
        if (!audio_de_ctx) 
            LOGD("audio decodec_ctx fail");
            releaseSources();
            return;
        
        
        // 设置音频解码上下文参数;这里来自于源文件的拷贝
        if (avcodec_parameters_to_context(audio_de_ctx, inFmtCtx->streams[audio_in_stream_index]->codecpar) < 0) 
            LOGD("audio set decodec ctx fail");
            releaseSources();
            return;
        
        
        if ((avcodec_open2(audio_de_ctx, codec, NULL)) < 0) 
            LOGD("audio avcodec_open2 fail");
            releaseSources();
            return;
        
    
    
    // 创建解码用的AVFrame
    if (!audio_de_frame) 
        audio_de_frame = av_frame_alloc();
    

    if ((ret = avcodec_send_packet(audio_de_ctx, packet)) < 0) 
        LOGD("audio avcodec_send_packet fail %d",ret);
        releaseSources();
        return;
    
    
    while (true) 
        if ((ret = avcodec_receive_frame(audio_de_ctx, audio_de_frame)) < 0) 
            if (ret == AVERROR_EOF) 
                LOGD("audio decode finish");
                // 解码缓冲区结束了,那么也要flush编码缓冲区
                doEncodeAudio(NULL);
            
            break;
        
        
        // 创建编码器用的AVFrame
        if (audio_en_frame == NULL) 
            audio_en_frame = get_audio_frame(audio_en_ctx->sample_fmt, audio_en_ctx->channel_layout, audio_en_ctx->sample_rate, audio_en_ctx->frame_size);
            if (audio_en_frame == NULL) 
                LOGD("can not create audio frame ");
                releaseSources();
                return;
            
        
        
        // 解码成功,然后再重新进行编码;
        // 为了避免数据污染,所以这里只需要要将解码后得到的AVFrame中data数据拷贝到编码用的audio_en_frame中,解码后的其它数据则丢弃
        int pts_num = 0;
        if (audio_need_convert) 
            int dst_nb_samples = (int)av_rescale_rnd(swr_get_delay(swr_ctx, audio_de_frame->sample_rate)+audio_de_frame->nb_samples, audio_en_ctx->sample_rate, audio_de_frame->sample_rate, AV_ROUND_UP);
            if (dst_nb_samples != audio_en_frame->nb_samples) 
                av_frame_free(&audio_en_frame);
                audio_en_frame = get_audio_frame(audio_en_ctx->sample_fmt, audio_en_ctx->channel_layout, audio_en_ctx->sample_rate, dst_nb_samples);
                if (audio_en_frame == NULL) 
                    LOGD("can not create audio frame2 ");
                    releaseSources();
                    return;
                
            
            /** 遇到问题:当音频编码方式不一致时转码后无声音
             *  分析原因:因为每个编码方式对应的AVFrame中的nb_samples不一样,所以再进行编码前要进行AVFrame的转换
             *  解决方案:进行编码前先转换
             */
            // 进行转换
            ret = swr_convert(swr_ctx, audio_en_frame->data, dst_nb_samples, (const uint8_t**)audio_de_frame->data, audio_de_frame->nb_samples);
            if (ret < 0) 
                LOGD("swr_convert() fail %d",ret);
                releaseSources();
                doEncodeAudio(NULL);
                break;
            
            pts_num = ret;
         else 
            av_frame_copy(audio_en_frame, audio_de_frame);
            pts_num = audio_en_frame->nb_samples;
        
        
        
        /** 遇到问题:得到的文件播放时音画不同步
         *  分析原因:由于音频的AVFrame的pts没有设置对;pts是基于AVCodecContext的时间基的时间,所以pts的设置公式:
         *  音频:pts  = (timebase.den/sample_rate)*nb_samples*index;  index为当前第几个音频AVFrame(索引从0开始),nb_samples为每个AVFrame中的采样数
         *  视频:pts = (timebase.den/fps)*index;
         *  解决方案:按照如下代码方式设置AVFrame的pts
        */
//        audio_en_frame->pts = audio_pts++;    // 造成了音画不同步的问题
        audio_en_frame->pts = av_rescale_q(audio_pts, (AVRational)1,audio_en_frame->sample_rate, audio_de_ctx->time_base);
        audio_pts += pts_num;
        doEncodeAudio(audio_en_frame);
    


void Transcode::doEncodeAudio(AVFrame *frame)

    int ret = 0;
    if ((ret = avcodec_send_frame(audio_en_ctx, frame)) < 0) 
        LOGD("audio avcodec_send_frame fail %d",ret);
        releaseSources();
    
    
    
    while (true) 
        AVPacket *pkt = av_packet_alloc();
        if ((ret = avcodec_receive_packet(audio_en_ctx, pkt)) < 0) 
            break;
        
        
        // 说明编码得到了一个完整的帧
        // 因为调用avformat_write_header()函数后内部会改变ouFmtCtx->streams[audio_ou_stream_index]->time_base的值,而
        // pkt是按照audio_en_ctx->time_base来进行编码的,所以这里写入文件之前要进行时间的转换
        AVStream *stream = ouFmtCtx->streams[audio_ou_stream_index];
        av_packet_rescale_ts(pkt, audio_en_ctx->time_base, stream->time_base);
        pkt->stream_index = audio_ou_stream_index;
        
        doWrite(pkt, false);
    


void Transcode::doWrite(AVPacket *packet,bool isVideo)

    if (!packet) 
        LOGD("packet is null");
        return;
    
    
    AVPacket *a_pkt = NULL;
    AVPacket *v_pkt = NULL;
    AVPacket *w_pkt = NULL;
    if (isVideo) 
        v_pkt = packet;
     else 
        a_pkt = packet;
    
    
    // 为了精确的比较时间,先缓存一点点数据
    if (last_video_pts == 0 && isVideo && videoCache.size() == 0) 
        videoCache.push_back(packet);
        last_video_pts = packet->pts;
        LOGD(" 还没有 %s 数据",audio_pts==0?"audio":"video");
        return;
    
    
    if (last_audio_pts == 0 && !isVideo && audioCache.size() == 0) 
        audioCache.push_back(packet);
        last_audio_pts = packet->pts;
        LOGD(" 还没有 %s 数据",video_pts==0?"video":"audio");
        return;
    
    
    if (videoCache.size() > 0)     // 里面有缓存了数据
        v_pkt = videoCache.front();
        if (isVideo) 
            videoCache.push_back(packet);
        
    
    
    if (audioCache.size() > 0)     // 里面有缓存了数据
        a_pkt = audioCache.front();
        if (!isVideo) 
            audioCache.push_back(packet);
        
    
    
    if (v_pkt) 
        last_video_pts = v_pkt->pts;
    
    
    if (a_pkt) 
        last_audio_pts = a_pkt->pts;
    
    
    
    if (a_pkt && v_pkt)    // 两个都有 则进行时间的比较
        AVStream *a_stream = ouFmtCtx->streams[audio_ou_stream_index];
        AVStream *v_stream = ouFmtCtx->streams[video_ou_stream_index];
        if (av_compare_ts(last_audio_pts,a_stream->time_base,last_video_pts,v_stream->time_base) <= 0)  // 视频在后
            w_pkt = a_pkt;
            if (audioCache.size() > 0) 
                vector<AVPacket*>::iterator begin = audioCache.begin();
                audioCache.erase(begin);
            
         else 
            w_pkt = v_pkt;
            if (videoCache.size() > 0) 
                vector<AVPacket*>::iterator begin = videoCache.begin();
                videoCache.erase(begin);
            
        
        
     else if (a_pkt) 
        w_pkt = a_pkt;
        if (audioCache.size() > 0) 
            vector<AVPacket*>::iterator begin = audioCache.begin();
            audioCache.erase(begin);
        
     else if (v_pkt) 
        w_pkt = v_pkt;
        if (videoCache.size() > 0) 
            vector<AVPacket*>::iterator begin = videoCache.begin();
            videoCache.erase(begin);
        
    
    
    static int sum = 0;
    if (!isVideo) 
        sum++;
    
    LOGD("index %d pts %d dts %d du %d a_size %d v_size %d sum %d",w_pkt->stream_index,w_pkt->pts,w_pkt->dts,w_pkt->duration,audioCache.size(),videoCache.size(),sum);
    if ((av_interleaved_write_frame(ouFmtCtx, w_pkt)) < 0) 
        LOGD("av_write_frame fail");
    
    
    av_packet_unref(w_pkt);


AVFrame* Transcode::get_audio_frame(enum AVSampleFormat smpfmt,int64_t ch_layout,int sample_rate,int nb_samples)

    AVFrame * audio_en_frame = av_frame_alloc();
    // 根据采样格式,采样率,声道类型以及采样数分配一个AVFrame
    audio_en_frame->sample_rate = sample_rate;
    audio_en_frame->format = smpfmt;
    audio_en_frame->channel_layout = ch_layout;
    audio_en_frame->nb_samples = nb_samples;
    int ret = 0;
    if ((ret = av_frame_get_buffer(audio_en_frame, 0)) < 0) 
        LOGD("audio get frame buffer fail %d",ret);
        return NULL;
    

    if ((ret =  av_frame_make_writable(audio_en_frame)) < 0) 
        LOGD("audio av_frame_make_writable fail %d",ret);
        return NULL;
    
    
    return audio_en_frame;


AVFrame* Transcode::get_video_frame(enum AVPixelFormat pixfmt,int width,int height)

    AVFrame *video_en_frame = av_frame_alloc();
    video_en_frame->format = pixfmt;
    video_en_frame->width = width;
    video_en_frame->height = height;
    int ret = 0;
    if ((ret = av_frame_get_buffer(video_en_frame, 0)) < 0) 
        LOGD("video get frame buffer fail %d",ret);
        return NULL;
    

    if ((ret =  av_frame_make_writable(video_en_frame)) < 0) 
        LOGD("video av_frame_make_writable fail %d",ret);
        return NULL;
    
    
    return video_en_frame;


void Transcode::releaseSources()

    if (inFmtCtx) 
        avformat_close_input(&inFmtCtx);
        inFmtCtx = NULL;
    
    
    if (ouFmtCtx) 
        avformat_free_context(ouFmtCtx);
        ouFmtCtx = NULL;
    
    
    if (video_en_ctx) 
        avcodec_free_context(&video_en_ctx);
        video_en_ctx = NULL;
    
    
    if (audio_en_ctx) 
        avcodec_free_context(&audio_en_ctx);
        audio_en_ctx = NULL;
    
    
    if (video_de_ctx) 
        avcodec_free_context(&video_de_ctx);
        video_de_ctx = NULL;
    
    
    if (audio_de_ctx) 
        avcodec_free_context(&audio_de_ctx);
        audio_de_ctx = NULL;
    
    
    if (video_de_frame) 
        av_frame_free(&video_de_frame);
        video_de_frame = NULL;
    
    
    if (audio_de_frame) 
        av_frame_free(&audio_de_frame);
    
    
    if (video_en_frame) 
        av_frame_free(&video_en_frame);
        video_en_frame = NULL;
    
    
    if (audio_en_frame) 
        av_frame_free(&audio_en_frame);
        audio_en_frame = NULL;
    

以上是关于FFmpeg音频的编码流程详解及demo的主要内容,如果未能解决你的问题,请参考以下文章

Qt之FFMpeg 实现视频解码编码转码流程详解

FFmpeg 音视频转码详细流程

ffmpeg音视频加速

ffmpeg转码flv到avi《转》

FFmpeg 视频处理命令备忘

Qt与FFmpeg联合开发指南——编码:完善功能和基础封装