ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)

Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一) 简介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_frame2.应用场景和用法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_maxnum;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个采样点并将一次最多处理帧数设为4gst_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完整代码#includegst/gst.h#includegst/audio/audio.h#includegst/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_templateGST_STATIC_PAD_TEMPLATE(sink,GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-raw));staticGstStaticPadTemplate encoder_src_templateGST_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_capsgst_caps_new_empty_simple(audio/x-test-encoded);resultgst_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(inputNULL)returnGST_FLOW_OK;/* 记录基类本次组织出的PCM字节数 */handle_frame_calls;input_sizegst_buffer_get_size(input);last_input_sizeinput_size;/* 测试编码器生成一个非空输出Buffer */outputgst_buffer_new_allocate(NULL,1,NULL);if(outputNULL)returnGST_FLOW_ERROR;/* 单声道S16LE每个采样点占2字节提交对应采样点数 */returngst_audio_encoder_finish_frame(enc,output,input_size/sizeof(gint16));}staticvoidbatch_encoder_class_init(BatchEncoderClass*klass){GstElementClass*element_classGST_ELEMENT_CLASS(klass);GstAudioEncoderClass*encoder_classGST_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_formatbatch_encoder_set_format;encoder_class-handle_framebatch_encoder_handle_frame;}staticvoidbatch_encoder_init(BatchEncoder*enc){}/* 为Harness两端定义匹配的Pad模板 */staticGstStaticPadTemplate harness_src_templateGST_STATIC_PAD_TEMPLATE(harness-src,GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-raw));staticGstStaticPadTemplate harness_sink_templateGST_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 */elementg_object_new(batch_encoder_get_type(),NULL);harnessgst_harness_new_full(element,harness_src_template,sink,harness_sink_template,src);gst_object_unref(element);/* 设置单声道S16LE、48000Hz输入格式触发set_format配置 */input_capsgst_caps_from_string(audio/x-raw,formatS16LE,layoutinterleaved,rate48000,channels1);gst_harness_set_src_caps(harness,input_caps);/* 4个固定帧组成1024个采样点每点2字节 */input_sizeSAMPLES_PER_FRAME*FRAME_MAX*sizeof(gint16);inputgst_buffer_new_allocate(NULL,input_size,NULL);/* 推入PCM触发基类收集并调用handle_frame */okgst_harness_push(harness,input)GST_FLOW_OK;/* 取出编码器输出Buffer确认输出路径已经完成 */outputok?gst_harness_pull(harness):NULL;okokoutput!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: 20485.总结gst_audio_encoder_set_frame_max()就是为GstAudioEncoder设置一次最多组织的固定大小音频帧数供基类决定交给子类handle_frame()的输入批次。
返回列表