ARTICLE DETAIL

资讯详情

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

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

Linux Gstreamer深度解析之gst_audio_encoder_set_hard_min调用流程与实战(六十二) 简介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强制处理时选择丢弃或交给子类4.实战案例4.1准备测试编码器4.2观察子类收到的数据4.3完整代码5.总结1.前言本篇目的理解gst_audio_encoder_set_hard_min()如何控制GstAudioEncoder在强制处理尾部数据时是否把不足最小样本数的数据交给编码器子类。编码器子类可以通过gst_audio_encoder_set_frame_samples_min()声明每次处理至少需要多少个样本。正常编码时如果累积数据还没达到这个下限基类会继续等待。到了EOS等需要排空数据的时机剩余数据可能仍然不足最小样本数。gst_audio_encoder_set_hard_min()决定这时如何处理启用后基类会跳过子类的handle_frame()丢弃这段尾部数据未启用时子类可能收到不足下限的数据。否是是否编码器子类设置最小样本数基类累积输入音频是否进入强制处理继续等待足够样本hard_min是否启用基类丢弃不足下限的尾部数据子类处理剩余数据2.应用场景和用法2.1处理不足最小样本数的尾部数据gst_audio_encoder_set_hard_min()适用于每次编码必须满足固定样本数的编码器。例如编码器每帧要求1024个样本而流结束时只剩下300个样本。场景hard_min设置EOS处理时的行为编码器支持短帧FALSE子类可以收到不足最小值的尾部数据编码器要求固定帧长TRUE基类跳过子类处理并丢弃尾部数据输入尚未结束任意值未进入强制处理时基类继续等待达到最小值启用该选项会丢失不足最小样本数的尾部音频。只有编码格式或子类实现确实不能处理短帧时才适合启用。2.2函数原型与状态变化函数原型如下voidgst_audio_encoder_set_hard_min(GstAudioEncoder*enc,gboolean enabled);参数含义enc已创建的GstAudioEncoder对象enabled是否启用硬性最小样本数处理该函数没有返回值。调用时会先检查enc是否为有效的GstAudioEncoder对象再通过对象锁更新私有字段hard_min。初始值为FALSE。最小调用方式如下/* 编码器不接受短帧时启用硬性最小样本数 */gst_audio_encoder_set_hard_min(GST_AUDIO_ENCODER(encoder),TRUE);它与相邻接口的职责不同接口职责gst_audio_encoder_set_frame_samples_min()设置子类每次至少需要的样本数gst_audio_encoder_set_hard_min()决定强制处理时如何处理不足下限的剩余数据gst_audio_encoder_get_hard_min()查询当前硬性最小值处理状态gst_audio_encoder_finish_frame()提交子类产生的编码数据及其对应样本数hard_min不会设置帧长也不会改变最小样本数。它只影响基类进入强制处理路径后的分支。3.调用流程剖析3.1设置硬性最小值状态gst_audio_encoder_set_hard_min()本身只更新一个布尔字段不会立即处理音频Buffer。voidgst_audio_encoder_set_hard_min(GstAudioEncoder*enc,gboolean enabled){g_return_if_fail(GST_IS_AUDIO_ENCODER(enc));GST_OBJECT_LOCK(enc);enc-priv-hard_minenabled;GST_OBJECT_UNLOCK(enc);}设置状态的过程是检查传入对象是否为GstAudioEncoder。获取对象锁更新enc-priv-hard_min。释放对象锁并返回。基类初始化时hard_min默认为FALSE。因此如果编码器子类没有主动设置尾部数据会沿默认处理策略进入后续流程。传入编码器和enabled检查GstAudioEncoder对象获取对象锁更新priv hard_min释放对象锁并返回3.2强制处理时选择丢弃或交给子类正常输入过程中基类会把输入数据放入Adapter并根据frame_samples_min判断是否满足子类要求。如果可用样本不足且当前没有强制处理基类会退出这轮处理等待更多输入。EOS等需要排空数据的路径会调用gst_audio_encoder_drain()再通过gst_audio_encoder_push_buffers()强制处理剩余数据。此时如果剩余样本仍然不足最小值基类会设置内部force状态。处理分支如下if(priv-forcepriv-hard_minbuf){retgst_audio_encoder_finish_frame(enc,NULL,-1);}else{retklass-handle_frame(enc,buf);}当force和hard_min同时为真且存在剩余Buffer时基类直接调用gst_audio_encoder_finish_frame()处理这段剩余数据不再调用子类的handle_frame()。这就是硬性最小值模式下丢弃不足下限尾部数据的路径。如果hard_min为FALSE上述条件不成立子类的handle_frame()会收到剩余Buffer。子类是否能正确编码短帧取决于自身实现和编码格式要求。是否是否EOS触发排空基类强制处理剩余数据剩余数据达到最小值调用子类handle_framehard_min是否启用基类跳过子类并丢弃尾部子类接收不足下限的数据4.实战案例4.1准备测试编码器下面的测试编码器把最小样本数设为4个样本/声道。测试输入为S16立体声格式每个音频Frame包含左右声道各一个样本。测试时只提交3个Frame也就是每声道3个样本。普通输入阶段还达不到4个样本因此子类不会立即收到数据。随后发送EOS基类开始强制排空。程序分别测试hard_minTRUE和hard_minFALSE两种情况。4.2观察子类收到的数据测试子类在handle_frame()中记录调用次数和输入Buffer大小hard_minTRUE时EOS排空阶段由基类丢弃不足下限的尾部子类的处理次数保持为0。hard_minFALSE时子类收到12字节输入也就是3个S16立体声Frame。这里的Demo用于观察GstAudioEncoder的分支行为不实现真实音频压缩算法。4.3完整代码#includegst/gst.h#includegst/audio/audio.h#includegst/check/gstharness.htypedefstruct_HardMinEncoder{GstAudioEncoder parent;guint handle_calls;gsize last_input_bytes;}HardMinEncoder;typedefstruct_HardMinEncoderClass{GstAudioEncoderClass parent_class;}HardMinEncoderClass;G_DEFINE_TYPE(HardMinEncoder,hard_min_encoder,GST_TYPE_AUDIO_ENCODER);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-custom));staticgbooleanhard_min_encoder_set_format(GstAudioEncoder*enc,GstAudioInfo*info){GstCaps*caps;/* 子类需要每声道至少4个样本再设置测试用输出Caps */gst_audio_encoder_set_frame_samples_min(enc,4);capsgst_caps_new_empty_simple(audio/x-test-custom);gst_audio_encoder_set_output_format(enc,caps);gst_caps_unref(caps);returnTRUE;}staticGstFlowReturnhard_min_encoder_handle_frame(GstAudioEncoder*enc,GstBuffer*input){HardMinEncoder*self(HardMinEncoder*)enc;GstBuffer*output;/* 记录基类是否把尾部输入交给了子类 */self-handle_calls;if(inputNULL)returnGST_FLOW_OK;self-last_input_bytesgst_buffer_get_size(input);/* 测试编码器只输出一个字节不代表真实压缩算法 */outputgst_buffer_new_allocate(NULL,1,NULL);/* 输入包含3个立体声Frame即每声道3个样本 */returngst_audio_encoder_finish_frame(enc,output,3);}staticvoidhard_min_encoder_class_init(HardMinEncoderClass*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,HardMinEncoder,Encoder/Audio,Test hard minimum handling,Example);encoder_class-set_formathard_min_encoder_set_format;encoder_class-handle_framehard_min_encoder_handle_frame;}staticvoidhard_min_encoder_init(HardMinEncoder*self){}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-custom));typedefstruct{guint handle_calls;gsize input_bytes;}TestResult;staticTestResultrun_case(gboolean hard_min){GstElement*element;GstHarness*harness;GstBuffer*input;HardMinEncoder*encoder;TestResult result{0,0};/* 创建编码器并设置本次测试的硬性最小值开关 */elementg_object_new(hard_min_encoder_get_type(),NULL);gst_audio_encoder_set_hard_min(GST_AUDIO_ENCODER(element),hard_min);harnessgst_harness_new_full(element,harness_src_template,sink,harness_sink_template,src);gst_object_unref(element);/* 配置S16立体声输入采样率为48000Hz */gst_harness_set_src_caps(harness,gst_caps_new_simple(audio/x-raw,format,G_TYPE_STRING,S16LE,rate,G_TYPE_INT,48000,channels,G_TYPE_INT,2,layout,G_TYPE_STRING,interleaved,NULL));/* 输入3个Frame每个Frame为左右声道各一个S16样本 */inputgst_buffer_new_allocate(NULL,3*2*sizeof(gint16),NULL);g_assert_cmpint(gst_harness_push(harness,input),,GST_FLOW_OK);/* EOS使基类强制处理Adapter中不足4个样本的尾部 */g_assert(gst_harness_push_event(harness,gst_event_new_eos()));encoder(HardMinEncoder*)harness-element;result.handle_callsencoder-handle_calls;result.input_bytesencoder-last_input_bytes;gst_harness_teardown(harness);returnresult;}intmain(intargc,char*argv[]){TestResult hard_result;TestResult soft_result;gst_init(argc,argv);hard_resultrun_case(TRUE);soft_resultrun_case(FALSE);/* 启用hard_min后尾部被基类丢弃子类没有收到handle_frame调用 */g_assert_cmpuint(hard_result.handle_calls,,0);/* 未启用hard_min时子类收到3个立体声Frame共12字节 */g_assert_cmpuint(soft_result.handle_calls,,1);g_assert_cmpuint(soft_result.input_bytes,,12);return0;}5.总结gst_audio_encoder_set_hard_min()用于控制GstAudioEncoder在强制排空时如何处理不足最小样本数的尾部数据启用后基类会跳过子类并丢弃这段数据。
返回列表