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\n");
                    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\n");
                    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\n");
                    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\n");
                    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\n");
                    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\n");
                    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.以上代码久经考验。放心使用。如果有什么问题,可以联系我 350197870.

时间: 2024-10-10 02:29:08

ffmpeg 音频转码的相关文章

ffmpeg使用转码学习

ffmpeg在官网上描述自身:是一个对视频和音频进行记录,转换,流化的完整的跨平台解决方案.事实上,现在有很多工具都是基于ffmpeg来进行视频音频的处理工具的.比如鼎鼎大名的格式工厂,就是使用ffmpeg来作为内核的转码工具. 理解一些音视频的编码知识. 我们平时看的视频文件格式:mp4/rmvb/mkv/avi其实是一个容器.这个容器中装的东西分为两大类:音频和视频.对于视频这部分,它包含的编码格式有:H264/H265/VP8/VC1等.对于音频这个部分,它包含的编码格式有:AAC/MP3

ffmpeg/ffplay源码剖析笔记&lt;转&gt;

转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html 内容摘自<ffmpeg/ffplay源码剖析> 1.播放器一般原理 可以直观的看到播放这个媒体文件的基本模块(filter),七个模块按广度顺序:读文件模块(source filter),解复用模块(Demux filter),视/音频解码模块(Decode filter),颜色空间转换模块(Co

最简单的基于FFMPEG的转码程序

本文介绍一个简单的基于FFmpeg的转码器.转码器在视音频编解码处理的程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3.从流程中可以看出

最简单的基于FFMPEG的转码程序 [转]

本文介绍一个简单的基于FFmpeg的转码器.它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式.转码器在视音频编解码处理的 程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视 频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是 FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式

C#实现 ffmpeg视频转码、播放

近来公司项目要求实现全景相机的视频截取,但是截取的视频需求转码上传.经过研究采用ffmpeg转码,奉上一个详细介绍的博文: 最简单的基于FFMPEG的转码程序 主要是转码的操作过程,能够实现了从相机获取的MP4转换成普通播放器播放的MP4格式; 1 //转码方法 2 private void Test1() 3 { 4 5 Process p = new Process(); 6 7 8 p.StartInfo.FileName = path +"ffmpeg.exe"; 9 10 p

ffmpeg音频编码

在弄音频采集时,需要设置缓存的大小,如果只是简单的采集和直接播放PCM数据,缓存的大小一般不影响播放和保存. 但是,如果需要使用FFMpeg音频编码,这时,音频缓存的大小必须设置av_samples_get_buffer_size函数返回的大小.以下是几点注意的 1. m_pFrame = av_frame_alloc();m_pFrame->format = ffSampleFormat;m_pFrame->nb_samples = nSampleRate;//帧的大小 2. m_nBuff

FFmpeg简单转码程序--视频剪辑

学习了雷神的文章,慕斯人分享精神,感其英年而逝,不胜唏嘘.他有分享一个转码程序<最简单的基于FFMPEG的转码程序>其中使用了filter(参考了ffmpeg.c中的流程),他曾说想再编写一个不需要filter的版本,可惜未有机会.恰好工作中有相关ffmpeg处理内容,故狗尾续貂,撰写本文. 相关流程: 1.打开输入文件 2.打开输出文件 3.设置解码环境 4.设置输出流信息 5.设置编码环境 6.打开输入流循环读取,解码再编码写入 7.fflush解码和编码ctx 8.关闭文件 本文的代码,

使用ffmpeg实现转码样例(代码实现)

使用ffmpeg转码主要工作如下: Demux -> Decoding -> Encoding -> Muxing 其中接口调用如下: av_register_all(); avformat_open_input avformat_find_stream_info open_codec_context av_image_alloc avcodec_alloc_frame avformat_alloc_output_context2 avcodec_open2 avcodec_alloc_

黄聪:FFmpeg视频转码技巧之-crf参数(H.264篇)

昨天,有个朋友给我出了个难题:他手上有一个视频,1080P的,49秒,200多兆:要求在确保质量的情况下把文件压缩到10M以内. 这是什么概念呢?按照文件大小10M来计算,码率是:10 x 8 / 49 = 1.6 Mbps.也就比VCD的质量略好一点(注:VCD的标准码率是1150 Kbps).谈何“确保质量”?mission impossible啊! 咱还是现实一点吧.在不明显损失画质的前提下,看看使用FFmpeg能够帮到多少忙.用iPhone拍了一个1920 x 1080的视频,33秒,4