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

简介: 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写入一段数据并读取

测试程序按以下顺序执行:

  1. 初始化RingBuffer规格并打开模拟设备。
  2. 调用采集接口,让子类申请内存,再由基类初始化段状态。
  3. 设置段0时间戳,启动RingBuffer,并模拟设备完成一个段。
  4. 从sample 0读取4个sample到目标数组。
  5. 检查读取数量、首尾样本值和返回时间戳,再释放资源。

模拟设备通过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,
      &timestamp);

  /* 检查读取长度、样本内容和所在段时间戳 */
  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位置定位环形段,复制可读音频或用静音补齐,并返回实际读取数量及对应段的时间戳。

相关推荐
一条破秋裤2 小时前
04_控制GPIO通用化_从寄存器到gpiod
linux
星恒随风2 小时前
Linux进程信号(二):深入理解信号阻塞、未决、sigaction、中断机制、可重入函数与SIGCHLD
linux·运维·服务器·笔记·学习
修船大队长2 小时前
KL15 高电平请求 FullCom:AUTOSAR 项目调用流程
android·java·服务器
额额额对了2 小时前
Linux 驱动实战:ADXL345 驱动
linux·运维·服务器
Web3&Basketball2 小时前
昇腾950DT迁移实测:3笔成本账与决策树
服务器·算法·决策树
牛马工作号5 小时前
Mosquitto + ACME:从 Docker Compose 部署到 TLS 证书自动续期与加载
运维·docker·容器
强哥聊算力7 小时前
《算力江湖·英伟达传奇》第七集:ChatGPT出现前夜,英伟达发生了什么?
服务器·人工智能·算法·ai·chatgpt·gpu算力
Initialize-le8 小时前
Nginx 504 超时、499 一堆?网关排查法
运维·nginx
砚凝霜10 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全