☰
Linux Gstreamer深度解析之gst_audio_decoder_finish_frame调用流程与实战(五十二)
2026/10/2 15:55:56 网站建设 项目流程

简介: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接收Buffer并更新输出状态
        • 3.2从解码子类进入下游输出
      • 🌻4.实战案例
        • 4.1准备最小音频解码器
        • 4.2提交输出Buffer并验证结果
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_decoder_finish_frame()如何接收解码后的音频Buffer,更新解码器输出状态,并把数据交给下游,同时通过最小解码器验证Buffer所有权、输出帧数和返回值之间的关系。

GstAudioDecoder基类负责处理输入缓存、格式协商、时间戳和下游输出。解码器子类只需要在handle_frame()中完成编码数据解析,然后把得到的原始音频Buffer提交给基类。

gst_audio_decoder_finish_frame()就是这个提交动作的核心接口。

它接收三个关键信息:

  • 当前解码器对象。
  • 已经包含原始音频数据的GstBuffer。
  • 该Buffer表示的解码后音频Frame数量。

函数调用后,基类会接管Buffer,并根据当前输出状态完成后续处理。它不负责执行具体的AAC、MP3或Opus解码,也不负责创建解码器子类。

handle_frame收到输入帧

子类生成原始音频Buffer

gst_audio_decoder_finish_frame

基类处理输出状态

向下游推送音频Buffer

🌻2.应用场景和用法

2.1提交解码结果和丢弃帧

gst_audio_decoder_finish_frame()主要用于以下场景。

使用场景buf参数frames参数结果
正常输出解码数据有效音频Buffer输出Buffer包含的音频Frame数基类处理并推送音频数据
解码器产生静音或空结果NULL被消耗的Frame数不推送Buffer,但推进输出时间
丢弃前导帧NULL需要跳过的Frame数这些Frame被标记为已处理
多次输出不同音频Buffer每次对应的输出Frame数子类可以分多次提交结果
下游出现错误有效音频Buffer正常Frame数返回对应的GstFlowReturn

这里的frames表示解码后音频数据包含的音频Frame数量,不是输入压缩Buffer的字节数,也不是编码数据包的数量。

例如,输出格式为S16立体声时,一个音频Frame包含左右两个采样值。若输出Buffer包含20个立体声Frame,那么frames应当传入20,而不是40个采样值或80个字节。

当buf为NULL时,函数不会向下游发送数据。这种用法适合处理解码器预热、前导静音或需要跳过的输出区间。

2.2函数原型与返回语义

函数原型如下:

GstFlowReturngst_audio_decoder_finish_frame(GstAudioDecoder*dec,GstBuffer*buf,gint frames);
参数含义
dec已经初始化并处于处理状态的GstAudioDecoder对象
buf解码后的原始音频Buffer,函数接管其所有权,也可以传入NULL
framesbuf中包含的解码后音频Frame数量

函数返回GstFlowReturn,常见结果包括:

  • GST_FLOW_OK:输出已经被基类接受。
  • GST_FLOW_FLUSHING:解码器正在刷新或停止处理。
  • GST_FLOW_EOS:下游已经结束。
  • 其他负值:下游或内部处理发生错误。

最小调用方式如下:

GstBuffer*output;GstFlowReturn ret;output=gst_audio_decoder_allocate_output_buffer(decoder,output_size);/* output交给finish_frame后,调用者不能再次释放 */ret=gst_audio_decoder_finish_frame(decoder,output,output_frames);returnret;

调用前必须满足以下条件:

  1. decoder是有效的GstAudioDecoder对象。
  2. 当buf包含有效数据时,源Pad输出Caps已经设置。
  3. frames与输出Buffer中的音频数据数量一致。
  4. buf没有被其他代码提前释放。

调用成功后,finish_frame()会接管buf的所有权。handle_frame()收到的输入Buffer也可能因为这次调用而失效,因此子类不能在调用后继续访问输入Buffer。

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

接口主要职责
handle_frame()接收输入编码帧并执行具体解码
gst_audio_decoder_set_output_format()设置解码器输出音频格式
gst_audio_decoder_allocate_output_buffer()按输出格式分配音频Buffer
gst_audio_decoder_finish_frame()提交解码后的音频Frame
gst_audio_decoder_finish_subframe()在一个输入帧内提交多个输出子帧
gst_audio_decoder_push_event()向下游发送事件

🌻3.调用流程剖析

3.1接收Buffer并更新输出状态

gst_audio_decoder_finish_frame()的核心职责不是解码,而是把子类生成的结果交给基类输出流程。

当buf有效时,基类需要确认当前输出Caps已经设置,然后处理Buffer的时间戳、持续时间和输出位置,最后尝试把数据推送到下游。

当buf为NULL时,基类不生成下游Buffer,只根据frames推进已经消耗的输出Frame数量。

可以把函数内部动作概括为:

检查解码器和参数 ↓ 判断是否存在输出Buffer ↓ 处理输出Frame数量 ↓ 更新输出时间状态 ↓ 尝试向下游推送或丢弃 ↓ 返回GstFlowReturn

frames是这条流程中的关键参数。它让基类知道当前调用代表了多少解码后音频数据,从而计算后续输出的时间位置。

是

否

传入解码器和输出参数

是否存在音频Buffer

检查输出Caps和Buffer

记录需要丢弃的Frame

更新输出时序和状态

返回下游Flow结果

需要特别区分两个对象:

  • handle_frame()中的输入Buffer是压缩或编码数据。
  • finish_frame()中的输出Buffer是解码后的原始音频数据。

frames描述的是第二个Buffer中的音频Frame数量。

3.2从解码子类进入下游输出

完整链路通常从基类调用子类的handle_frame()开始。

子类完成编码数据解析后,通常按以下顺序处理:

  1. 根据输出音频格式申请Buffer。
  2. 将PCM采样写入Buffer。
  3. 调用gst_audio_decoder_finish_frame()。
  4. 将返回的GstFlowReturn继续返回给基类。

基类接管Buffer后,还可能执行以下工作:

  • 根据上游输入时间戳计算输出时间戳。
  • 根据输出Frame数量计算Buffer持续时间。
  • 调用pre_push虚函数进行推送前处理。
  • 根据配置聚合音频Buffer。
  • 调用源Pad的推送逻辑并返回下游结果。

这些工作由GstAudioDecoder基类负责,解码器子类不应该重复实现。

如果一个输入编码帧对应多个输出音频区间,子类可以多次调用gst_audio_decoder_finish_subframe(),也可以根据解码器设计使用多次gst_audio_decoder_finish_frame()。如果只是普通的一进一出解码流程,直接调用finish_frame()即可。

基类调用handle_frame

子类完成编码数据解析

申请并填充原始音频Buffer

gst_audio_decoder_finish_frame

基类处理时间戳和输出状态

源Pad推送并返回Flow结果

finish_frame()返回的不是“解码是否成功”的普通布尔值,而是完整的下游流控结果。子类应当把这个返回值继续交给调用者处理,不能只返回GST_FLOW_OK。

🌻4.实战案例

4.1准备最小音频解码器

本案例定义一个测试用GstAudioDecoder子类。

它接收audio/x-test-custom格式的输入,并输出44100Hz、单声道、S16格式的原始音频。handle_frame()不实现真实的AAC或MP3解析,只申请一个包含单个S16采样的输出Buffer,然后调用gst_audio_decoder_finish_frame()提交一个音频Frame。

测试重点观察三件事:

  • finish_frame()是否接管输出Buffer。
  • frames=1是否能够推动一个输出音频Frame。
  • 返回值是否能够通过Harness传递到下游。
4.2提交输出Buffer并验证结果

测试步骤如下:

  1. 创建解码器子类和GstHarness,设置输入Caps。
  2. 向Harness提交一个输入Buffer,子类在handle_frame()中申请输出Buffer并调用finish_frame()。
  3. 从Harness拉取输出Buffer,检查输出大小和流控结果。

由于输出格式是S16单声道,一个音频Frame占用2字节。因此,测试代码申请2字节输出Buffer,并把frames设置为1。

如果finish_frame()执行成功,Harness可以拉取到一个下游输出Buffer。代码还会检查Buffer大小,确认提交的数据确实按照输出格式生成。

4.3完整代码
#include<gst/gst.h>#include<gst/audio/audio.h>#include<gst/check/gstharness.h>typedefstruct_TestAudioDecoder{GstAudioDecoder parent;}TestAudioDecoder;typedefstruct_TestAudioDecoderClass{GstAudioDecoderClass parent_class;}TestAudioDecoderClass;G_DEFINE_TYPE(TestAudioDecoder,test_audio_decoder,GST_TYPE_AUDIO_DECODER);/* 定义解码器的输入和输出Pad能力 */staticGstStaticPadTemplate decoder_sink_template=GST_STATIC_PAD_TEMPLATE("sink",GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-test-custom"));staticGstStaticPadTemplate decoder_src_template=GST_STATIC_PAD_TEMPLATE("src",GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-raw"));/* 设置解码器输出为44100Hz、单声道、S16 */staticgbooleantest_audio_decoder_set_format(GstAudioDecoder*decoder,GstCaps*caps){GstAudioInfo info;gst_audio_info_set_format(&info,GST_AUDIO_FORMAT_S16,44100,1,NULL);returngst_audio_decoder_set_output_format(decoder,&info);}/* 生成一个原始音频Frame并提交给AudioDecoder基类 */staticGstFlowReturntest_audio_decoder_handle_frame(GstAudioDecoder*decoder,GstBuffer*input){GstBuffer*output;GstMapInfo map;if(input==NULL)returnGST_FLOW_OK;/* 单声道S16每个Frame占用2字节 */output=gst_audio_decoder_allocate_output_buffer(decoder,sizeof(gint16));if(output==NULL)returnGST_FLOW_ERROR;/* 写入一个可观察的PCM采样值 */if(!gst_buffer_map(output,&map,GST_MAP_WRITE)){gst_buffer_unref(output);returnGST_FLOW_ERROR;}*(gint16*)map.data=0;gst_buffer_unmap(output,&map);/* * finish_frame会接管output的所有权。 * frames=1表示这个Buffer包含一个解码后的音频Frame。 */returngst_audio_decoder_finish_frame(decoder,output,1);}staticvoidtest_audio_decoder_class_init(TestAudioDecoderClass*klass){GstElementClass*element_class=GST_ELEMENT_CLASS(klass);GstAudioDecoderClass*decoder_class=GST_AUDIO_DECODER_CLASS(klass);gst_element_class_add_static_pad_template(element_class,&decoder_sink_template);gst_element_class_add_static_pad_template(element_class,&decoder_src_template);gst_element_class_set_metadata(element_class,"Test Audio Decoder","Decoder/Audio","finish_frame test decoder","GStreamer");decoder_class->set_format=test_audio_decoder_set_format;decoder_class->handle_frame=test_audio_decoder_handle_frame;}staticvoidtest_audio_decoder_init(TestAudioDecoder*decoder){}/* Harness输入端接收编码测试数据 */staticGstStaticPadTemplate harness_src_template=GST_STATIC_PAD_TEMPLATE("harness-src",GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-test-custom"));/* Harness输出端接收解码后的原始音频 */staticGstStaticPadTemplate harness_sink_template=GST_STATIC_PAD_TEMPLATE("harness-sink",GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-raw"));intmain(intargc,char*argv[]){GstElement*decoder_element;GstHarness*harness;GstBuffer*input;GstBuffer*output;GstFlowReturn ret;gboolean valid_output;gst_init(&argc,&argv);/* 创建测试解码器,并交给Harness管理 */decoder_element=g_object_new(test_audio_decoder_get_type(),NULL);harness=gst_harness_new_full(decoder_element,&harness_src_template,"sink",&harness_sink_template,"src");gst_object_unref(decoder_element);/* 设置输入Caps,触发解码器格式配置 */gst_harness_set_src_caps(harness,gst_caps_new_simple("audio/x-test-custom",NULL));/* 创建一个最小输入Buffer */input=gst_buffer_new_allocate(NULL,1,NULL);GST_BUFFER_PTS(input)=0;GST_BUFFER_DURATION(input)=GST_MSECOND;/* 推入输入数据,触发handle_frame */ret=gst_harness_push(harness,input);/* * finish_frame返回的Flow结果会沿着handle_frame返回。 * 这里先确认下游处理没有失败。 */if(ret!=GST_FLOW_OK){gst_harness_teardown(harness);return1;}/* 拉取finish_frame提交的原始音频Buffer */output=gst_harness_pull(harness);if(output==NULL){gst_harness_teardown(harness);return1;}/* S16单声道一个Frame应当占用2字节 */valid_output=gst_buffer_get_size(output)==sizeof(gint16);g_print("flow-result: %s\n",ret==GST_FLOW_OK?"GST_FLOW_OK":"ERROR");g_print("output-size: %"G_GSIZE_FORMAT"\n",gst_buffer_get_size(output));gst_buffer_unref(output);gst_harness_teardown(harness);returnvalid_output?0:1;}

🌻5.总结

gst_audio_decoder_finish_frame()就是把解码器子类生成的原始音频Buffer及其Frame数量交给GstAudioDecoder基类,由基类完成输出状态处理、下游推送和流控结果返回。

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

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

立即咨询