ffmpeg不依赖AVFilter的转码 二

ffmpeg版本4.3

不依赖AVFilter,只转码了视频格式和封装格式的转码程序

源视频: 封装格式:FLV,视频编码:H264,音频编码:AAC
转码后: 封装格式:MP4,视频编码:mpeg1video,音频编码:mp3
涉及:复用,解复用,解码,编码,音频时间戳,视频时间戳,重采样,音频AVAudioFifo
在这里插入图片描述

源视频信息:
在这里插入图片描述

#define OUTPUT_CHANNELS 2
#define OUTPUT_BIT_RATE 96000
static int nVideoStream_idx_in = -1;
static int nAudioStream_idx_in = -1;
static int nVideoStream_idx_out = -1;
static int nAudioStream_idx_out = -1;
static int64_t pts = 0;
static int64_t dts = 0;
static int init_resampler(AVCodecContext *input_codec_context,
    AVCodecContext *output_codec_context,
    SwrContext **resample_context){
    int error;

    *resample_context = swr_alloc_set_opts(NULL,
        av_get_default_channel_layout(output_codec_context->channels), output_codec_context->sample_fmt, output_codec_context->sample_rate,
        av_get_default_channel_layout(input_codec_context->channels), input_codec_context->sample_fmt, input_codec_context->sample_rate,
        0, nullptr);
    if (!*resample_context) {
        fprintf(stderr, "Could not allocate resample context\n");
        return AVERROR(ENOMEM);
    }

    if ((error = swr_init(*resample_context)) < 0) {
        fprintf(stderr, "Could not open resample context\n");
        swr_free(resample_context);
        return error;
    }
    return 0;
}

static void openVidoEncoder_mpg1video(int width, int height,AVFormatContext *ofmt_ctx,AVCodecContext** enc_ctx)
{
    int nRet = 0;
    AVStream *stream = nullptr;
    // 3.1 查找编码器AVCodec,本例使用AV_CODEC_ID_MPEG1VIDEO
    AVCodec *codec_mpeg1 = avcodec_find_encoder(AV_CODEC_ID_MPEG1VIDEO);
    if(!codec_mpeg1)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    // 2. 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
    if (!(stream = avformat_new_stream(ofmt_ctx, codec_mpeg1)))
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    nVideoStream_idx_out = stream->index;
    (*enc_ctx) = avcodec_alloc_context3(codec_mpeg1);
    if ((* enc_ctx) == nullptr)
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    // AVCodecContext初始化:配置图像/声音相关属性
    /* put sample parameters */
    (*enc_ctx)->bit_rate = 100000;
    /* resolution must be a multiple of two */
    (*enc_ctx)->width = width;
    (*enc_ctx)->height = height;
    /* frames per second */
    (*enc_ctx)->time_base = (AVRational){1,25};
    (*enc_ctx)->framerate = (AVRational){25,1};
    /* emit one intra frame every ten frames
     * check frame pict_type before passing frame
     * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
     * then gop_size is ignored and the output of encoder
     * will always be I frame irrespective to gop_size
     */
    (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
    (*enc_ctx)->codec_id = codec_mpeg1->id;
    (*enc_ctx)->codec_type = AVMEDIA_TYPE_VIDEO;
    (*enc_ctx)->gop_size = 20;
    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
        ofmt_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    if(avcodec_open2((*enc_ctx),codec_mpeg1,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    //设置输出流codecpar
    nRet = avcodec_parameters_from_context(stream->codecpar,(*enc_ctx));
    if(nRet < 0)
    {
        printf("avcodec_parameters_to_context fail.");
        return ;
    }
    return;
}

static void openAudioEncoder_mp3( AVCodecContext** enc_ctx,
                                  AVFormatContext **ofmt_ctx,
                                  AVCodecContext* pInCodecCtx_a)
{
    AVStream *stream = nullptr;
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if(!codec)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    if (!(stream = avformat_new_stream(*ofmt_ctx, codec)))
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    nAudioStream_idx_out = stream->index;
    (*enc_ctx) = avcodec_alloc_context3(codec);
    if ((* enc_ctx) == nullptr)
    {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    /**
    * Set the basic encoder parameters.
    * The input file's sample rate is used to avoid a sample rate conversion.
    */
    (*enc_ctx)->codec_id = AV_CODEC_ID_MP3;
    (*enc_ctx)->codec_type = AVMEDIA_TYPE_AUDIO;
    (*enc_ctx)->channels = OUTPUT_CHANNELS;
    (*enc_ctx)->channel_layout = av_get_default_channel_layout(OUTPUT_CHANNELS);
    //samples per second
    (*enc_ctx)->sample_rate = pInCodecCtx_a->sample_rate;
    (*enc_ctx)->sample_fmt = codec->sample_fmts[0];
    (*enc_ctx)->bit_rate = OUTPUT_BIT_RATE;

    /** Allow the use of the experimental AAC encoder */
    (*enc_ctx)->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;

    /** Set the sample rate for the container. */
    stream->time_base.den = pInCodecCtx_a->sample_rate;
    stream->time_base.num = 1;
    /**
    * Some container formats (like MP4) require global headers to be present
    * Mark the encoder so that it behaves accordingly.
    */
    if(avcodec_open2((*enc_ctx),codec,nullptr) < 0)
    {
        printf("avcodec_open2 fail.\n");
    }
    stream->codecpar->codec_tag = 0;
    if ((*ofmt_ctx)->oformat->flags & AVFMT_GLOBALHEADER)
    {
        (*enc_ctx)->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
    //将AVCodecContext的成员复制到AVCodecParameters结构体。前后两行不能调换顺序
    avcodec_parameters_from_context(stream->codecpar, (* enc_ctx));
    return;
}
static void open_input(const char* pInFileName,
                       AVFormatContext **pInFmtCtx,
                       AVCodecContext** pInCodecCtx_v,
                       AVCodecContext** pInCodecCtx_a)
{
    int nRet = 0;
    AVDictionary *pDic = nullptr;
    // 1. 打开视频文件:读取文件头
    nRet = avformat_open_input(pInFmtCtx,pInFileName,nullptr,&pDic);
    if( nRet < 0)
    {
        printf("Could not open input file.");
        return;
    }
    // 2. 搜索流信息:读取一段视频文件数据,尝试解码,将取到的流信息填入ifmt_ctx.streams
    // ifmt_ctx.streams是一个指针数组,数组大小是ifmt_ctx.nb_streams
    avformat_find_stream_info(*pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");
    av_dump_format(*pInFmtCtx, 0, pInFileName, 0);
    printf("======================================\n");
    for (int i = 0; i < (*pInFmtCtx)->nb_streams; i++)
    {
        if((*pInFmtCtx)->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            nVideoStream_idx_in = i;
            AVStream * in_stream_v = (*pInFmtCtx)->streams[i];
            // 3.1 获取解码器AVCodec
            AVCodec *pInCodec_v = avcodec_find_decoder(in_stream_v->codecpar->codec_id);
            if(nullptr == pInCodec_v)
            {
                printf("inpout video avcodec_find_decoder fail.");
                return;
            }
            // 3.2 AVCodecContext初始化:分配结构体,使用AVCodec初始化AVCodecContext相应成员为默认值
            *pInCodecCtx_v = avcodec_alloc_context3(pInCodec_v);
            // 3.3 AVCodecContext初始化:使用codec参数codecpar初始化AVCodecContext相应成员
            nRet = avcodec_parameters_to_context(*pInCodecCtx_v, in_stream_v->codecpar);
            if(nRet < 0)
            {
                printf("avcodec_parameters_to_context fail.");
                return;
            }
            /* Reencode video & audio and remux subtitles etc. */
            // 音频流视频流需要重新编码,字幕流只需要重新封装
            //打开视频解码器
            if(avcodec_open2(*pInCodecCtx_v, pInCodec_v, nullptr) < 0)
            {
                printf("Error: Can't open codec!\n");
                return ;
            }
            if(nRet < 0)
            {
                printf("avcodec_parameters_from_context fail.");
                return;
            }
            printf("width = %d\n", (*pInCodecCtx_v)->width);
            printf("height = %d\n",(*pInCodecCtx_v)->height);
            if(nRet < 0)
            {
                printf("inpout video avcodec_parameters_to_context fail.");
                return;
            }
        }
        if((*pInFmtCtx)->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            nAudioStream_idx_in = i;
            AVStream * in_stream_a = (*pInFmtCtx)->streams[i];
            AVCodec *pInCodec_a = avcodec_find_decoder(in_stream_a->codecpar->codec_id);
            if(nullptr == pInCodec_a)
            {
                printf("inpout audio avcodec_find_decoder fail.\n");
                return;
            }
            *pInCodecCtx_a = avcodec_alloc_context3(pInCodec_a);

            nRet = avcodec_parameters_to_context(*pInCodecCtx_a, in_stream_a->codecpar);
            if(nRet < 0)
            {
                printf("avcodec_parameters_to_context fail.\n");
                return;
            }
            if(nRet < 0)
            {
                printf("avcodec_parameters_from_context fail.\n");
                return;
            }
            //打开音频解码器
            if(avcodec_open2(*pInCodecCtx_a, pInCodec_a, nullptr) < 0)
            {
                printf("Error: Can't open codec!\n");
                return ;
            }
        }
    }
}

static void open_output(const char* pOutFileName,
                        AVCodecContext* pInCodecCtx_v,
                        AVCodecContext* pInCodecCtx_a,
                        AVFormatContext** ofmt_ctx,
                        AVCodecContext** encVideoCtx,
                        AVCodecContext** encAudioctx)
{
    //Output
    // 1. 分配输出ctx
    avformat_alloc_output_context2(ofmt_ctx, nullptr, nullptr, pOutFileName);
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        return ;
    }
    // 每路音频流/视频流一个AVCodecContext encVideoCtx
    openVidoEncoder_mpg1video(pInCodecCtx_v->width,pInCodecCtx_v->height,*ofmt_ctx,encVideoCtx);
    // 每路音频流/视频流一个AVCodecContext encAudioctx
    openAudioEncoder_mp3(encAudioctx,ofmt_ctx,pInCodecCtx_a);

    //Open output file
    if (!((*ofmt_ctx)->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&(*ofmt_ctx)->pb, pOutFileName, AVIO_FLAG_WRITE) < 0)
        {
            printf( "Could not open output file '%s'", pOutFileName);
            return ;
        }
    }
    printf("==========Output Information==========\n");
    av_dump_format(*ofmt_ctx, 0, pOutFileName, 1);
    printf("======================================\n");
}
static int encode_audio_frame(AVFrame *output_frame,int nbsamples,AVFormatContext *ofmt_ctx,AVCodecContext *encAudioctx)
{
    AVPacket newpkt;
    av_init_packet(&newpkt);
    //送原始数据给编码器进行编码
    int nRet = avcodec_send_frame(encAudioctx,output_frame);
    if(nRet < 0)
    {
        printf("avcodec_send_frame fail.\n");
        return -1;
    }
    //从编码器获取编号的数据
    while(nRet >= 0)
    {
        nRet = avcodec_receive_packet(encAudioctx,&newpkt);
        //返回值为-11意味着需要新的输入数据才能返回新的输出。
        //AVERROR_EOF表示读完了
        //在解码或编码开始时,编解码器可能会接收多个输入帧/数据包而不返回帧,直到其内部缓冲区被填充为止。
        if( AVERROR(EAGAIN) == nRet || AVERROR_EOF == nRet)
        {
            av_packet_unref(&newpkt);
            break;
        }
        else if(nRet < 0 )
        {
            printf("avcodec_receive_packet fail.\n");
            return -1;
        }
        newpkt.stream_index = nAudioStream_idx_out;
        if (newpkt.pts == AV_NOPTS_VALUE){
            pts += nbsamples;
            newpkt.pts = pts;
        }
        //写入(Write)
        printf("audio Write 1 Packet. size:%5d\tpts:%lld\n",newpkt.size,newpkt.pts);
        nRet = av_interleaved_write_frame(ofmt_ctx, &newpkt);
        if (nRet < 0)
        {
            printf( "Error muxing packet\n");
            av_packet_unref(&newpkt);
            break;
        }
        av_packet_unref(&newpkt);
    }
    return 0;
}
static int encode_video_frame(AVFrame *pFrame,
                              AVFormatContext *pInFmtCtx,
                              AVFormatContext *ofmt_ctx,
                              AVCodecContext *encVideoCtx)
{
    int nRet = 0;

    AVStream *in_stream = nullptr;
    AVStream *out_stream= nullptr;
    AVPacket newpkt;
    av_init_packet(&newpkt);
    //送原始数据给编码器进行编码
// 关于:avcodec_send_frame()与avcodec_receive_packet()
// 1. 按pts递增的顺序向编码器送入原始帧frame,编码器按dts递增的顺序输出编码帧packet,实际上编码器关注输入frame的pts不关注其dts,它只管依次处理收到的frame,按需缓冲和编码
// 2. avcodec_receive_packet()输出packet时,会设置packet.dts,从0开始,每次输出的packet的dts加1,这是视频层的dts,用户写输出前应将其转换为容器层的dts
// 3. avcodec_receive_packet()输出packet时,packet.pts拷贝自对应的frame.pts,这是视频层的pts,用户写输出前应将其转换为容器层的pts
// 4. avcodec_send_frame()发送NULL frame时,编码器进入flush模式
// 5. avcodec_send_frame()发送第一个NULL会返回成功,后续的NULL会返回AVERROR_EOF
// 6. avcodec_send_frame()多次发送NULL并不会导致编码器中缓存的帧丢失,使用avcodec_flush_buffers()可以立即丢掉编码器中缓存帧。因此编码完毕时应使用avcodec_send_frame(NULL)
//    来取完缓存的帧,而SEEK操作或切换流时应调用avcodec_flush_buffers()来直接丢弃缓存帧。
// 7. 编码器通常的冲洗方法:调用一次avcodec_send_frame(NULL)(返回成功),然后不停调用avcodec_receive_packet()直到其返回AVERROR_EOF,取出所有缓存帧,avcodec_receive_packet()返回
//    AVERROR_EOF这一次是没有有效数据的,仅仅获取到一个结束标志。
// 8. 对音频来说,如果AV_CODEC_CAP_VARIABLE_FRAME_SIZE(在AVCodecContext.codec.capabilities变量中,只读)标志有效,表示编码器支持可变尺寸音频帧,送入编码器的音频帧可以包含
//    任意数量的采样点。如果此标志无效,则每一个音频帧的采样点数目(frame->nb_samples)必须等于编码器设定的音频帧尺寸(avctx->frame_size),最后一帧除外,最后一帧音频帧采样点数
//    可以小于avctx->frame_size
    nRet = avcodec_send_frame(encVideoCtx,pFrame);
    if (nRet == AVERROR_EOF)
    {
        //av_log(NULL, AV_LOG_INFO, "avcodec_send_frame() encoder flushed\n");
    }
    if(nRet < 0)
    {
        printf("avcodec_send_frame fail.\n");
        return -1;
    }
    //从编码器获取编号的数据
    while(nRet >= 0)
    {
        nRet = avcodec_receive_packet(encVideoCtx,&newpkt);
        //返回值为-11意味着需要新的输入数据才能返回新的输出。
        //在解码或编码开始时,编解码器可能会接收多个输入帧/数据包而不返回帧,直到其内部缓冲区被填充为止。
        if( AVERROR(EAGAIN) == nRet || AVERROR_EOF == nRet)
        {
            av_packet_unref(&newpkt);
            break;
        }
        else if(nRet < 0 )
        {
            printf("avcodec_receive_packet fail.\n");
            return -1;
        }
        //转换PTS/DTS(Convert PTS/DTS)
        in_stream  = pInFmtCtx->streams[nVideoStream_idx_in];
        out_stream = ofmt_ctx->streams[nVideoStream_idx_out];
        newpkt.pts = av_rescale_q_rnd(newpkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        newpkt.dts = av_rescale_q_rnd(newpkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        newpkt.duration = av_rescale_q(newpkt.duration, in_stream->time_base, out_stream->time_base);
        newpkt.pos = -1;
        dts = newpkt.dts;
        newpkt.stream_index = nVideoStream_idx_out;
        printf("video Write 1 Packet. size:%5d\tpts:%lld\n",newpkt.size,newpkt.pts);
        //写入(Write)
        nRet = av_interleaved_write_frame(ofmt_ctx, &newpkt);
        if (nRet < 0)
        {
            printf( "Error muxing packet\n");
            av_packet_unref(&newpkt);
            break;
        }
        av_packet_unref(&newpkt);
    }

    return 0;
}
static void test()
{
    int nRet = 0;
    const char *pInFileName = "D:/videos/new_killer.flv";
    const char *pOutFileName = "D:/videos/_new_killer.mp4"; 
    AVFormatContext *pInFmtCtx = nullptr;
    AVFormatContext *ofmt_ctx = nullptr;
    AVCodecContext* encVideoCtx = nullptr;
    AVCodecContext* encAudioctx = nullptr;
    AVCodecContext* pInCodecCtx_v = nullptr;
    AVCodecContext* pInCodecCtx_a = nullptr;
    SwrContext *resample_context = nullptr;
    AVAudioFifo* audiofifo = nullptr;
    int got_picture = 0;
    int frame_size = 0;
    AVPacket pkt;
    av_init_packet(&pkt);
    AVFrame *pFrame = av_frame_alloc();

    open_input(pInFileName,
               &pInFmtCtx,
               &pInCodecCtx_v,
               &pInCodecCtx_a);

    open_output(pOutFileName,
                pInCodecCtx_v,
                pInCodecCtx_a,
                &ofmt_ctx,
                &encVideoCtx,
                &encAudioctx);

    /** Initialize the resampler to be able to convert audio sample formats. */
    if (init_resampler(pInCodecCtx_a, encAudioctx, &resample_context))
        return;

    //Write file header
    nRet = avformat_write_header(ofmt_ctx, nullptr);
    if ( nRet < 0)
    {
        printf( "Error occurred when opening output file\n");
        return ;
    }

    int out_framesize = encAudioctx->frame_size;
    AVSampleFormat out_sample_fmt = encAudioctx->sample_fmt;
    int out_channels = av_get_channel_layout_nb_channels(encAudioctx->channel_layout);

    // 初始化一个FIFO用于存储待编码的音频帧,初始化FIFO大小的1个采样点
    // av_audio_fifo_alloc()第二个参数是声道数,第三个参数是单个声道的采样点数
    // 采样格式及声道数在初始化FIFO时已设置,各处涉及FIFO大小的地方都是用的单个声道的采样点数
    audiofifo = av_audio_fifo_alloc(out_sample_fmt, out_channels, 1);

    int readFinished = 0;
    // 关于:avcodec_send_packet()与avcodec_receive_frame()
    // 1. 按dts递增的顺序向解码器送入编码帧packet,解码器按pts递增的顺序输出原始帧frame,实际上解码器不关注输入packet的dts(错值都没关系),它只管依次处理收到的packet,按需缓冲和解码
    // 2. avcodec_receive_frame()输出frame时,会根据各种因素设置好frame->best_effort_timestamp(文档明确说明),实测frame->pts也会被设置(通常直接拷贝自对应的packet.pts,文档未明确说明)
    //    用户应确保avcodec_send_packet()发送的packet具有正确的pts,编码帧packet与原始帧frame间的对应关系通过pts确定
    // 3. avcodec_receive_frame()输出frame时,frame->pkt_dts拷贝自当前avcodec_send_packet()发送的packet中的dts,如果当前packet为flush packet,解码器进入flush模式,
    //    当前及剩余的frame->pkt_dts值总为AV_NOPTS_VALUE。因为解码器中有缓存帧,当前输出的frame并不是由当前输入的packet解码得到的,所以这个frame->pkt_dts没什么实际意义,可以不必关注
    // 4. avcodec_send_packet()发送第一个 flush packet 会返回成功,后续的 flush packet 会返回AVERROR_EOF。
    // 5. avcodec_send_packet()多次发送 flush packet 并不会导致解码器中缓存的帧丢失,使用avcodec_flush_buffers()可以立即丢掉解码器中缓存帧。因此播放完毕时应avcodec_send_packet(NULL)
    //    来取完缓存的帧,而SEEK操作或切换流时应调用avcodec_flush_buffers()来直接丢弃缓存帧。
    // 6. 解码器通常的冲洗方法:调用一次avcodec_send_packet(NULL)(返回成功),然后不停调用avcodec_receive_frame()直到其返回AVERROR_EOF,取现所有缓存帧,avcodec_receive_frame()返回
    //    AVERROR_EOF这一次是没有有效数据的,仅仅获取到一个结束标志。
    while (1)
    {
        nRet = av_read_frame(pInFmtCtx, &pkt);
        if( nRet < 0)
        {
            readFinished = 1;
            printf( "av_read_frame end \n");
            break ;
        }
        if( pkt.stream_index == nVideoStream_idx_in)
        {
            // 1. 将packet发送给解码器
            //    发送packet的顺序是按dts递增的顺序,如IPBBPBB
            //    pkt.pos变量可以标识当前packet在视频文件中的地址偏移
            //    发送第一个 flush packet 会返回成功,后续的 flush packet 会返回AVERROR_EOF
            nRet = avcodec_send_packet(pInCodecCtx_v, &pkt);
            if(nRet <0 )
            {
                av_packet_unref(&pkt);
                std::cout<<"avcodec_send_packet fail \n";
                break;
            }
            got_picture =avcodec_receive_frame(pInCodecCtx_v, pFrame);
            if(0 == got_picture)//
            {
                nRet = encode_video_frame(pFrame,pInFmtCtx,ofmt_ctx,encVideoCtx);
                if(nRet == -1)
                {
                    break;
                }
            }
        }
        else  if(pkt.stream_index == nAudioStream_idx_in)
        {
            while (av_audio_fifo_size(audiofifo) < out_framesize)
            {
                AVFrame *input_frame = av_frame_alloc();
                uint8_t ** audio_data_buffer = nullptr;
                int got_frame = 0;

                avcodec_send_packet(pInCodecCtx_a, &pkt);
                got_frame =avcodec_receive_frame(pInCodecCtx_a, input_frame);

                if (0 == got_frame)
                {
                    av_samples_alloc_array_and_samples(&audio_data_buffer,
                                                       nullptr,
                                                       out_channels,
                                                       input_frame->nb_samples,
                                                       out_sample_fmt,
                                                       1);
                    //这里的out nb_samples 必须传输入采样数
                    nRet = swr_convert(resample_context,
                                     audio_data_buffer,
                                     input_frame->nb_samples,
                                     (const uint8_t**)input_frame->data,
                                     input_frame->nb_samples);
                    if(nRet < 0)
                    {
                        printf("resample fail\n");
                    }
                    av_audio_fifo_realloc(audiofifo,
                                          av_audio_fifo_size(audiofifo) + input_frame->nb_samples);
                    av_audio_fifo_write(audiofifo,
                                        (void **)audio_data_buffer,
                                        input_frame->nb_samples);

                }
                av_packet_unref(&pkt);
                av_frame_free(&input_frame);
                if (audio_data_buffer)
                {
                    av_free(audio_data_buffer[0]);
                    av_free(audio_data_buffer);
                }
            }
            while (av_audio_fifo_size(audiofifo) >= out_framesize || av_audio_fifo_size(audiofifo)>0)
            {

                frame_size = FFMIN(av_audio_fifo_size(audiofifo), out_framesize);
                AVFrame* output_frame_fifio = nullptr;
                output_frame_fifio = av_frame_alloc();
                output_frame_fifio->nb_samples = frame_size;
                output_frame_fifio->channel_layout = encAudioctx->channel_layout;
                output_frame_fifio->format = encAudioctx->sample_fmt;
                output_frame_fifio->sample_rate = encAudioctx->sample_rate;
                av_frame_get_buffer(output_frame_fifio, 0);
                av_audio_fifo_read(audiofifo, (void **)output_frame_fifio->data, frame_size);
                encode_audio_frame(output_frame_fifio, frame_size, ofmt_ctx, encAudioctx);
            }
        }
        av_packet_unref(&pkt);     
    }  
    //flush
    if( pkt.stream_index == nVideoStream_idx_in)
    {
        while(1)
        {
            printf("video flush Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size);
            nRet = encode_video_frame(nullptr,pInFmtCtx,ofmt_ctx,encVideoCtx);
            if(nRet == -1)
            {
                break;
            }
        }
    }
    else if( pkt.stream_index == nAudioStream_idx_in)
    {
        while(1)
        {
            printf("audio flush Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size);
            nRet = encode_audio_frame(nullptr, frame_size, ofmt_ctx, encAudioctx);
            if(nRet == -1)
            {
                break;
            }
        }
    }
    //Write file trailer
    av_write_trailer(ofmt_ctx);
    printf( "av_write_trailer \n");
    printf( "==================================================== \n");
    av_dump_format(ofmt_ctx, 0, pOutFileName, 1);
    printf( "==================================================== \n");
    avformat_close_input(&pInFmtCtx);
    avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
}
void main()
{
    test();
    std::cout<<"end";
}





转码后视频信息:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值