简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:理解
gst_audio_ring_buffer_read()如何根据sample位置从环形缓冲区读取音频数据,分析分段等待、数据复制、静音补齐和时间戳返回,并通过内存后端示例验证读取结果。
GstAudioRingBuffer把设备数据组织成多个固定大小的段。gst_audio_ring_buffer_read()按照sample位置找到对应段,把数据复制到调用者提供的内存中。读取位置尚未就绪时,它可能等待;读取位置已经落后于设备时,它会用静音数据补齐。
这个函数负责RingBuffer中的分段读取,不负责创建输出GstBuffer,也不负责决定音频流的时间戳策略。上层音频源元素会调用它,再将读取结果包装成输出Buffer。
#mermaid-svg-xHqp2L4BeoKHeQCL{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-xHqp2L4BeoKHeQCL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xHqp2L4BeoKHeQCL .error-icon{fill:#552222;}#mermaid-svg-xHqp2L4BeoKHeQCL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xHqp2L4BeoKHeQCL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xHqp2L4BeoKHeQCL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xHqp2L4BeoKHeQCL .marker.cross{stroke:#333333;}#mermaid-svg-xHqp2L4BeoKHeQCL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xHqp2L4BeoKHeQCL p{margin:0;}#mermaid-svg-xHqp2L4BeoKHeQCL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster-label text{fill:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster-label span{color:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster-label span p{background-color:transparent;}#mermaid-svg-xHqp2L4BeoKHeQCL .label text,#mermaid-svg-xHqp2L4BeoKHeQCL span{fill:#333;color:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL .node rect,#mermaid-svg-xHqp2L4BeoKHeQCL .node circle,#mermaid-svg-xHqp2L4BeoKHeQCL .node ellipse,#mermaid-svg-xHqp2L4BeoKHeQCL .node polygon,#mermaid-svg-xHqp2L4BeoKHeQCL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xHqp2L4BeoKHeQCL .rough-node .label text,#mermaid-svg-xHqp2L4BeoKHeQCL .node .label text,#mermaid-svg-xHqp2L4BeoKHeQCL .image-shape .label,#mermaid-svg-xHqp2L4BeoKHeQCL .icon-shape .label{text-anchor:middle;}#mermaid-svg-xHqp2L4BeoKHeQCL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xHqp2L4BeoKHeQCL .rough-node .label,#mermaid-svg-xHqp2L4BeoKHeQCL .node .label,#mermaid-svg-xHqp2L4BeoKHeQCL .image-shape .label,#mermaid-svg-xHqp2L4BeoKHeQCL .icon-shape .label{text-align:center;}#mermaid-svg-xHqp2L4BeoKHeQCL .node.clickable{cursor:pointer;}#mermaid-svg-xHqp2L4BeoKHeQCL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xHqp2L4BeoKHeQCL .arrowheadPath{fill:#333333;}#mermaid-svg-xHqp2L4BeoKHeQCL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xHqp2L4BeoKHeQCL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xHqp2L4BeoKHeQCL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xHqp2L4BeoKHeQCL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xHqp2L4BeoKHeQCL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xHqp2L4BeoKHeQCL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster text{fill:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL .cluster span{color:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL 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-xHqp2L4BeoKHeQCL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xHqp2L4BeoKHeQCL rect.text{fill:none;stroke-width:0;}#mermaid-svg-xHqp2L4BeoKHeQCL .icon-shape,#mermaid-svg-xHqp2L4BeoKHeQCL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xHqp2L4BeoKHeQCL .icon-shape p,#mermaid-svg-xHqp2L4BeoKHeQCL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xHqp2L4BeoKHeQCL .icon-shape .label rect,#mermaid-svg-xHqp2L4BeoKHeQCL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xHqp2L4BeoKHeQCL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xHqp2L4BeoKHeQCL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xHqp2L4BeoKHeQCL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 已采集并运行的GstAudioRingBuffer
gst_audio_ring_buffer_read
sample位置与目标长度
定位环形段并检查可读状态
复制音频或补充静音
返回读取数量和段时间戳
🌻2.应用场景和用法
2.1从环形缓冲区读取音频数据
gst_audio_ring_buffer_read()主要用于音频源元素从设备环形缓冲区中读取数据。读取位置以sample表示,读取长度也以sample计数;在音频RingBuffer中,一个sample对应一个时刻的全部声道数据,也就是一个音频Frame。
| 使用场景 | 读取行为 | 需要关注的结果 |
|---|---|---|
| 录音源输出数据 | 从指定sample位置读取若干sample | 返回实际读取数量 |
| 读取跨越多个段的数据 | 复制当前段后继续处理下一段 | 目标内存需要容纳完整长度 |
| 读取位置尚未就绪 | 等待设备处理新的段 | RingBuffer启动、暂停或停止状态 |
| 读取位置已被设备覆盖 | 从静音段补齐数据 | 读取数量可能仍达到请求长度 |
| 声道顺序需要调整 | 将设备声道排列转换为GStreamer排列 | 输出数据声道顺序 |
调用前,RingBuffer必须已经采集资源,buf->memory和data都必须有效。len是sample数量,不是字节数。目标内存至少需要len × bpf字节,其中bpf是每个音频Frame占用的字节数。
2.2函数原型与返回语义
函数原型如下:
c
guint
gst_audio_ring_buffer_read (GstAudioRingBuffer *buf,
guint64 sample,
guint8 *data,
guint len,
GstClockTime *timestamp);
| 参数或返回值 | 含义 |
|---|---|
buf |
已采集的音频环形缓冲区 |
sample |
需要读取的起始sample位置 |
data |
接收音频数据的目标内存 |
len |
请求读取的sample数量 |
timestamp |
可选输出参数,接收读取段对应的时间戳 |
| 返回值 | 实际读取的sample数量;发生参数检查错误时返回无符号形式的-1 |
函数会按sample / samples_per_seg计算段号,并按sample % samples_per_seg计算段内偏移。每次从一个段复制数据后,会更新sample位置和目标地址,再继续处理剩余长度。
相邻接口的职责不同:
| 接口 | 职责 |
|---|---|
gst_audio_ring_buffer_read() |
从环形缓冲区读取sample数据 |
gst_audio_ring_buffer_prepare_read() |
返回当前段的内存地址,主要供子类使用 |
gst_audio_ring_buffer_set_timestamp() |
设置指定段的时间戳 |
gst_audio_ring_buffer_advance() |
通知RingBuffer又处理完成了多少段 |
gst_audio_base_src_create() |
分配输出Buffer并调用read接口填充数据 |
🌻3.调用流程剖析
3.1按sample位置定位数据段
函数先检查buf、RingBuffer内存和目标指针。之后读取段大小、段总数、声道数、BPF和每段sample数量等参数。
每轮循环都会根据当前sample位置重新计算段号和段内偏移。由于实际存储是环形的,确定段可读后,段号会再对segtotal取模,映射到实际内存中的段。
函数通过segdone - segbase得到当前已经处理到的相对段位置,再与待读段号比较:
diff > 0:待读段已经可读。diff <= 0:待读段尚未可读,函数进入等待逻辑。diff >= segtotal:设备已经覆盖了待读段,函数把静音数据复制到目标内存。
#mermaid-svg-8Rsx3pWBt1354AHr{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-8Rsx3pWBt1354AHr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8Rsx3pWBt1354AHr .error-icon{fill:#552222;}#mermaid-svg-8Rsx3pWBt1354AHr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8Rsx3pWBt1354AHr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8Rsx3pWBt1354AHr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8Rsx3pWBt1354AHr .marker.cross{stroke:#333333;}#mermaid-svg-8Rsx3pWBt1354AHr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8Rsx3pWBt1354AHr p{margin:0;}#mermaid-svg-8Rsx3pWBt1354AHr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8Rsx3pWBt1354AHr .cluster-label text{fill:#333;}#mermaid-svg-8Rsx3pWBt1354AHr .cluster-label span{color:#333;}#mermaid-svg-8Rsx3pWBt1354AHr .cluster-label span p{background-color:transparent;}#mermaid-svg-8Rsx3pWBt1354AHr .label text,#mermaid-svg-8Rsx3pWBt1354AHr span{fill:#333;color:#333;}#mermaid-svg-8Rsx3pWBt1354AHr .node rect,#mermaid-svg-8Rsx3pWBt1354AHr .node circle,#mermaid-svg-8Rsx3pWBt1354AHr .node ellipse,#mermaid-svg-8Rsx3pWBt1354AHr .node polygon,#mermaid-svg-8Rsx3pWBt1354AHr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8Rsx3pWBt1354AHr .rough-node .label text,#mermaid-svg-8Rsx3pWBt1354AHr .node .label text,#mermaid-svg-8Rsx3pWBt1354AHr .image-shape .label,#mermaid-svg-8Rsx3pWBt1354AHr .icon-shape .label{text-anchor:middle;}#mermaid-svg-8Rsx3pWBt1354AHr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8Rsx3pWBt1354AHr .rough-node .label,#mermaid-svg-8Rsx3pWBt1354AHr .node .label,#mermaid-svg-8Rsx3pWBt1354AHr .image-shape .label,#mermaid-svg-8Rsx3pWBt1354AHr .icon-shape .label{text-align:center;}#mermaid-svg-8Rsx3pWBt1354AHr .node.clickable{cursor:pointer;}#mermaid-svg-8Rsx3pWBt1354AHr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8Rsx3pWBt1354AHr .arrowheadPath{fill:#333333;}#mermaid-svg-8Rsx3pWBt1354AHr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8Rsx3pWBt1354AHr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8Rsx3pWBt1354AHr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8Rsx3pWBt1354AHr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8Rsx3pWBt1354AHr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8Rsx3pWBt1354AHr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8Rsx3pWBt1354AHr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8Rsx3pWBt1354AHr .cluster text{fill:#333;}#mermaid-svg-8Rsx3pWBt1354AHr .cluster span{color:#333;}#mermaid-svg-8Rsx3pWBt1354AHr 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-8Rsx3pWBt1354AHr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8Rsx3pWBt1354AHr rect.text{fill:none;stroke-width:0;}#mermaid-svg-8Rsx3pWBt1354AHr .icon-shape,#mermaid-svg-8Rsx3pWBt1354AHr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8Rsx3pWBt1354AHr .icon-shape p,#mermaid-svg-8Rsx3pWBt1354AHr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8Rsx3pWBt1354AHr .icon-shape .label rect,#mermaid-svg-8Rsx3pWBt1354AHr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8Rsx3pWBt1354AHr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8Rsx3pWBt1354AHr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8Rsx3pWBt1354AHr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 尚未就绪
已经被覆盖
可以读取
是
否
读取sample和当前段计数
计算段号及段内偏移
待读段是否可读
等待设备处理新段
复制静音数据
映射到环形段
复制数据或调整声道顺序
推进sample和目标地址
是否还有数据
返回读取数量和时间戳
diff <= 0时,内部等待逻辑会检查RingBuffer是否处于运行状态。若允许启动,等待逻辑可以启动RingBuffer;若数据仍未到达,则等待段处理信号。停止或刷新状态会让等待结束,read接口返回当前已经读取的数量,因此返回值可能小于len。
3.2由音频源元素接收读取结果
GstAudioBaseSrc在创建输出Buffer时,先检查RingBuffer是否已经采集,再根据协商格式计算BPF和读取长度。它将Buffer映射为可写内存后,调用gst_audio_ring_buffer_read()填充数据。
如果read接口返回的数量小于请求值,音频源会检查RingBuffer状态。遇到错误状态时走错误处理;如果只是播放状态中断,则等待重新运行,再从未读完的sample位置继续读取。
读取完成后,GstAudioBaseSrc根据sample位置、采样率和RingBuffer返回的时间戳设置输出Buffer的时间信息,并根据实际情况标记DISCONT。因此,read接口提供的是数据段读取结果,上层音频源负责将其整理为GStreamer数据流。
🌻4.实战案例
4.1准备内存RingBuffer测试对象
下面用一个简单的GstAudioRingBuffer子类模拟音频设备。子类申请一块内存作为环形缓冲区,避免依赖声卡。规格使用S16LE、48kHz、双声道,每段1024字节,共4段。
每个sample包含左右两个声道,每声道16位,因此BPF为4字节。每段可容纳256个sample。
4.2写入一段数据并读取
测试程序按以下顺序执行:
- 初始化RingBuffer规格并打开模拟设备。
- 调用采集接口,让子类申请内存,再由基类初始化段状态。
- 设置段0时间戳,启动RingBuffer,并模拟设备完成一个段。
- 从sample 0读取4个sample到目标数组。
- 检查读取数量、首尾样本值和返回时间戳,再释放资源。
模拟设备通过gst_audio_ring_buffer_advance()报告段处理进度。该调用只用于测试中模拟设备行为;真实设备子类会根据实际硬件处理进度通知RingBuffer。
4.3完整代码
c
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <string.h>
typedef struct _TestRingBuffer
{
GstAudioRingBuffer parent;
} 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;
}
/* 测试后端为音频段分配内存 */
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 gboolean
test_ring_buffer_start (GstAudioRingBuffer *buf)
{
return TRUE;
}
static gboolean
test_ring_buffer_stop (GstAudioRingBuffer *buf)
{
return TRUE;
}
/* 测试后端模拟设备关闭 */
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->start =
test_ring_buffer_start;
ring_buffer_class->stop =
test_ring_buffer_stop;
ring_buffer_class->close_device =
test_ring_buffer_close_device;
}
static void
test_ring_buffer_init (TestRingBuffer *ring_buffer)
{
}
int
main (int argc, char *argv[])
{
TestRingBuffer *test_buffer;
GstAudioRingBuffer *ring_buffer;
GstAudioRingBufferSpec *spec;
gint16 source_samples[] = {
100, 200,
300, 400,
500, 600,
700, 800
};
gint16 output_samples[8] = { 0 };
GstClockTime timestamp = GST_CLOCK_TIME_NONE;
guint read_samples;
gboolean opened = FALSE;
gboolean acquired = FALSE;
gboolean started = 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;
/* 设置S16双声道格式和环形段大小 */
memset (spec, 0, sizeof (*spec));
gst_audio_info_init (&spec->info);
if (!gst_audio_info_set_format (
&spec->info,
GST_AUDIO_FORMAT_S16,
48000,
2,
NULL))
goto done;
spec->type = GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW;
spec->segsize = 1024;
spec->segtotal = 4;
spec->seglatency = -1;
/* read前必须先打开并采集RingBuffer */
if (!gst_audio_ring_buffer_open_device (ring_buffer))
goto done;
opened = TRUE;
if (!gst_audio_ring_buffer_acquire (ring_buffer, spec))
goto done;
acquired = TRUE;
/* 将测试样本写入第一个段 */
memcpy (ring_buffer->memory,
source_samples,
sizeof (source_samples));
/* 保存第一个段的时间戳 */
gst_audio_ring_buffer_set_timestamp (
ring_buffer,
0,
100 * GST_MSECOND);
/* 允许启动,模拟设备完成一个段 */
gst_audio_ring_buffer_may_start (ring_buffer, TRUE);
if (!gst_audio_ring_buffer_start (ring_buffer))
goto done;
started = TRUE;
gst_audio_ring_buffer_advance (ring_buffer, 1);
/* 从sample 0读取4个sample,即4个双声道Frame */
read_samples = gst_audio_ring_buffer_read (
ring_buffer,
0,
(guint8 *) output_samples,
4,
×tamp);
/* 检查读取长度、样本内容和所在段时间戳 */
ok = read_samples == 4
&& output_samples[0] == 100
&& output_samples[7] == 800
&& timestamp == 100 * GST_MSECOND;
done:
/* 依次停止、释放采集资源并关闭设备 */
if (started)
gst_audio_ring_buffer_stop (ring_buffer);
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_read()根据sample位置定位环形段,复制可读音频或用静音补齐,并返回实际读取数量及对应段的时间戳。