ffmpeg命令分析-map_channel - 弦外之音

/ 0评 / 0

本文 以 ffmpeg4.4 源码为准,a.mp4下载链接:百度网盘,提取码:nl0s 。


命令如下:

ffmpeg.exe -i a.mp4 -map_channel 0.1.0 left.aac -map_channel 0.1.1 right.aac

上面的命令 是把 a.mp4 里面的左右声道分布存储在 left.aac ,right.aac 里面。

下面主要分析 一下 -map_channel 0.1.0 参数的作用。

在 ffmpeg_opt.c 的定义如下:

{ "map_channel",    HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_map_channel },
    "map an audio channel from one stream to another", "file.stream.channel[:syncfile.syncstream]" },

从定义得知,0.1.0 的意思如下:

从上面定义可以看出,-map_channel 会调用 opt_map_channel() 函数,这个函数的逻辑比较简单,不进行分析了,自动查看即可。

opt_map_channel() 里面做的是就是 操作两个变量,o->audio_channel_mapso->nb_audio_channel_maps,定义如下:

typedef struct {
    int  file_idx,  stream_idx,  channel_idx; // input
    int ofile_idx, ostream_idx;               // output
} AudioChannelMap;


下面继续分析 ,audio_channel_maps 变量在什么地方用到。全局搜索可知,audio_channel_mapsnew_audio_stream() 函数里面使用了。

new_audio_stream() 函数相对来说也是比较简单,主要就是把 o->audio_channel_maps 转移给 ost->audio_channels_map,重点代码如下:

//ffmpeg_opt.c 1964行
ost->audio_channels_map[ost->audio_channels_mapped++] = map->channel_idx;

ost->audio_channels_map 的作用是这样的,音频流(输出)的声道 默认是 取 的 输入音频流的全部声道,这个 ost->audio_channels_map 就是表明,我这个输出流 只取 输入流的哪些声道。


下面继续分析 ost->audio_channels_map 在哪些地方有被使用。全局搜索可知,ost->audio_channels_map 主要在 ffmpeg_filter.c 文件的 configure_output_audio_filter() 函数里面使用,重点代码如下:

#define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do {                 \
    AVFilterContext *filt_ctx;                                              \
                                                                            \
    av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi "            \
           "similarly to -af " filter_name "=%s.\n", arg);                  \
                                                                            \
    ret = avfilter_graph_create_filter(&filt_ctx,                           \
                                       avfilter_get_by_name(filter_name),   \
                                       filter_name, arg, NULL, fg->graph);  \
    if (ret < 0)                                                            \
        return ret;                                                         \
                                                                            \
    ret = avfilter_link(last_filter, pad_idx, filt_ctx, 0);                 \
    if (ret < 0)                                                            \
        return ret;                                                         \
                                                                            \
    last_filter = filt_ctx;                                                 \
    pad_idx = 0;                                                            \
} while (0)
//ffmpeg_filter.c 567行
if (ost->audio_channels_mapped) {
        int i;
        AVBPrint pan_buf;
        av_bprint_init(&pan_buf, 256, 8192);
        av_bprintf(&pan_buf, "0x%"PRIx64,
                   av_get_default_channel_layout(ost->audio_channels_mapped));
        for (i = 0; i < ost->audio_channels_mapped; i++)
            if (ost->audio_channels_map[i] != -1)
                av_bprintf(&pan_buf, "|c%d=c%d", i, ost->audio_channels_map[i]);
​
        AUTO_INSERT_FILTER("-map_channel", "pan", pan_buf.str);
        av_bprint_finalize(&pan_buf, NULL);
   }

从上面代码可以看出 调用了 pan filter 来实现这个这个 channel map 的功能。从代码的 log 可以看出,-map_channel 0.1.0 是一种过时的写法,主要为了与 lavfi 进行兼容,新的命令行语法如下:

ffmpeg.exe  -i a.mp4 -af pan="0x4|c0=c0" left.aac -af pan="0x4|c0=c1" right.aac -y

上面的 0x4 声道布局的描述,是 AV_CH_FRONT_CENTER 的宏定义,如下:

#define AV_CH_FRONT_CENTER           0x00000004

声道数 对应 的声道布局 channel_layout_map 的定义在 源码 libavutil\channel_layout.c 里面,代码如下:

static const struct {
    const char *name;
    int         nb_channels;
    uint64_t     layout;
} channel_layout_map[] = {
    { "mono",        1,  AV_CH_LAYOUT_MONO },
    { "stereo",      2,  AV_CH_LAYOUT_STEREO },
    { "2.1",         3,  AV_CH_LAYOUT_2POINT1 },
    { "3.0",         3,  AV_CH_LAYOUT_SURROUND },
    { "3.0(back)",   3,  AV_CH_LAYOUT_2_1 },
    { "4.0",         4,  AV_CH_LAYOUT_4POINT0 },
    { "quad",        4,  AV_CH_LAYOUT_QUAD },
    { "quad(side)",  4,  AV_CH_LAYOUT_2_2 },
    { "3.1",         4,  AV_CH_LAYOUT_3POINT1 },
    { "5.0",         5,  AV_CH_LAYOUT_5POINT0_BACK },
    { "5.0(side)",   5,  AV_CH_LAYOUT_5POINT0 },
    { "4.1",         5,  AV_CH_LAYOUT_4POINT1 },
    { "5.1",         6,  AV_CH_LAYOUT_5POINT1_BACK },
    { "5.1(side)",   6,  AV_CH_LAYOUT_5POINT1 },
    { "6.0",         6,  AV_CH_LAYOUT_6POINT0 },
    { "6.0(front)",  6,  AV_CH_LAYOUT_6POINT0_FRONT },
    { "hexagonal",   6,  AV_CH_LAYOUT_HEXAGONAL },
    { "6.1",         7,  AV_CH_LAYOUT_6POINT1 },
    { "6.1(back)",   7,  AV_CH_LAYOUT_6POINT1_BACK },
    { "6.1(front)",  7,  AV_CH_LAYOUT_6POINT1_FRONT },
    { "7.0",         7,  AV_CH_LAYOUT_7POINT0 },
    { "7.0(front)",  7,  AV_CH_LAYOUT_7POINT0_FRONT },
    { "7.1",         8,  AV_CH_LAYOUT_7POINT1 },
    { "7.1(wide)",   8,  AV_CH_LAYOUT_7POINT1_WIDE_BACK },
    { "7.1(wide-side)",   8,  AV_CH_LAYOUT_7POINT1_WIDE },
    { "octagonal",   8,  AV_CH_LAYOUT_OCTAGONAL },
    { "hexadecagonal", 16, AV_CH_LAYOUT_HEXADECAGONAL },
    { "downmix",     2,  AV_CH_LAYOUT_STEREO_DOWNMIX, },
    { "22.2",          24, AV_CH_LAYOUT_22POINT2, },
};

map_channel 分析完毕,总结:通过 pan filter 实现。

本文命令最后还有一个知识点没有分析,就是 ffmpeg 命令行输出多文件的逻辑,请看《ffmpeg命令分析-输出多文件》


版权所属:知识星球:弦外之音,QQ:2338195090。 由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注