)
简介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_decoder_get_tolerance()如何读取GstAudioDecoder当前的时间戳抖动容差并分析这个值如何参与解码器输出时间戳的重新同步。音频解码器接收到的输入Buffer通常带有上游时间戳。理想情况下解码器可以根据采样率和已经输出的采样数计算出连续的输出时间戳。但是实际输入时间戳可能存在抖动。例如当前Buffer的时间戳比解码器预计的时间戳提前或延后几毫秒。如果每次出现微小偏差都重新同步输出时间戳可能频繁跳变。GstAudioDecoder使用tolerance字段表示可以接受的时间戳偏差范围。gst_audio_decoder_get_tolerance()只负责读取当前容差值不负责比较时间戳不负责修改输出Buffer也不会主动触发重新同步。读取GstAudioDecodergst_audio_decoder_get_tolerance获得当前抖动容差输出流程比较时间戳偏差保持理想时间戳或重新同步2.应用场景和用法2.1音频时间戳抖动控制gst_audio_decoder_get_tolerance()适合用于读取解码器当前的时间戳策略。使用场景查询动作实际作用检查默认配置读取当前tolerance判断是否启用时间戳容差机制调试时间戳跳变获取运行时容差值分析解码器是否会频繁重新同步验证动态配置设置新值后再次读取确认配置已经写入基类状态子类调试在解码流程中查询观察当前解码器的时间戳策略参数监控周期性读取获取当前配置不改变解码状态tolerance的单位是纳秒类型是GstClockTime。当前实现中默认容差为0。当容差为0时基类不会执行带容差的偏差判断出现有效输入时间戳时会直接以该时间戳重新建立时间基准。当容差大于0时基类会计算理想时间戳与输入时间戳之间的差值。偏差不超过容差时继续使用连续的理想时间戳。偏差超过容差时才重新同步到上游时间戳。2.2函数原型与返回语义函数原型如下GstClockTimegst_audio_decoder_get_tolerance(GstAudioDecoder*dec);项目含义dec已经创建的GstAudioDecoder对象返回值当前配置的音频时间戳抖动容差单位为纳秒线程安全函数内部使用对象锁接口标记为MT safe无效对象参数检查失败时返回0最小调用方式如下GstClockTime tolerance;tolerancegst_audio_decoder_get_tolerance(decoder);g_print(tolerance: %GST_TIME_FORMAT\n,GST_TIME_ARGS(tolerance));调用前decoder必须是有效的GstAudioDecoder实例。调用过程中函数只读取解码器私有字段tolerance不会创建新对象也不会修改解码器状态。调用结束后返回值可以用于日志记录、配置检查或调试时间戳重新同步问题。几个相邻接口的职责不同接口主要职责gst_audio_decoder_set_tolerance()写入新的时间戳抖动容差gst_audio_decoder_get_tolerance()读取当前时间戳抖动容差gst_audio_decoder_finish_frame()提交解码后的音频帧gst_audio_decoder_output()处理输出时间戳、标签和下游发送gst_audio_decoder_get_latency()查询解码器延迟范围gst_audio_decoder_get_tolerance()是查询接口不会调用子类回调也不会跨越进程边界。3.调用流程剖析3.1读取私有容差字段gst_audio_decoder_get_tolerance()的实现非常短核心动作是加锁、读取字段、解锁并返回。GstClockTimegst_audio_decoder_get_tolerance(GstAudioDecoder*dec){GstClockTime result;g_return_val_if_fail(GST_IS_AUDIO_DECODER(dec),0);GST_OBJECT_LOCK(dec);resultdec-priv-tolerance;GST_OBJECT_UNLOCK(dec);returnresult;}调用流程可以拆成四步使用GST_IS_AUDIO_DECODER()检查对象类型。使用GST_OBJECT_LOCK()保护私有状态。读取dec-priv-tolerance。释放对象锁并返回读取结果。该函数不会把容差转换成毫秒也不会根据采样率重新计算。调用者拿到的就是当前保存的纳秒值。传入GstAudioDecoder检查对象类型获取对象锁读取priv tolerance字段释放对象锁并返回与之对应的设置函数会执行相反方向的操作voidgst_audio_decoder_set_tolerance(GstAudioDecoder*dec,GstClockTime tolerance){g_return_if_fail(GST_IS_AUDIO_DECODER(dec));g_return_if_fail(GST_CLOCK_TIME_IS_VALID(tolerance));GST_OBJECT_LOCK(dec);dec-priv-tolerancetolerance;GST_OBJECT_UNLOCK(dec);}设置函数负责修改字段查询函数负责读取字段。两者都通过对象锁保护tolerance因此可以安全地用于不同线程之间的配置读取和更新。3.2容差参与输出时间戳重新同步查询函数本身不处理时间戳真正使用tolerance的是解码器输出路径。当基类准备输出音频Buffer时会根据以下信息计算理想时间戳base_ts当前输出时间基准。samples已经输出的采样数。ctx-info.rate输出采样率。当前输入Buffer携带的上游时间戳。priv-tolerance允许的抖动范围。理想时间戳可以表示为next_tsbase_tsgst_util_uint64_scale(samples,GST_SECOND,ctx-info.rate);随后基类计算理想时间戳和输入时间戳之间的差值diffGST_CLOCK_DIFF(next_ts,ts);当tolerance大于0且偏差没有超过容差时基类继续使用连续的理想时间戳。如果偏差超过容差或者输入Buffer带有GST_BUFFER_FLAG_RESYNC标志基类会把base_ts重置为当前输入时间戳并将已累计的采样数清零。否是输入Buffer时间戳计算理想输出时间戳计算时间戳偏差偏差是否超过tolerance继续使用连续时间戳重置时间基准并重新同步因此gst_audio_decoder_get_tolerance()和时间戳重新同步之间不是直接调用关系。它只读取配置值输出路径再使用这个值决定是否接受当前时间戳偏差。4.实战案例4.1准备自定义音频解码器本案例定义一个最小的GstAudioDecoder子类只用于验证容差配置。该子类注册了输入和输出Pad模板但不执行真实音频解码。程序创建对象后使用gst_audio_decoder_set_tolerance()写入50毫秒容差再调用gst_audio_decoder_get_tolerance()读取配置。50毫秒需要转换为纳秒50*GST_MSECOND读取结果应该与设置值一致。4.2设置容差并执行查询程序按以下顺序运行初始化GStreamer并创建自定义GstAudioDecoder对象。调用gst_audio_decoder_set_tolerance()写入50毫秒容差。调用gst_audio_decoder_get_tolerance()读取并验证返回值。这个Demo验证的是查询接口的直接语义不会主动构造带抖动的输入Buffer也不会触发真实的时间戳重新同步。4.3完整代码与关键输出#includegst/gst.h#includegst/audio/audio.htypedefstruct_ToleranceDecoder{GstAudioDecoder parent;}ToleranceDecoder;typedefstruct_ToleranceDecoderClass{GstAudioDecoderClass parent_class;}ToleranceDecoderClass;G_DEFINE_TYPE(ToleranceDecoder,tolerance_decoder,GST_TYPE_AUDIO_DECODER);/* 注册解码器输入Pad模板 */staticGstStaticPadTemplate decoder_sink_templateGST_STATIC_PAD_TEMPLATE(sink,GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-test-custom));/* 注册解码器输出Pad模板 */staticGstStaticPadTemplate decoder_src_templateGST_STATIC_PAD_TEMPLATE(src,GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-raw));staticvoidtolerance_decoder_class_init(ToleranceDecoderClass*klass){GstElementClass*element_classGST_ELEMENT_CLASS(klass);/* 将输入和输出Pad模板添加到解码器类型 */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,Tolerance Decoder,Decoder/Audio,Audio decoder tolerance test,GStreamer);}staticvoidtolerance_decoder_init(ToleranceDecoder*decoder){}intmain(intargc,char*argv[]){GstAudioDecoder*decoder;GstClockTime configured_tolerance;GstClockTime queried_tolerance;gst_init(argc,argv);/* 创建自定义GstAudioDecoder对象 */decoderGST_AUDIO_DECODER(g_object_new(tolerance_decoder_get_type(),NULL));/* 设置50毫秒的时间戳抖动容差 */configured_tolerance50*GST_MSECOND;gst_audio_decoder_set_tolerance(decoder,configured_tolerance);/* 读取基类私有字段中的当前容差 */queried_tolerancegst_audio_decoder_get_tolerance(decoder);/* 验证查询值与设置值是否一致 */if(queried_tolerance!configured_tolerance){g_printerr(tolerance verification failed\n);gst_object_unref(decoder);return1;}/* 以时间格式打印当前容差 */g_print(tolerance: %GST_TIME_FORMAT\n,GST_TIME_ARGS(queried_tolerance));gst_object_unref(decoder);return0;}编译运行gcc test_audio_decoder_get_tolerance.c\-otest_audio_decoder_get_tolerance\$(pkg-config--cflags--libs\gstreamer-1.0\gstreamer-audio-1.0)./test_audio_decoder_get_tolerance关键输出tolerance: 0:00:00.050000000这个结果说明gst_audio_decoder_get_tolerance()返回的是解码器当前保存的GstClockTime值。它没有改变解码器状态也没有触发音频处理。5.总结gst_audio_decoder_get_tolerance()就是在对象锁保护下读取GstAudioDecoder当前的时间戳抖动容差供输出流程判断是否需要重新同步。