简介: 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=0、FRONT_RIGHT=1、FRONT_CENTER=2、LFE1=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_LEFT、FRONT_RIGHT、FRONT_CENTER、LFE1、REAR_LEFT、REAR_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分别表示为FL、FR、FC、LFE1、RL、RR。
整个实战最终验证:
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数组。