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

简介: 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 准备输入和输出格式](#🌻4.1 准备输入和输出格式)
        • [🌻4.2 创建并使用转换器](#🌻4.2 创建并使用转换器)
        • [🌻4.3 验证转换结果](#🌻4.3 验证转换结果)
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_converter_new()如何根据输入、输出音频信息创建可复用的GstAudioConverter,以及它为什么只是建立转换方案,而不是立即处理音频数据。

音频处理过程中,输入和输出格式经常存在差异:

text 复制代码
输入:S16、48000Hz、2声道、交错布局
输出:F32、48000Hz、2声道、交错布局

也可能同时发生采样率、声道数量或内存布局变化。如果每次处理Buffer时都重新判断这些差异,转换逻辑会分散在数据处理路径中,难以管理和优化。

gst_audio_converter_new()接收两个GstAudioInfo,分别描述输入和输出音频格式,然后创建一个不透明的GstAudioConverter对象。它会在创建阶段检查参数、保存配置,并预先组织后续需要使用的转换链。

需要注意:

text 复制代码
gst_audio_converter_new()
        ≠
立即转换一块音频数据

它负责建立转换器和转换方案。真正的数据处理由后续的gst_audio_converter_samples()gst_audio_converter_convert()完成。
#mermaid-svg-59WPOl13bR7eD9Qu{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-59WPOl13bR7eD9Qu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-59WPOl13bR7eD9Qu .error-icon{fill:#552222;}#mermaid-svg-59WPOl13bR7eD9Qu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-59WPOl13bR7eD9Qu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-59WPOl13bR7eD9Qu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-59WPOl13bR7eD9Qu .marker.cross{stroke:#333333;}#mermaid-svg-59WPOl13bR7eD9Qu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-59WPOl13bR7eD9Qu p{margin:0;}#mermaid-svg-59WPOl13bR7eD9Qu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-59WPOl13bR7eD9Qu .cluster-label text{fill:#333;}#mermaid-svg-59WPOl13bR7eD9Qu .cluster-label span{color:#333;}#mermaid-svg-59WPOl13bR7eD9Qu .cluster-label span p{background-color:transparent;}#mermaid-svg-59WPOl13bR7eD9Qu .label text,#mermaid-svg-59WPOl13bR7eD9Qu span{fill:#333;color:#333;}#mermaid-svg-59WPOl13bR7eD9Qu .node rect,#mermaid-svg-59WPOl13bR7eD9Qu .node circle,#mermaid-svg-59WPOl13bR7eD9Qu .node ellipse,#mermaid-svg-59WPOl13bR7eD9Qu .node polygon,#mermaid-svg-59WPOl13bR7eD9Qu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-59WPOl13bR7eD9Qu .rough-node .label text,#mermaid-svg-59WPOl13bR7eD9Qu .node .label text,#mermaid-svg-59WPOl13bR7eD9Qu .image-shape .label,#mermaid-svg-59WPOl13bR7eD9Qu .icon-shape .label{text-anchor:middle;}#mermaid-svg-59WPOl13bR7eD9Qu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-59WPOl13bR7eD9Qu .rough-node .label,#mermaid-svg-59WPOl13bR7eD9Qu .node .label,#mermaid-svg-59WPOl13bR7eD9Qu .image-shape .label,#mermaid-svg-59WPOl13bR7eD9Qu .icon-shape .label{text-align:center;}#mermaid-svg-59WPOl13bR7eD9Qu .node.clickable{cursor:pointer;}#mermaid-svg-59WPOl13bR7eD9Qu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-59WPOl13bR7eD9Qu .arrowheadPath{fill:#333333;}#mermaid-svg-59WPOl13bR7eD9Qu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-59WPOl13bR7eD9Qu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-59WPOl13bR7eD9Qu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-59WPOl13bR7eD9Qu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-59WPOl13bR7eD9Qu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-59WPOl13bR7eD9Qu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-59WPOl13bR7eD9Qu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-59WPOl13bR7eD9Qu .cluster text{fill:#333;}#mermaid-svg-59WPOl13bR7eD9Qu .cluster span{color:#333;}#mermaid-svg-59WPOl13bR7eD9Qu 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-59WPOl13bR7eD9Qu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-59WPOl13bR7eD9Qu rect.text{fill:none;stroke-width:0;}#mermaid-svg-59WPOl13bR7eD9Qu .icon-shape,#mermaid-svg-59WPOl13bR7eD9Qu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-59WPOl13bR7eD9Qu .icon-shape p,#mermaid-svg-59WPOl13bR7eD9Qu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-59WPOl13bR7eD9Qu .icon-shape .label rect,#mermaid-svg-59WPOl13bR7eD9Qu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-59WPOl13bR7eD9Qu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-59WPOl13bR7eD9Qu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-59WPOl13bR7eD9Qu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 输入GstAudioInfo
② 输出GstAudioInfo
③ 创建转换器
④ 组装转换链
⑤ 处理音频数据


🌻2.应用场景和用法

🌻2.1 应用场景

GstAudioConverter可以理解成一份已经准备好的音频转换方案。

输入和输出的差异由两个GstAudioInfo描述:

text 复制代码
in_info  = 输入格式
out_info = 输出格式

gst_audio_converter_new()根据这两个对象决定是否需要:

text 复制代码
格式转换
通道混音
采样率转换
交错与非交错布局转换
量化、抖动和噪声整形

常见应用场景主要有以下几类。

第一类是audioconvertElement根据协商后的输入Caps和输出Caps创建转换器。Caps确定后,Element把Caps解析成GstAudioInfo,再将转换器保存到Element内部,后续逐个处理音频Buffer。

第二类是audioresample等需要持续处理音频流的Element。转换器创建后可以被重复使用,避免每个Buffer都重新构造格式转换逻辑。

第三类是自定义音频处理模块需要组合多种转换操作。例如输入是整数格式,输出是浮点格式,同时需要调整采样率和声道布局,这些操作可以由同一个GstAudioConverter统一组织。

第四类是转换配置可能发生变化的场景。如果只是输入和输出采样率变化,并且创建时启用了GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE,可以使用gst_audio_converter_update_config()更新速率。格式、声道数或布局发生变化时,通常需要释放旧转换器并重新创建。

对象关系可以概括为:
#mermaid-svg-7ZYIbrsdonl39BXg{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-7ZYIbrsdonl39BXg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7ZYIbrsdonl39BXg .error-icon{fill:#552222;}#mermaid-svg-7ZYIbrsdonl39BXg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7ZYIbrsdonl39BXg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7ZYIbrsdonl39BXg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7ZYIbrsdonl39BXg .marker.cross{stroke:#333333;}#mermaid-svg-7ZYIbrsdonl39BXg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7ZYIbrsdonl39BXg p{margin:0;}#mermaid-svg-7ZYIbrsdonl39BXg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7ZYIbrsdonl39BXg .cluster-label text{fill:#333;}#mermaid-svg-7ZYIbrsdonl39BXg .cluster-label span{color:#333;}#mermaid-svg-7ZYIbrsdonl39BXg .cluster-label span p{background-color:transparent;}#mermaid-svg-7ZYIbrsdonl39BXg .label text,#mermaid-svg-7ZYIbrsdonl39BXg span{fill:#333;color:#333;}#mermaid-svg-7ZYIbrsdonl39BXg .node rect,#mermaid-svg-7ZYIbrsdonl39BXg .node circle,#mermaid-svg-7ZYIbrsdonl39BXg .node ellipse,#mermaid-svg-7ZYIbrsdonl39BXg .node polygon,#mermaid-svg-7ZYIbrsdonl39BXg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7ZYIbrsdonl39BXg .rough-node .label text,#mermaid-svg-7ZYIbrsdonl39BXg .node .label text,#mermaid-svg-7ZYIbrsdonl39BXg .image-shape .label,#mermaid-svg-7ZYIbrsdonl39BXg .icon-shape .label{text-anchor:middle;}#mermaid-svg-7ZYIbrsdonl39BXg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7ZYIbrsdonl39BXg .rough-node .label,#mermaid-svg-7ZYIbrsdonl39BXg .node .label,#mermaid-svg-7ZYIbrsdonl39BXg .image-shape .label,#mermaid-svg-7ZYIbrsdonl39BXg .icon-shape .label{text-align:center;}#mermaid-svg-7ZYIbrsdonl39BXg .node.clickable{cursor:pointer;}#mermaid-svg-7ZYIbrsdonl39BXg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7ZYIbrsdonl39BXg .arrowheadPath{fill:#333333;}#mermaid-svg-7ZYIbrsdonl39BXg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7ZYIbrsdonl39BXg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7ZYIbrsdonl39BXg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ZYIbrsdonl39BXg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7ZYIbrsdonl39BXg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ZYIbrsdonl39BXg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7ZYIbrsdonl39BXg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7ZYIbrsdonl39BXg .cluster text{fill:#333;}#mermaid-svg-7ZYIbrsdonl39BXg .cluster span{color:#333;}#mermaid-svg-7ZYIbrsdonl39BXg 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-7ZYIbrsdonl39BXg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7ZYIbrsdonl39BXg rect.text{fill:none;stroke-width:0;}#mermaid-svg-7ZYIbrsdonl39BXg .icon-shape,#mermaid-svg-7ZYIbrsdonl39BXg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ZYIbrsdonl39BXg .icon-shape p,#mermaid-svg-7ZYIbrsdonl39BXg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7ZYIbrsdonl39BXg .icon-shape .label rect,#mermaid-svg-7ZYIbrsdonl39BXg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ZYIbrsdonl39BXg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7ZYIbrsdonl39BXg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7ZYIbrsdonl39BXg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入Caps或格式
输入GstAudioInfo
gst_audio_converter_new
GstAudioConverter
samples/convert处理数据

gst_audio_converter_new()不是单独的声道重排接口,也不是单独的采样率转换接口:

text 复制代码
只交换声道位置:
gst_audio_reorder_channels()

只执行采样率转换:
GstAudioResampler

组合格式、通道、采样率和布局转换:
GstAudioConverter

🌻2.2 函数用法

函数原型:

cpp 复制代码
GstAudioConverter *gst_audio_converter_new(
        GstAudioConverterFlags flags,
        GstAudioInfo *in_info,
        GstAudioInfo *out_info,
        GstStructure *config);

参数:

参数 作用
flags 控制输入可写性和采样率更新能力
in_info 输入音频格式描述
out_info 输出音频格式描述
config 可选的转换配置,传入后所有权交给函数

常用Flag:

Flag 作用
GST_AUDIO_CONVERTER_FLAG_NONE 使用默认行为
GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE 表示输入Sample数组可写,可以作为转换过程中的临时存储
GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE 允许通过gst_audio_converter_update_config()更新输入和输出采样率

config可以设置重采样方法、抖动方法、噪声整形、量化参数和声道混音矩阵等选项。该参数标记为transfer full,如果传入非空GstStructure,调用者不应在函数返回后继续释放或使用它。

返回值:

text 复制代码
成功:返回新创建的GstAudioConverter*
失败:返回NULL

失败通常表示:

text 复制代码
输入或输出信息为空
混音矩阵尺寸不正确
输入输出声道数不同且缺少必要的定位信息
转换方案无法建立

最小调用方式:

cpp 复制代码
GstAudioInfo in_info;
GstAudioInfo out_info;
GstAudioConverter *converter;

gst_audio_info_set_format(
        &in_info,
        GST_AUDIO_FORMAT_S16,
        48000,
        2,
        NULL);

gst_audio_info_set_format(
        &out_info,
        GST_AUDIO_FORMAT_F32,
        48000,
        2,
        NULL);

converter = gst_audio_converter_new(
        GST_AUDIO_CONVERTER_FLAG_NONE,
        &in_info,
        &out_info,
        NULL);

这里的in_infoout_info由调用者提供。构造函数会把它们复制到转换器内部,不会接管这两个对象的生命周期。

返回的GstAudioConverter是一个不透明的Boxed对象,不是GObject。使用完成后应调用:

cpp 复制代码
gst_audio_converter_free(converter);

不能使用gst_object_unref()释放。

调用前:

text 复制代码
只有输入和输出格式描述
没有可执行的转换链

调用后:

text 复制代码
得到GstAudioConverter
保存输入和输出信息
保存转换配置
建立内部转换链
确定是否支持原地处理或旁路处理

🌻3.调用流程剖析

🌻3.1 校验参数并建立转换器对象

gst_audio_converter_new()首先检查输入和输出GstAudioInfo指针:

cpp 复制代码
g_return_val_if_fail(in_info != NULL, FALSE);
g_return_val_if_fail(out_info != NULL, FALSE);

虽然源码使用的是g_return_val_if_fail(),但函数返回类型是指针,因此失败时实际表现为返回NULL

如果config不为空,函数会先读取其中的混音矩阵配置,并检查矩阵尺寸是否匹配输入和输出声道数。

例如:

text 复制代码
输入2声道,输出1声道
混音矩阵必须有1行、2列

如果输入输出声道数不同,并且任意一方是未定位声道,同时又没有提供混音矩阵,函数会拒绝创建转换器。

参数检查通过后,函数分配转换器对象:

cpp 复制代码
convert = g_new0(GstAudioConverter, 1);

convert->flags = flags;
convert->in = *in_info;
convert->out = *out_info;

这一步有两个关键结果:

text 复制代码
flags被复制到转换器内部
in_info和out_info的内容被复制到转换器内部

调用者仍然拥有原来的GstAudioInfo,但之后修改原对象不会自动改变已经创建的转换器。

函数随后创建默认配置:

cpp 复制代码
convert->config =
    gst_structure_new_empty("GstAudioConverter");

如果调用者传入了config,构造函数会通过gst_audio_converter_update_config()复制配置内容,并释放传入的GstStructure
#mermaid-svg-kH4sK5ZZC12qiAVv{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-kH4sK5ZZC12qiAVv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kH4sK5ZZC12qiAVv .error-icon{fill:#552222;}#mermaid-svg-kH4sK5ZZC12qiAVv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kH4sK5ZZC12qiAVv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kH4sK5ZZC12qiAVv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kH4sK5ZZC12qiAVv .marker.cross{stroke:#333333;}#mermaid-svg-kH4sK5ZZC12qiAVv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kH4sK5ZZC12qiAVv p{margin:0;}#mermaid-svg-kH4sK5ZZC12qiAVv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster-label text{fill:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster-label span{color:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster-label span p{background-color:transparent;}#mermaid-svg-kH4sK5ZZC12qiAVv .label text,#mermaid-svg-kH4sK5ZZC12qiAVv span{fill:#333;color:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv .node rect,#mermaid-svg-kH4sK5ZZC12qiAVv .node circle,#mermaid-svg-kH4sK5ZZC12qiAVv .node ellipse,#mermaid-svg-kH4sK5ZZC12qiAVv .node polygon,#mermaid-svg-kH4sK5ZZC12qiAVv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kH4sK5ZZC12qiAVv .rough-node .label text,#mermaid-svg-kH4sK5ZZC12qiAVv .node .label text,#mermaid-svg-kH4sK5ZZC12qiAVv .image-shape .label,#mermaid-svg-kH4sK5ZZC12qiAVv .icon-shape .label{text-anchor:middle;}#mermaid-svg-kH4sK5ZZC12qiAVv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kH4sK5ZZC12qiAVv .rough-node .label,#mermaid-svg-kH4sK5ZZC12qiAVv .node .label,#mermaid-svg-kH4sK5ZZC12qiAVv .image-shape .label,#mermaid-svg-kH4sK5ZZC12qiAVv .icon-shape .label{text-align:center;}#mermaid-svg-kH4sK5ZZC12qiAVv .node.clickable{cursor:pointer;}#mermaid-svg-kH4sK5ZZC12qiAVv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kH4sK5ZZC12qiAVv .arrowheadPath{fill:#333333;}#mermaid-svg-kH4sK5ZZC12qiAVv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kH4sK5ZZC12qiAVv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kH4sK5ZZC12qiAVv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kH4sK5ZZC12qiAVv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kH4sK5ZZC12qiAVv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kH4sK5ZZC12qiAVv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster text{fill:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv .cluster span{color:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv 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-kH4sK5ZZC12qiAVv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kH4sK5ZZC12qiAVv rect.text{fill:none;stroke-width:0;}#mermaid-svg-kH4sK5ZZC12qiAVv .icon-shape,#mermaid-svg-kH4sK5ZZC12qiAVv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kH4sK5ZZC12qiAVv .icon-shape p,#mermaid-svg-kH4sK5ZZC12qiAVv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kH4sK5ZZC12qiAVv .icon-shape .label rect,#mermaid-svg-kH4sK5ZZC12qiAVv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kH4sK5ZZC12qiAVv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kH4sK5ZZC12qiAVv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kH4sK5ZZC12qiAVv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} in_info/out_info
检查指针和配置
检查混音矩阵与声道定位
分配GstAudioConverter
复制格式和配置

这部分仍然没有处理任何音频Sample,只是在为后续转换建立合法的输入条件和内部对象。


🌻3.2 组装转换链并选择执行路径

创建对象后,函数按照固定顺序调用内部chain_*()函数,组织音频转换阶段:

text 复制代码
chain_unpack()
chain_convert_in()
chain_mix()
chain_resample()
chain_convert_out()
chain_quantize()
chain_change_layout()
chain_pack()

这些阶段并不一定全部执行实际转换。

例如:

  • 输入输出格式相同且属于中间格式时,可能不需要真正解包。
  • 输入输出都是整数格式时,不需要增加浮点转换阶段。
  • 输入输出采样率相同时,不建立重采样器。
  • 输入输出声道布局相同时,不需要改变布局。
  • 输入输出格式、布局和采样率都相同时,可能直接进入旁路模式。

各阶段的职责如下:

阶段 作用
chain_unpack() 将输入格式解包为内部处理格式
chain_convert_in() 必要时把整数Sample转换为浮点内部格式
chain_mix() 处理声道数量和混音矩阵
chain_resample() 处理输入和输出采样率差异
chain_convert_out() 将内部浮点格式转换为输出处理格式
chain_quantize() 处理位深变化、量化和抖动
chain_change_layout() 转换交错或非交错布局
chain_pack() 将内部格式打包为输出格式

#mermaid-svg-NCbUS7ZuGBojh45m{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-NCbUS7ZuGBojh45m .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NCbUS7ZuGBojh45m .error-icon{fill:#552222;}#mermaid-svg-NCbUS7ZuGBojh45m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NCbUS7ZuGBojh45m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NCbUS7ZuGBojh45m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NCbUS7ZuGBojh45m .marker.cross{stroke:#333333;}#mermaid-svg-NCbUS7ZuGBojh45m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NCbUS7ZuGBojh45m p{margin:0;}#mermaid-svg-NCbUS7ZuGBojh45m .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NCbUS7ZuGBojh45m .cluster-label text{fill:#333;}#mermaid-svg-NCbUS7ZuGBojh45m .cluster-label span{color:#333;}#mermaid-svg-NCbUS7ZuGBojh45m .cluster-label span p{background-color:transparent;}#mermaid-svg-NCbUS7ZuGBojh45m .label text,#mermaid-svg-NCbUS7ZuGBojh45m span{fill:#333;color:#333;}#mermaid-svg-NCbUS7ZuGBojh45m .node rect,#mermaid-svg-NCbUS7ZuGBojh45m .node circle,#mermaid-svg-NCbUS7ZuGBojh45m .node ellipse,#mermaid-svg-NCbUS7ZuGBojh45m .node polygon,#mermaid-svg-NCbUS7ZuGBojh45m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NCbUS7ZuGBojh45m .rough-node .label text,#mermaid-svg-NCbUS7ZuGBojh45m .node .label text,#mermaid-svg-NCbUS7ZuGBojh45m .image-shape .label,#mermaid-svg-NCbUS7ZuGBojh45m .icon-shape .label{text-anchor:middle;}#mermaid-svg-NCbUS7ZuGBojh45m .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NCbUS7ZuGBojh45m .rough-node .label,#mermaid-svg-NCbUS7ZuGBojh45m .node .label,#mermaid-svg-NCbUS7ZuGBojh45m .image-shape .label,#mermaid-svg-NCbUS7ZuGBojh45m .icon-shape .label{text-align:center;}#mermaid-svg-NCbUS7ZuGBojh45m .node.clickable{cursor:pointer;}#mermaid-svg-NCbUS7ZuGBojh45m .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NCbUS7ZuGBojh45m .arrowheadPath{fill:#333333;}#mermaid-svg-NCbUS7ZuGBojh45m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NCbUS7ZuGBojh45m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NCbUS7ZuGBojh45m .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NCbUS7ZuGBojh45m .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NCbUS7ZuGBojh45m .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NCbUS7ZuGBojh45m .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NCbUS7ZuGBojh45m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NCbUS7ZuGBojh45m .cluster text{fill:#333;}#mermaid-svg-NCbUS7ZuGBojh45m .cluster span{color:#333;}#mermaid-svg-NCbUS7ZuGBojh45m 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-NCbUS7ZuGBojh45m .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NCbUS7ZuGBojh45m rect.text{fill:none;stroke-width:0;}#mermaid-svg-NCbUS7ZuGBojh45m .icon-shape,#mermaid-svg-NCbUS7ZuGBojh45m .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NCbUS7ZuGBojh45m .icon-shape p,#mermaid-svg-NCbUS7ZuGBojh45m .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NCbUS7ZuGBojh45m .icon-shape .label rect,#mermaid-svg-NCbUS7ZuGBojh45m .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NCbUS7ZuGBojh45m .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NCbUS7ZuGBojh45m .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NCbUS7ZuGBojh45m :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入Sample
解包和内部格式
通道与采样率处理
量化与布局处理
打包为输出Sample

转换链建立后,函数还会进行路径优化。

当输入输出格式相同、布局相同、没有重采样,并且声道混音器判断为旁路时,转换器会设置:

text 复制代码
convert = converter_passthrough
in_place = TRUE
passthrough = TRUE

这表示后续处理不需要真正转换数据,并且可以在原内存中完成。

如果格式发生变化,或者需要重采样、混音和布局转换,转换器通常会使用通用转换路径,并根据中间阶段决定是否分配临时内存。

最后,setup_allocators()根据输出格式和各转换阶段的可写性配置内存分配策略。

整个构造过程是同步的:

text 复制代码
不进入Pipeline
不发送协议消息
不触发异步回调
不处理音频Buffer

构造完成后,应用再通过:

cpp 复制代码
gst_audio_converter_samples()

或:

cpp 复制代码
gst_audio_converter_convert()

提交音频数据。

如果需要计算输出空间,可以先调用:

cpp 复制代码
gst_audio_converter_get_out_frames()

获取预计输出Frame数量。


🌻4.实战案例

🌻4.1 准备输入和输出格式

下面构造一个最小格式转换案例:

text 复制代码
输入:S16、48000Hz、2声道、交错布局
输出:F32、48000Hz、2声道、交错布局

输入和输出的采样率、声道数和布局相同,只有Sample格式不同。

因此该案例主要验证:

text 复制代码
gst_audio_converter_new()
        ↓
创建格式转换器
        ↓
gst_audio_converter_convert()
        ↓
分配F32输出数据

🌻4.2 创建并使用转换器

创建转换器:

cpp 复制代码
converter = gst_audio_converter_new(
        GST_AUDIO_CONVERTER_FLAG_NONE,
        &in_info,
        &out_info,
        NULL);

返回值必须先判断:

cpp 复制代码
if (converter == NULL) {
    g_printerr("Could not create audio converter\n");
    return 1;
}

随后使用gst_audio_converter_convert()处理输入数据。该接口会根据输入Frame数量计算输出Frame数量,并分配输出内存。

输出内存由调用者负责释放:

cpp 复制代码
g_free(output);

转换器本身则使用:

cpp 复制代码
gst_audio_converter_free(converter);

🌻4.3 验证转换结果

完整测试代码:

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

int
main(int argc, char *argv[])
{
    GstAudioInfo in_info;
    GstAudioInfo out_info;
    GstAudioConverter *converter;
    gint16 input[] = {
        1000, -1000,
        2000, -2000
    };
    gpointer output = NULL;
    gsize output_size = 0;

    gst_init(&argc, &argv);

    gst_audio_info_set_format(
        &in_info,
        GST_AUDIO_FORMAT_S16,
        48000,
        2,
        NULL);

    gst_audio_info_set_format(
        &out_info,
        GST_AUDIO_FORMAT_F32,
        48000,
        2,
        NULL);

    converter = gst_audio_converter_new(
        GST_AUDIO_CONVERTER_FLAG_NONE,
        &in_info,
        &out_info,
        NULL);

    if (converter == NULL) {
        g_printerr("Could not create audio converter\n");
        return 1;
    }

    g_print(
        "supports-inplace = %s\n",
        gst_audio_converter_supports_inplace(converter)
        ? "TRUE" : "FALSE");

    g_print(
        "passthrough = %s\n",
        gst_audio_converter_is_passthrough(converter)
        ? "TRUE" : "FALSE");

    if (!gst_audio_converter_convert(
            converter,
            GST_AUDIO_CONVERTER_FLAG_NONE,
            input,
            sizeof(input),
            &output,
            &output_size)) {
        g_printerr("Audio conversion failed\n");
        g_free(output);
        gst_audio_converter_free(converter);
        return 1;
    }

    g_print(
        "input-size = %" G_GSIZE_FORMAT "\n",
        sizeof(input));

    g_print(
        "output-size = %" G_GSIZE_FORMAT "\n",
        output_size);

    g_free(output);
    gst_audio_converter_free(converter);

    return 0;
}

编译:

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

运行:

bash 复制代码
./audio_converter_new

关键输出应类似:

text 复制代码
supports-inplace = FALSE
passthrough = FALSE
input-size = 8
output-size = 16

输入数组包含4个S16Sample:

text 复制代码
4 × 2Byte = 8Byte

它表示2个双声道Audio Frame。

输出格式是F32,每个Sample占4Byte:

text 复制代码
4个Sample × 4Byte = 16Byte

因此输出大小从8Byte变成16Byte,说明转换器没有原地复用输入内存,而是按照输出格式分配了新的结果区域。

这个案例验证了三个事实:

text 复制代码
gst_audio_converter_new()创建可复用的转换器对象
gst_audio_converter_convert()才真正处理音频数据
格式变化时通常不能使用旁路和原地转换

🌻5.总结

gst_audio_converter_new()就是根据输入输出GstAudioInfo和配置创建一条可复用的音频转换链,后续由其他接口使用它完成格式、声道、采样率或布局转换。

相关推荐
海宇服务1 小时前
零信任架构实战:基于海宇对外投资历史查询服务构建自动化供应商准入网关
运维·人工智能·架构·自动化
上海云盾-小余1 小时前
BGP 高防底层原理:TCP 异常流量识别与清洗机制
运维·网络·tcp/ip
captain3762 小时前
▲网络原理(2)-TCP
java·服务器·网络·tcp/ip·java-ee
言乐63 小时前
Python加速器4跨境网络加速器
运维·服务器·开发语言·网络·python
蓝速科技6 小时前
医院导诊 AI 数字人一体机场景适配与落地指南丨蓝速科技
运维·数据库·人工智能·科技·自然语言处理·技术分享
H_oRIZoN_6 小时前
Linux入门DAY41(51 单片机 串口与通信协议)
linux·运维·单片机
小马同学-8 小时前
OpenStack 使用实战:Web 界面与 CLI 命令行实验
运维·云计算·openstack
GeW8 小时前
RHCE备考别瞎学!按这个计划走,30天提高通过率,快速拿证
linux
Escalating_xu8 小时前
【System V 信号量】从 P/V 原语到 Builder 封装:写出可控、可清理的进程互斥组件
java·linux·开发语言·jvm