Linux Gstreamer深度解析之gst_audio_decoder_allocate_output_buffer调用流程与实战(三十七)
2026/9/24 17:29:16 网站建设 项目流程

简介:CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀

人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮


🍉🍉🍉文章目录🍉🍉🍉

      • 🌻1.前言
      • 🌻2.应用场景和用法
        • 2.1解码输出Buffer分配场景
        • 2.2函数原型与返回语义
      • 🌻3.调用流程剖析
        • 3.1解码子类到输出Buffer
        • 3.2协商、分配与回退
      • 🌻4.实战案例
        • 4.1准备真实解码管线
        • 4.2观察协商与Buffer分配
        • 4.3完整验证命令与关键结果
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_decoder_allocate_output_buffer()如何为GstAudioDecoder子类创建可写输出Buffer,并通过真实解码管线验证协商、分配、写入和提交之间的关系。

GstAudioDecoder子类完成编码数据解析后,需要把解码得到的PCM写入一个GstBuffer

这个Buffer不能只使用普通分配方式创建。输出格式可能刚刚发生变化,下游可能通过ALLOCATION查询提供了专用GstAllocatorGstAllocationParams,srcPad也可能处于需要重新协商的状态。

gst_audio_decoder_allocate_output_buffer()负责检查这些状态,必要时触发输出协商,再使用当前分配参数创建Buffer。如果协商分配失败,它还会回退到默认分配器。

这个函数只负责创建输出Buffer,不负责解码PCM、不负责填充数据、不负责设置时间戳,也不负责调用gst_audio_decoder_finish_frame()把Buffer提交给下游。

解码器得到PCM字节数

gst_audio_decoder_allocate_output_buffer

检查重协商和分配参数

创建可写GstBuffer

子类写入PCM并提交输出

🌻2.应用场景和用法

2.1解码输出Buffer分配场景

gst_audio_decoder_allocate_output_buffer()主要用于以下场景:

场景调用动作得到的结果
普通解码输出根据PCM样本数和BPF计算size后调用得到可写输出Buffer
输出格式刚刚改变先设置新的输出格式,再调用函数内部可以触发重新协商
下游提供专用分配器使用下游协商得到的allocator和params输出Buffer遵循当前分配约束
解码器输出长度可变每次根据实际解码结果重新计算size避免固定Buffer容量不足
解码器需要重试释放容量不足的旧Buffer后再次调用按新的容量重新分配
协商或专用分配失败进入内部回退路径尝试使用默认分配器创建Buffer

size的单位是字节,不是样本数,也不是解码帧数。

例如S16立体声输出中,一个AudioFrame占4字节。如果解码得到480个AudioFrame,调用参数应该是:

size = 480 × 4 = 1920字节

如果解码器返回0个样本,不能把size设置为0继续调用。源码在函数入口检查size > 0,零值会触发GLib前置条件检查并返回NULL

2.2函数原型与返回语义

函数原型如下:

GstBuffer*gst_audio_decoder_allocate_output_buffer(GstAudioDecoder*dec,gsize size);
参数或返回值含义
dec有效的GstAudioDecoder对象,通常是当前解码子类实例
size需要分配的输出Buffer字节数,必须大于0
返回值新创建的GstBuffer,返回所有权由调用者持有,失败时返回NULL

最小调用方式如下:

GstBuffer*outbuf;outbuf=gst_audio_decoder_allocate_output_buffer(dec,size);if(outbuf==NULL)returnGST_FLOW_ERROR;

调用前,解码子类需要先确定PCM输出长度,并且已经通过gst_audio_decoder_set_output_format()gst_audio_decoder_set_output_caps()设置输出格式。

调用过程中,函数会持有GstAudioDecoder的流锁,检查srcPad是否需要重新协商。必要时,它会重新设置srcPadCaps并获取下游提供的分配参数。

调用成功后,调用者可以映射Buffer、写入PCM,然后将Buffer交给:

gst_audio_decoder_finish_frame(dec,outbuf,frames);

finish_frame()会接管Buffer所有权。调用者不应在成功提交后再次释放同一个Buffer。

几个相邻接口的职责不同:

接口作用
gst_audio_decoder_set_output_format()设置输出音频格式并标记输出格式变化
gst_audio_decoder_negotiate()主动执行输出Caps和分配协商
gst_audio_decoder_get_allocator()查询当前分配器和分配参数
gst_audio_decoder_allocate_output_buffer()按照当前协商状态创建输出Buffer
gst_audio_decoder_finish_frame()接收解码Buffer并交给基类完成时间戳和下游处理
gst_buffer_new_allocate()直接创建Buffer,不处理GstAudioDecoder的协商状态

这个函数是同步执行的,不启动新的线程,不触发解码回调,也不负责填充PCM内容。

🌻3.调用流程剖析

3.1解码子类到输出Buffer

GstAudioDecoder基类把输入数据交给子类的handle_frame虚函数。子类调用具体解码库得到PCM样本,再根据样本数、声道数和采样格式计算字节数。

典型调用关系如下:

size=samples*bytes_per_frame;outbuf=gst_audio_decoder_allocate_output_buffer(dec,size);if(outbuf==NULL)returnGST_FLOW_ERROR;gst_buffer_map(outbuf,&map,GST_MAP_WRITE);decode_to_pcm(map.data,map.size);gst_buffer_unmap(outbuf,&map);returngst_audio_decoder_finish_frame(dec,outbuf,frames);

Vorbis和Opus解码器都采用了类似路径。

Opus解码器还会根据实际解码结果处理容量不足的情况。第一次分配的Buffer不够时,它会解除映射、释放旧Buffer、增大样本容量,再次调用分配函数。

解码子类计算PCM字节数

调用输出Buffer分配

是否需要重新协商

协商srcCaps和分配参数

沿用当前allocator和params

创建GstBuffer

函数返回的只是一个已经分配好的Buffer。PCM数据仍然由解码子类写入,时间戳、Duration和下游提交则由finish_frame()继续处理。

3.2协商、分配与回退

GStreamer1.24.2中的核心逻辑可以概括为:

g_return_val_if_fail(size>0,NULL);GST_AUDIO_DECODER_STREAM_LOCK(dec);needs_reconfigure=gst_pad_check_reconfigure(dec->srcpad);if(dec->priv->ctx.output_format_changed||(GST_AUDIO_INFO_IS_VALID(&dec->priv->ctx.info)&&needs_reconfigure)){if(!gst_audio_decoder_negotiate_unlocked(dec)){gst_pad_mark_reconfigure(dec->srcpad);gotofallback;}}buffer=gst_buffer_new_allocate(dec->priv->ctx.allocator,size,&dec->priv->ctx.params);if(!buffer)gotofallback;GST_AUDIO_DECODER_STREAM_UNLOCK(dec);returnbuffer;fallback:buffer=gst_buffer_new_allocate(NULL,size,NULL);GST_AUDIO_DECODER_STREAM_UNLOCK(dec);returnbuffer;

函数首先检查size,然后持有流锁。

如果输出格式发生变化,或者srcPad被标记为需要重新协商,函数会调用gst_audio_decoder_negotiate_unlocked()。协商成功后,ctx.allocatorctx.params会更新为当前配置。

正常分配路径使用:

gst_buffer_new_allocate(ctx.allocator,size,&ctx.params);

如果协商失败,函数会标记srcPad继续需要协商,并使用默认分配器回退。如果当前分配器创建Buffer失败,也会进入回退路径。

因此,分配函数返回成功并不代表一定使用了下游专用内存。回退路径可能返回普通系统内存Buffer,调用者需要结合调试日志和下游能力判断最终内存类型。

持有decoder流锁

读取allocator和params

配置分配是否成功

返回已分配GstBuffer

使用默认allocator回退

回退Buffer是否成功

返回NULL

回退分配只负责尽量创建Buffer,不负责修复输出Caps,也不保证满足下游对专用内存、对齐方式或内存池的要求。

🌻4.实战案例

4.1准备真实解码管线

使用audiotestsrc生成原始音频,再通过opusenc编码,最后交给opusdec解码。

opusdecGstAudioDecoder的子类,它会在解码PCM后调用gst_audio_decoder_allocate_output_buffer()

先确认插件存在:

gst-inspect-1.0 opusenc opusdec

再准备测试管线:

audiotestsrc num-buffers=20wave=sine!\audioconvert!audioresample!\audio/x-raw,format=S16LE,rate=48000,channels=2!\opusenc!opusdec!fakesinksync=false
4.2观察协商与Buffer分配

audiodecoder打开调试日志:

GST_DEBUG_NO_COLOR=1GST_DEBUG="audiodecoder:6"\gst-launch-1.0\audiotestsrc num-buffers=20wave=sine!\audioconvert!audioresample!\audio/x-raw,format=S16LE,rate=48000,channels=2!\opusenc!opusdec!fakesinksync=false

正常情况下,管线会完成编码、解码并收到EOS。重点观察以下日志信息:

alloc src buffer setting src caps ALLOCATION

这些日志分别对应输出Buffer分配入口、srcPad输出Caps设置和下游分配参数处理。

如果日志中出现:

Failed to negotiate, fallback allocation

说明输出协商没有成功,函数已经进入默认分配器回退路径。此时仍可能得到一个普通GstBuffer,但不应再假设它来自下游提供的专用allocator。

如果输入或输出样本数为0,解码子类不得继续以size = 0调用分配函数,应直接处理无输出帧或丢弃当前解码帧。

4.3完整验证命令与关键结果
set-opipefail gst-inspect-1.0 opusenc opusdec>/tmp/gst_opus_inspect.logGST_DEBUG_NO_COLOR=1\GST_DEBUG="audiodecoder:6"\gst-launch-1.0\audiotestsrc num-buffers=20wave=sine!\audioconvert!audioresample!\audio/x-raw,format=S16LE,rate=48000,channels=2!\opusenc!opusdec!fakesinksync=false\2>&1|tee/tmp/gst_audio_decoder_allocate.log

验证重点:

1. gst-inspect-1.0能够找到opusenc和opusdec。 2. gst-launch-1.0正常结束并收到EOS。 3. audiodecoder调试日志出现alloc src buffer。 4. 输出格式变化时能够看到setting src caps或ALLOCATION相关日志。 5. 没有出现size > 0断言失败。

这组命令验证了完整链路:opusdec计算PCM输出容量,GstAudioDecoder检查协商状态并创建Buffer,解码子类写入PCM,最后通过finish_frame()提交解码结果。

🌻5.总结

gst_audio_decoder_allocate_output_buffer()就是根据解码器当前输出协商状态和分配参数创建可写GstBuffer,它不负责解码、填充PCM或提交下游数据。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询