简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 🌻2.应用场景和用法
- 2.1解码器格式需求场景
- 2.2函数原型与返回语义
- 🌻3.调用流程剖析
- 3.1子类初始化与Caps记录
- 3.2输入Buffer的格式门控
- 🌻4.实战案例
- 4.1准备`flacdec`解码器实例
- 4.2设置格式要求并观察Buffer结果
- 4.3完整代码与关键输出
- 🌻5.总结
🌻1.前言
本篇目的:理解
gst_audio_decoder_set_needs_format()如何控制音频解码器的输入格式前置条件,并通过实际调用验证格式未协商时的处理结果。
GstAudioDecoder负责接收编码音频数据,并通过子类的set_format、parse和handle_frame完成解码。
不同解码器对输入格式的依赖不同。有些解码器必须先获得Caps才能创建底层Codec对象,有些解码器则可以从编码数据中自行推断格式。
gst_audio_decoder_set_needs_format()用于配置这项要求:
- 设置为
TRUE:输入Caps没有协商成功前,不允许处理输入Buffer。 - 设置为
FALSE:允许子类在没有调用set_format的情况下接收数据,子类必须自行完成初始化。
这个函数只修改needs_format状态,不发送Caps事件,不调用子类的set_format,也不直接执行解码。
🌻2.应用场景和用法
2.1解码器格式需求场景
gst_audio_decoder_set_needs_format()主要用于声明解码器能否在输入Caps缺失时工作。
| 场景 | 设置方式 | 实际作用 |
|---|---|---|
| 底层Codec必须先获得采样率和声道数 | 设置为TRUE | 等待输入Caps和set_format完成后再处理Buffer |
| 子类可以从编码数据中推断参数 | 设置为FALSE | 允许数据先进入parse或handle_frame |
| 解码器实例初始化 | 通常在子类init中调用 | 确定该解码器的默认格式策略 |
排查not-negotiated | 读取当前状态 | 判断是否因为缺少输入Caps被拦截 |
GStreamer中的flacdec、mpg123audiodec和opusdec等解码器会在实例初始化时把该状态设置为TRUE,因为它们需要先通过格式协商建立解码上下文。
默认值是FALSE。如果子类没有主动设置,基类不会因为缺少input_caps而提前拒绝输入Buffer,但子类必须确保自己的handle_frame能够处理未配置状态。
2.2函数原型与返回语义
函数原型如下:
voidgst_audio_decoder_set_needs_format(GstAudioDecoder*dec,gboolean enabled);| 参数 | 含义 |
|---|---|
dec | 有效的GstAudioDecoder对象 |
enabled | TRUE表示必须先完成输入格式协商,FALSE表示允许未协商格式时接收数据 |
该函数返回void。它通过GST_OBJECT_LOCK保护私有字段needs_format,属于线程安全接口。
最小调用方式如下:
gst_audio_decoder_set_needs_format(decoder,TRUE);gboolean needs_format=gst_audio_decoder_get_needs_format(decoder);调用前,解码器对象已经创建。调用过程中,函数只修改私有状态字段。调用后,不会立即触发Caps协商,也不会调用子类的set_format。
真正的格式处理由后续Caps事件触发。gst_audio_decoder_sink_setcaps()收到新的输入Caps后,会调用子类的set_format。只有该回调成功,基类才会保存ctx.input_caps。
几个相邻接口的职责不同:
| 接口 | 作用 |
|---|---|
gst_audio_decoder_set_needs_format() | 设置是否必须先完成输入格式协商 |
gst_audio_decoder_get_needs_format() | 读取当前格式前置条件 |
子类set_format回调 | 接收输入Caps并配置解码器 |
gst_audio_decoder_set_output_format() | 声明解码后的PCM输出格式 |
| 基类输入链路 | 根据needs_format和input_caps决定是否允许处理Buffer |
set_needs_format控制的是输入格式前置条件,set_output_format控制的是解码后的输出格式,两者不能混用。
🌻3.调用流程剖析
3.1子类初始化与Caps记录
解码器子类通常在实例初始化阶段调用gst_audio_decoder_set_needs_format()。
以需要格式协商的解码器为例,初始化过程会把needs_format设置为TRUE。之后,上游发送Caps事件,基类进入gst_audio_decoder_sink_setcaps()。
gst_audio_decoder_sink_setcaps()执行以下动作:
- 检查新的Caps是否发生变化。
- 调用子类的
set_format回调。 - 回调成功后,把Caps保存到
priv->ctx.input_caps。
设置函数本身只完成第一个状态配置动作,不负责后续Caps传递。
如果子类的set_format返回失败,input_caps不会更新。即使needs_format已经是TRUE,解码器仍然不能进入正常数据处理阶段。
3.2输入Buffer的格式门控
输入Buffer到达解码器时,基类首先获得GstAudioDecoder对象并加上流锁。
随后执行格式门控:
if(dec->priv->ctx.input_caps==NULL&&dec->priv->needs_format)gotonot_negotiated;进入not_negotiated分支后,基类会:
- 释放流锁。
- 发布
GST_ELEMENT_ERROR,错误类型为协商失败。 - 释放当前Buffer。
- 返回
GST_FLOW_NOT_NEGOTIATED。
如果needs_format为FALSE,或者input_caps已经记录,基类才会继续进入正向或反向数据处理链路,后续可能调用子类的parse和handle_frame。
因此,gst_audio_decoder_set_needs_format()影响的是Buffer能否进入解码链路,而不是Caps本身的传递过程。
🌻4.实战案例
4.1准备flacdec解码器实例
本案例使用GStreamer提供的flacdec作为测试对象。该解码器在实例初始化时会把needs_format设置为TRUE。
先确认插件已经安装:
gst-inspect-1.0 flacdec程序创建flacdec实例后,读取默认状态,再主动设置格式要求,并直接向解码器SINKPad提交一个没有输入Caps的测试Buffer。
4.2设置格式要求并观察Buffer结果
程序按以下顺序运行:
- 创建
flacdec实例,读取当前needs_format。 - 调用
gst_audio_decoder_set_needs_format()设置为TRUE。 - 在没有输入Caps的情况下向SINKPad提交一个测试Buffer。
- 观察基类返回的
GST_FLOW_NOT_NEGOTIATED。 - 将状态改为
FALSE,通过Getter确认字段已经变化。
该实验验证的是格式门控,不是真正的FLAC解码。由于输入Buffer没有携带FLAC格式Caps,needs_format=TRUE时,基类会在进入子类解码函数前直接拒绝数据。
4.3完整代码与关键输出
#include<gst/gst.h>#include<gst/audio/audio.h>intmain(intargc,char*argv[]){GstElement*element;GstAudioDecoder*decoder;GstPad*sink_pad;GstBuffer*buffer;GstFlowReturn flow;gst_init(&argc,&argv);element=gst_element_factory_make("flacdec","decoder");if(element==NULL){g_printerr("flacdec plugin is not available\n");return1;}decoder=GST_AUDIO_DECODER(element);g_print("Initial needs_format: %s\n",gst_audio_decoder_get_needs_format(decoder)?"TRUE":"FALSE");gst_audio_decoder_set_needs_format(decoder,TRUE);g_print("After enabling: %s\n",gst_audio_decoder_get_needs_format(decoder)?"TRUE":"FALSE");sink_pad=GST_AUDIO_DECODER_SINK_PAD(decoder);if(!gst_pad_set_active(sink_pad,TRUE)){g_printerr("Failed to activate decoder sink pad\n");gst_object_unref(element);return1;}buffer=gst_buffer_new_allocate(NULL,1,NULL);if(buffer==NULL){g_printerr("Failed to allocate test buffer\n");gst_pad_set_active(sink_pad,FALSE);gst_object_unref(element);return1;}flow=gst_pad_chain(sink_pad,buffer);g_print("Chain result without input caps: %s\n",gst_flow_get_name(flow));gst_audio_decoder_set_needs_format(decoder,FALSE);g_print("After disabling: %s\n",gst_audio_decoder_get_needs_format(decoder)?"TRUE":"FALSE");gst_pad_set_active(sink_pad,FALSE);gst_object_unref(element);returnflow==GST_FLOW_NOT_NEGOTIATED?0:1;}编译运行:
gcc test_audio_decoder_needs_format.c\-otest_audio_decoder_needs_format\$(pkg-config--cflags--libsgstreamer-1.0 gstreamer-audio-1.0)./test_audio_decoder_needs_format关键输出:
Initial needs_format: TRUE After enabling: TRUE Chain result without input caps: not-negotiated After disabling: FALSE如果系统没有安装flacdec插件,程序会在创建元素时退出。实际插件名称和可用状态以gst-inspect-1.0 flacdec的输出为准。
🌻5.总结
gst_audio_decoder_set_needs_format()就是为GstAudioDecoder设置输入格式前置条件,让基类在Caps未协商时决定是否允许Buffer进入子类解码链路。