简介: 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 整数格式查找流程](#🌻3.1 整数格式查找流程)
- [🌻3.2 四个格式条件匹配流程](#🌻3.2 四个格式条件匹配流程)
- 🌻4.实战案例
-
- [🌻4.1 准备PCM格式参数](#🌻4.1 准备PCM格式参数)
- [🌻4.2 调用gst_audio_format_build_integer](#🌻4.2 调用gst_audio_format_build_integer)
- [🌻4.3 验证格式映射结果](#🌻4.3 验证格式映射结果)
- 🌻5.总结
-
🌻1.前言
本篇目的:理解
gst_audio_format_build_integer()如何根据符号位、字节序、存储位宽和有效位深,从GStreamer支持的整数PCM格式中找到对应的GstAudioFormat。
在GStreamer中,Raw Audio的PCM格式通常使用GstAudioFormat表示,例如:
text
GST_AUDIO_FORMAT_S16LE
GST_AUDIO_FORMAT_S24LE
GST_AUDIO_FORMAT_S24_32LE
GST_AUDIO_FORMAT_U16BE
如果程序已经知道格式名称,可以直接使用这些枚举。
但有些场景拿到的并不是:
text
S16LE
S24LE
S24_32LE
而是一组底层PCM属性:
text
是否有符号
字节序
存储位宽width
有效位深depth
例如:
text
signed = TRUE
endianness = Little Endian
width = 16
depth = 16
此时需要把这组底层参数转换成GStreamer认识的:
text
GST_AUDIO_FORMAT_S16LE
gst_audio_format_build_integer()就是完成这个转换的函数。官方定义也是:根据给定参数构造对应的GstAudioFormat;不存在对应格式时返回GST_AUDIO_FORMAT_UNKNOWN。
需要特别注意:
text
gst_audio_format_build_integer()
≠
创建一个新的音频格式
它实际完成的是:
text
PCM属性
↓
查找GStreamer已有整数格式
↓
返回对应GstAudioFormat枚举
#mermaid-svg-lYmSG8lOufiSZAa5{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-lYmSG8lOufiSZAa5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lYmSG8lOufiSZAa5 .error-icon{fill:#552222;}#mermaid-svg-lYmSG8lOufiSZAa5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lYmSG8lOufiSZAa5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lYmSG8lOufiSZAa5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lYmSG8lOufiSZAa5 .marker.cross{stroke:#333333;}#mermaid-svg-lYmSG8lOufiSZAa5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lYmSG8lOufiSZAa5 p{margin:0;}#mermaid-svg-lYmSG8lOufiSZAa5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster-label text{fill:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster-label span{color:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster-label span p{background-color:transparent;}#mermaid-svg-lYmSG8lOufiSZAa5 .label text,#mermaid-svg-lYmSG8lOufiSZAa5 span{fill:#333;color:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 .node rect,#mermaid-svg-lYmSG8lOufiSZAa5 .node circle,#mermaid-svg-lYmSG8lOufiSZAa5 .node ellipse,#mermaid-svg-lYmSG8lOufiSZAa5 .node polygon,#mermaid-svg-lYmSG8lOufiSZAa5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lYmSG8lOufiSZAa5 .rough-node .label text,#mermaid-svg-lYmSG8lOufiSZAa5 .node .label text,#mermaid-svg-lYmSG8lOufiSZAa5 .image-shape .label,#mermaid-svg-lYmSG8lOufiSZAa5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-lYmSG8lOufiSZAa5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lYmSG8lOufiSZAa5 .rough-node .label,#mermaid-svg-lYmSG8lOufiSZAa5 .node .label,#mermaid-svg-lYmSG8lOufiSZAa5 .image-shape .label,#mermaid-svg-lYmSG8lOufiSZAa5 .icon-shape .label{text-align:center;}#mermaid-svg-lYmSG8lOufiSZAa5 .node.clickable{cursor:pointer;}#mermaid-svg-lYmSG8lOufiSZAa5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lYmSG8lOufiSZAa5 .arrowheadPath{fill:#333333;}#mermaid-svg-lYmSG8lOufiSZAa5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lYmSG8lOufiSZAa5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lYmSG8lOufiSZAa5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lYmSG8lOufiSZAa5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lYmSG8lOufiSZAa5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lYmSG8lOufiSZAa5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster text{fill:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 .cluster span{color:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 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-lYmSG8lOufiSZAa5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lYmSG8lOufiSZAa5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-lYmSG8lOufiSZAa5 .icon-shape,#mermaid-svg-lYmSG8lOufiSZAa5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lYmSG8lOufiSZAa5 .icon-shape p,#mermaid-svg-lYmSG8lOufiSZAa5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lYmSG8lOufiSZAa5 .icon-shape .label rect,#mermaid-svg-lYmSG8lOufiSZAa5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lYmSG8lOufiSZAa5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lYmSG8lOufiSZAa5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lYmSG8lOufiSZAa5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① PCM参数
② build_integer
③ 匹配整数格式
④ 找到格式
⑤ GstAudioFormat
因此可以先建立一个最简单的认识:
text
gst_audio_format_build_integer()
就是:
整数PCM参数
↓
转换
↓
GStreamer音频格式枚举
🌻2.应用场景和用法
🌻2.1 应用场景
为什么已经存在GST_AUDIO_FORMAT_S16LE,还需要gst_audio_format_build_integer()?
因为实际多媒体系统中,上游经常给出的不是GStreamer格式枚举,而是外部音频格式描述。
例如WAV、WASAPI或者其他底层音频接口可能告诉程序:
text
PCM
Signed
Little Endian
16-bit Container
16 Valid Bits
GStreamer内部需要的是:
text
GST_AUDIO_FORMAT_S16LE
因此中间需要完成一次格式映射。
可以把它理解成:
text
PCM参数 = 一个人的特征
signed
little endian
width=16
depth=16
↓
gst_audio_format_build_integer()
↓
根据特征查表
↓
GST_AUDIO_FORMAT_S16LE
GStreamer自身的WASAPI代码就存在这种真实使用方式:从Windows的WAVEFORMATEX/WAVEFORMATEXTENSIBLE取得位宽和有效位深后,再调用gst_audio_format_build_integer()转换为GStreamer格式。
例如:
#mermaid-svg-Dkq5s3iCrP5Pjwxx{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-Dkq5s3iCrP5Pjwxx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .error-icon{fill:#552222;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .marker.cross{stroke:#333333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx p{margin:0;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster-label text{fill:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster-label span{color:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster-label span p{background-color:transparent;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .label text,#mermaid-svg-Dkq5s3iCrP5Pjwxx span{fill:#333;color:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .node rect,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node circle,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node ellipse,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node polygon,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .rough-node .label text,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node .label text,#mermaid-svg-Dkq5s3iCrP5Pjwxx .image-shape .label,#mermaid-svg-Dkq5s3iCrP5Pjwxx .icon-shape .label{text-anchor:middle;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .rough-node .label,#mermaid-svg-Dkq5s3iCrP5Pjwxx .node .label,#mermaid-svg-Dkq5s3iCrP5Pjwxx .image-shape .label,#mermaid-svg-Dkq5s3iCrP5Pjwxx .icon-shape .label{text-align:center;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .node.clickable{cursor:pointer;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .arrowheadPath{fill:#333333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Dkq5s3iCrP5Pjwxx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dkq5s3iCrP5Pjwxx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster text{fill:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .cluster span{color:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx 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-Dkq5s3iCrP5Pjwxx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Dkq5s3iCrP5Pjwxx rect.text{fill:none;stroke-width:0;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .icon-shape,#mermaid-svg-Dkq5s3iCrP5Pjwxx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .icon-shape p,#mermaid-svg-Dkq5s3iCrP5Pjwxx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .icon-shape .label rect,#mermaid-svg-Dkq5s3iCrP5Pjwxx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dkq5s3iCrP5Pjwxx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Dkq5s3iCrP5Pjwxx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Dkq5s3iCrP5Pjwxx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部PCM描述
sign/endian
width/depth
build_integer
GstAudioFormat
audio/x-raw
这里最容易混淆的是:
text
width
和:
text
depth
两者并不一定相同。
width表示:
text
一个Sample实际占用多少bit存储空间
depth表示:
text
这些bit中真正用于表达采样值的有效bit数量
例如S16LE:
text
|<------ width=16 ------>|
|<------ depth=16 ------>|
[15.....................0]
16bit存储空间全部有效,所以:
text
width = 16
depth = 16
而S24_32LE则是:
text
|<----------- width=32 ----------->|
|<---- depth=24 ---->|
[31...............................0]
一个Sample占32bit存储空间,但真正的音频有效位是24bit,因此:
text
width = 32
depth = 24
GStreamer内置格式表中正是以这些属性描述具体格式,例如S16LE对应16/16,S24_32LE对应32/24,而S20LE、S18LE分别对应24/20和24/18。
因此常见映射关系如下:
| sign | endianness | width | depth | 返回格式 |
|---|---|---|---|---|
| TRUE | LITTLE | 16 | 16 | S16LE |
| TRUE | BIG | 16 | 16 | S16BE |
| FALSE | LITTLE | 16 | 16 | U16LE |
| TRUE | LITTLE | 24 | 24 | S24LE |
| TRUE | LITTLE | 32 | 24 | S24_32LE |
| TRUE | LITTLE | 24 | 20 | S20LE |
| TRUE | LITTLE | 24 | 18 | S18LE |
所以:
text
width=24 depth=24
和:
text
width=32 depth=24
虽然都是24bit有效数据,却表示两种不同的存储格式:
text
S24LE
和:
text
S24_32LE
这正是width和depth必须同时参与匹配的原因。
🌻2.2 函数用法
函数原型:
cpp
GstAudioFormat
gst_audio_format_build_integer(gboolean sign,
gint endianness,
gint width,
gint depth);
参数:
| 参数 | 作用 |
|---|---|
sign |
TRUE表示有符号整数PCM,FALSE表示无符号整数PCM |
endianness |
字节序,通常为G_LITTLE_ENDIAN或G_BIG_ENDIAN |
width |
每个Sample占用的存储位数 |
depth |
width中真正有效的音频位数 |
返回值:
text
匹配成功
↓
具体GstAudioFormat
匹配失败
↓
GST_AUDIO_FORMAT_UNKNOWN
官方API明确规定,不存在满足这些参数的音频格式时返回GST_AUDIO_FORMAT_UNKNOWN。
最简单的调用:
cpp
GstAudioFormat format;
format = gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
16,
16);
输入:
text
TRUE
↓
Signed
G_LITTLE_ENDIAN
↓
Little Endian
width=16
↓
16bit存储
depth=16
↓
16bit有效
最终返回:
text
GST_AUDIO_FORMAT_S16LE
可以继续调用:
cpp
const gchar *name;
name = gst_audio_format_to_string(format);
得到:
text
S16LE
需要特别注意,这个函数只处理:
text
Integer PCM
它不会根据参数返回:
text
F32LE
F32BE
F64LE
F64BE
因为源码匹配时首先要求候选格式带有GST_AUDIO_FORMAT_FLAG_INTEGER标志;浮点格式会直接被跳过。
🌻3.调用流程剖析
🌻3.1 整数格式查找流程
gst_audio_format_build_integer()没有创建新的GstAudioFormatInfo。
GStreamer内部已经存在一个静态格式表:
text
formats[]
其中保存了系统支持的各种音频格式描述。
可以简化理解成:
text
formats[]
S8
U8
S16LE
S16BE
U16LE
U16BE
S24_32LE
S24_32BE
S32LE
...
S24LE
S20LE
S18LE
F32LE
F64LE
...
每一个格式项本质上包含:
text
format
name
flags
endianness
width
depth
...
例如:
text
S16LE
flags = INTEGER + SIGNED
endianness = LITTLE
width = 16
depth = 16
而:
text
S24_32LE
flags = INTEGER + SIGNED
endianness = LITTLE
width = 32
depth = 24
源码中的formats[]正是这些格式元数据的统一表。
因此调用:
cpp
gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
16,
16);
本质上就是:
#mermaid-svg-FKp1iXEFjV8N2YJv{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-FKp1iXEFjV8N2YJv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FKp1iXEFjV8N2YJv .error-icon{fill:#552222;}#mermaid-svg-FKp1iXEFjV8N2YJv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FKp1iXEFjV8N2YJv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FKp1iXEFjV8N2YJv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FKp1iXEFjV8N2YJv .marker.cross{stroke:#333333;}#mermaid-svg-FKp1iXEFjV8N2YJv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FKp1iXEFjV8N2YJv p{margin:0;}#mermaid-svg-FKp1iXEFjV8N2YJv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster-label text{fill:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster-label span{color:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster-label span p{background-color:transparent;}#mermaid-svg-FKp1iXEFjV8N2YJv .label text,#mermaid-svg-FKp1iXEFjV8N2YJv span{fill:#333;color:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv .node rect,#mermaid-svg-FKp1iXEFjV8N2YJv .node circle,#mermaid-svg-FKp1iXEFjV8N2YJv .node ellipse,#mermaid-svg-FKp1iXEFjV8N2YJv .node polygon,#mermaid-svg-FKp1iXEFjV8N2YJv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FKp1iXEFjV8N2YJv .rough-node .label text,#mermaid-svg-FKp1iXEFjV8N2YJv .node .label text,#mermaid-svg-FKp1iXEFjV8N2YJv .image-shape .label,#mermaid-svg-FKp1iXEFjV8N2YJv .icon-shape .label{text-anchor:middle;}#mermaid-svg-FKp1iXEFjV8N2YJv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FKp1iXEFjV8N2YJv .rough-node .label,#mermaid-svg-FKp1iXEFjV8N2YJv .node .label,#mermaid-svg-FKp1iXEFjV8N2YJv .image-shape .label,#mermaid-svg-FKp1iXEFjV8N2YJv .icon-shape .label{text-align:center;}#mermaid-svg-FKp1iXEFjV8N2YJv .node.clickable{cursor:pointer;}#mermaid-svg-FKp1iXEFjV8N2YJv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FKp1iXEFjV8N2YJv .arrowheadPath{fill:#333333;}#mermaid-svg-FKp1iXEFjV8N2YJv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FKp1iXEFjV8N2YJv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FKp1iXEFjV8N2YJv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FKp1iXEFjV8N2YJv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FKp1iXEFjV8N2YJv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FKp1iXEFjV8N2YJv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster text{fill:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv .cluster span{color:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv 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-FKp1iXEFjV8N2YJv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FKp1iXEFjV8N2YJv rect.text{fill:none;stroke-width:0;}#mermaid-svg-FKp1iXEFjV8N2YJv .icon-shape,#mermaid-svg-FKp1iXEFjV8N2YJv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FKp1iXEFjV8N2YJv .icon-shape p,#mermaid-svg-FKp1iXEFjV8N2YJv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FKp1iXEFjV8N2YJv .icon-shape .label rect,#mermaid-svg-FKp1iXEFjV8N2YJv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FKp1iXEFjV8N2YJv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FKp1iXEFjV8N2YJv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FKp1iXEFjV8N2YJv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入4个参数
遍历formats\[\]
过滤Integer格式
比较格式属性
返回GstAudioFormat
源码核心逻辑可以简化为:
cpp
for (每一个 GstAudioFormatInfo) {
if (不是整数格式)
continue;
if (width不相同 || depth不相同)
continue;
if (字节序不相同)
continue;
if (signed属性不相同)
continue;
return 当前格式;
}
return GST_AUDIO_FORMAT_UNKNOWN;
因此这个函数虽然叫:
text
build_integer
但它实际上并不是:
text
动态构造一个新的格式对象
而更接近:
text
find_integer_format
也就是:
text
根据4个PCM属性
↓
在formats[]中查找
↓
找到已有GstAudioFormat
🌻3.2 四个格式条件匹配流程
遍历formats[]以后,并不是简单比较格式名字,而是依次检查4类关键属性。
第一步检查:
text
是不是Integer格式
源码通过:
cpp
GST_AUDIO_FORMAT_INFO_IS_INTEGER(finfo)
判断。
因此:
text
S16LE
S24LE
U16LE
可以继续参与匹配,而:
text
F32LE
F64LE
会被跳过。
第二步检查:
text
width
+
depth
必须同时相同。
例如调用:
cpp
gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
32,
24);
遍历到:
text
S24LE
width=24
depth=24
不匹配。
继续遍历到:
text
S24_32LE
width=32
depth=24
匹配。
因此得到:
text
GST_AUDIO_FORMAT_S24_32LE
第三步检查:
text
endianness
例如:
text
S16LE
↓
G_LITTLE_ENDIAN
S16BE
↓
G_BIG_ENDIAN
所以:
cpp
TRUE + LITTLE + 16 + 16
得到:
text
S16LE
而:
cpp
TRUE + BIG + 16 + 16
得到:
text
S16BE
有一个特殊情况是8bit格式:
text
S8
U8
单个Sample只有一个Byte,因此不存在多字节排列问题。
GStreamer格式表中S8/U8的endianness记录为0;源码只有当候选格式本身存在有效endianness时才要求它和传入值相同。
因此对于:
text
S8/U8
大小端并没有实际意义。
第四步检查:
text
sign
例如:
text
TRUE
+
LITTLE
+
16
+
16
匹配:
text
S16LE
而:
text
FALSE
+
LITTLE
+
16
+
16
匹配:
text
U16LE
整个判断关系可以概括为:
#mermaid-svg-4xrYpSCIC6yXGoR3{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-4xrYpSCIC6yXGoR3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4xrYpSCIC6yXGoR3 .error-icon{fill:#552222;}#mermaid-svg-4xrYpSCIC6yXGoR3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4xrYpSCIC6yXGoR3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .marker.cross{stroke:#333333;}#mermaid-svg-4xrYpSCIC6yXGoR3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4xrYpSCIC6yXGoR3 p{margin:0;}#mermaid-svg-4xrYpSCIC6yXGoR3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster-label text{fill:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster-label span{color:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster-label span p{background-color:transparent;}#mermaid-svg-4xrYpSCIC6yXGoR3 .label text,#mermaid-svg-4xrYpSCIC6yXGoR3 span{fill:#333;color:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .node rect,#mermaid-svg-4xrYpSCIC6yXGoR3 .node circle,#mermaid-svg-4xrYpSCIC6yXGoR3 .node ellipse,#mermaid-svg-4xrYpSCIC6yXGoR3 .node polygon,#mermaid-svg-4xrYpSCIC6yXGoR3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .rough-node .label text,#mermaid-svg-4xrYpSCIC6yXGoR3 .node .label text,#mermaid-svg-4xrYpSCIC6yXGoR3 .image-shape .label,#mermaid-svg-4xrYpSCIC6yXGoR3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-4xrYpSCIC6yXGoR3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .rough-node .label,#mermaid-svg-4xrYpSCIC6yXGoR3 .node .label,#mermaid-svg-4xrYpSCIC6yXGoR3 .image-shape .label,#mermaid-svg-4xrYpSCIC6yXGoR3 .icon-shape .label{text-align:center;}#mermaid-svg-4xrYpSCIC6yXGoR3 .node.clickable{cursor:pointer;}#mermaid-svg-4xrYpSCIC6yXGoR3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .arrowheadPath{fill:#333333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4xrYpSCIC6yXGoR3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4xrYpSCIC6yXGoR3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4xrYpSCIC6yXGoR3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster text{fill:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 .cluster span{color:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 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-4xrYpSCIC6yXGoR3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4xrYpSCIC6yXGoR3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-4xrYpSCIC6yXGoR3 .icon-shape,#mermaid-svg-4xrYpSCIC6yXGoR3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4xrYpSCIC6yXGoR3 .icon-shape p,#mermaid-svg-4xrYpSCIC6yXGoR3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4xrYpSCIC6yXGoR3 .icon-shape .label rect,#mermaid-svg-4xrYpSCIC6yXGoR3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4xrYpSCIC6yXGoR3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4xrYpSCIC6yXGoR3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4xrYpSCIC6yXGoR3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
formats\[\]候选格式
Integer?
width/depth匹配?
endian/sign匹配?
返回Format
以:
cpp
gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
32,
24);
为例,可以把源码匹配过程理解成:
text
formats[]
↓
S8
Integer ✓
width 8 != 32
跳过
↓
U8
width不匹配
跳过
↓
S16LE
width不匹配
跳过
↓
...
↓
S24_32LE
Integer ✓
width=32 ✓
depth=24 ✓
Little Endian ✓
Signed ✓
↓
返回GST_AUDIO_FORMAT_S24_32LE
如果把参数改成一个GStreamer没有定义的组合,例如:
cpp
gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
20,
20);
不会得到:
text
S20LE
因为GStreamer中的S20LE实际描述的是:
text
width = 24
depth = 20
而不是:
text
width = 20
depth = 20
源码格式表明确将S20LE定义为24bit存储、20bit有效数据。
因此这个调用最终会:
text
遍历完整个formats[]
↓
没有完全匹配
↓
GST_AUDIO_FORMAT_UNKNOWN
这说明:
text
gst_audio_format_build_integer()
不是根据参数"创造"任意PCM格式,而只能返回:
text
GStreamer已经支持的格式
🌻4.实战案例
🌻4.1 准备PCM格式参数
下面通过一个最小程序验证3组典型参数:
text
16bit存储 / 16bit有效
↓
S16LE
32bit存储 / 24bit有效
↓
S24_32LE
20bit存储 / 20bit有效
↓
UNKNOWN
首先定义:
cpp
#include <gst/gst.h>
#include <gst/audio/audio.h>
int main(int argc, char *argv[])
{
GstAudioFormat format;
gst_init(&argc, &argv);
return 0;
}
这里不需要创建:
text
Pipeline
Element
Pad
GstBuffer
因为gst_audio_format_build_integer()本身只是音频格式映射函数。
我们只需要提供:
text
sign
endianness
width
depth
即可验证。
🌻4.2 调用gst_audio_format_build_integer
首先测试最常见的:
text
Signed
Little Endian
16bit存储
16bit有效
代码:
cpp
format = gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
16,
16);
g_print("16/16 = %s\n",
format != GST_AUDIO_FORMAT_UNKNOWN ?
gst_audio_format_to_string(format) : "UNKNOWN");
预期:
text
16/16 = S16LE
第二组测试:
cpp
format = gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
32,
24);
g_print("32/24 = %s\n",
format != GST_AUDIO_FORMAT_UNKNOWN ?
gst_audio_format_to_string(format) : "UNKNOWN");
预期:
text
32/24 = S24_32LE
这里可以直观看到:
text
depth=24
并不能单独决定最终格式。
因为:
text
width=24 depth=24
↓
S24LE
width=32 depth=24
↓
S24_32LE
第三组故意传入GStreamer不存在的组合:
cpp
format = gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
20,
20);
g_print("20/20 = %s\n",
format != GST_AUDIO_FORMAT_UNKNOWN ?
gst_audio_format_to_string(format) : "UNKNOWN");
预期:
text
20/20 = UNKNOWN
因为GStreamer支持的S20LE是:
text
width=24
depth=20
所以正确参数应该是:
cpp
gst_audio_format_build_integer(
TRUE,
G_LITTLE_ENDIAN,
24,
20);
此时才会返回:
text
GST_AUDIO_FORMAT_S20LE
🌻4.3 验证格式映射结果
完整测试代码:
cpp
#include <gst/gst.h>
#include <gst/audio/audio.h>
static void
test_format(gboolean sign,
gint endianness,
gint width,
gint depth)
{
GstAudioFormat format;
format = gst_audio_format_build_integer(
sign,
endianness,
width,
depth);
g_print("sign=%d width=%d depth=%d -> %s\n",
sign,
width,
depth,
format != GST_AUDIO_FORMAT_UNKNOWN ?
gst_audio_format_to_string(format) : "UNKNOWN");
}
int main(int argc, char *argv[])
{
gst_init(&argc, &argv);
test_format(TRUE, G_LITTLE_ENDIAN, 16, 16);
test_format(FALSE, G_LITTLE_ENDIAN, 16, 16);
test_format(TRUE, G_LITTLE_ENDIAN, 32, 24);
test_format(TRUE, G_LITTLE_ENDIAN, 24, 20);
test_format(TRUE, G_LITTLE_ENDIAN, 20, 20);
return 0;
}
编译:
bash
gcc test_audio_format.c -o test_audio_format \
$(pkg-config --cflags --libs \
gstreamer-1.0 \
gstreamer-audio-1.0)
运行:
bash
./test_audio_format
重点观察:
text
sign=1 width=16 depth=16 -> S16LE
sign=0 width=16 depth=16 -> U16LE
sign=1 width=32 depth=24 -> S24_32LE
sign=1 width=24 depth=20 -> S20LE
sign=1 width=20 depth=20 -> UNKNOWN
整个实战对应的内部过程就是:
text
sign/endian/width/depth
↓
gst_audio_format_build_integer()
↓
遍历formats[]
↓
比较Integer/width/depth/endian/sign
↓
完全匹配
↓ ↓
是 否
↓ ↓
GstAudioFormat UNKNOWN
因此实际开发中一定要区分:
text
width = PCM Sample占用的存储位数
depth = 存储空间中真正有效的采样位数
尤其是下面几种格式:
text
S24LE
↓
width=24
depth=24
S24_32LE
↓
width=32
depth=24
S20LE
↓
width=24
depth=20
S18LE
↓
width=24
depth=18
这也是理解gst_audio_format_build_integer()最关键的地方。
🌻5.总结
gst_audio_format_build_integer()就是根据sign、endianness、width和depth在GStreamer内置整数PCM格式表中查找匹配项,并返回对应的GstAudioFormat,找不到时返回GST_AUDIO_FORMAT_UNKNOWN。