)
简介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或格式输入GstAudioInfogst_audio_converter_newGstAudioConvertersamples/convert处理数据gst_audio_converter_new()不是单独的声道重排接口也不是单独的采样率转换接口只交换声道位置 gst_audio_reorder_channels() 只执行采样率转换 GstAudioResampler 组合格式、通道、采样率和布局转换 GstAudioConverter2.2 函数用法函数原型GstAudioConverter*gst_audio_converter_new(GstAudioConverterFlags flags,GstAudioInfo*in_info,GstAudioInfo*out_info,GstStructure*config);参数参数作用flags控制输入可写性和采样率更新能力in_info输入音频格式描述out_info输出音频格式描述config可选的转换配置传入后所有权交给函数常用FlagFlag作用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);convertergst_audio_converter_new(GST_AUDIO_CONVERTER_FLAG_NONE,in_info,out_info,NULL);这里的in_info和out_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列如果输入输出声道数不同并且任意一方是未定位声道同时又没有提供混音矩阵函数会拒绝创建转换器。参数检查通过后函数分配转换器对象convertg_new0(GstAudioConverter,1);convert-flagsflags;convert-in*in_info;convert-out*out_info;这一步有两个关键结果flags被复制到转换器内部 in_info和out_info的内容被复制到转换器内部调用者仍然拥有原来的GstAudioInfo但之后修改原对象不会自动改变已经创建的转换器。函数随后创建默认配置convert-configgst_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 创建并使用转换器创建转换器convertergst_audio_converter_new(GST_AUDIO_CONVERTER_FLAG_NONE,in_info,out_info,NULL);返回值必须先判断if(converterNULL){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 验证转换结果完整测试代码#includegst/gst.h#includegst/audio/audio.hintmain(intargc,char*argv[]){GstAudioInfo in_info;GstAudioInfo out_info;GstAudioConverter*converter;gint16 input[]{1000,-1000,2000,-2000};gpointer outputNULL;gsize output_size0;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);convertergst_audio_converter_new(GST_AUDIO_CONVERTER_FLAG_NONE,in_info,out_info,NULL);if(converterNULL){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个S16Sample4 × 2Byte 8Byte它表示2个双声道Audio Frame。输出格式是F32每个Sample占4Byte4个Sample × 4Byte 16Byte因此输出大小从8Byte变成16Byte说明转换器没有原地复用输入内存而是按照输出格式分配了新的结果区域。这个案例验证了三个事实gst_audio_converter_new()创建可复用的转换器对象 gst_audio_converter_convert()才真正处理音频数据 格式变化时通常不能使用旁路和原地转换5.总结gst_audio_converter_new()就是根据输入输出GstAudioInfo和配置创建一条可复用的音频转换链后续由其他接口使用它完成格式、声道、采样率或布局转换。