news 2026/10/15 12:42:29

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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.前言
      • 🌻2.应用场景和用法
        • 2.1从环形缓冲区读取音频数据
        • 2.2函数原型与返回语义
      • 🌻3.调用流程剖析
        • 3.1按sample位置定位数据段
        • 3.2由音频源元素接收读取结果
      • 🌻4.实战案例
        • 4.1准备内存RingBuffer测试对象
        • 4.2写入一段数据并读取
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_ring_buffer_read()如何根据sample位置从环形缓冲区读取音频数据,分析分段等待、数据复制、静音补齐和时间戳返回,并通过内存后端示例验证读取结果。

GstAudioRingBuffer把设备数据组织成多个固定大小的段。gst_audio_ring_buffer_read()按照sample位置找到对应段,把数据复制到调用者提供的内存中。读取位置尚未就绪时,它可能等待;读取位置已经落后于设备时,它会用静音数据补齐。

这个函数负责RingBuffer中的分段读取,不负责创建输出GstBuffer,也不负责决定音频流的时间戳策略。上层音频源元素会调用它,再将读取结果包装成输出Buffer。

已采集并运行的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函数原型与返回语义

函数原型如下:

guintgst_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:设备已经覆盖了待读段,函数把静音数据复制到目标内存。

尚未就绪

已经被覆盖

可以读取

是

否

读取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完整代码
#include<gst/gst.h>#include<gst/audio/audio.h>#include<string.h>typedefstruct_TestRingBuffer{GstAudioRingBuffer parent;}TestRingBuffer;typedefstruct_TestRingBufferClass{GstAudioRingBufferClass parent_class;}TestRingBufferClass;G_DEFINE_TYPE(TestRingBuffer,test_ring_buffer,GST_TYPE_AUDIO_RING_BUFFER);/* 测试后端模拟设备打开 */staticgbooleantest_ring_buffer_open_device(GstAudioRingBuffer*buf){returnTRUE;}/* 测试后端为音频段分配内存 */staticgbooleantest_ring_buffer_acquire(GstAudioRingBuffer*buf,GstAudioRingBufferSpec*spec){buf->size=spec->segsize*spec->segtotal;buf->memory=g_malloc0(buf->size);returnTRUE;}/* 释放测试后端申请的内存 */staticgbooleantest_ring_buffer_release(GstAudioRingBuffer*buf){g_free(buf->memory);buf->memory=NULL;buf->size=0;returnTRUE;}/* 测试后端模拟设备启动和停止 */staticgbooleantest_ring_buffer_start(GstAudioRingBuffer*buf){returnTRUE;}staticgbooleantest_ring_buffer_stop(GstAudioRingBuffer*buf){returnTRUE;}/* 测试后端模拟设备关闭 */staticgbooleantest_ring_buffer_close_device(GstAudioRingBuffer*buf){returnTRUE;}staticvoidtest_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;}staticvoidtest_ring_buffer_init(TestRingBuffer*ring_buffer){}intmain(intargc,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))gotodone;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))gotodone;opened=TRUE;if(!gst_audio_ring_buffer_acquire(ring_buffer,spec))gotodone;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))gotodone;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);returnok?0:1;}

🌻5.总结

gst_audio_ring_buffer_read()根据sample位置定位环形段,复制可读音频或用静音补齐,并返回实际读取数量及对应段的时间戳。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 12:42:08

大范围地图导出:Google Earth切片与羽化拼接实践

简介&#xff1a;SGGS-Google Earth拼图工具是一款面向GIS从业者、地理学者及城市规划人员的卫星图像拼接应用&#xff0c;主要用于解决Google Earth分块卫星图不便连续查看的问题&#xff0c;适合需要对大范围地表进行无缝制图与精细分析的场景。资源包整体仅3.4MB&#xff0c…

作者头像 李华
网站建设 2026/10/15 12:42:07

HST时频分析技术原理与工程实践详解

1. 水平同步压缩变换&#xff08;HST&#xff09;技术解析水平同步压缩变换&#xff08;Horizontal Synchronous Transform&#xff0c;简称HST&#xff09;是近年来信号处理领域针对非平稳信号分析提出的一种改进型时频分析方法。我在处理机械振动信号和地震波分析时&#xff…

作者头像 李华
网站建设 2026/10/15 12:41:56

成功学的三大误区与科学成功逻辑

1. 成功学的迷思与真相"胜者基因"这个概念在各类成功学书籍和励志演讲中被反复提及&#xff0c;仿佛成功人士天生就携带着某种神秘的成功密码。这种论调往往包装在热血沸腾的鸡汤故事里&#xff0c;用"碾压周围人"的夸张表述来强化其说服力。但作为一名长期…

作者头像 李华
网站建设 2026/10/15 12:41:50

VIO第六章滑动窗口优化与边缘化:公式推导、编程实现与调试指南

1. 从“能跑通”到“真明白”&#xff1a;为什么我坚持手推VIO第六章的每个公式很多人做视觉惯性里程计&#xff08;VIO&#xff09;的作业&#xff0c;习惯直接翻开源代码&#xff0c;把参数一填、编译一跑&#xff0c;看到轨迹和真值对上了就收工。我最初也是这个路子&#x…

作者头像 李华
网站建设 2026/10/15 12:41:49

云原生时代CI/CD工具选型:Jenkins、Tekton与Arbess深度对比

1. 持续交付工具选型困境与解决思路在云原生和敏捷开发成为主流的今天&#xff0c;团队平均每天要处理数十次代码提交。上周我帮一个电商团队做咨询时发现&#xff0c;他们用了3年多的Jenkins流水线突然开始频繁崩溃&#xff0c;每次发版都变成一场噩梦。这引出了一个关键问题&…

作者头像 李华
网站建设 2026/10/15 12:41:15

kind集群1分钟启动原理与工程实践指南

1. 为什么“1分钟启动”不是营销话术&#xff0c;而是 kind 真实的工程设计结果 你有没有过这样的经历&#xff1a;在写完一段 Kubernetes 的 Deployment YAML 后&#xff0c;想立刻验证它在真实集群里的行为——结果打开终端&#xff0c;敲下 minikube start &#xff0c;然…

作者头像 李华