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

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀

Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀

Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀

专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀

专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀

专题四:Android15快速自定义与集成音效实战课 🚀

专题五:Android15音频策略实战课 🚀

专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

🌻1.前言

本篇目的:理解gst_audio_ring_buffer_delay()如何查询音频设备中尚未处理的样本帧,并通过播放端、录制端的时钟计算和内存后端示例说明它的作用。

GstAudioRingBuffer既保存环形缓冲区状态,也通过子类接口查询设备队列。gst_audio_ring_buffer_delay()检查RingBuffer是否已采集,然后调用具体后端的delay虚函数。它返回的是设备队列中尚未播放或尚未读入RingBuffer的样本帧数,不是字节数,也不是纳秒时长。
#mermaid-svg-cf6qJSvLpuLsrzwa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cf6qJSvLpuLsrzwa .error-icon{fill:#552222;}#mermaid-svg-cf6qJSvLpuLsrzwa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cf6qJSvLpuLsrzwa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cf6qJSvLpuLsrzwa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cf6qJSvLpuLsrzwa .marker.cross{stroke:#333333;}#mermaid-svg-cf6qJSvLpuLsrzwa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cf6qJSvLpuLsrzwa p{margin:0;}#mermaid-svg-cf6qJSvLpuLsrzwa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster-label text{fill:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster-label span{color:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster-label span p{background-color:transparent;}#mermaid-svg-cf6qJSvLpuLsrzwa .label text,#mermaid-svg-cf6qJSvLpuLsrzwa span{fill:#333;color:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa .node rect,#mermaid-svg-cf6qJSvLpuLsrzwa .node circle,#mermaid-svg-cf6qJSvLpuLsrzwa .node ellipse,#mermaid-svg-cf6qJSvLpuLsrzwa .node polygon,#mermaid-svg-cf6qJSvLpuLsrzwa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cf6qJSvLpuLsrzwa .rough-node .label text,#mermaid-svg-cf6qJSvLpuLsrzwa .node .label text,#mermaid-svg-cf6qJSvLpuLsrzwa .image-shape .label,#mermaid-svg-cf6qJSvLpuLsrzwa .icon-shape .label{text-anchor:middle;}#mermaid-svg-cf6qJSvLpuLsrzwa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cf6qJSvLpuLsrzwa .rough-node .label,#mermaid-svg-cf6qJSvLpuLsrzwa .node .label,#mermaid-svg-cf6qJSvLpuLsrzwa .image-shape .label,#mermaid-svg-cf6qJSvLpuLsrzwa .icon-shape .label{text-align:center;}#mermaid-svg-cf6qJSvLpuLsrzwa .node.clickable{cursor:pointer;}#mermaid-svg-cf6qJSvLpuLsrzwa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cf6qJSvLpuLsrzwa .arrowheadPath{fill:#333333;}#mermaid-svg-cf6qJSvLpuLsrzwa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cf6qJSvLpuLsrzwa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cf6qJSvLpuLsrzwa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cf6qJSvLpuLsrzwa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cf6qJSvLpuLsrzwa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cf6qJSvLpuLsrzwa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster text{fill:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa .cluster span{color:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cf6qJSvLpuLsrzwa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cf6qJSvLpuLsrzwa rect.text{fill:none;stroke-width:0;}#mermaid-svg-cf6qJSvLpuLsrzwa .icon-shape,#mermaid-svg-cf6qJSvLpuLsrzwa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cf6qJSvLpuLsrzwa .icon-shape p,#mermaid-svg-cf6qJSvLpuLsrzwa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cf6qJSvLpuLsrzwa .icon-shape .label rect,#mermaid-svg-cf6qJSvLpuLsrzwa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cf6qJSvLpuLsrzwa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cf6qJSvLpuLsrzwa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cf6qJSvLpuLsrzwa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 已采集的GstAudioRingBuffer
gst_audio_ring_buffer_delay
调用后端delay虚函数
读取设备队列中的样本帧数
播放端或录制端修正时钟

🌻2.应用场景和用法

2.1查询设备队列中的未处理样本

播放和录制都可能出现"样本已经离开RingBuffer,但还没有完成设备侧处理"的阶段。播放时,这些样本已经交给设备但还没有播放;录制时,这些样本已经到达设备但还没有读进RingBuffer。

gst_audio_ring_buffer_delay()把这部分样本数量交给上层使用。音频基类会结合已处理样本数和采样率计算时钟时间。

场景 delay表示什么 基类如何使用
播放 已交给设备但尚未播放的样本帧数 从已处理样本数中扣除
录制 已在设备中但尚未读入RingBuffer的样本帧数 加到已采集样本数中
设备内部还有额外缓冲 设备队列可能大于一个RingBuffer分段 按后端实际队列查询
RingBuffer尚未采集 无法查询有效设备队列 返回0

如果返回0,不能直接判断设备队列一定为空。RingBuffer未采集、后端没有实现delay虚函数,或者设备实际队列为空,都可能得到0。

2.2函数原型与返回语义

函数原型如下:

c 复制代码
guint
gst_audio_ring_buffer_delay (GstAudioRingBuffer *buf);

buf是要查询的GstAudioRingBuffer对象。返回值是设备队列中未处理的样本帧数,单位与采样率配合后可换算为时间:

text 复制代码
队列时长 = delay × GST_SECOND ÷ 采样率

这个函数没有错误码。传入无效对象或RingBuffer尚未采集时返回0。对象已采集但类没有提供delay虚函数时也返回0,因此应用如果需要区分"设备队列为空"和"后端不支持查询",还要结合对象状态和具体后端实现判断。

相邻接口各自承担不同工作:

接口 职责
gst_audio_ring_buffer_samples_done() 查询RingBuffer启动后已经处理的样本数
gst_audio_ring_buffer_delay() 查询仍在设备队列中等待处理的样本数
gst_audio_ring_buffer_convert() 在样本数、字节数和时间等格式之间转换
gst_audio_ring_buffer_start() 启动RingBuffer处理流程

🌻3.调用流程剖析

3.1基类检查采集状态并分派查询

gst_audio_ring_buffer_delay()的基类实现很短。它先验证对象,再检查RingBuffer是否已经采集。

未采集时,函数记录调试信息并返回0。已采集时,基类读取对象类型对应的GstAudioRingBufferClass,调用delay虚函数。若类没有实现该虚函数,基类同样返回0。
#mermaid-svg-X92QCqgNcCOrC5GM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-X92QCqgNcCOrC5GM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-X92QCqgNcCOrC5GM .error-icon{fill:#552222;}#mermaid-svg-X92QCqgNcCOrC5GM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X92QCqgNcCOrC5GM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X92QCqgNcCOrC5GM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X92QCqgNcCOrC5GM .marker.cross{stroke:#333333;}#mermaid-svg-X92QCqgNcCOrC5GM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X92QCqgNcCOrC5GM p{margin:0;}#mermaid-svg-X92QCqgNcCOrC5GM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-X92QCqgNcCOrC5GM .cluster-label text{fill:#333;}#mermaid-svg-X92QCqgNcCOrC5GM .cluster-label span{color:#333;}#mermaid-svg-X92QCqgNcCOrC5GM .cluster-label span p{background-color:transparent;}#mermaid-svg-X92QCqgNcCOrC5GM .label text,#mermaid-svg-X92QCqgNcCOrC5GM span{fill:#333;color:#333;}#mermaid-svg-X92QCqgNcCOrC5GM .node rect,#mermaid-svg-X92QCqgNcCOrC5GM .node circle,#mermaid-svg-X92QCqgNcCOrC5GM .node ellipse,#mermaid-svg-X92QCqgNcCOrC5GM .node polygon,#mermaid-svg-X92QCqgNcCOrC5GM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-X92QCqgNcCOrC5GM .rough-node .label text,#mermaid-svg-X92QCqgNcCOrC5GM .node .label text,#mermaid-svg-X92QCqgNcCOrC5GM .image-shape .label,#mermaid-svg-X92QCqgNcCOrC5GM .icon-shape .label{text-anchor:middle;}#mermaid-svg-X92QCqgNcCOrC5GM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-X92QCqgNcCOrC5GM .rough-node .label,#mermaid-svg-X92QCqgNcCOrC5GM .node .label,#mermaid-svg-X92QCqgNcCOrC5GM .image-shape .label,#mermaid-svg-X92QCqgNcCOrC5GM .icon-shape .label{text-align:center;}#mermaid-svg-X92QCqgNcCOrC5GM .node.clickable{cursor:pointer;}#mermaid-svg-X92QCqgNcCOrC5GM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-X92QCqgNcCOrC5GM .arrowheadPath{fill:#333333;}#mermaid-svg-X92QCqgNcCOrC5GM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-X92QCqgNcCOrC5GM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-X92QCqgNcCOrC5GM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X92QCqgNcCOrC5GM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-X92QCqgNcCOrC5GM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X92QCqgNcCOrC5GM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-X92QCqgNcCOrC5GM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-X92QCqgNcCOrC5GM .cluster text{fill:#333;}#mermaid-svg-X92QCqgNcCOrC5GM .cluster span{color:#333;}#mermaid-svg-X92QCqgNcCOrC5GM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-X92QCqgNcCOrC5GM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-X92QCqgNcCOrC5GM rect.text{fill:none;stroke-width:0;}#mermaid-svg-X92QCqgNcCOrC5GM .icon-shape,#mermaid-svg-X92QCqgNcCOrC5GM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X92QCqgNcCOrC5GM .icon-shape p,#mermaid-svg-X92QCqgNcCOrC5GM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-X92QCqgNcCOrC5GM .icon-shape .label rect,#mermaid-svg-X92QCqgNcCOrC5GM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X92QCqgNcCOrC5GM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-X92QCqgNcCOrC5GM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-X92QCqgNcCOrC5GM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
调用delay查询
检查RingBuffer对象
是否已采集
返回0
读取类的delay虚函数
后端是否实现
返回0
返回设备队列样本帧数

基类不直接查询ALSA或其他设备驱动。具体后端负责取得队列状态,并将结果转换成样本帧数。以OSS后端为例,它先尝试通过设备控制接口查询队列字节数,再根据每帧字节数换算为样本帧数。

3.2播放与录制时钟采用不同方向修正

播放和录制中的delay意义相反,因此音频基类的时钟计算方向也不同。

播放端通过gst_audio_base_sink_get_time()读取已处理样本数,再减去设备中尚未播放的样本帧数。若队列延迟大于已处理样本数,计算结果会限制为0。

录制端通过gst_audio_base_src_get_time()读取已处理样本数,再加上设备中尚未读入RingBuffer的样本帧数。之后,两者都会根据采样率把样本数换算成时间。
#mermaid-svg-Q50nsjYRAl7Bbjvf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q50nsjYRAl7Bbjvf .error-icon{fill:#552222;}#mermaid-svg-Q50nsjYRAl7Bbjvf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q50nsjYRAl7Bbjvf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .marker.cross{stroke:#333333;}#mermaid-svg-Q50nsjYRAl7Bbjvf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q50nsjYRAl7Bbjvf p{margin:0;}#mermaid-svg-Q50nsjYRAl7Bbjvf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster-label text{fill:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster-label span{color:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster-label span p{background-color:transparent;}#mermaid-svg-Q50nsjYRAl7Bbjvf .label text,#mermaid-svg-Q50nsjYRAl7Bbjvf span{fill:#333;color:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .node rect,#mermaid-svg-Q50nsjYRAl7Bbjvf .node circle,#mermaid-svg-Q50nsjYRAl7Bbjvf .node ellipse,#mermaid-svg-Q50nsjYRAl7Bbjvf .node polygon,#mermaid-svg-Q50nsjYRAl7Bbjvf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .rough-node .label text,#mermaid-svg-Q50nsjYRAl7Bbjvf .node .label text,#mermaid-svg-Q50nsjYRAl7Bbjvf .image-shape .label,#mermaid-svg-Q50nsjYRAl7Bbjvf .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q50nsjYRAl7Bbjvf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .rough-node .label,#mermaid-svg-Q50nsjYRAl7Bbjvf .node .label,#mermaid-svg-Q50nsjYRAl7Bbjvf .image-shape .label,#mermaid-svg-Q50nsjYRAl7Bbjvf .icon-shape .label{text-align:center;}#mermaid-svg-Q50nsjYRAl7Bbjvf .node.clickable{cursor:pointer;}#mermaid-svg-Q50nsjYRAl7Bbjvf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .arrowheadPath{fill:#333333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q50nsjYRAl7Bbjvf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q50nsjYRAl7Bbjvf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q50nsjYRAl7Bbjvf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster text{fill:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf .cluster span{color:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q50nsjYRAl7Bbjvf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q50nsjYRAl7Bbjvf rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q50nsjYRAl7Bbjvf .icon-shape,#mermaid-svg-Q50nsjYRAl7Bbjvf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q50nsjYRAl7Bbjvf .icon-shape p,#mermaid-svg-Q50nsjYRAl7Bbjvf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q50nsjYRAl7Bbjvf .icon-shape .label rect,#mermaid-svg-Q50nsjYRAl7Bbjvf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q50nsjYRAl7Bbjvf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q50nsjYRAl7Bbjvf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q50nsjYRAl7Bbjvf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 播放端已处理样本数
减去设备待播放样本
得到播放时钟样本位置
录制端已采集样本数
加上设备待读取样本
得到录制时钟样本位置
按采样率换算时间

因此,delay不是独立的播放延迟配置,也不是直接返回时钟时间的函数。它提供样本帧数,时钟计算由上层结合gst_audio_ring_buffer_samples_done()和采样率完成。

🌻4.实战案例

4.1准备一个模拟音频后端

下面的案例创建一个测试用GstAudioRingBuffer子类,不连接真实声卡。它提供open_device、acquire、release和delay虚函数。

测试后端把当前队列帧数保存在子类对象中。delay虚函数返回该值,用来模拟播放设备中尚未播放的帧数。它只验证基类的状态检查和虚函数分派,不代表真实设备的队列查询结果。

4.2查询队列并验证状态门槛

测试先打开设备并采集RingBuffer资源,再将模拟队列设为96帧并调用gst_audio_ring_buffer_delay()。随后把队列值改为24帧,再次查询。

两次结果应分别与模拟后端保存的队列值相同。释放RingBuffer后再查询,基类会因为对象未处于已采集状态而返回0。

4.3完整代码与检查断言
c 复制代码
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <string.h>

typedef struct _TestRingBuffer
{
  GstAudioRingBuffer parent;
  guint queued_frames;
} TestRingBuffer;

typedef struct _TestRingBufferClass
{
  GstAudioRingBufferClass parent_class;
} TestRingBufferClass;

G_DEFINE_TYPE (TestRingBuffer, test_ring_buffer,
    GST_TYPE_AUDIO_RING_BUFFER);

/* 模拟打开设备,不连接真实声卡 */
static gboolean
test_ring_buffer_open_device (GstAudioRingBuffer *buf)
{
  return TRUE;
}

/* 为测试后端分配RingBuffer内存 */
static gboolean
test_ring_buffer_acquire (GstAudioRingBuffer *buf,
    GstAudioRingBufferSpec *spec)
{
  buf->size = spec->segsize * spec->segtotal;
  buf->memory = g_malloc0 (buf->size);

  return TRUE;
}

/* 释放测试后端分配的内存 */
static gboolean
test_ring_buffer_release (GstAudioRingBuffer *buf)
{
  g_free (buf->memory);
  buf->memory = NULL;
  buf->size = 0;

  return TRUE;
}

/* 返回模拟的设备队列帧数 */
static guint
test_ring_buffer_delay (GstAudioRingBuffer *buf)
{
  TestRingBuffer *test_buffer = (TestRingBuffer *) buf;

  return test_buffer->queued_frames;
}

/* 模拟关闭设备 */
static gboolean
test_ring_buffer_close_device (GstAudioRingBuffer *buf)
{
  return TRUE;
}

static void
test_ring_buffer_class_init (TestRingBufferClass *klass)
{
  GstAudioRingBufferClass *ring_buffer_class =
      GST_AUDIO_RING_BUFFER_CLASS (klass);

  ring_buffer_class->open_device =
      test_ring_buffer_open_device;
  ring_buffer_class->acquire =
      test_ring_buffer_acquire;
  ring_buffer_class->release =
      test_ring_buffer_release;
  ring_buffer_class->delay =
      test_ring_buffer_delay;
  ring_buffer_class->close_device =
      test_ring_buffer_close_device;
}

static void
test_ring_buffer_init (TestRingBuffer *test_buffer)
{
  test_buffer->queued_frames = 0;
}

int
main (int argc, char *argv[])
{
  TestRingBuffer *test_buffer;
  GstAudioRingBuffer *ring_buffer;
  GstAudioRingBufferSpec *spec;
  gboolean opened = FALSE;
  gboolean acquired = FALSE;
  gboolean ok = FALSE;

  gst_init (&argc, &argv);

  test_buffer = g_object_new (
      test_ring_buffer_get_type (),
      NULL);
  ring_buffer = GST_AUDIO_RING_BUFFER (test_buffer);
  spec = &ring_buffer->spec;

  /* 设置S16LE、48kHz、双声道的测试规格 */
  memset (spec, 0, sizeof (*spec));
  gst_audio_info_init (&spec->info);
  gst_audio_info_set_format (
      &spec->info,
      GST_AUDIO_FORMAT_S16,
      48000,
      2,
      NULL);
  spec->type = GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW;
  spec->segsize = 1024;
  spec->segtotal = 4;
  spec->seglatency = -1;

  /* 先打开设备,满足delay查询的生命周期前置条件 */
  if (!gst_audio_ring_buffer_open_device (ring_buffer))
    goto done;
  opened = TRUE;

  /* 采集资源后,基类才允许分派delay查询 */
  if (!gst_audio_ring_buffer_acquire (ring_buffer, spec))
    goto done;
  acquired = TRUE;

  /* 模拟设备中有96个尚未播放的样本帧 */
  test_buffer->queued_frames = 96;
  if (gst_audio_ring_buffer_delay (ring_buffer) != 96)
    goto done;

  /* 队列变化后再次查询,返回值应反映后端当前状态 */
  test_buffer->queued_frames = 24;
  if (gst_audio_ring_buffer_delay (ring_buffer) != 24)
    goto done;

  ok = TRUE;

done:
  /* 先释放采集资源,再关闭设备 */
  if (acquired)
    gst_audio_ring_buffer_release (ring_buffer);
  if (opened)
    gst_audio_ring_buffer_close_device (ring_buffer);

  /* 释放测试对象 */
  gst_object_unref (ring_buffer);

  return ok ? 0 : 1;
}

🌻5.总结

gst_audio_ring_buffer_delay()在RingBuffer已采集时调用后端接口,返回设备队列中尚未处理的样本帧数,供播放端或录制端计算时钟位置。

相关推荐
快乐的韭菜根根2 小时前
ubuntu 安装slurm并配置
linux·运维·ubuntu
数字新视界2 小时前
机房动环监控提升运维效率
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
U-Mail邮件系统2 小时前
自建邮箱服务器有哪些软件?三款主流邮件服务器软件推荐
运维·服务器
倒头就睡的小比特3 小时前
Linux基础指令3
linux·服务器
Smoothcloud润云4 小时前
PyTorch推理服务模型热切换与失败回滚实战:降低版本更新中断
服务器·人工智能·pytorch·python·深度学习·gpu
天远数科4 小时前
零信任架构实战:基于天远贷前风险报告构建自动化消金信贷风控与多头共债排查网关
运维·人工智能·架构·自动化
梦帮科技4 小时前
Kali Linux Scapy 畸形报文构造学——深度解剖非法 TCP Flags 与反常序列号
linux·爬虫·神经网络·算法·数学建模·系统架构·模拟退火算法
半杯咖啡半行码5 小时前
Linux 驱动开发入门:内核模块编程基础
linux·服务器·网络
别动我齐刘海5 小时前
Linux 系统调试学习2
linux·运维·c++·人工智能·学习·机器学习·机器人