一、成员
mReleased
mReleased
是 AudioTrack
中的一个关键状态变量,类型为 Modulo<uint32_t>
(用于处理环形缓冲区的位置回绕)。它记录了 客户端已释放给音频服务器处理的音频帧数量,在流式传输(非静态缓冲区)模式下尤为重要。以下是其核心作用及变化点:
1. 初始化
- 构造函数:
mReleased
初始化为0
。mReleased = 0; // 初始无帧释放
2. 写入数据时更新
write()
方法:
当应用通过write()
写入音频数据时,每次成功写入后调用releaseBuffer()
,增加mReleased
:
3. 播放控制时的重置
stop()
:
停止播放时,若非 offloaded 模式,重置mReleased = 0
(清空进度):if (!isOffloaded_l()) { mState = STATE_STOPPED; mReleased = 0; // 重置释放计数器 }
flush()
:
清空缓冲区时,调用flush_l()
重置状态:void flush_l() { mState = STATE_FLUSHED; mReleased = 0; // 释放帧数归零 }
4. 位置查询与同步
getPosition()
:
通过updateAndGetPosition_l()
计算服务端已消费的帧数时,会参考mReleased
:Modulo<uint32_t> position = updateAndGetPosition_l(); // 依赖 mReleased
- 位置恢复(如轨道重建):
在restoreTrack_l()
中恢复播放位置时,会从之前的mReleased
恢复:if (mStaticProxy == 0) { // 流模式 mPosition = mReleased; // 从释放位置恢复客户端位置 }
5. 状态跟踪与调试
- 日志输出:
stop()
时记录当前释放帧数(用于调试卡顿问题):ALOGD_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 源码为例):
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()
配对使用:AudioTrack::Buffer buffer; obtainBuffer(&buffer, ...); // 获取可写空间 memcpy(buffer.i8, data, size); // 填充数据 releaseBuffer(&buffer); // 提交数据
- 未调用
releaseBuffer()
会导致服务端无法读取新数据,引发播放卡顿或中断。
- 必须与
静态模式(
MODE_STATIC
)- 数据一次性写入,无需多次调用 `releaseBuffer()。
- 仅在初始化时通过
write()
提交全部数据,后续直接play()
即可。
线程安全
- 共享内存控制块通过原子操作和内存屏障保证多线程安全,无需额外锁。
- 若跨线程调用,需确保
AudioTrack
状态一致(如不在stop()
后调用。
五、与 obtainBuffer()
的关系
- 数据流协作
obtainBuffer()
获取可写空间,返回Buffer
结构体(含起始地址和最大容量)。releaseBuffer()
提交实际写入量(若写入量小于申请量,会调整剩余空间。
- 环形缓冲区管理
共享内存控制块(audio_track_cblk_t
)通过以下字段协调读写:mFront
:服务端读位置(由AudioFlinger
更新)。mRear
:客户端写位置(由releaseBuffer()
更新)。mUnderrun
:标志位,缓冲区无数据时置位。
六、总结
- 核心作用:
releaseBuffer()
是流模式下数据提交的终点,通过更新环形缓冲区写指针驱动播放流程。 - 性能影响:延迟调用会导致播放卡顿;过早调用可能覆盖未读取的数据(需结合
obtainBuffer()
精确控制)。 - 最佳实践:
在write()
后立即调用releaseBuffer()
;
静态模式无需主动调用;
避免跨线程操作时状态冲突。