)
简介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延迟从解码器进入Pipeline3.2参数校验、状态更新与消息发布4.实战案例4.1准备最小GstAudioDecoder子类4.2设置延迟并观察Bus消息4.3完整代码与关键输出5.总结1.前言本篇目的理解gst_audio_decoder_set_latency()如何保存解码器延迟、通知Pipeline重新计算全局延迟并通过最小程序验证延迟值和LATENCY消息的变化。GstAudioDecoder已经完成了解码器对象、输入输出Pad和音频格式管理但具体解码器仍然需要告诉框架当前算法会额外缓存多长时间的数据。例如Opus启用FEC后可能暂存一个音频包FLAC的延迟与当前BlockSize有关LC3的延迟与FrameDuration有关。gst_audio_decoder_set_latency()负责登记解码器的最小延迟和最大延迟并在延迟值变化时向Bus发布GST_MESSAGE_LATENCY。它不负责解码PCM不修改Buffer时间戳不主动执行GST_QUERY_LATENCY也不直接改变解码器的输出数据。解码器获得算法延迟gst_audio_decoder_set_latency保存最小和最大延迟发布LATENCY消息Pipeline重新评估全局延迟2.应用场景和用法2.1解码器延迟登记场景gst_audio_decoder_set_latency()适用于解码器已经确定内部缓存量但Pipeline还不知道这部分延迟的场景。场景调用方式作用固定Frame延迟min和max传入相同时间表示解码器延迟固定FEC或重排缓存根据缓存包数量计算延迟让Pipeline把内部缓存纳入总延迟BlockSize变化格式协商后重新计算延迟让延迟随着当前编码参数更新可变解码路径传入最小值和最大值描述算法延迟变化范围延迟发生变化再次调用set_latency()触发新的LATENCY消息Opus启用In-bandFEC时会设置固定的120ms解码延迟。FLAC解码器会根据当前BlockSize计算延迟。LC3解码器则根据FrameDuration计算延迟。如果只修改min-latency属性改变的是解码器的输出聚合策略不等价于向Pipeline声明解码算法延迟。如果不调用gst_audio_decoder_set_latency()解码器内部即使缓存了数据Pipeline的全局延迟查询也可能无法包含这部分时间。2.2函数原型与返回语义函数原型如下voidgst_audio_decoder_set_latency(GstAudioDecoder*dec,GstClockTime min,GstClockTime max);参数含义dec有效的GstAudioDecoder对象min解码器能够产生的最小延迟max解码器可能产生的最大延迟必须不小于min这个函数返回void。调用失败时不会返回错误码而是通过g_return_if_fail()检查以下前置条件dec必须是GstAudioDecoder对象。min必须是有效的GstClockTime。min不能大于max。最小调用方式如下GstClockTime latency;latency20*GST_MSECOND;gst_audio_decoder_set_latency(decoder,latency,latency);调用前解码器应该已经知道当前编码格式、FrameDuration、BlockSize或缓存策略。调用过程中函数在对象锁内比较并更新min_latency和max_latency。如果值发生变化或者当前对象还没有发布过延迟消息就会发布一个GST_MESSAGE_LATENCY。调用后延迟值可以通过gst_audio_decoder_get_latency()读取。函数不会创建对象不会分配音频Buffer不会触发解码回调也不会直接修改输出Buffer。几个相邻接口的职责不同接口作用gst_audio_decoder_set_latency()设置解码器对Pipeline声明的算法延迟gst_audio_decoder_get_latency()读取当前声明的最小和最大延迟gst_audio_decoder_set_min_latency()设置输出聚合使用的最小等待时间gst_audio_decoder_get_min_latency()读取输出聚合延迟gst_audio_decoder_sink_query()处理GST_QUERY_LATENCY并把解码器延迟加入查询结果gst_element_post_message()把GST_MESSAGE_LATENCY发布到元素所在的Busset_latency()描述解码器自身的算法延迟set_min_latency()控制输出聚合两者不能互相替代。3.调用流程剖析3.1延迟从解码器进入Pipeline解码器子类通常在start()或set_format()中确定延迟。当编码格式或解码策略发生变化时子类再次调用gst_audio_decoder_set_latency()。函数先更新GstAudioDecoder上下文中的延迟字段再发布LATENCY消息。Pipeline收到该消息后可以重新执行延迟配置。后续发生GST_QUERY_LATENCY时GstAudioDecoder会先查询上游Peer再把自身保存的延迟累加到查询结果中。解码器确定算法延迟gst_audio_decoder_set_latency更新Decoder上下文发布LATENCY消息Pipeline触发延迟重算Latency查询加入Decoder延迟gst_audio_decoder_set_latency()只负责更新自身上下文并发送通知不会在函数内部执行完整的Pipeline延迟协商。GST_QUERY_LATENCY的处理发生在后续查询阶段。解码器SinkPad先向上游Peer发起查询得到live、min_latency和max_latency再在对象锁内加入自身的延迟。如果上游或解码器的最大延迟是无限值查询结果的最大延迟仍保持无限。3.2参数校验、状态更新与消息发布当前实现的核心流程可以简化为gboolean post_messageFALSE;g_return_if_fail(GST_IS_AUDIO_DECODER(dec));g_return_if_fail(GST_CLOCK_TIME_IS_VALID(min));g_return_if_fail(minmax);GST_OBJECT_LOCK(dec);if(dec-priv-ctx.min_latency!min){dec-priv-ctx.min_latencymin;post_messageTRUE;}if(dec-priv-ctx.max_latency!max){dec-priv-ctx.max_latencymax;post_messageTRUE;}if(!dec-priv-ctx.posted_latency_msg){dec-priv-ctx.posted_latency_msgTRUE;post_messageTRUE;}GST_OBJECT_UNLOCK(dec);if(post_message)gst_element_post_message(GST_ELEMENT(dec),gst_message_new_latency(GST_OBJECT(dec)));函数有三个关键状态边界。第一参数校验失败时函数直接返回不修改延迟字段。第二延迟字段只在对象锁内更新。gst_audio_decoder_get_latency()也使用同一把对象锁读取这些字段。第三消息发布发生在解锁之后。这样可以避免在持有对象锁时执行Bus消息发布。posted_latency_msg用于保证对象至少发布过一次延迟消息。即使第一次调用传入的值与默认值相同只要对象尚未发布过消息仍然会发布一次。后续再次传入完全相同的min和max时不会重复发布LATENCY消息。是否传入Decoder和延迟范围检查对象和参数对象锁内比较并写入值变化或首次通知发布LATENCY消息不重复发布消息4.实战案例4.1准备最小GstAudioDecoder子类GstAudioDecoder是抽象基类不能直接通过g_object_new()创建。实战程序先定义一个最小子类为它提供SinkPad和SrcPad模板再把实例加入Pipeline。这个子类不执行真实音频解码只用于验证三件事初始延迟是否为0。第一次设置后是否收到LATENCY消息。重复设置相同值时是否不再重复发布消息。4.2设置延迟并观察Bus消息程序按以下顺序运行创建最小解码器子类和Pipeline。设置20ms最小延迟、40ms最大延迟。从PipelineBus读取GST_MESSAGE_LATENCY。使用gst_audio_decoder_get_latency()读取更新后的值。重复设置相同值确认不会产生第二条消息。修改为30ms到50ms确认新的延迟变化再次触发消息。这里验证的是gst_audio_decoder_set_latency()的状态和通知行为不是具体Codec的解码质量。4.3完整代码与关键输出#includegst/gst.h#includegst/audio/gstaudiodecoder.htypedefstruct_DemoDecoder{GstAudioDecoder parent;}DemoDecoder;typedefstruct_DemoDecoderClass{GstAudioDecoderClass parent_class;}DemoDecoderClass;staticGstStaticPadTemplate demo_sink_templateGST_STATIC_PAD_TEMPLATE(sink,GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS_ANY);staticGstStaticPadTemplate demo_src_templateGST_STATIC_PAD_TEMPLATE(src,GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS_ANY);G_DEFINE_TYPE(DemoDecoder,demo_decoder,GST_TYPE_AUDIO_DECODER);staticvoiddemo_decoder_class_init(DemoDecoderClass*klass){GstElementClass*element_classGST_ELEMENT_CLASS(klass);gst_element_class_add_static_pad_template(element_class,demo_sink_template);gst_element_class_add_static_pad_template(element_class,demo_src_template);gst_element_class_set_static_metadata(element_class,Latency test decoder,Codec/Decoder/Audio,Minimal decoder for latency verification,GStreamer example);}staticvoiddemo_decoder_init(DemoDecoder*decoder){}staticgbooleanpop_latency_message(GstBus*bus,GstClockTime timeout){GstMessage*message;messagegst_bus_timed_pop_filtered(bus,timeout,GST_MESSAGE_LATENCY);if(messageNULL)returnFALSE;gst_message_unref(message);returnTRUE;}staticvoidprint_latency(constgchar*label,GstAudioDecoder*decoder){GstClockTime min;GstClockTime max;gst_audio_decoder_get_latency(decoder,min,max);g_print(%s: %G_GUINT64_FORMAT - %G_GUINT64_FORMAT ns\n,label,min,max);}intmain(intargc,char*argv[]){GstElement*pipeline;GstElement*decoder;GstBus*bus;GstAudioDecoder*audio_decoder;gboolean message_received;gst_init(argc,argv);pipelinegst_pipeline_new(latency-test);decoderg_object_new(demo_decoder_get_type(),NULL);if(pipelineNULL||decoderNULL){g_printerr(Failed to create test objects\n);if(pipeline!NULL)gst_object_unref(pipeline);if(decoder!NULL)gst_object_unref(decoder);return1;}if(!gst_bin_add(GST_BIN(pipeline),decoder)){g_printerr(Failed to add decoder to pipeline\n);gst_object_unref(pipeline);return1;}busgst_element_get_bus(pipeline);audio_decoderGST_AUDIO_DECODER(decoder);print_latency(Initial latency,audio_decoder);gst_audio_decoder_set_latency(audio_decoder,20*GST_MSECOND,40*GST_MSECOND);message_receivedpop_latency_message(bus,GST_SECOND);g_print(First LATENCY message: %s\n,message_received?TRUE:FALSE);print_latency(After first update,audio_decoder);gst_audio_decoder_set_latency(audio_decoder,20*GST_MSECOND,40*GST_MSECOND);message_receivedpop_latency_message(bus,0);g_print(Repeated LATENCY message: %s\n,message_received?TRUE:FALSE);gst_audio_decoder_set_latency(audio_decoder,30*GST_MSECOND,50*GST_MSECOND);message_receivedpop_latency_message(bus,GST_SECOND);g_print(Second LATENCY message: %s\n,message_received?TRUE:FALSE);print_latency(After second update,audio_decoder);gst_object_unref(bus);gst_object_unref(pipeline);return0;}编译运行gcc test_decoder_latency.c-otest_decoder_latency\$(pkg-config--cflags--libsgstreamer-1.0 gstreamer-audio-1.0)./test_decoder_latency关键输出Initial latency: 0 - 0 ns First LATENCY message: TRUE After first update: 20000000 - 40000000 ns Repeated LATENCY message: FALSE Second LATENCY message: TRUE After second update: 30000000 - 50000000 ns5.总结gst_audio_decoder_set_latency()就是把解码器的算法延迟写入GstAudioDecoder上下文并通过GST_MESSAGE_LATENCY通知Pipeline重新评估全局延迟。