使用 libav* 库的 FFMPEG 音频转码

2023-11-24

我正在使用 ffmpeg 库编写音频转码应用程序。 这是我的代码

    /*
     * File:   main.cpp
     * Author: vinod
     * Compile with "g++ -std=c++11 -o audiotranscode main.cpp -lavformat -lavcodec -lavutil -lavfilter"
     * 
     */


    #if !defined PRId64 || PRI_MACROS_BROKEN
    #undef PRId64
    #define PRId64 "lld"
    #endif

    #define __STDC_FORMAT_MACROS

    #ifdef   __cplusplus
    extern "C" {
    #endif

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <stdint.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/samplefmt.h>
    #include <libavutil/frame.h>
    #include <libavutil/timestamp.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavfilter/buffersrc.h>
    #include <libavfilter/buffersink.h>
    #include <libswscale/swscale.h>
    #include <libavutil/opt.h>

    #ifdef   __cplusplus
    }
    #endif

    #include <iostream>
    using namespace std;

    int select_stream, got_frame, got_packet;

    AVFormatContext *in_fmt_ctx = NULL, *out_fmt_ctx = NULL;
    AVCodec *dec_codec = NULL, * enc_codec = NULL;
    AVStream *audio_st = NULL;
    AVCodecContext *enc_ctx = NULL, *dec_ctx = NULL;

    AVFrame *pFrame = NULL, * pFrameFiltered = NULL;

    AVFilterGraph *filter_graph = NULL;
    AVFilterContext *buffersrc_ctx = NULL;
    AVFilterContext *buffersink_ctx = NULL;

    AVPacket packet;

    string inFileName = "/home/vinod/vinod/Media/univac.webm";
    string outFileName = "audio_extracted.m4a";

    int target_bit_rate = 128000,
        sample_rate = 22050,
        channels = 1;
    AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16;
    string filter_description = "aresample=22050,aformat=sample_fmts=s16:channel_layouts=mono";

    int log_averror(int errcode)
    {
            char *errbuf = (char *) calloc(AV_ERROR_MAX_STRING_SIZE, sizeof(char));
            av_strerror(errcode, errbuf, AV_ERROR_MAX_STRING_SIZE);
            std::cout << "Error - " << errbuf << std::endl;
            delete [] errbuf;
            return -1;
    }

    /**
     * Initialize conversion filter */
    int initialize_audio_filter()
    {
            char args[512];
            int ret;
            AVFilter *buffersrc = avfilter_get_by_name("abuffer");
            AVFilter *buffersink = avfilter_get_by_name("abuffersink");
            AVFilterInOut *outputs = avfilter_inout_alloc();
            AVFilterInOut *inputs = avfilter_inout_alloc();
            filter_graph = avfilter_graph_alloc();
            const enum AVSampleFormat out_sample_fmts[] = {sample_fmt, AV_SAMPLE_FMT_NONE};
            const int64_t out_channel_layouts[] = {av_get_default_channel_layout(out_fmt_ctx -> streams[0] -> codec -> channels), -1};
            const int out_sample_rates[] = {out_fmt_ctx -> streams[0] -> codec -> sample_rate, -1};

            if (!dec_ctx->channel_layout)
                    dec_ctx->channel_layout = av_get_default_channel_layout(dec_ctx->channels);

            snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
                     in_fmt_ctx -> streams[select_stream] -> time_base.num, in_fmt_ctx -> streams[select_stream] -> time_base.den,
                     dec_ctx->sample_rate,
                     av_get_sample_fmt_name(dec_ctx->sample_fmt),
                     dec_ctx->channel_layout);
            ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);

            if (ret < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
                    return -1;
            }

            ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph);

            if (ret < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Cannot create 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;
            /* Endpoints for the filter graph. */
            inputs -> name = av_strdup("out");
            inputs -> filter_ctx = buffersink_ctx;
            inputs -> pad_idx = 0;
            inputs -> next = NULL;
            string filter_desc = filter_description;

            if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_desc.c_str(), &inputs, &outputs, NULL)) < 0) {
                    log_averror(ret);
                    exit(1);
            }

            if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
                    log_averror(ret);
                    exit(1);
            }

            /* Print summary of the sink buffer
             * Note: args buffer is reused to store channel layout string */
            AVFilterLink *outlink = buffersink_ctx->inputs[0];
            av_get_channel_layout_string(args, sizeof(args), -1, outlink->channel_layout);
            av_log(NULL, AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n",
                   (int) outlink->sample_rate,
                   (char *) av_x_if_null(av_get_sample_fmt_name((AVSampleFormat) outlink->format), "?"),
                   args);
            return 0;
    }

    /*
     *
     */
    int main(int argc, char **argv)
    {
            int ret;
            cout << "Hello World" << endl;
            printf("abcd");
            avcodec_register_all();
            av_register_all();
            avfilter_register_all();

            /* open input file, and allocate format context */
            if (avformat_open_input(&in_fmt_ctx, inFileName.c_str(), NULL, NULL) < 0) {
                    std::cout << "error opening input file - " << inFileName << std::endl;
                    return -1;
            }

            /* retrieve stream information */
            if (avformat_find_stream_info(in_fmt_ctx, NULL) < 0) {
                    std::cerr << "Could not find stream information in the input file " << inFileName << std::endl;
            }

            /* Dump format details */
            printf("\n ---------------------------------------------------------------------- \n");
            av_dump_format(in_fmt_ctx, 0, inFileName.c_str(), 0);
            printf("\n ---------------------------------------------------------------------- \n");
            /* Choose a audio stream */
            select_stream = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec_codec, 0);

            if (select_stream == AVERROR_STREAM_NOT_FOUND) {
                    std::cerr << "No audio stream found" << std::endl;
                    return -1;
            }

            if (select_stream == AVERROR_DECODER_NOT_FOUND) {
                    std::cerr << "No suitable decoder found" << std::endl;
                    return -1;
            }

            dec_ctx = in_fmt_ctx -> streams[ select_stream] -> codec;
            av_opt_set_int(dec_ctx, "refcounted_frames", 1, 0);

            /* init the audio decoder */
            if ((ret = avcodec_open2(dec_ctx, dec_codec, NULL)) < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Cannot open audio decoder\n");
                    return ret;
            }

            /* allocate output context */
            ret = avformat_alloc_output_context2(&out_fmt_ctx, NULL, NULL,
                                                 outFileName.c_str());

            if (ret < 0) {
                    std::cerr << "Could not create output context for the file " << outFileName << std::endl;
                    return -1;
            }

            /* find the encoder */
            enum AVCodecID codec_id = out_fmt_ctx -> oformat -> audio_codec;
            enc_codec = avcodec_find_encoder(codec_id);

            if (!(enc_codec)) {
                    std::cerr << "Could not find encoder for - " << avcodec_get_name(codec_id) << std::endl;
                    return -1;
            }

            /* add a new stream */
            audio_st = avformat_new_stream(out_fmt_ctx, enc_codec);

            if (!audio_st) {
                    std::cerr << "Could not add audio stream - " << std::endl;
            }

            /* Initialise audio codec */
            audio_st -> id = out_fmt_ctx -> nb_streams - 1;
            enc_ctx = audio_st -> codec;
            enc_ctx -> codec_id = codec_id;
            enc_ctx -> codec_type = AVMEDIA_TYPE_AUDIO;
            enc_ctx -> bit_rate = target_bit_rate;
            enc_ctx -> sample_rate = sample_rate;
            enc_ctx -> sample_fmt = sample_fmt;
            enc_ctx -> channels = channels;
            enc_ctx -> channel_layout = av_get_default_channel_layout(enc_ctx -> channels);

            /* Some formats want stream headers to be separate. */
            if (out_fmt_ctx -> oformat -> flags & AVFMT_GLOBALHEADER) {
                    enc_ctx -> flags |= CODEC_FLAG_GLOBAL_HEADER;
            }

            ret = avcodec_open2(out_fmt_ctx -> streams[0] -> codec, enc_codec, NULL);

            if (ret < 0) {
                    std::cerr << "Could not create codec context for the file " << outFileName << std::endl;
                    return -1;
            }

            /* Initialize filter */
            initialize_audio_filter();

            if (!(out_fmt_ctx -> oformat -> flags & AVFMT_NOFILE)) {
                    int ret = avio_open(& out_fmt_ctx -> pb, outFileName.c_str(),
                                        AVIO_FLAG_WRITE);

                    if (ret < 0) {
                            log_averror(ret);
                            return -1;
                    }
            }

            /* Write header */
            if (avformat_write_header(out_fmt_ctx, NULL) < 0) {
                    if (ret < 0) {
                            log_averror(ret);
                            return -1;
                    }
            }

            /* Allocate frame */
            pFrame = av_frame_alloc();

            if (!pFrame) {
                    std::cerr << "Could not allocate frame\n";
                    return -1;
            }

            pFrameFiltered = av_frame_alloc();

            if (!pFrameFiltered) {
                    std::cerr << "Could not allocate frame\n";
                    return -1;
            }

            av_init_packet(&packet);
            packet.data = NULL;
            packet.size = 0;

            /* Read packet from the stream */
            while (av_read_frame(in_fmt_ctx, &packet) >= 0) {
                    if (packet.stream_index == select_stream) {
                            avcodec_get_frame_defaults(pFrame);
                            ret = avcodec_decode_audio4(dec_ctx, pFrame, &got_frame, &packet);

                            if (ret < 0) {
                                    log_averror(ret);
                                    return ret;
                            }

                            printf("Decoded packet pts : %ld ", packet.pts);
                            printf("Frame Best Effor pts : %ld \n", pFrame->best_effort_timestamp);
                            /* Set frame pts */
                            pFrame -> pts = av_frame_get_best_effort_timestamp(pFrame);

                            if (got_frame) {
                                    /* push the decoded frame into the filtergraph */
                                    ret = av_buffersrc_add_frame_flags(buffersrc_ctx, pFrame, AV_BUFFERSRC_FLAG_KEEP_REF);

                                    if (ret < 0) {
                                            log_averror(ret);
                                            return ret;
                                    }

                                    /* pull filtered frames from the filtergraph */
                                    while (1) {
                                            ret = av_buffersink_get_frame(buffersink_ctx, pFrameFiltered);

                                            if ((ret == AVERROR(EAGAIN)) || (ret == AVERROR_EOF)) {
                                                    break;
                                            }

                                            if (ret < 0) {
                                                    printf("Error while getting filtered frames from filtergraph\n");
                                                    log_averror(ret);
                                                    return -1;
                                            }

                                            /* Initialize the packets */
                                            AVPacket encodedPacket = {0};
                                            av_init_packet(&encodedPacket);
                                            ret = avcodec_encode_audio2(out_fmt_ctx -> streams[0] -> codec, &encodedPacket, pFrameFiltered, &got_packet);

                                            if (!ret && got_packet && encodedPacket.size) {
                                                    /* Set correct pts and dts */
                                                    if (encodedPacket.pts != AV_NOPTS_VALUE) {
                                                            encodedPacket.pts = av_rescale_q(encodedPacket.pts, buffersink_ctx -> inputs[0] -> time_base,
                                                                                             out_fmt_ctx -> streams[0] -> time_base);
                                                    }

                                                    if (encodedPacket.dts != AV_NOPTS_VALUE) {
                                                            encodedPacket.dts = av_rescale_q(encodedPacket.dts, buffersink_ctx -> inputs[0] -> time_base,
                                                                                             out_fmt_ctx -> streams[0] -> time_base);
                                                    }

                                                    printf("Encoded packet pts %ld\n", encodedPacket.pts);
                                                    /* Write the compressed frame to the media file. */
                                                    ret = av_interleaved_write_frame(out_fmt_ctx, &encodedPacket);

                                                    if (ret < 0) {
                                                            log_averror(ret);
                                                            return -1;
                                                    }
                                            } else if (ret < 0) {
                                                    log_averror(ret);
                                                    return -1;
                                            }

                                            av_frame_unref(pFrameFiltered);
                                    }

                                    av_frame_unref(pFrame);
                            }
                    }
            }

            /* Flush delayed frames from encoder*/
            got_packet=1;
            while (got_packet) {
                    AVPacket encodedPacket = {0};
                    av_init_packet(&encodedPacket);
                    ret = avcodec_encode_audio2(out_fmt_ctx -> streams[0] -> codec, &encodedPacket, NULL, &got_packet);

                    if (!ret && got_packet && encodedPacket.size) {
                            /* Set correct pts and dts */
                            if (encodedPacket.pts != AV_NOPTS_VALUE) {
                                    encodedPacket.pts = av_rescale_q(encodedPacket.pts, buffersink_ctx -> inputs[0] -> time_base,
                                                                     out_fmt_ctx -> streams[0] -> time_base);
                            }

                            if (encodedPacket.dts != AV_NOPTS_VALUE) {
                                    encodedPacket.dts = av_rescale_q(encodedPacket.dts, buffersink_ctx -> inputs[0] -> time_base,
                                                                     out_fmt_ctx -> streams[0] -> time_base);
                            }

                            printf("Encoded packet pts %ld\n", encodedPacket.pts);
                            /* Write the compressed frame to the media file. */
                            ret = av_interleaved_write_frame(out_fmt_ctx, &encodedPacket);

                            if (ret < 0) {
                                    log_averror(ret);
                                    return -1;
                            }
                    } else if (ret < 0) {
                            log_averror(ret);
                            return -1;
                    }
            }

            /* Write Trailer */
            av_write_trailer(out_fmt_ctx);
            avfilter_graph_free(&filter_graph);

            if (dec_ctx)
                    avcodec_close(dec_ctx);

            avformat_close_input(&in_fmt_ctx);
            av_frame_free(&pFrame);
            av_frame_free(&pFrameFiltered);

            if (!(out_fmt_ctx -> oformat -> flags & AVFMT_NOFILE))
                    avio_close(out_fmt_ctx -> pb);
            avcodec_close(out_fmt_ctx->streams[0]->codec);
            avformat_free_context(out_fmt_ctx);
            return 0;
    }

转码后的音频文件与输入的持续时间相同。但它完全吵闹。有人可以告诉我我在这里做错了什么吗!


我已经找到问题出在哪里并且已经解决了。

当大胆打开输出文件时,我们发现音频信号中插入了不需要的静音。问题在于提供给编码器的“每帧样本数”。

不同的编解码器需要不同的帧大小进行编码。而aac编码器期望的大小为1024。这可以通过观察看出enc_ctx->frame_size执行后avcodec_open2().

滤波器需要向编码器提供每通道 1024 个样本的帧。 所以在我的代码中,pFrameFiltered每个通道需要正好有 1024 个样本。如果它小于 1024 ,编码器会附加零以使其达到 1024 个样本,然后对其进行编码。

这可以通过使用我们自己的 fifo 队列或使用 ffmpeg 音频过滤器提供的过滤器来解决。我们需要使用过滤器asetnsamples=n=1024:p=0正如所解释的here。所以需要的修改是

`string filter_description = 
        "aresample=22050,aformat=sample_fmts=s16:channel_layouts=mono,asetnsamples=n=1024:p=0";`

只是玩弄价值n在过滤器中以便更好地理解。检查enc_ctx->frame_size由 avcodec_open2( ) 设置的字段并设置值n适当地。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 libav* 库的 FFMPEG 音频转码 的相关文章

随机推荐

  • Angular 6:HttpErrorResponse SyntaxError:JSON 中出现意外的 token

    我正在发布一个请求 我应该会收到一个 成功 字符串作为响应 我收到一个 HttpResponseError 其中包含下图中发布的以下信息 采购订单服务 postPurchaseOrderCustom purchaseOrderCustom
  • 在 PHP 中创建基于边缘检测的图像

    我很好奇 是否可以用 PHP 实现 1 发送图像文件到服务器 2 处理图像 检测边缘并根据边缘创建简单的笔画 3 将文件保存在服务器上 将其发送到用户的浏览器 其他 这是一些 示例 文件 P 如您所见 它不是使用任何启用边缘检测的程序制作的
  • GAE python 线程不并行执行

    我正在尝试在 GAE 上使用 Python 创建一个简单的 Web 应用程序 应用程序需要根据收到的请求生成一些线程 为此 我使用 python 的线程库 我生成所有线程 然后等待它们 t1 start t2 start t3 start
  • sqlalchemy 在多个列中是唯一的

    假设我有一个代表位置的类 地点 属于 客户 位置由 unicode 10 字符代码标识 位置代码 在特定客户的位置中应该是唯一的 The two below fields in combination should be unique cu
  • Odoo/OpenERP:从树视图中隐藏创建按钮

    我这里有一个情况 我正在使用 OpenERP 7 我试图从我的产品的树视图中隐藏 创建 按钮 这可以通过使用来完成
  • 隐藏的 div 高度(改变我的建议)

    好吧 我要在这里回答某人关于为什么他们的脚本不起作用的问题 他们将内容加载到隐藏的 div 中 然后获取高度 以便为包裹的 div 制作动画 但我总是尝试测试我提供的代码 所以我做了这个演示向他们证明这一点 那么 嗯 我现在是进入了暮光区还
  • 关于java字符串文字池和字符串连接的混淆

    全部 当我编写下面的代码时遇到问题 String hello Hello String str5 Hel lo String str8 Hel String str9 lo String str10 str8 str9 System out
  • 在C#中执行包含GO语句的SQL批处理

    我正在尝试构建一个程序 它可以批量执行sql语句并进行错误处理 因此我没有使用SMO 问题是GO不是 SQL 的一部分 当使用 NET 执行语句时 它最终会出现错误 SMO 会处理它 但不会给出执行是否失败的任何指示 string stat
  • 如果包含的单元格为空,则使用 jQuery 隐藏表格列

    我有一个以下类型的表 table width 500 border 1 cellspacing 0 cellpadding 0 thead tr th span 1 span th th span 2 span th th span 3 s
  • Select() 查询中使用的 Lambda 表达式

    我正在尝试构建一个 lambda 表达式 其中包含两个赋值 如下所示 然后我可以将其传递给 Queryable Select 方法 我试图将字符串变量传递到方法中 然后使用该变量构建 lambda 表达式 以便我可以在 LINQ Selec
  • 如何更新 XAMPP 的 PHP 版本 [重复]

    这个问题在这里已经有答案了 可能的重复 在 Windows 版 XAMPP 中升级 PHP 我目前使用 XAMPP 版本 1 8 1 其中 PHP 版本 5 4 3 我从一天前发布的 PHP 站点版本 5 4 11 找到了最新版本的 PHP
  • 在 Eclipse (Galileo) 中安装 Maven 插件 (m2eclipse) 时出现问题

    我已经安装了 Eclipse Galileo 适用于 Java EE 开发人员 现在我正在尝试安装 m2eclipse Maven 插件 我按照以下描述的基本步骤进行操作http m2eclipse sonatype org install
  • Django:仅更新UpdateView中已更改的字段

    我正在使用 UpdateView 来更新一系列字段 但是 我只想将已修改的字段保存到数据库中 如果在更新过程中未为字段提供值 我希望将以前的值用作默认值 如果为字段提供了新值 则只应更新该字段 我该如何实现这一目标 views py cla
  • 如何动态加载css文件

    我们正在使用Vue js and Vuetify对于我的申请 作为我的应用程序的一部分 我将根据该 API 响应在页面加载时进行 API 调用 整个应用程序将呈现所有组件 作为此 API 的一部分 我将获得名为CSS方向它告诉哪个 css
  • 在 O(n) 中找到输入字符串的最小周期?

    鉴于以下问题 定义 令 S 为字母表 上的字符串 S 是最小周期S if S 是满足以下条件的最小字符串 S S k S where S 是一个前缀S 如果没有这样的S 存在 那么S是 不是周期性的 例子 S abcabcabcabca T
  • Laravel 时间戳(来自 json 响应)与 artisan & mysql select 命令生成的时间戳不同

    TL DR 没有2020 08 09在 json 响应中 我刚刚发现我的行为很奇怪Laravel 7 x今天的项目 正如上面标题所提到的 created at and updated atjson 响应与 artisan 命令生成的响应不同
  • 比较两个数组并查找第二个数组中缺少的项目[重复]

    这个问题在这里已经有答案了 我有两个数组 它们一开始是相同的 但用户可以从第二个数组中删除 无法添加 只能删除 项目 我想查找第一个数组中但不在第二个数组中的项目 我可以想到几种方法来做到这一点 但由于这些数组可能非常大 我很好奇是否有人可
  • LLVM。如何根据名称访问结构体字段?

    我有很少的 C 示例代码 struct RecordTest int value1 int value2 void test RecordTest rt rt value1 15 rt value2 75 以及 LLVM 3 4 IR st
  • 堆栈限制与线程的关系

    ulimit s 之间有什么关系
  • 使用 libav* 库的 FFMPEG 音频转码

    我正在使用 ffmpeg 库编写音频转码应用程序 这是我的代码 File main cpp Author vinod Compile with g std c 11 o audiotranscode main cpp lavformat l