简介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解码子类到输出Buffer3.2协商、分配与回退4.实战案例4.1准备真实解码管线4.2观察协商与Buffer分配4.3完整验证命令与关键结果5.总结1.前言本篇目的理解gst_audio_decoder_allocate_output_buffer()如何为GstAudioDecoder子类创建可写输出Buffer并通过真实解码管线验证协商、分配、写入和提交之间的关系。GstAudioDecoder子类完成编码数据解析后需要把解码得到的PCM写入一个GstBuffer。这个Buffer不能只使用普通分配方式创建。输出格式可能刚刚发生变化下游可能通过ALLOCATION查询提供了专用GstAllocator和GstAllocationParamssrcPad也可能处于需要重新协商的状态。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后再次调用按新的容量重新分配协商或专用分配失败进入内部回退路径尝试使用默认分配器创建Buffersize的单位是字节不是样本数也不是解码帧数。例如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;outbufgst_audio_decoder_allocate_output_buffer(dec,size);if(outbufNULL)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()按照当前协商状态创建输出Buffergst_audio_decoder_finish_frame()接收解码Buffer并交给基类完成时间戳和下游处理gst_buffer_new_allocate()直接创建Buffer不处理GstAudioDecoder的协商状态这个函数是同步执行的不启动新的线程不触发解码回调也不负责填充PCM内容。3.调用流程剖析3.1解码子类到输出BufferGstAudioDecoder基类把输入数据交给子类的handle_frame虚函数。子类调用具体解码库得到PCM样本再根据样本数、声道数和采样格式计算字节数。典型调用关系如下sizesamples*bytes_per_frame;outbufgst_audio_decoder_allocate_output_buffer(dec,size);if(outbufNULL)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(size0,NULL);GST_AUDIO_DECODER_STREAM_LOCK(dec);needs_reconfiguregst_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;}}buffergst_buffer_new_allocate(dec-priv-ctx.allocator,size,dec-priv-ctx.params);if(!buffer)gotofallback;GST_AUDIO_DECODER_STREAM_UNLOCK(dec);returnbuffer;fallback:buffergst_buffer_new_allocate(NULL,size,NULL);GST_AUDIO_DECODER_STREAM_UNLOCK(dec);returnbuffer;函数首先检查size然后持有流锁。如果输出格式发生变化或者srcPad被标记为需要重新协商函数会调用gst_audio_decoder_negotiate_unlocked()。协商成功后ctx.allocator和ctx.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解码。opusdec是GstAudioDecoder的子类它会在解码PCM后调用gst_audio_decoder_allocate_output_buffer()。先确认插件存在gst-inspect-1.0 opusenc opusdec再准备测试管线audiotestsrc num-buffers20wavesine!\audioconvert!audioresample!\audio/x-raw,formatS16LE,rate48000,channels2!\opusenc!opusdec!fakesinksyncfalse4.2观察协商与Buffer分配为audiodecoder打开调试日志GST_DEBUG_NO_COLOR1GST_DEBUGaudiodecoder:6\gst-launch-1.0\audiotestsrc num-buffers20wavesine!\audioconvert!audioresample!\audio/x-raw,formatS16LE,rate48000,channels2!\opusenc!opusdec!fakesinksyncfalse正常情况下管线会完成编码、解码并收到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_COLOR1\GST_DEBUGaudiodecoder:6\gst-launch-1.0\audiotestsrc num-buffers20wavesine!\audioconvert!audioresample!\audio/x-raw,formatS16LE,rate48000,channels2!\opusenc!opusdec!fakesinksyncfalse\21|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或提交下游数据。