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

发布时间:2026/10/15 12:42:30
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。已采集并运行的GstAudioRingBuffergst_audio_ring_buffer_readsample位置与目标长度定位环形段并检查可读状态复制音频或补充静音返回读取数量和段时间戳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写入一段数据并读取测试程序按以下顺序执行初始化RingBuffer规格并打开模拟设备。调用采集接口让子类申请内存再由基类初始化段状态。设置段0时间戳启动RingBuffer并模拟设备完成一个段。从sample 0读取4个sample到目标数组。检查读取数量、首尾样本值和返回时间戳再释放资源。模拟设备通过gst_audio_ring_buffer_advance()报告段处理进度。该调用只用于测试中模拟设备行为真实设备子类会根据实际硬件处理进度通知RingBuffer。4.3完整代码#includegst/gst.h#includegst/audio/audio.h#includestring.htypedefstruct_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-sizespec-segsize*spec-segtotal;buf-memoryg_malloc0(buf-size);returnTRUE;}/* 释放测试后端申请的内存 */staticgbooleantest_ring_buffer_release(GstAudioRingBuffer*buf){g_free(buf-memory);buf-memoryNULL;buf-size0;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_classGST_AUDIO_RING_BUFFER_CLASS(klass);ring_buffer_class-open_devicetest_ring_buffer_open_device;ring_buffer_class-acquiretest_ring_buffer_acquire;ring_buffer_class-releasetest_ring_buffer_release;ring_buffer_class-starttest_ring_buffer_start;ring_buffer_class-stoptest_ring_buffer_stop;ring_buffer_class-close_devicetest_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 timestampGST_CLOCK_TIME_NONE;guint read_samples;gboolean openedFALSE;gboolean acquiredFALSE;gboolean startedFALSE;gboolean okFALSE;gst_init(argc,argv);test_bufferg_object_new(test_ring_buffer_get_type(),NULL);ring_bufferGST_AUDIO_RING_BUFFER(test_buffer);specring_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-typeGST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW;spec-segsize1024;spec-segtotal4;spec-seglatency-1;/* read前必须先打开并采集RingBuffer */if(!gst_audio_ring_buffer_open_device(ring_buffer))gotodone;openedTRUE;if(!gst_audio_ring_buffer_acquire(ring_buffer,spec))gotodone;acquiredTRUE;/* 将测试样本写入第一个段 */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;startedTRUE;gst_audio_ring_buffer_advance(ring_buffer,1);/* 从sample 0读取4个sample即4个双声道Frame */read_samplesgst_audio_ring_buffer_read(ring_buffer,0,(guint8*)output_samples,4,timestamp);/* 检查读取长度、样本内容和所在段时间戳 */okread_samples4output_samples[0]100output_samples[7]800timestamp100*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位置定位环形段复制可读音频或用静音补齐并返回实际读取数量及对应段的时间戳。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询