news 2026/10/14 21:10:53

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux Gstreamer深度解析之gst_audio_ring_buffer_commit调用流程与实战(七十三)

简介: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准备一个内存RingBuffer
        • 4.2提交4个Frame并检查写入位置
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_ring_buffer_commit()如何把输入样本写入GstAudioRingBuffer,分析它如何处理环形分段、采样位置和速率换算,并通过内存后端观察提交结果。

GstAudioRingBuffer保存供音频设备读取或写入的分段缓冲区。gst_audio_ring_buffer_commit()把调用者提供的样本写入指定位置,并更新样本位置和换算累加器。它不会打开设备,也不会直接把数据写入声卡。

输入样本和目标位置

gst_audio_ring_buffer_commit

按分段位置写入RingBuffer

更新样本位置和累加器

返回已处理的输入样本数

🌻2.应用场景和用法

2.1向环形缓冲区提交样本

gst_audio_ring_buffer_commit()主要用于把上游已经准备好的音频样本提交到环形缓冲区。

场景调用时提供的数据作用
常规播放输入样本数与相同数量的输出样本数按样本位置复制到RingBuffer
速率换算输入样本数与不同的输出样本数在RingBuffer写入过程中完成样本增减
反向播放正数输入样本数与负数输出样本数按反向顺序写入目标位置
分段写入跨越一个或多个缓冲段的样本自动处理段边界,必要时等待可写段
中断后续写上次返回的样本位置和accum从尚未处理的数据继续提交

这里的“样本数”指音频Frame数量,不是字节数。对于立体声PCM,一个样本Frame包含左右声道在同一时刻的值。输入数据的内存布局必须与RingBuffer音频格式一致。

2.2函数原型与返回语义

函数原型如下:

guintgst_audio_ring_buffer_commit(GstAudioRingBuffer*buf,guint64*sample,guint8*data,gint in_samples,gint out_samples,gint*accum);
参数含义
buf已采集并包含可写内存的GstAudioRingBuffer
sample输入输出参数,表示写入位置;提交后更新为新的样本位置
data待提交的输入样本数据
in_samples输入数据中的样本数
out_samples目标写入样本数;负数表示反向提交
accum速率换算的累加状态,首次调用前应设为0

返回值为已处理的输入样本数。返回值可能小于本次输入样本数,例如提交期间RingBuffer被停止或刷新。guint接口用(guint)-1表示错误。

当in_samples或out_samples为0时,函数直接返回in_samples,不会调用类的commit虚函数。正常提交时,基类会分派到GstAudioRingBufferClass.commit;默认实现负责分段写入、通道重排和按输入输出样本数进行速率换算。子类也可以提供自己的实现。

相邻接口的职责不同:

接口职责
gst_audio_ring_buffer_acquire()申请并初始化RingBuffer资源
gst_audio_ring_buffer_commit()把样本写入RingBuffer内存
gst_audio_ring_buffer_read()从RingBuffer读取样本
gst_audio_ring_buffer_advance()通知RingBuffer设备侧已处理的分段
gst_audio_ring_buffer_release()停止并释放RingBuffer资源

🌻3.调用流程剖析

3.1从公开接口分派到默认实现

公开接口先检查buf是否为有效的GstAudioRingBuffer。当输入样本数或输出样本数为0时,它直接返回输入样本数。

其余情况根据对象的实际类型取得GstAudioRingBufferClass,再调用commit虚函数。基类默认实现是default_commit()。该实现要求RingBuffer内存和输入数据指针都有效,然后读取格式信息、每Frame字节数、每段大小和总段数。

默认实现根据sample计算首个写入段和段内偏移。写入长度超过当前段剩余空间时,会继续写到下一段。可写段尚未到达时调用等待逻辑;目标位置已经落后于设备处理位置时,则跳过落后的写入区域。

是

否

调用者提供样本与位置

检查样本数量

数量是否为零

直接返回输入样本数

分派到RingBuffer类的commit

默认实现计算分段和偏移

3.2按分段位置写入并更新进度

默认实现使用sample / samples_per_seg确定写入段,使用sample % samples_per_seg确定段内偏移。每写入一段可用空间,就增加样本位置;跨段后从下一段开头继续处理。

当in_samples和out_samples相同时,默认实现逐Frame复制。两者不同时,它依据输入输出数量和accum执行样本速率换算。out_samples为负数时,按反向播放路径读取输入数据并写入。

GstAudioBaseSink会在渲染音频时调用该接口。若返回值小于本次输入样本数,Sink会等待Preroll或恢复条件,再根据返回值调整数据偏移和剩余样本数。该循环让提交过程能够处理停止、刷新等中断情况。

根据sample定位段和偏移

判断目标段是否可写

复制或按比例换算样本

推进sample和accum

返回已处理数量

默认实现只写入RingBuffer管理的内存。设备侧如何消费这些分段,由对应的RingBuffer子类负责。gst_audio_ring_buffer_commit()本身不会直接调用声卡驱动。

🌻4.实战案例

4.1准备一个内存RingBuffer

案例创建一个测试用GstAudioRingBuffer子类,以一段普通内存模拟设备缓冲区。子类只负责打开测试后端、按规格分配内存和释放内存,commit虚函数沿用基类默认实现。

音频格式为48kHz、S16、双声道。每个Frame占4字节。测试提交4个Frame,因此输入数据长度为16字节。

4.2提交4个Frame并检查写入位置

程序先打开并采集RingBuffer,再把sample和accum初始化为0,提交4个输入Frame和4个目标Frame。

成功后检查三项结果:

  1. 返回值为4,表示4个输入Frame均已处理。
  2. sample更新为4,表示写入位置推进了4个Frame。
  3. RingBuffer内存的前16字节与输入数据一致。
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_open_device(GstAudioRingBuffer*buf){returnTRUE;}/* 按分段规格申请测试用RingBuffer内存 */staticgbooleantest_acquire(GstAudioRingBuffer*buf,GstAudioRingBufferSpec*spec){buf->size=spec->segsize*spec->segtotal;buf->memory=g_malloc0(buf->size);returnTRUE;}/* 释放测试后端分配的内存 */staticgbooleantest_release(GstAudioRingBuffer*buf){g_free(buf->memory);buf->memory=NULL;buf->size=0;returnTRUE;}/* 关闭测试后端 */staticgbooleantest_close_device(GstAudioRingBuffer*buf){returnTRUE;}staticvoidtest_ring_buffer_class_init(TestRingBufferClass*klass){GstAudioRingBufferClass*ring_class=GST_AUDIO_RING_BUFFER_CLASS(klass);ring_class->open_device=test_open_device;ring_class->acquire=test_acquire;ring_class->release=test_release;ring_class->close_device=test_close_device;}staticvoidtest_ring_buffer_init(TestRingBuffer*ring_buffer){}intmain(intargc,char*argv[]){TestRingBuffer*test_buffer;GstAudioRingBuffer*ring_buffer;GstAudioRingBufferSpec*spec;gint16 pcm[]={100,-100,200,-200,300,-300,400,-400};guint64 sample=0;gint accum=0;guint written;gboolean opened=FALSE;gboolean acquired=FALSE;gboolean ok=FALSE;gst_init(&argc,&argv);/* 创建测试用RingBuffer对象 */test_buffer=g_object_new(test_ring_buffer_get_type(),NULL);ring_buffer=GST_AUDIO_RING_BUFFER(test_buffer);spec=&ring_buffer->spec;/* 配置48kHz、S16、双声道的原始音频 */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;/* 每段1024字节,共4段 */spec->segsize=1024;spec->segtotal=4;spec->seglatency=-1;/* 采集前先打开设备 */if(!gst_audio_ring_buffer_open_device(ring_buffer))gotodone;opened=TRUE;/* 申请内存,并由基类初始化分段状态 */if(!gst_audio_ring_buffer_acquire(ring_buffer,spec))gotodone;acquired=TRUE;/* 首次提交时,样本位置和速率换算累加器都从0开始 */written=gst_audio_ring_buffer_commit(ring_buffer,&sample,(guint8*)pcm,4,4,&accum);/* 检查输入消费数量、写入位置和RingBuffer内存内容 */ok=written==4&&sample==4&&memcmp(ring_buffer->memory,pcm,sizeof(pcm))==0;done:/* 按资源创建顺序的逆序释放RingBuffer资源 */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_commit()根据样本位置和分段规格将输入数据提交到RingBuffer内存,并更新写入位置与速率换算状态。

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

JeecgBoot 3.2.0在线用户异常排查:token刷新与Redis存储策略优化

JeecgBoot 3.2.0 用了一段时间后&#xff0c;很多团队都会撞上同一个尴尬场景&#xff1a;系统监控里的"在线用户"列表&#xff0c;同一账号翻来覆去出现好几条记录&#xff0c;IP、登录时间还各不相同&#xff1b;管理员在列表里点"强制下线"&#xff0c;…

作者头像 李华
网站建设 2026/10/14 21:08:26

易语言封装大漠多线程API:队列调度架构与低误报发布实践

1. 项目概述与设计思路拆解 1.1 这个模块要解决什么问题 易语言做自动化脚本开发&#xff0c;痛点从来不在“能不能调用”&#xff0c;而在“调了之后怎么收场”。大漠插件的API接口本身并不复杂&#xff0c;找图、找色、识字、后台键鼠这些基础能力&#xff0c;插件文档里写得…

作者头像 李华
网站建设 2026/10/14 21:07:55

面试官问我:“AI 写代码比你快 100 倍,你的价值在哪?”

&#x1f4a1; 很多 Java 同学都有一个疑惑&#xff1a;背了几百道八股文&#xff0c;刷了一堆面试题&#xff0c;可面试依旧频频挂掉&#xff1f; 不是你知识点记不住&#xff0c;而是面试官早就变了考核思路&#xff01; 以前面试官可能会问&#xff1a;什么是 JVM 内存模型&…

作者头像 李华
网站建设 2026/10/14 21:07:32

IPD技术评审TR1-TR6落地指南:从风险闸门到闭环管理

简介&#xff1a;IPD技术评审&#xff08;TR1-TR6&#xff09;是产品开发从概念到量产的关键控制点&#xff0c;覆盖技术成熟度、质量与市场准备度的阶段性评估。这份PDF面向研发管理者、项目经理、SE与PQA等角色&#xff0c;系统梳理了TR评审定义、评审计划、各阶段关注点及裁…

作者头像 李华