news 2026/9/14 21:32:20

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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.2 函数用法
      • 🌻3.调用流程剖析
        • 🌻3.1 校验参数并建立转换器对象
        • 🌻3.2 组装转换链并选择执行路径
      • 🌻4.实战案例
        • 🌻4.1 准备输入和输出格式
        • 🌻4.2 创建并使用转换器
        • 🌻4.3 验证转换结果
      • 🌻5.总结

🌻1.前言

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

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

输入:S16、48000Hz、2声道、交错布局 输出:F32、48000Hz、2声道、交错布局

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

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

需要注意:

gst_audio_converter_new() ≠ 立即转换一块音频数据

它负责建立转换器和转换方案。真正的数据处理由后续的gst_audio_converter_samples()gst_audio_converter_convert()完成。

① 输入GstAudioInfo

② 输出GstAudioInfo

③ 创建转换器

④ 组装转换链

⑤ 处理音频数据


🌻2.应用场景和用法

🌻2.1 应用场景

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

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

in_info = 输入格式 out_info = 输出格式

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

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

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

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

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

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

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

对象关系可以概括为:

输入Caps或格式

输入GstAudioInfo

gst_audio_converter_new

GstAudioConverter

samples/convert处理数据

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

只交换声道位置: gst_audio_reorder_channels() 只执行采样率转换: GstAudioResampler 组合格式、通道、采样率和布局转换: GstAudioConverter

🌻2.2 函数用法

函数原型:

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,调用者不应在函数返回后继续释放或使用它。

返回值:

成功:返回新创建的GstAudioConverter* 失败:返回NULL

失败通常表示:

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

最小调用方式:

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。使用完成后应调用:

gst_audio_converter_free(converter);

不能使用gst_object_unref()释放。

调用前:

只有输入和输出格式描述 没有可执行的转换链

调用后:

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

🌻3.调用流程剖析

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

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

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不为空,函数会先读取其中的混音矩阵配置,并检查矩阵尺寸是否匹配输入和输出声道数。

例如:

输入2声道,输出1声道 混音矩阵必须有1行、2列

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

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

convert=g_new0(GstAudioConverter,1);convert->flags=flags;convert->in=*in_info;convert->out=*out_info;

这一步有两个关键结果:

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

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

函数随后创建默认配置:

convert->config=gst_structure_new_empty("GstAudioConverter");

如果调用者传入了config,构造函数会通过gst_audio_converter_update_config()复制配置内容,并释放传入的GstStructure

in_info/out_info

检查指针和配置

检查混音矩阵与声道定位

分配GstAudioConverter

复制格式和配置

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


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

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

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()将内部格式打包为输出格式

输入Sample

解包和内部格式

通道与采样率处理

量化与布局处理

打包为输出Sample

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

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

convert = converter_passthrough in_place = TRUE passthrough = TRUE

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

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

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

整个构造过程是同步的:

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

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

gst_audio_converter_samples()

或:

gst_audio_converter_convert()

提交音频数据。

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

gst_audio_converter_get_out_frames()

获取预计输出Frame数量。


🌻4.实战案例

🌻4.1 准备输入和输出格式

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

输入:S16、48000Hz、2声道、交错布局 输出:F32、48000Hz、2声道、交错布局

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

因此该案例主要验证:

gst_audio_converter_new() ↓ 创建格式转换器 ↓ gst_audio_converter_convert() ↓ 分配F32输出数据

🌻4.2 创建并使用转换器

创建转换器:

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");return1;}

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

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

g_free(output);

转换器本身则使用:

gst_audio_converter_free(converter);

🌻4.3 验证转换结果

完整测试代码:

#include<gst/gst.h>#include<gst/audio/audio.h>intmain(intargc,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");return1;}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);return1;}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);return0;}

编译:

gcc audio_converter_new.c-oaudio_converter_new\$(pkg-config--cflags--libs\gstreamer-audio-1.0)

运行:

./audio_converter_new

关键输出应类似:

supports-inplace = FALSE passthrough = FALSE input-size = 8 output-size = 16

输入数组包含4个S16Sample:

4 × 2Byte = 8Byte

它表示2个双声道Audio Frame。

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

4个Sample × 4Byte = 16Byte

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

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

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

🌻5.总结

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 21:29:08

Python plot函数画图,简单到让人想哭

plot()函数具备的最为基础的功能是绘制二维折线图, 用户只要提供横纵坐标数据, 便能够生成默认样式的图形。. as pltnumpy as npx被设定为采用np中按照0, 2*np.pi这样、以100为划分数量, 也就是生成0经过2*np.pi之间那100个等间距的点。y np.sin(x)# 计算正弦函数值plt.plot(x…

作者头像 李华
网站建设 2026/9/14 21:29:00

Vite 项目构建提速,5 个配置优化打包编译速度

在前端项目迭代过程中&#xff0c;很多开发者都会遇到一个棘手问题&#xff1a;随着业务代码、组件、静态资源不断增多&#xff0c;原本极速的 Vite 项目&#xff0c;启动热更新变慢、生产打包耗时成倍增加。很多人误以为 Vite 天生就比 Webpack 快&#xff0c;不需要额外优化&…

作者头像 李华
网站建设 2026/9/14 21:27:56

第30届GOPS上海站:7位大咖深度拆解AI智能体+Harness落地清单!

【行业难题待解】行业巨变当下&#xff0c;Harness、Loop Engineering面对大型代码库&#xff0c;如何将Vibe Coding转化成可理解、可约束、可验证的闭环&#xff1f;【大会信息速览】10月16 - 17日&#xff0c;第30届GOPS全球运维大会暨研运数智化技术峰会上海站&#xff0c;特…

作者头像 李华
网站建设 2026/9/14 21:27:26

住所地公证书在哪办?从材料准备到出证一篇总结,附办理要点

住所地公证书&#xff0c;可以在线下公证处窗口办理&#xff0c;也能通过证天下公证小程序线上申请办理&#xff0c;不用来回跑现场。住所地公证&#xff0c;简单来说就是证明申请人当前居住地址真实有效的文书&#xff0c;多用于涉外办事场景。 公证认证百科http://www.gongz…

作者头像 李华
网站建设 2026/9/14 21:27:25

部署中的 OpenClaw 模型报错?TaoToken 这样设 base_url

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 21:26:54

长沙AIGC培训招生信息汇总,开班班型名额政策全解析

正文摘要本文从招生人群画像、开班时间与班型设置、小班名额配置、招生优惠政策四个维度&#xff0c;梳理长沙 AIGC 培训的最新招生动态&#xff0c;结合本地机构的开班节奏与培养模式&#xff0c;为计划近期报名的学习者提供客观参考&#xff0c;助力快速锁定适配班型。信息来…

作者头像 李华