简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 🌻2.应用场景和用法
- 2.1停止RingBuffer的场景
- 2.2函数原型与返回语义
- 🌻3.调用流程剖析
- 3.1状态切换与子类停止回调
- 3.2停止与释放、关闭的边界
- 🌻4.实战案例
- 4.1准备带停止回调的测试RingBuffer
- 4.2启动后调用stop并检查结果
- 4.3完整代码
- 🌻5.总结
🌻1.前言
本篇目的:理解
gst_audio_ring_buffer_stop()如何停止GstAudioRingBuffer中的音频处理,梳理它与RingBuffer状态及子类停止回调的关系,并通过模拟后端观察停止前后的状态变化。
GstAudioRingBuffer的状态可能是运行、暂停、错误或已停止。gst_audio_ring_buffer_stop()负责把运行、暂停或错误状态切换到已停止,并在状态确实发生切换时调用子类的stop虚函数。
它不释放RingBuffer资源,也不关闭设备。资源释放和设备关闭分别由gst_audio_ring_buffer_release()和gst_audio_ring_buffer_close_device()负责。
🌻2.应用场景和用法
2.1停止RingBuffer的场景
gst_audio_ring_buffer_stop()用于停止当前的样本处理。播放端和录制端都可以通过RingBuffer子类停止对应设备上的处理动作。
| 场景 | 调用时的状态 | 调用后的结果 |
|---|---|---|
| 停止播放 | STARTED | 状态切换为STOPPED,子类停止回调执行 |
| 暂停后结束 | PAUSED | 状态切换为STOPPED,子类停止回调执行 |
| 发生错误后停止 | ERROR | 状态切换为STOPPED,子类停止回调执行 |
| 重复停止 | 已经是STOPPED | 返回成功,不重复调用子类回调 |
调用后RingBuffer仍可能持有已采集的设备资源。需要释放这些资源时,应继续调用gst_audio_ring_buffer_release();设备不再使用时,再调用gst_audio_ring_buffer_close_device()。
2.2函数原型与返回语义
函数原型如下:
gbooleangst_audio_ring_buffer_stop(GstAudioRingBuffer*buf);| 参数或返回值 | 含义 |
|---|---|
buf | 要停止的GstAudioRingBuffer对象 |
TRUE | RingBuffer已经停止,或无需执行状态切换 |
FALSE | 对象无效,或子类停止回调报告失败 |
函数会检查buf对象并获取对象锁。只有状态从STARTED、PAUSED或ERROR切换到STOPPED时,才会调用子类的stop虚函数。如果状态已经是STOPPED,函数直接返回TRUE。
调用者不需要重复调用来确保已停止。调用后若还要释放资源,应使用相邻的生命周期接口:
| 接口 | 职责 |
|---|---|
gst_audio_ring_buffer_stop() | 停止样本处理并更新运行状态 |
gst_audio_ring_buffer_release() | 释放已经采集的缓冲区和设备资源 |
gst_audio_ring_buffer_close_device() | 关闭已经打开的设备 |
🌻3.调用流程剖析
3.1状态切换与子类停止回调
函数进入后先获取RingBuffer对象锁,再尝试把状态从STARTED切换为STOPPED。
如果当前不是STARTED,函数会继续尝试PAUSED和ERROR状态。若三种状态都不匹配,说明RingBuffer已经停止或无需改变状态,函数直接返回TRUE。
发生状态切换后,基类会唤醒等待中的线程,再调用当前RingBuffer子类的stop虚函数。播放端的通用GstAudioSinkRingBuffer会调用具体Sink的stop方法;没有该方法时会尝试调用reset。录制端的通用GstAudioSrcRingBuffer则会调用具体Source的reset。
如果子类停止回调返回失败,基类会把状态恢复为STARTED,然后返回FALSE。因此,FALSE表示停止动作没有成功完成,不应把它当作普通的“当前状态不是运行中”。
3.2停止与释放、关闭的边界
gst_audio_ring_buffer_stop()只处理运行状态和设备停止动作,不清理RingBuffer已采集的内存,也不关闭设备。
在释放流程中,gst_audio_ring_buffer_release()会先调用gst_audio_ring_buffer_stop(),再进入子类的资源释放回调。设备关闭则要求资源已经释放。这三个动作按职责形成停止、释放、关闭的顺序。
🌻4.实战案例
4.1准备带停止回调的测试RingBuffer
下面的例子定义一个内存型GstAudioRingBuffer子类。它提供打开、采集、启动、停止、释放和关闭回调,不连接真实声卡。停止回调只记录调用次数,用来区分“状态确实发生切换”和“重复停止直接返回”的行为。
4.2启动后调用stop并检查结果
程序先打开设备并采集缓冲资源,再清除Flushing状态、允许RingBuffer启动。调用gst_audio_ring_buffer_start()后,gst_audio_ring_buffer_stop()应调用子类停止回调一次。
程序随后再次调用停止接口。因为状态已经是STOPPED,第二次调用应返回成功,但停止回调次数仍保持为一次。
4.3完整代码
#include<gst/gst.h>#include<gst/audio/audio.h>typedefstruct_TestRingBuffer{GstAudioRingBuffer parent;guint start_calls;guint stop_calls;}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;}/* 为测试RingBuffer申请一段内存 */staticgbooleantest_ring_buffer_acquire(GstAudioRingBuffer*buf,GstAudioRingBufferSpec*spec){if(spec->segsize<=0||spec->segtotal<=0)returnFALSE;buf->size=(gsize)spec->segsize*spec->segtotal;buf->memory=g_malloc0(buf->size);returnTRUE;}/* 记录子类启动回调 */staticgbooleantest_ring_buffer_start(GstAudioRingBuffer*buf){TestRingBuffer*test_buffer=(TestRingBuffer*)buf;test_buffer->start_calls++;returnTRUE;}/* 记录子类停止回调 */staticgbooleantest_ring_buffer_stop(GstAudioRingBuffer*buf){TestRingBuffer*test_buffer=(TestRingBuffer*)buf;test_buffer->stop_calls++;returnTRUE;}/* 释放测试后端申请的内存 */staticgbooleantest_ring_buffer_release(GstAudioRingBuffer*buf){g_free(buf->memory);buf->memory=NULL;buf->size=0;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->start=test_ring_buffer_start;ring_buffer_class->stop=test_ring_buffer_stop;ring_buffer_class->release=test_ring_buffer_release;ring_buffer_class->close_device=test_ring_buffer_close_device;}staticvoidtest_ring_buffer_init(TestRingBuffer*test_buffer){}intmain(intargc,char*argv[]){TestRingBuffer*test_buffer;GstAudioRingBuffer*ring_buffer;GstAudioRingBufferSpec*spec;gboolean opened=FALSE;gboolean acquired=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双声道每个Frame占4字节 */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;/* acquire之前先打开设备 */if(!gst_audio_ring_buffer_open_device(ring_buffer))gotodone;opened=TRUE;/* 采集资源并完成基类的RingBuffer初始化 */if(!gst_audio_ring_buffer_acquire(ring_buffer,spec))gotodone;acquired=TRUE;/* 允许启动,并退出初始化时的Flushing状态 */gst_audio_ring_buffer_may_start(ring_buffer,TRUE);gst_audio_ring_buffer_set_flushing(ring_buffer,FALSE);/* 启动后停止,验证子类stop回调被调用 */if(!gst_audio_ring_buffer_start(ring_buffer))gotodone;if(!gst_audio_ring_buffer_stop(ring_buffer))gotodone;/* 已经停止,再调用一次不会重复触发子类stop回调 */if(!gst_audio_ring_buffer_stop(ring_buffer))gotodone;ok=test_buffer->start_calls==1&&test_buffer->stop_calls==1&&!ring_buffer->acquired;/* 释放阶段会再次确认RingBuffer已停止 */if(acquired){gst_audio_ring_buffer_release(ring_buffer);acquired=FALSE;}done: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_stop()负责把RingBuffer从运行、暂停或错误状态切换到停止状态,并调用子类停止动作,但不负责释放采集资源或关闭设备。