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

简介: 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,而S20LES18LE分别对应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

这正是widthdepth必须同时参与匹配的原因。


🌻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_ENDIANG_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()就是根据signendiannesswidthdepth在GStreamer内置整数PCM格式表中查找匹配项,并返回对应的GstAudioFormat,找不到时返回GST_AUDIO_FORMAT_UNKNOWN

相关推荐
天远API1 小时前
零信任架构实战:基于天远行驶OCR证识别构建自动化智能停车网关
运维·人工智能·架构·自动化
吴声子夜歌1 小时前
Shell脚本——流程控制:for循环
linux·运维·shell
可乐鸡翅yeah_2 小时前
MPEG‑TS 分片 PTS/DTS 时间戳异常排错,HLS 音画不同步定位实战
运维·测试用例·音视频·媒体·m3u8
事圆则缓2 小时前
Ubuntu 安装与配置 Samba 服务器
linux·服务器·ubuntu
zhangzeyuaaa2 小时前
Python asyncio 事件循环演进:从手动管理到现代化实践
java·服务器·python
GeW2 小时前
每天2小时,21天闭环计划-RHCE通关时间表全公开
linux
abby2 小时前
美国多IP站群服务器Nginx配置与网络优化实操
服务器
老余说AI2 小时前
从开源换脸工具到商业级 AI 换脸平台:SoundView 视频换脸的产业定位与边界
人工智能·音视频·视频换脸
Likeadust2 小时前
多部门协同调度效率低?一套视频直播/点播/视频会议/集群语音对讲EasyDSS搞定企业可视化音视频调度
音视频·媒体·easydss