一、成员
mReleased
mReleased 是 AudioTrack 中的一个关键状态变量,类型为 Modulo<uint32_t>(用于处理环形缓冲区的位置回绕)。它记录了 客户端已释放给音频服务器处理的音频帧数量,在流式传输(非静态缓冲区)模式下尤为重要。以下是其核心作用及变化点:
1. 初始化
-
构造函数 :
mReleased初始化为0。cppmReleased = 0; // 初始无帧释放
2. 写入数据时更新
write()方法 :
当应用通过write()写入音频数据时,每次成功写入后调用releaseBuffer(),增加mReleased:
3. 播放控制时的重置
-
stop():
停止播放时,若非 offloaded 模式,重置mReleased = 0(清空进度):cppif (!isOffloaded_l()) { mState = STATE_STOPPED; mReleased = 0; // 重置释放计数器 } -
flush():
清空缓冲区时,调用flush_l()重置状态:cppvoid flush_l() { mState = STATE_FLUSHED; mReleased = 0; // 释放帧数归零 }
4. 位置查询与同步
-
getPosition():
通过updateAndGetPosition_l()计算服务端已消费的帧数时,会参考mReleased:cppModulo<uint32_t> position = updateAndGetPosition_l(); // 依赖 mReleased -
位置恢复(如轨道重建) :
在restoreTrack_l()中恢复播放位置时,会从之前的mReleased恢复:cppif (mStaticProxy == 0) { // 流模式 mPosition = mReleased; // 从释放位置恢复客户端位置 }
5. 状态跟踪与调试
-
日志输出 :
stop()时记录当前释放帧数(用于调试卡顿问题):cppALOGD_IF(mSharedBuffer == nullptr, "%s(%d): called with %u frames delivered", __func__, mPortId, mReleased.value());
6.关键场景总结
| 操作 | mReleased 变化 |
说明 |
|---|---|---|
| 初始化 | 设为 0 |
初始状态 |
成功写入数据 (write()) |
增加写入帧数 | 表示数据已提交给服务端 |
停止 (stop()) |
非 offloaded 模式时重置为 0 |
播放停止,进度清零 |
清空 (flush()) |
重置为 0 |
缓冲区清空,丢弃所有数据 |
| 轨道恢复 | 从之前保存的值恢复 | 服务崩溃后恢复播放位置 |
7.重点
- 进度同步 :
mReleased是客户端与服务端之间的"数据移交点",确保双方对已处理数据量达成一致。 - 流控基础 :
结合mFrameCount(缓冲区大小)和mPosition(服务端消费位置),实现写入阻塞(避免溢出)。 - 状态恢复 :
在音频服务重启或轨道重建时,通过mReleased恢复播放进度,保证连续性。
注 :在静态缓冲区模式(
mSharedBuffer != 0)下,mReleased作用较弱,因为数据一次性加载,无需流式更新。
二、成员函数
releaseBuffer()
是 Android AudioTrack 中管理共享内存缓冲区的核心方法,主要用于更新客户端写入位置 和通知服务端有新数据可读。
一、核心作用
-
更新环形缓冲区写指针
- 在
MODE_STREAM模式下,AudioTrack与AudioFlinger通过环形缓冲区传递数据。 - 当应用通过
write()写入数据后,需调用releaseBuffer()更新缓冲区的写指针位置 (mReleased变量),标记已写入的数据范围。
- 在
-
触发服务端数据消费
releaseBuffer()内部通过mProxy->releaseBuffer()修改共享内存控制块(audio_track_cblk_t)的状态,通知AudioFlinger的PlaybackThread有新数据待读取。
二、内部工作流程
以下为 releaseBuffer() 的典型执行流程(以 Android 源码为例):
cpp
void AudioTrack::releaseBuffer(const Buffer* audioBuffer) {
// 1. 计算本次写入的帧数
size_t stepCount = audioBuffer->size / mFrameSize;
// 2. 封装缓冲区信息
Proxy::Buffer buffer;
buffer.mFrameCount = stepCount;
buffer.mRaw = audioBuffer->raw;
// 3. 通过代理更新共享内存控制块
mProxy->releaseBuffer(&buffer);
}
-
帧数计算
stepCount = size / mFrameSize确定本次写入的音频帧数量 (mFrameSize由采样位宽和声道数决定)[citation:4][citation:6]。 -
代理操作
mProxy是AudioTrackClientProxy(流模式)或StaticAudioTrackClientProxy(静态模式)的实例。- 调用其
releaseBuffer()会更新共享内存中的u.mStreaming.mRear指针 (环形缓冲区的写位置),并清除UNDERRUN标志[citation:8]。
-
服务端唤醒
若
AudioFlinger的播放线程因缓冲区无数据而阻塞,写指针更新会触发其唤醒并读取新数据[citation:2][citation:5]。
三、关键参数解析
| 参数 | 作用 |
|---|---|
audioBuffer->size |
本次写入的字节数,需为帧大小的整数倍(否则截断处理)[citation:6]。 |
mFrameSize |
单帧大小(字节)= 采样位宽(如16bit=2字节) × 声道数(如双声道=2)[citation:6]。 |
mProxy |
代理对象,封装了共享内存操作(如指针更新、状态同步)[citation:8]。 |
四、使用场景与注意事项
-
流模式(
MODE_STREAM)-
必须与
obtainBuffer()配对使用 :cppAudioTrack::Buffer buffer; obtainBuffer(&buffer, ...); // 获取可写空间 memcpy(buffer.i8, data, size); // 填充数据 releaseBuffer(&buffer); // 提交数据 -
未调用
releaseBuffer()会导致服务端无法读取新数据,引发播放卡顿或中断。
-
-
静态模式(
MODE_STATIC)- 数据一次性写入,无需多次调用 `releaseBuffer()。
- 仅在初始化时通过
write()提交全部数据,后续直接play()即可。
-
线程安全
- 共享内存控制块通过原子操作和内存屏障保证多线程安全,无需额外锁。
- 若跨线程调用,需确保
AudioTrack状态一致(如不在stop()后调用。
五、与 obtainBuffer() 的关系
- 数据流协作
获取空闲缓冲区 更新写指针 obtainBuffer write数据 releaseBuffer AudioFlinger消费数据
obtainBuffer()获取可写空间,返回Buffer结构体(含起始地址和最大容量)。releaseBuffer()提交实际写入量(若写入量小于申请量,会调整剩余空间。
- 环形缓冲区管理
共享内存控制块(audio_track_cblk_t)通过以下字段协调读写:mFront:服务端读位置(由AudioFlinger更新)。mRear:客户端写位置(由releaseBuffer()更新)。mUnderrun:标志位,缓冲区无数据时置位。
六、总结
- 核心作用 :
releaseBuffer()是流模式下数据提交的终点,通过更新环形缓冲区写指针驱动播放流程。 - 性能影响 :延迟调用会导致播放卡顿;过早调用可能覆盖未读取的数据(需结合
obtainBuffer()精确控制)。 - 最佳实践 :
在write()后立即调用releaseBuffer();
静态模式无需主动调用;
避免跨线程操作时状态冲突。