ffmpeg amerge 使用

简介

本文从命令行和api分别介绍下amerge使用,并且说下api使用遇到的一个坑

amerge介绍

合并两个或两个以上的音频流到一个多通道流
滤镜接受下面的选项:
inputs
设置输入数量,默认为2
如果输入的通道布局是不相交的,因此可兼容,输出将设置相应的通道布局和渠道,并在必要时重新排 序。如果输入的通道布局是不可分离的,则输出将会是第一个输入的所有通道,然后第二个输入的所 有通道,在这种顺序下,输出的通道布局将默认通道数设为总数。
例如:如果第一个输入是 2.1 ( FL+FR+LF )和第二个输入为 FC+BL+BR ,则输出是 5.1 通道布 局,并且按: a1,a2,b1,a3,b2,b3 设置输出通道布局(这里a1是第一个输入的第一个通道 FL ,b1 是第二个输入的第一个通道 FC )
在另外的应用中,如果两个输入都是立体声,则输出会默认为: a1,a2,b1,b2 ,即输出流显示为一个 4通道音频流,这可能是一个非预期的值。
所有的输入必须有相同的采样率和格式。
如果输入没有相同的持续时间,输出将在最短时间停止

上述2.1布局 5.1布局 都是通道布局名称,常见还有以下
mono, stereo, 4.0, quad, 5.0, 5.0(side), 5.1, 5.1(side), 7.1, 7.1(wide), downmix

单通道名称常见有一下
(FL, FR, FC, LFE, BL, BR, FLC, FRC, BC, SL, SR, TC, TFL, TFC, TFR, TBL, TBC, TBR, DL, DR)

具体可以搜索ffmpeg源码 关键字 get_channel_name函数 channel_layout_map定义

命令行使用

本次开发功能是,将两股音频A,B流合并到一起,左声道显示A流,右声道显示B流,命令行如下:

ffmpeg -threads 1 -i A.aac -threads 1 -i B.aac -threads 1 -filter_complex "[0:a][1:a]amerge=inputs=2,pan=stereo|c0<c0+c1|c1<c2+c3[aout]" -map [aout] -y c.mp3

A流B流 都是立体声,所有amerge合成后的的流,就是4通道,排列A1A2B1B2,使用pan滤镜,将4通道转为双声道立体声。
c1 c2 是指单通道下标

api使用

由于amerge的输出是有输入的通道布局决定,对于输入,我们先对通道进行转换

		inFmt->aGraph = avfilter_graph_alloc();
        inFmt->aGraph->nb_threads = 1;
        // audio, out
        a_out = filter_new(inFmt->aGraph, "abuffersink", "aout", 0);
     
        AVFilterContext *alink = 0;
        // audio, input abuffer
        snprintf(args, 1024,
                 "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
                 inFmt->aCodeCtx->time_base.num, inFmt->aCodeCtx->time_base.den,
                 inFmt->aCodeCtx->sample_rate,
                 av_get_sample_fmt_name(inFmt->aCodeCtx->sample_fmt),
                 inFmt->aCodeCtx->channel_layout);
        snprintf(name, 128, "in_a%d", i);
        LLOG(LOG_INFO, "input[%d] will add abuffer to graph", inFmt->index);
        inFmt->aIns = filter_new(inFmt->aGraph, "abuffer", name, args);
        alink = inFmt->aIns;

     	snprintf(args, 1024,
          "sample_rates=%d:sample_fmts=%s:channel_layouts=%s",
          48000, //format output
          av_get_sample_fmt_name(AV_SAMPLE_FMT_S32P),
          "stereo"); //format output
		 //aformat 滤镜
          AVFilterContext *aformat = filter_new(inFmt->aGraph, "aformat", "aforamt_out", args);
          filter_graph_add_link(inFmt->aGraph, inFmt->aIns, 0, aformat, 0);
          alink = aformat;
        
        filter_graph_add_link(inFmt->aGraph, alink, 0, a_out, 0);
        if (filter_graph_complete(inFmt->aGraph) < 0)
        {
            LLOG(LOG_ERROR, "input[%d] filter_graph_complete aGraph error", inFmt->index);
            goto end;
        }

我们目标是mp3 fmt为AV_SAMPLE_FMT_S32P,所以输入也转为AV_SAMPLE_FMT_S32P

输出滤镜

	aGraph = avfilter_graph_alloc();
    static AVFilterContext *amerge = NULL;
    aGraph->nb_threads = 1;
    aFilterCtx_out = filter_new(aGraph, "abuffersink", "out_aout", 0);
    snprintf(args, sizeof(args), "inputs=%d", audio_count);
    amerge = filter_new(aGraph, "amerge", "out_amerge", args);
    int i = 0, j = 0;
    for (i; i < mix_ctx.input_count; i++)
    {
        if (audio_idx[i] == 0)
            continue;
        LLOG(LOG_INFO, "+++++++ init_audio_mix_rightleft_graph will add audio[%d] into graph +++++++", j);
       
        snprintf(args, 1024,
                    "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
                    format_in[i].aCodeCtx->time_base.num, format_in[i].aCodeCtx->time_base.den,
                    48000,
                    av_get_sample_fmt_name(AV_SAMPLE_FMT_S32P),
                    av_get_default_channel_layout(2));
        snprintf(name, 128, "out_in_a%d", i);
        //todo test
        aFilterCtx_in[i] = filter_new(aGraph, "abuffer", name, args);
        //aFilterCtx_in[i] = filter_new_new(aGraph, "abuffer", name, format_in[i].aCodeCtx->sample_fmt, format_in[i].aCodeCtx->time_base.num, 
         //                                 format_in[i].aCodeCtx->sample_rate, av_get_default_channel_layout(format_in[i].aCodeCtx->channels));
        if (filter_graph_add_link(aGraph, aFilterCtx_in[i], 0, amerge, j++))
        {
            return;
        }
        LLOG(LOG_INFO, "+++++++ init_audio_mix_graph add audio[%d] into graph success +++++++", i);
    }

    static AVFilterContext *pan = NULL;
    if(j == 1) {
        snprintf(args, sizeof(args), "stereo|c0<c0+c1|c1<c0+c1");
    } else {
        snprintf(args, sizeof(args), "stereo|c0<c0+c1|c1<c2+c3");
    }

    pan = filter_new(aGraph, "pan", "out_pan", args);
    filter_graph_add_link(aGraph, amerge, 0, pan, 0)
    filter_graph_add_link(aGraph, pan, 0, aFilterCtx_out, 0)
    if (filter_graph_complete(aGraph) < 0)
    {
        LLOG(LOG_ERROR, "init_audio_mix_rightleft_graph filter_graph_complete aGraph error");
        return;
    }

遇到的问题

swr_convert 函数中崩
查看原因,由于转换的时候,设置的双通道,但是只有一个通道有数据,swr_convert获取另一个通道数据发生错误

输入数据fmt为AV_SAMPLE_FMT_S32P,存储模式是Planar模式,但是经过amerge后,变成了packet模式,导致通道读取失败

这篇博客 介绍了planar 和 packet区别
问题分析:
命令行日志:
在这里插入图片描述
这里经过了两次auto_resampler 第一次 将fltp–>flt 第二次将 flt–>fltp
在api 中看日志 只有一次auto_resampler,导致输入输出 fmt 错误

解决方法:
第一种:
在swr_init 的时候,swr_alloc_set_opts 中,将输入fmt写为AV_SAMPLE_FMT_S32 模式。

第二种:
在输出滤镜末尾,再加一个aformat滤镜,自动resample,比较好

    static AVFilterContext *aformat = NULL;
    snprintf(args, 1024,
    "sample_fmts=%s", av_get_sample_fmt_name(audio_codec->sample_fmt)); //format output

    aformat = filter_new(aGraph, "aformat", "aforamt_out", args);
    filter_graph_add_link(aGraph, pan, 0, aformat, 0);

    if (filter_graph_add_link(aGraph, aformat, 0, aFilterCtx_out, 0))
    {
        LLOG(LOG_ERROR, "init_audio_mix_rightleft_graph filter_graph_add_link aOut error");
        return;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值