zoukankan      html  css  js  c++  java
  • ffmpeg 音频转码

       大多数厂家摄像机输出的音频流格式都是PCM,有一些场合(比如讲音视频流保存成Ts流)需要将PCM格式转成AAC格式。基本的思路是先解码得到音频帧,再将音频帧编码成AAC格式。编码和解码之间需要添加一个filter。filter起到适配的作用。

       首先解码:

            AVFrame * decode(AVPacket* sample)
            {    
                int gotframe = 0;
                AVFrame* frame = av_frame_alloc();
                AVFrame *filt_frame = nullptr;
                auto length = avcodec_decode_audio4(decoderContext, frame, &gotframe, sample);
                frame->pts = frame->pkt_pts;
                if(length >= 0 && gotframe != 0)
                {
                    if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_PUSH) < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph
    ");
                        av_frame_free(&frame);
                        return nullptr;
                    }
                    frame->pts = AV_NOPTS_VALUE;
    
                    /* pull filtered audio from the filtergraph */
                    filt_frame = av_frame_alloc();
                    while (1) {
                        int ret = av_buffersink_get_frame_flags(buffersink_ctx, filt_frame, AV_BUFFERSINK_FLAG_NO_REQUEST);
                        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                            break;
                        if(ret < 0)
                        {
                            av_frame_free(&frame);
                            av_frame_free(&filt_frame);
                            return nullptr;
                        }
    
                        int64_t frame_pts = AV_NOPTS_VALUE;
                        if (filt_frame->pts != AV_NOPTS_VALUE) {
                            startTime = (startTime == AV_NOPTS_VALUE) ? 0 : startTime;
                            AVRational av_time_base_q;
                            av_time_base_q.num = 1;
                            av_time_base_q.den = AV_TIME_BASE;
                            filt_frame->pts = frame_pts = 
                                av_rescale_q(filt_frame->pts, buffersink_ctx->inputs[0]->time_base,    encoderContext->time_base) 
                                - av_rescale_q(startTime, av_time_base_q,    encoderContext->time_base);
                        }
                        av_frame_free(&frame);
                        return filt_frame;
                    }
                }
                av_frame_free(&filt_frame);
                av_frame_free(&frame);
                return  nullptr;
            }

      decode 得到AVFrame 也即音频帧,这个frame是不能做为编码的源要经过filter,原因之一是有些摄像机输出的音频包每个packet是320个字节,AAC每个Packet是1024个字节。

       初始化Filter:

            int initFilters()
            {
                    char args[512];
                    int ret;
                    AVFilter *abuffersrc  = avfilter_get_by_name("abuffer");
                    AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
                    AVFilterInOut *outputs = avfilter_inout_alloc();
                    AVFilterInOut *inputs  = avfilter_inout_alloc();
                    static const enum AVSampleFormat out_sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
                    static const int64_t out_channel_layouts[] = { AV_CH_LAYOUT_MONO, -1 };
                    static const int out_sample_rates[] = {decoderContext->sample_rate , -1 };
                    AVRational time_base = input->time_base;
                    filter_graph = avfilter_graph_alloc();
    
                    /* buffer audio source: the decoded frames from the decoder will be inserted here. */
    
                    if (!decoderContext->channel_layout)
                        decoderContext->channel_layout = av_get_default_channel_layout(decoderContext->channels);
                    sprintf_s(args, sizeof(args),
                        "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
                        time_base.num, time_base.den, decoderContext->sample_rate,
                        av_get_sample_fmt_name(decoderContext->sample_fmt), decoderContext->channel_layout);
                    ret = avfilter_graph_create_filter(&buffersrc_ctx, abuffersrc, "in",
                        args, NULL, filter_graph);
                    if (ret < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source
    ");
                        return ret;
                    }
    
                    /* buffer audio sink: to terminate the filter chain. */
                    ret = avfilter_graph_create_filter(&buffersink_ctx, abuffersink, "out",
                        NULL, NULL, filter_graph);
                    if (ret < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink
    ");
                        return ret;
                    }
    
                    ret = av_opt_set_int_list(buffersink_ctx, "sample_fmts", out_sample_fmts, -1,
                        AV_OPT_SEARCH_CHILDREN);
                    if (ret < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format
    ");
                        return ret;
                    }
    
                    ret = av_opt_set_int_list(buffersink_ctx, "channel_layouts", out_channel_layouts, -1,
                        AV_OPT_SEARCH_CHILDREN);
                    if (ret < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout
    ");
                        return ret;
                    }
    
                    ret = av_opt_set_int_list(buffersink_ctx, "sample_rates", out_sample_rates, -1,
                        AV_OPT_SEARCH_CHILDREN);
                    if (ret < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate
    ");
                        return ret;
                    }
    
                    /* Endpoints for the filter graph. */
                    outputs->name       = av_strdup("in");
                    outputs->filter_ctx = buffersrc_ctx;
                    outputs->pad_idx    = 0;
                    outputs->next       = NULL;
    
                    inputs->name       = av_strdup("out");
                    inputs->filter_ctx = buffersink_ctx;
                    inputs->pad_idx    = 0;
                    inputs->next       = NULL;
    
                    if ((ret = avfilter_graph_parse_ptr(filter_graph, "anull",
                        &inputs, &outputs, nullptr)) < 0)
                        return ret;
    
                    if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
                        return ret;
    
                    av_buffersink_set_frame_size(buffersink_ctx, 1024);
                return 0;
            }

       Filter可以简理解为FIFO(当然实际上不是)输入是解码后的AVFrame,输出是编码的源头。AVFrame 经过Filter以后就可以编码了。

            shared_ptr<AVPacket> encode(AVFrame * frame)
            {
                int gotpacket = 0;
                shared_ptr<AVPacket> packet((AVPacket*)av_malloc(sizeof(AVPacket)), [&](AVPacket *p){av_free_packet(p);av_freep(&p);});
                auto pkt = packet.get();
                av_init_packet(pkt);
                pkt->data = nullptr;
                pkt->size = 0;
                frame->nb_samples = encoderContext->frame_size;
                frame->format = encoderContext->sample_fmt;
                frame->channel_layout = encoderContext->channel_layout;
                int hr = avcodec_encode_audio2(encoderContext.get(), pkt, frame, &gotpacket);
                av_frame_free(&frame);
                if(gotpacket)
                {
                    if (pkt->pts != AV_NOPTS_VALUE)
                        pkt->pts      = av_rescale_q(pkt->pts,      encoderContext->time_base, output->time_base);
                    if (pkt->dts != AV_NOPTS_VALUE)
                        pkt->dts      = av_rescale_q(pkt->dts,      encoderContext->time_base,output->time_base);
                    if (pkt->duration > 0)
                        pkt->duration = int(av_rescale_q(pkt->duration, encoderContext->time_base, output->time_base));
                    return packet;
                }
                return nullptr;            
            }

      实际运用中我们用到了智能指针shared_ptr<AVPacket>,也可以不用。但是要注意内存泄露问题。如果程序运行在多核上,建议AVFilterGraph 中thread设置为1.以上代码久经考验。放心使用。如果有什么问题,可以加群 流媒体/Ffmpeg/音视频 127903734进行交流

    视频下载地址:http://www.chungen90.com/?news_33/

     Demo下载地址: http://www.chungen90.com/?news_34

  • 相关阅读:
    实验一 软件开发文档与工具的安装与使用
    ATM管理系统
    举例分析流程图与活动图的区别与联系
    四则运算
    机器学习 实验三
    机器学习 实验四
    机器视觉实验二
    实验三
    实验二
    实验一
  • 原文地址:https://www.cnblogs.com/wanggang123/p/5589488.html
Copyright © 2011-2022 走看看