Linux Gstreamer深度解析之gst_audio_channel_positions_to_mask调用流程与实战(十七)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀

Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀

Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀

专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀

专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀

专题四:Android15快速自定义与集成音效实战课 🚀

专题五:Android15音频策略实战课 🚀

专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • 🌻1.前言
      • 🌻2.应用场景和用法
        • [🌻2.1 应用场景](#🌻2.1 应用场景)
        • [🌻2.2 函数用法](#🌻2.2 函数用法)
      • 🌻3.调用流程剖析
        • [🌻3.1 channel-mask解析流程](#🌻3.1 channel-mask解析流程)
        • [🌻3.2 特殊Mask和结果处理流程](#🌻3.2 特殊Mask和结果处理流程)
      • 🌻4.实战案例
        • [🌻4.1 构造5.1声道Mask](#🌻4.1 构造5.1声道Mask)
        • [🌻4.2 转换声道位置](#🌻4.2 转换声道位置)
        • [🌻4.3 验证转换结果](#🌻4.3 验证转换结果)
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_channel_positions_from_mask()如何把channel-mask位掩码转换成GstAudioChannelPosition声道位置数组。

在GStreamer多声道音频中,仅知道:

text 复制代码
channels = 6

只能说明存在6个声道,却不能说明这6个声道分别是什么。

它们可能包含:

text 复制代码
Front Left
Front Right
Front Center
LFE
Rear Left
Rear Right

GStreamer通常使用channel-mask紧凑表示一组声道位置。在这个64位整数中,每一个Bit对应一种GstAudioChannelPosition。官方文档明确说明,Caps中的实际声道布局由声道数和channel-mask共同描述,位掩码中的Bit位置对应声道位置枚举。

例如:

text 复制代码
channel-mask = 0x3

二进制:
0000 0011
       ↑↑
       │└─ bit0:FRONT_LEFT
       └── bit1:FRONT_RIGHT

但Element内部真正处理音频时,通常更方便使用:

cpp 复制代码
GstAudioChannelPosition position[];

因此需要:

cpp 复制代码
gst_audio_channel_positions_from_mask()

完成:

text 复制代码
64位channel-mask
        ↓
声道位置数组

整体流程:
#mermaid-svg-eV01NFsW5GGNiiRK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eV01NFsW5GGNiiRK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eV01NFsW5GGNiiRK .error-icon{fill:#552222;}#mermaid-svg-eV01NFsW5GGNiiRK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eV01NFsW5GGNiiRK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eV01NFsW5GGNiiRK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eV01NFsW5GGNiiRK .marker.cross{stroke:#333333;}#mermaid-svg-eV01NFsW5GGNiiRK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eV01NFsW5GGNiiRK p{margin:0;}#mermaid-svg-eV01NFsW5GGNiiRK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eV01NFsW5GGNiiRK .cluster-label text{fill:#333;}#mermaid-svg-eV01NFsW5GGNiiRK .cluster-label span{color:#333;}#mermaid-svg-eV01NFsW5GGNiiRK .cluster-label span p{background-color:transparent;}#mermaid-svg-eV01NFsW5GGNiiRK .label text,#mermaid-svg-eV01NFsW5GGNiiRK span{fill:#333;color:#333;}#mermaid-svg-eV01NFsW5GGNiiRK .node rect,#mermaid-svg-eV01NFsW5GGNiiRK .node circle,#mermaid-svg-eV01NFsW5GGNiiRK .node ellipse,#mermaid-svg-eV01NFsW5GGNiiRK .node polygon,#mermaid-svg-eV01NFsW5GGNiiRK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eV01NFsW5GGNiiRK .rough-node .label text,#mermaid-svg-eV01NFsW5GGNiiRK .node .label text,#mermaid-svg-eV01NFsW5GGNiiRK .image-shape .label,#mermaid-svg-eV01NFsW5GGNiiRK .icon-shape .label{text-anchor:middle;}#mermaid-svg-eV01NFsW5GGNiiRK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eV01NFsW5GGNiiRK .rough-node .label,#mermaid-svg-eV01NFsW5GGNiiRK .node .label,#mermaid-svg-eV01NFsW5GGNiiRK .image-shape .label,#mermaid-svg-eV01NFsW5GGNiiRK .icon-shape .label{text-align:center;}#mermaid-svg-eV01NFsW5GGNiiRK .node.clickable{cursor:pointer;}#mermaid-svg-eV01NFsW5GGNiiRK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eV01NFsW5GGNiiRK .arrowheadPath{fill:#333333;}#mermaid-svg-eV01NFsW5GGNiiRK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eV01NFsW5GGNiiRK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eV01NFsW5GGNiiRK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eV01NFsW5GGNiiRK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eV01NFsW5GGNiiRK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eV01NFsW5GGNiiRK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eV01NFsW5GGNiiRK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eV01NFsW5GGNiiRK .cluster text{fill:#333;}#mermaid-svg-eV01NFsW5GGNiiRK .cluster span{color:#333;}#mermaid-svg-eV01NFsW5GGNiiRK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eV01NFsW5GGNiiRK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eV01NFsW5GGNiiRK rect.text{fill:none;stroke-width:0;}#mermaid-svg-eV01NFsW5GGNiiRK .icon-shape,#mermaid-svg-eV01NFsW5GGNiiRK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eV01NFsW5GGNiiRK .icon-shape p,#mermaid-svg-eV01NFsW5GGNiiRK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eV01NFsW5GGNiiRK .icon-shape .label rect,#mermaid-svg-eV01NFsW5GGNiiRK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eV01NFsW5GGNiiRK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eV01NFsW5GGNiiRK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eV01NFsW5GGNiiRK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① channels + mask
② 检查channel-mask
③ 扫描64个Bit
④ 映射声道位置
⑤ position数组

可以把它简单理解成:

text 复制代码
channel-mask = 一张座位表

Bit置1
    =
这个位置有人

gst_audio_channel_positions_from_mask()
    =
把座位表展开成具体座位名单

最终得到:

text 复制代码
position[0] = FRONT_LEFT
position[1] = FRONT_RIGHT
...

🌻2.应用场景和用法

🌻2.1 应用场景

多声道音频不能只靠channels描述。

例如:

text 复制代码
channels = 2

通常可以理解成:

text 复制代码
Front Left
Front Right

但是:

text 复制代码
channels = 6

仅仅表示存在6路音频数据。

它并不能回答:

text 复制代码
第0路是什么?
第1路是什么?
有没有Center?
有没有LFE?
后置声道还是侧置声道?

所以GStreamer还需要描述Channel Position。

常见声道位置包括:

枚举 含义
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT 前左声道
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT 前右声道
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER 前中置
GST_AUDIO_CHANNEL_POSITION_LFE1 低频效果声道
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT 后左声道
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT 后右声道
GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT 侧左声道
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT 侧右声道

这些正常声道位置的枚举值同时用于确定channel-mask中的Bit位置,例如FRONT_LEFT=0FRONT_RIGHT=1FRONT_CENTER=2LFE1=3

因此:

text 复制代码
FRONT_LEFT
    ↓
enum = 0
    ↓
bit0 = 1

而:

text 复制代码
FRONT_RIGHT
    ↓
enum = 1
    ↓
bit1 = 1

所以标准双声道:

text 复制代码
FL + FR

bit1 bit0
 1    1

channel-mask = 0x3

对象关系可以表示为:
#mermaid-svg-OmzKRQZgOp5zWnaF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OmzKRQZgOp5zWnaF .error-icon{fill:#552222;}#mermaid-svg-OmzKRQZgOp5zWnaF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OmzKRQZgOp5zWnaF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OmzKRQZgOp5zWnaF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OmzKRQZgOp5zWnaF .marker.cross{stroke:#333333;}#mermaid-svg-OmzKRQZgOp5zWnaF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OmzKRQZgOp5zWnaF p{margin:0;}#mermaid-svg-OmzKRQZgOp5zWnaF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster-label text{fill:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster-label span{color:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster-label span p{background-color:transparent;}#mermaid-svg-OmzKRQZgOp5zWnaF .label text,#mermaid-svg-OmzKRQZgOp5zWnaF span{fill:#333;color:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF .node rect,#mermaid-svg-OmzKRQZgOp5zWnaF .node circle,#mermaid-svg-OmzKRQZgOp5zWnaF .node ellipse,#mermaid-svg-OmzKRQZgOp5zWnaF .node polygon,#mermaid-svg-OmzKRQZgOp5zWnaF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OmzKRQZgOp5zWnaF .rough-node .label text,#mermaid-svg-OmzKRQZgOp5zWnaF .node .label text,#mermaid-svg-OmzKRQZgOp5zWnaF .image-shape .label,#mermaid-svg-OmzKRQZgOp5zWnaF .icon-shape .label{text-anchor:middle;}#mermaid-svg-OmzKRQZgOp5zWnaF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OmzKRQZgOp5zWnaF .rough-node .label,#mermaid-svg-OmzKRQZgOp5zWnaF .node .label,#mermaid-svg-OmzKRQZgOp5zWnaF .image-shape .label,#mermaid-svg-OmzKRQZgOp5zWnaF .icon-shape .label{text-align:center;}#mermaid-svg-OmzKRQZgOp5zWnaF .node.clickable{cursor:pointer;}#mermaid-svg-OmzKRQZgOp5zWnaF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OmzKRQZgOp5zWnaF .arrowheadPath{fill:#333333;}#mermaid-svg-OmzKRQZgOp5zWnaF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OmzKRQZgOp5zWnaF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OmzKRQZgOp5zWnaF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OmzKRQZgOp5zWnaF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OmzKRQZgOp5zWnaF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OmzKRQZgOp5zWnaF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster text{fill:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF .cluster span{color:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OmzKRQZgOp5zWnaF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OmzKRQZgOp5zWnaF rect.text{fill:none;stroke-width:0;}#mermaid-svg-OmzKRQZgOp5zWnaF .icon-shape,#mermaid-svg-OmzKRQZgOp5zWnaF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OmzKRQZgOp5zWnaF .icon-shape p,#mermaid-svg-OmzKRQZgOp5zWnaF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OmzKRQZgOp5zWnaF .icon-shape .label rect,#mermaid-svg-OmzKRQZgOp5zWnaF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OmzKRQZgOp5zWnaF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OmzKRQZgOp5zWnaF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OmzKRQZgOp5zWnaF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} channel-mask

0x3
Bit0
Bit1
FRONT_LEFT
FRONT_RIGHT

gst_audio_channel_positions_from_mask()就是负责把这种紧凑的位掩码转换成程序可以直接访问的声道位置数组。

典型场景包括:

text 复制代码
GstCaps
    ↓
channels
channel-mask
    ↓
解析声道布局
    ↓
GstAudioChannelPosition[]

GStreamer自己的gst_audio_info_from_caps()也会在解析非0的channel-mask时调用该函数,然后再使用得到的Position数组配置GstAudioInfo


🌻2.2 函数用法

函数原型:

cpp 复制代码
gboolean
gst_audio_channel_positions_from_mask (
    gint channels,
    guint64 channel_mask,
    GstAudioChannelPosition *position);

参数:

参数 作用
channels 当前音频的声道数量
channel_mask 描述有哪些声道位置的64位掩码
position 调用者提供的输出数组,至少能够保存channels个元素

返回值:

text 复制代码
TRUE

表示当前声道数量和Mask能够由该接口处理。

text 复制代码
FALSE

表示无法完成转换。官方接口说明同时指出,调用者必须保证position至少具有channels个元素。

最简单的双声道示例:

cpp 复制代码
GstAudioChannelPosition position[2];

gboolean ret =
    gst_audio_channel_positions_from_mask(
        2,
        0x3,
        position);

输入:

text 复制代码
channels     = 2
channel_mask = 0x3

0x3 =
0000 0011
       ↑↑
       │└─ FRONT_LEFT
       └── FRONT_RIGHT

输出:

text 复制代码
position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT
position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT

转换关系:

text 复制代码
0x3
 ↓
Bit0 = 1
Bit1 = 1
 ↓
FRONT_LEFT
FRONT_RIGHT

这个函数并不创建新的数组。

数组:

cpp 复制代码
GstAudioChannelPosition position[2];

由调用者准备。

函数只是把转换结果写进去。


🌻3.调用流程剖析

🌻3.1 channel-mask解析流程

进入:

cpp 复制代码
gst_audio_channel_positions_from_mask(
        channels,
        channel_mask,
        position);

以后,函数首先检查两个基本条件:

text 复制代码
position != NULL
channels != 0

否则直接返回FALSE

之后根据:

text 复制代码
channel_mask == 0

还是:

text 复制代码
channel_mask != 0

进入两条不同路径。

主流程:
#mermaid-svg-JEsyywULFOVD0hxu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JEsyywULFOVD0hxu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JEsyywULFOVD0hxu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JEsyywULFOVD0hxu .error-icon{fill:#552222;}#mermaid-svg-JEsyywULFOVD0hxu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JEsyywULFOVD0hxu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JEsyywULFOVD0hxu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JEsyywULFOVD0hxu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JEsyywULFOVD0hxu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JEsyywULFOVD0hxu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JEsyywULFOVD0hxu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JEsyywULFOVD0hxu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JEsyywULFOVD0hxu .marker.cross{stroke:#333333;}#mermaid-svg-JEsyywULFOVD0hxu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JEsyywULFOVD0hxu p{margin:0;}#mermaid-svg-JEsyywULFOVD0hxu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JEsyywULFOVD0hxu .cluster-label text{fill:#333;}#mermaid-svg-JEsyywULFOVD0hxu .cluster-label span{color:#333;}#mermaid-svg-JEsyywULFOVD0hxu .cluster-label span p{background-color:transparent;}#mermaid-svg-JEsyywULFOVD0hxu .label text,#mermaid-svg-JEsyywULFOVD0hxu span{fill:#333;color:#333;}#mermaid-svg-JEsyywULFOVD0hxu .node rect,#mermaid-svg-JEsyywULFOVD0hxu .node circle,#mermaid-svg-JEsyywULFOVD0hxu .node ellipse,#mermaid-svg-JEsyywULFOVD0hxu .node polygon,#mermaid-svg-JEsyywULFOVD0hxu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JEsyywULFOVD0hxu .rough-node .label text,#mermaid-svg-JEsyywULFOVD0hxu .node .label text,#mermaid-svg-JEsyywULFOVD0hxu .image-shape .label,#mermaid-svg-JEsyywULFOVD0hxu .icon-shape .label{text-anchor:middle;}#mermaid-svg-JEsyywULFOVD0hxu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JEsyywULFOVD0hxu .rough-node .label,#mermaid-svg-JEsyywULFOVD0hxu .node .label,#mermaid-svg-JEsyywULFOVD0hxu .image-shape .label,#mermaid-svg-JEsyywULFOVD0hxu .icon-shape .label{text-align:center;}#mermaid-svg-JEsyywULFOVD0hxu .node.clickable{cursor:pointer;}#mermaid-svg-JEsyywULFOVD0hxu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JEsyywULFOVD0hxu .arrowheadPath{fill:#333333;}#mermaid-svg-JEsyywULFOVD0hxu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JEsyywULFOVD0hxu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JEsyywULFOVD0hxu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JEsyywULFOVD0hxu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JEsyywULFOVD0hxu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JEsyywULFOVD0hxu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JEsyywULFOVD0hxu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JEsyywULFOVD0hxu .cluster text{fill:#333;}#mermaid-svg-JEsyywULFOVD0hxu .cluster span{color:#333;}#mermaid-svg-JEsyywULFOVD0hxu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JEsyywULFOVD0hxu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JEsyywULFOVD0hxu rect.text{fill:none;stroke-width:0;}#mermaid-svg-JEsyywULFOVD0hxu .icon-shape,#mermaid-svg-JEsyywULFOVD0hxu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JEsyywULFOVD0hxu .icon-shape p,#mermaid-svg-JEsyywULFOVD0hxu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JEsyywULFOVD0hxu .icon-shape .label rect,#mermaid-svg-JEsyywULFOVD0hxu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JEsyywULFOVD0hxu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JEsyywULFOVD0hxu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JEsyywULFOVD0hxu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

channels + mask
mask == 0?
Mono/Stereo特殊处理
扫描64个Bit
写入position

对于非0的channel_mask,源码核心逻辑可以简化成:

cpp 复制代码
j = 0;

for (i = 0; i < 64; i++) {
    if (channel_mask & (G_GUINT64_CONSTANT(1) << i)) {
        if (j < channels)
            position[j] = default_channel_order[i];

        j++;
    }
}

核心实际上只做了三件事。

第一步:

text 复制代码
i = 0 → 63

依次扫描channel_mask的64个Bit。

第二步:

text 复制代码
当前Bit == 1

说明这个声道位置存在。

第三步,通过:

cpp 复制代码
default_channel_order[i]

找到这个Bit对应的:

text 复制代码
GstAudioChannelPosition

然后按顺序写进:

cpp 复制代码
position[j]

例如:

text 复制代码
channel_mask = 0x3F

二进制低6位:

text 复制代码
bit5 bit4 bit3 bit2 bit1 bit0
 1    1    1    1    1    1

对应:

text 复制代码
bit0 → FRONT_LEFT
bit1 → FRONT_RIGHT
bit2 → FRONT_CENTER
bit3 → LFE1
bit4 → REAR_LEFT
bit5 → REAR_RIGHT

所以:

text 复制代码
position[0] = FRONT_LEFT
position[1] = FRONT_RIGHT
position[2] = FRONT_CENTER
position[3] = LFE1
position[4] = REAR_LEFT
position[5] = REAR_RIGHT

源码中的default_channel_order[64]就是Bit索引到GstAudioChannelPosition的映射表,其前几项依次为FRONT_LEFTFRONT_RIGHTFRONT_CENTERLFE1REAR_LEFTREAR_RIGHT等。

因此:

text 复制代码
channel-mask
      ↓
扫描哪几个Bit为1
      ↓
default_channel_order[bit]
      ↓
GstAudioChannelPosition
      ↓
position[]

这就是该函数的核心机制。


🌻3.2 特殊Mask和结果处理流程

channel_mask == 0是这个函数最容易产生误解的地方。

官方说明:

text 复制代码
channel_mask = 0

在该函数中按"没有提供Channel Mask"处理。

但Mono和Stereo有特殊规则。

源码逻辑可以简化成:

cpp 复制代码
if (!channel_mask) {
    if (channels == 1) {
        position[0] =
            GST_AUDIO_CHANNEL_POSITION_MONO;
    } else if (channels == 2) {
        position[0] =
            GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
        position[1] =
            GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
    } else {
        return FALSE;
    }
}

也就是说:
#mermaid-svg-9PbMfyjeqt79dt6R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9PbMfyjeqt79dt6R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9PbMfyjeqt79dt6R .error-icon{fill:#552222;}#mermaid-svg-9PbMfyjeqt79dt6R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9PbMfyjeqt79dt6R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9PbMfyjeqt79dt6R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9PbMfyjeqt79dt6R .marker.cross{stroke:#333333;}#mermaid-svg-9PbMfyjeqt79dt6R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9PbMfyjeqt79dt6R p{margin:0;}#mermaid-svg-9PbMfyjeqt79dt6R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9PbMfyjeqt79dt6R .cluster-label text{fill:#333;}#mermaid-svg-9PbMfyjeqt79dt6R .cluster-label span{color:#333;}#mermaid-svg-9PbMfyjeqt79dt6R .cluster-label span p{background-color:transparent;}#mermaid-svg-9PbMfyjeqt79dt6R .label text,#mermaid-svg-9PbMfyjeqt79dt6R span{fill:#333;color:#333;}#mermaid-svg-9PbMfyjeqt79dt6R .node rect,#mermaid-svg-9PbMfyjeqt79dt6R .node circle,#mermaid-svg-9PbMfyjeqt79dt6R .node ellipse,#mermaid-svg-9PbMfyjeqt79dt6R .node polygon,#mermaid-svg-9PbMfyjeqt79dt6R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9PbMfyjeqt79dt6R .rough-node .label text,#mermaid-svg-9PbMfyjeqt79dt6R .node .label text,#mermaid-svg-9PbMfyjeqt79dt6R .image-shape .label,#mermaid-svg-9PbMfyjeqt79dt6R .icon-shape .label{text-anchor:middle;}#mermaid-svg-9PbMfyjeqt79dt6R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9PbMfyjeqt79dt6R .rough-node .label,#mermaid-svg-9PbMfyjeqt79dt6R .node .label,#mermaid-svg-9PbMfyjeqt79dt6R .image-shape .label,#mermaid-svg-9PbMfyjeqt79dt6R .icon-shape .label{text-align:center;}#mermaid-svg-9PbMfyjeqt79dt6R .node.clickable{cursor:pointer;}#mermaid-svg-9PbMfyjeqt79dt6R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9PbMfyjeqt79dt6R .arrowheadPath{fill:#333333;}#mermaid-svg-9PbMfyjeqt79dt6R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9PbMfyjeqt79dt6R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9PbMfyjeqt79dt6R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9PbMfyjeqt79dt6R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9PbMfyjeqt79dt6R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9PbMfyjeqt79dt6R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9PbMfyjeqt79dt6R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9PbMfyjeqt79dt6R .cluster text{fill:#333;}#mermaid-svg-9PbMfyjeqt79dt6R .cluster span{color:#333;}#mermaid-svg-9PbMfyjeqt79dt6R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9PbMfyjeqt79dt6R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9PbMfyjeqt79dt6R rect.text{fill:none;stroke-width:0;}#mermaid-svg-9PbMfyjeqt79dt6R .icon-shape,#mermaid-svg-9PbMfyjeqt79dt6R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9PbMfyjeqt79dt6R .icon-shape p,#mermaid-svg-9PbMfyjeqt79dt6R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9PbMfyjeqt79dt6R .icon-shape .label rect,#mermaid-svg-9PbMfyjeqt79dt6R .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9PbMfyjeqt79dt6R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9PbMfyjeqt79dt6R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9PbMfyjeqt79dt6R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
其他
channel-mask = 0
channels
1ch → MONO
2ch → FL + FR
>2ch → FALSE

Mono:

text 复制代码
channels = 1
mask     = 0

转换成:

text 复制代码
position[0] = MONO

这是因为MONO表示没有方向信息的单声道,本身不是普通的Bit位置。官方明确规定,GST_AUDIO_CHANNEL_POSITION_MONO只用于一个没有方向信息的单声道,并在Caps中通过单声道且没有Channel Mask表示。

Stereo:

text 复制代码
channels = 2
mask     = 0

则默认解释成:

text 复制代码
FRONT_LEFT
FRONT_RIGHT

这也是GStreamer定义的特殊情况:两个声道没有Channel Mask时,隐含表示标准前左、前右Stereo。

但如果:

text 复制代码
channels = 6
mask     = 0

该函数无法知道6个声道分别位于哪里,因此执行失败:

text 复制代码
return FALSE

还有一个需要特别注意的源码行为:

text 复制代码
Mask中置1的Bit数量
        ≠
channels

并不一定导致函数返回FALSE

例如:

text 复制代码
channels = 6

但Mask只有:

text 复制代码
FL + FR

源码扫描结束后发现:

text 复制代码
j != channels

会打印:

text 复制代码
Only partially valid channel mask ...

但随后仍然:

cpp 复制代码
return TRUE;

官方文档也明确说明,Bit数量少于声道数量的"部分有效Mask"仍被该函数视为可转换。

因此不能简单写成:

text 复制代码
ret == TRUE
    ↓
position[0...channels-1]一定全部有效

更准确地说:

text 复制代码
ret == TRUE
    ↓
这个Mask通过了该函数的转换规则

对于实际已经协商完成的Raw Audio Caps,正常情况下仍应保证声道数和有效Channel Mask一致;GStreamer文档指出,"Mask中允许比channels更多的Bit"主要用于协商阶段表达允许的Channel Position集合,而协商完成的Caps不应保留这种不一致状态。

另一个重要关系是:

text 复制代码
GstCaps
  │
  ├── channels
  └── channel-mask
          ↓
gst_audio_info_from_caps()
          ↓
gst_audio_channel_positions_from_mask()
          ↓
position[]
          ↓
GstAudioInfo

源码中gst_audio_info_from_caps()读取channel-mask后,当Mask为非0时就会调用gst_audio_channel_positions_from_mask(),再把得到的位置数组交给gst_audio_info_set_format()

因此这个函数虽然很小,却处在:

text 复制代码
Caps声道描述
        ↓
GstAudioInfo声道位置

这条转换链路的关键位置。


🌻4.实战案例

🌻4.1 构造5.1声道Mask

下面通过一个最小程序,把一个5.1声道channel-mask转换成GstAudioChannelPosition数组。

5.1声道使用:

text 复制代码
Front Left
Front Right
Front Center
LFE1
Rear Left
Rear Right

对应Bit:

text 复制代码
FL  → bit0
FR  → bit1
FC  → bit2
LFE → bit3
RL  → bit4
RR  → bit5

因此可以构造:

cpp 复制代码
guint64 channel_mask =
    GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_LEFT) |
    GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_RIGHT) |
    GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_CENTER) |
    GST_AUDIO_CHANNEL_POSITION_MASK(LFE1) |
    GST_AUDIO_CHANNEL_POSITION_MASK(REAR_LEFT) |
    GST_AUDIO_CHANNEL_POSITION_MASK(REAR_RIGHT);

GST_AUDIO_CHANNEL_POSITION_MASK(pos)本质就是根据Channel Position建立对应的64位Bit。官方宏定义的核心形式是把1左移到相应Position枚举值的位置。

这组声道对应:

text 复制代码
bit5~bit0 = 111111

因此:

text 复制代码
channel_mask = 0x3F

🌻4.2 转换声道位置

准备6个声道位置:

cpp 复制代码
GstAudioChannelPosition position[6];

然后执行:

cpp 复制代码
gboolean ret;

ret = gst_audio_channel_positions_from_mask(
        6,
        channel_mask,
        position);

核心流程:
#mermaid-svg-JU2mBgF6Dlf3k5nZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .error-icon{fill:#552222;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .marker.cross{stroke:#333333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ p{margin:0;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster-label text{fill:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster-label span{color:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster-label span p{background-color:transparent;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .label text,#mermaid-svg-JU2mBgF6Dlf3k5nZ span{fill:#333;color:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .node rect,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node circle,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node ellipse,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node polygon,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .rough-node .label text,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node .label text,#mermaid-svg-JU2mBgF6Dlf3k5nZ .image-shape .label,#mermaid-svg-JU2mBgF6Dlf3k5nZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .rough-node .label,#mermaid-svg-JU2mBgF6Dlf3k5nZ .node .label,#mermaid-svg-JU2mBgF6Dlf3k5nZ .image-shape .label,#mermaid-svg-JU2mBgF6Dlf3k5nZ .icon-shape .label{text-align:center;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .node.clickable{cursor:pointer;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .arrowheadPath{fill:#333333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JU2mBgF6Dlf3k5nZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JU2mBgF6Dlf3k5nZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster text{fill:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .cluster span{color:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JU2mBgF6Dlf3k5nZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .icon-shape,#mermaid-svg-JU2mBgF6Dlf3k5nZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .icon-shape p,#mermaid-svg-JU2mBgF6Dlf3k5nZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .icon-shape .label rect,#mermaid-svg-JU2mBgF6Dlf3k5nZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JU2mBgF6Dlf3k5nZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JU2mBgF6Dlf3k5nZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JU2mBgF6Dlf3k5nZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 6ch + 0x3F
扫描Bit0~63
发现Bit0~5
映射Channel Position
position6

完整测试代码:

cpp 复制代码
#include <gst/gst.h>
#include <gst/audio/audio.h>

int main(int argc, char *argv[])
{
    GstAudioChannelPosition position[6];
    guint64 channel_mask;
    gchar *str;
    gboolean ret;

    gst_init(&argc, &argv);

    channel_mask =
        GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_LEFT) |
        GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_RIGHT) |
        GST_AUDIO_CHANNEL_POSITION_MASK(FRONT_CENTER) |
        GST_AUDIO_CHANNEL_POSITION_MASK(LFE1) |
        GST_AUDIO_CHANNEL_POSITION_MASK(REAR_LEFT) |
        GST_AUDIO_CHANNEL_POSITION_MASK(REAR_RIGHT);

    ret = gst_audio_channel_positions_from_mask(
        6,
        channel_mask,
        position);

    if (!ret) {
        g_printerr("convert channel mask failed\n");
        return -1;
    }

    str = gst_audio_channel_positions_to_string(
        position,
        6);

    g_print("channel-mask = 0x%016" G_GINT64_MODIFIER "x\n",
            channel_mask);

    g_print("positions = %s\n", str);

    g_free(str);

    return 0;
}

这里使用:

cpp 复制代码
gst_audio_channel_positions_to_string()

只是为了把Position数组转成人类容易阅读的字符串,方便验证转换结果。

真正被验证的核心接口仍然是:

cpp 复制代码
gst_audio_channel_positions_from_mask()

🌻4.3 验证转换结果

编译:

bash 复制代码
gcc test_channel_mask.c \
    -o test_channel_mask \
    $(pkg-config --cflags --libs \
      gstreamer-1.0 \
      gstreamer-audio-1.0)

运行:

bash 复制代码
./test_channel_mask

核心转换关系应该是:

text 复制代码
channel-mask = 0x3F
        ↓
bit0
bit1
bit2
bit3
bit4
bit5
        ↓
FRONT_LEFT
FRONT_RIGHT
FRONT_CENTER
LFE1
REAR_LEFT
REAR_RIGHT

因此position数组最终为:

text 复制代码
position[0] = FRONT_LEFT
position[1] = FRONT_RIGHT
position[2] = FRONT_CENTER
position[3] = LFE1
position[4] = REAR_LEFT
position[5] = REAR_RIGHT

使用gst_audio_channel_positions_to_string()打印时,这些位置会以简写形式表示,例如:

text 复制代码
FL
FR
FC
LFE1
RL
RR

源码中的调试字符串映射确实把这些Channel Position分别表示为FLFRFCLFE1RLRR

整个实战最终验证:

text 复制代码
0x3F
 ↓
64位Bit Mask
 ↓
gst_audio_channel_positions_from_mask()
 ↓
GstAudioChannelPosition[6]
 ↓
FL / FR / FC / LFE1 / RL / RR

因此,channel-mask解决的是:

text 复制代码
"有哪些声道位置"

而:

cpp 复制代码
GstAudioChannelPosition position[]

解决的是:

text 复制代码
"按照GStreamer的声道顺序,把这些位置展开成可以直接访问的数组"

🌻5.总结

gst_audio_channel_positions_from_mask()就是解析64位channel-mask中置1的声道位置,并按照GStreamer声道顺序将其转换成GstAudioChannelPosition数组。

相关推荐
hanbo17C23 小时前
企业官网能不能看出公司实力?选建站公司前,先看这6条硬标准
运维·服务器·前端
小程序设计3 小时前
基于某数据管理系统的目标遍历漏洞成因与防御设计技术与实现
运维·网络
33三 三like3 小时前
Jiangxi
linux·运维·服务器
昇腾知识体系3 小时前
昇腾 Atlas 800I A5 服务器:机型定位与部署入口
服务器·人工智能·华为·架构·知识图谱
2601_953988073 小时前
Ricon组态 - 让数据可视化如此简单
运维·后端·物联网·数学建模·前端框架·c4前端
小马同学-3 小时前
01 容器技术概述
运维·docker·云计算
海宇大数据3 小时前
零信任架构实战:基于海宇单人婚姻状态查询构建自动化线上房产交易合规网关
运维·人工智能·架构·自动化
wuminyu3 小时前
Kafka的写入延迟与磁盘IO抖动原理分析
java·linux·c语言·jvm·c++
bloglin999993 小时前
windows中如何像linux的lsof一样查看端口
运维