☰
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
2026/10/7 2:45:35 网站建设 项目流程

简介: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基类按配置组织PCM数据
      • 🌻4.实战案例
        • 4.1配置固定帧大小和批次数
        • 4.2提交输入并观察批次
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_encoder_set_frame_max()如何设置GstAudioEncoder一次交给子类处理的最大音频帧数,并通过一个最小编码器验证它对handle_frame()输入数据的影响。

GstAudioEncoder基类会先收集输入PCM,再按照子类提供的帧参数组织数据,最后调用子类的handle_frame()。如果编码器要求每个编码帧必须包含固定数量的采样点,子类还可以设置一次最多处理多少个这样的帧。

gst_audio_encoder_set_frame_max()保存的就是这个上限。它只更新基类的帧配置,不会自行收集PCM、调用编码器或发送输出Buffer。

输入PCM数据

GstAudioEncoder收集数据

读取固定采样数和frame_max

组织多个完整音频帧

调用子类handle_frame

🌻2.应用场景和用法

2.1一次组织多个固定大小的音频帧

有些编码器按照固定大小的PCM帧工作。例如编码器每帧需要1024个采样点,子类可以把最小采样数和最大采样数都设为1024,再设置frame_max。

假设设置为4,基类在PCM数据足够时,最多可以把4个完整帧组织在一次handle_frame()调用中。单声道S16LE每个采样点占2字节,4帧输入数据为:

1024 × 4 × 2 = 8192字节

一次交付多个完整帧,可以让编码器子类按批次处理输入。实际批次还受当前已收集PCM数量影响,数据不足时,基类不会凭空补齐完整帧。

frame_max表示一次最多组织的帧数,不是输出Buffer数量,也不是编码器可以缓存的最大字节数。

2.2函数原型与配置关系

函数原型如下:

voidgst_audio_encoder_set_frame_max(GstAudioEncoder*enc,gint num);
参数含义
enc已创建的GstAudioEncoder对象
num一次最多接收的音频帧数

函数没有返回值。传入的值由基类保存在内部上下文的frame_max字段中。该接口检查enc是否为有效的GstAudioEncoder对象,但不提供设置成功或失败的返回码。

要让frame_max按多个固定帧组织数据,frame_samples_min和frame_samples_max必须相等:

gst_audio_encoder_set_frame_samples_min(enc,1024);gst_audio_encoder_set_frame_samples_max(enc,1024);gst_audio_encoder_set_frame_max(enc,4);

frame_samples_min与frame_samples_max描述每个音频帧包含多少个采样点。frame_max描述一次最多组合多少个这样的帧。只设置frame_max,没有配置一致的固定帧大小,不能达到多帧批量组织的效果。

这些值应在子类的set_format()中设置。输入格式发生重新协商时,基类会在调用set_format()前将这些配置重置为0,因此在其他位置提前设置可能会被清除。

相邻接口的职责如下:

接口职责
gst_audio_encoder_set_frame_samples_min()设置子类每帧至少需要的采样点数
gst_audio_encoder_set_frame_samples_max()设置子类每帧最多需要的采样点数
gst_audio_encoder_set_frame_max()设置一次最多交给子类处理的帧数
gst_audio_encoder_get_frame_max()查询当前配置的一次最大帧数
gst_audio_encoder_finish_frame()提交编码输出,并告知已处理的输入采样点数

🌻3.调用流程剖析

3.1函数保存一次最大帧数

gst_audio_encoder_set_frame_max()本身逻辑很短,关键操作是将参数写入GstAudioEncoder私有上下文:

voidgst_audio_encoder_set_frame_max(GstAudioEncoder*enc,gint num){g_return_if_fail(GST_IS_AUDIO_ENCODER(enc));enc->priv->ctx.frame_max=num;GST_LOG_OBJECT(enc,"set to %d",num);}

函数不会在这里校验frame_samples_min和frame_samples_max是否相等,也不会立即重新组织输入数据。它只是保存配置,供基类后续收集PCM时读取。

每次调用子类的set_format()之前,基类会将frame_samples_min、frame_samples_max和frame_max重置为0。子类应在set_format()中重新声明当前输入格式对应的帧处理要求。

子类进入set_format

配置每帧最小采样数

配置每帧最大采样数

调用set_frame_max保存上限

frame_max写入私有上下文

3.2基类按配置组织PCM数据

输入PCM到达后,基类把数据放入内部GstAdapter,再根据可用字节数和帧配置决定交给子类的数据大小。

当frame_samples_min和frame_samples_max相等时,基类可以把输入按固定大小的完整帧组织。如果frame_max大于1,实际交付的帧数不会超过frame_max,也不会超过当前已经收集到的完整帧数。

当frame_max为0时,基类会按照当前可用PCM组织完整帧;设置为大于1的值时,则会对一次组织的完整帧数进行限制。

否

是

PCM进入内部GstAdapter

计算可用字节数

每帧最小值等于最大值

按普通帧配置处理

计算当前完整帧数

应用frame_max上限

构造输入Buffer并调用handle_frame

子类在handle_frame()中处理收到的PCM。编码完成后,子类调用gst_audio_encoder_finish_frame()提交输出,并用samples参数说明编码输出代表多少个输入采样点。frame_max控制输入批次,finish_frame()负责提交编码结果,两者作用不同。

🌻4.实战案例

4.1配置固定帧大小和批次数

下面的测试编码器使用单声道S16LE输入,每个固定帧包含256个采样点,并将一次最多处理帧数设为4:

gst_audio_encoder_set_frame_samples_min(enc,256);gst_audio_encoder_set_frame_samples_max(enc,256);gst_audio_encoder_set_frame_max(enc,4);

当基类已经收集到1024个采样点时,预计一次交给handle_frame()的数据大小为:

256 × 4 × 2 = 2048字节

这里每个采样点占2字节,完整输入Buffer包含4个256采样点的固定帧。

4.2提交输入并观察批次

测试程序使用GstHarness创建自定义编码器,提交2048字节的输入Buffer,再在handle_frame()中记录收到的Buffer大小和调用次数。

验证重点是:一次输入数据包含4个固定帧,frame_max设为4时,基类将这4帧组织为一次handle_frame()输入。

4.3完整代码
#include<gst/gst.h>#include<gst/audio/audio.h>#include<gst/check/gstharness.h>#defineSAMPLES_PER_FRAME256#defineFRAME_MAX4typedefstruct_BatchEncoder{GstAudioEncoder parent;}BatchEncoder;typedefstruct_BatchEncoderClass{GstAudioEncoderClass parent_class;}BatchEncoderClass;G_DEFINE_TYPE(BatchEncoder,batch_encoder,GST_TYPE_AUDIO_ENCODER);staticguint handle_frame_calls;staticgsize last_input_size;/* 编码器接收原始音频,输出测试用压缩格式 */staticGstStaticPadTemplate encoder_sink_template=GST_STATIC_PAD_TEMPLATE("sink",GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-raw"));staticGstStaticPadTemplate encoder_src_template=GST_STATIC_PAD_TEMPLATE("src",GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-test-encoded"));staticgbooleanbatch_encoder_set_format(GstAudioEncoder*enc,GstAudioInfo*info){GstCaps*output_caps;gboolean result;/* 固定每帧采样点数,基类据此按完整帧收集PCM */gst_audio_encoder_set_frame_samples_min(enc,SAMPLES_PER_FRAME);gst_audio_encoder_set_frame_samples_max(enc,SAMPLES_PER_FRAME);/* 一次最多将4个完整帧交给handle_frame */gst_audio_encoder_set_frame_max(enc,FRAME_MAX);output_caps=gst_caps_new_empty_simple("audio/x-test-encoded");result=gst_audio_encoder_set_output_format(enc,output_caps);gst_caps_unref(output_caps);returnresult;}staticGstFlowReturnbatch_encoder_handle_frame(GstAudioEncoder*enc,GstBuffer*input){GstBuffer*output;gsize input_size;if(input==NULL)returnGST_FLOW_OK;/* 记录基类本次组织出的PCM字节数 */handle_frame_calls++;input_size=gst_buffer_get_size(input);last_input_size=input_size;/* 测试编码器生成一个非空输出Buffer */output=gst_buffer_new_allocate(NULL,1,NULL);if(output==NULL)returnGST_FLOW_ERROR;/* 单声道S16LE每个采样点占2字节,提交对应采样点数 */returngst_audio_encoder_finish_frame(enc,output,input_size/sizeof(gint16));}staticvoidbatch_encoder_class_init(BatchEncoderClass*klass){GstElementClass*element_class=GST_ELEMENT_CLASS(klass);GstAudioEncoderClass*encoder_class=GST_AUDIO_ENCODER_CLASS(klass);gst_element_class_add_static_pad_template(element_class,&encoder_sink_template);gst_element_class_add_static_pad_template(element_class,&encoder_src_template);gst_element_class_set_metadata(element_class,"Batch Encoder","Codec/Encoder/Audio","Audio encoder frame batching test","GStreamer");encoder_class->set_format=batch_encoder_set_format;encoder_class->handle_frame=batch_encoder_handle_frame;}staticvoidbatch_encoder_init(BatchEncoder*enc){}/* 为Harness两端定义匹配的Pad模板 */staticGstStaticPadTemplate harness_src_template=GST_STATIC_PAD_TEMPLATE("harness-src",GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-raw"));staticGstStaticPadTemplate harness_sink_template=GST_STATIC_PAD_TEMPLATE("harness-sink",GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS("audio/x-test-encoded"));intmain(intargc,char*argv[]){GstElement*element;GstHarness*harness;GstCaps*input_caps;GstBuffer*input;GstBuffer*output;gsize input_size;gboolean ok;gst_init(&argc,&argv);/* 创建编码器,并用Harness连接输入和输出Pad */element=g_object_new(batch_encoder_get_type(),NULL);harness=gst_harness_new_full(element,&harness_src_template,"sink",&harness_sink_template,"src");gst_object_unref(element);/* 设置单声道S16LE、48000Hz输入格式,触发set_format配置 */input_caps=gst_caps_from_string("audio/x-raw,format=S16LE,layout=interleaved,""rate=48000,channels=1");gst_harness_set_src_caps(harness,input_caps);/* 4个固定帧组成1024个采样点,每点2字节 */input_size=SAMPLES_PER_FRAME*FRAME_MAX*sizeof(gint16);input=gst_buffer_new_allocate(NULL,input_size,NULL);/* 推入PCM,触发基类收集并调用handle_frame */ok=gst_harness_push(harness,input)==GST_FLOW_OK;/* 取出编码器输出Buffer,确认输出路径已经完成 */output=ok?gst_harness_pull(harness):NULL;ok=ok&&output!=NULL;if(output)gst_buffer_unref(output);if(ok){g_print("frame_max: %d\n",gst_audio_encoder_get_frame_max(GST_AUDIO_ENCODER(harness->element)));g_print("handle_frame calls: %u\n",handle_frame_calls);g_print("handle_frame input bytes: %"G_GSIZE_FORMAT"\n",last_input_size);}gst_harness_teardown(harness);returnok?0:1;}

按这组输入和帧配置,预期观察到:

frame_max: 4 handle_frame calls: 1 handle_frame input bytes: 2048

🌻5.总结

gst_audio_encoder_set_frame_max()就是为GstAudioEncoder设置一次最多组织的固定大小音频帧数,供基类决定交给子类handle_frame()的输入批次。

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

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

立即咨询