上篇(Android音频录制核心:RecordThread::threadLoop解析)讲到在 Android AudioFlinger 的录音通路(RecordThread)中,存在两个层级的环形缓冲区,下面对第二个环形缓冲区(Track应用读取的缓冲区)写入数据的核心代码进行分析;关键在于getNextBuffer是如何拿到activeTrack->sinkBuffer() 。
代码追踪:
frameworks/av/services/audioflinger/Threads.cpp
-->bool RecordThread::threadLoop()
-->status_t status = activeTrack->getNextBuffer(&activeTrack->sinkBuffer());
frameworks/av/services/audioflinger/Tracks.cpp
-->status_t RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
-->mServerProxy->obtainBuffer(&buf);
frameworks/av/media/libaudioclient/AudioTrackShared.cpp
-->status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
追到在ServerProxy::obtainBuffer()中获取到buffer指针;ServerProxy 是 Android AudioFlinger 共享内存缓冲区(环形buffer)的服务端代理,负责音频服务端(AudioFlinger/RecordThread 等)与客户端(AudioTrack/AudioRecord)之间的数据交互。
obtainBuffer(Buffer_ buffer, bool ackFlush)用于服务端获取一段可用的 buffer 区域(写入或读取),并返回其内存指针与可用帧数(frameCount)。写入指的是录音,读取则是播放。
这是“生产者-消费者”模式的核心函数之一,保证多线程并发安全与高效。
下面对ServerProxy::obtainBuffer 函数是如何得到buffer,进行详细分步解析,重点说明其数据结构、流程、同步机制、以及每个关键变量和分支的作用,Record和Playback都会调用到这个函数,所以会一并讲解,分析Playback时也可以阅读此文。
源码:
status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
"%s: null or zero frame buffer, buffer:%p", __func__, buffer);
if (mIsShutdown) {
goto no_init;
}
{
audio_track_cblk_t* cblk = mCblk;
// compute number of frames available to write (AudioTrack) or read (AudioRecord),
// or use previous cached value from framesReady(), with added barrier if it omits.
int32_t front;
int32_t rear;
// See notes on barriers at ClientProxy::obtainBuffer()
if (mIsOut) {
flushBufferIfNeeded(); // might modify mFront
rear = getRear();
front = cblk->u.mStreaming.mFront;
} else {
front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
rear = cblk->u.mStreaming.mRear;
}
ssize_t filled = audio_utils::safe_sub_overflow(rear, front);
// pipe should not already be overfull
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); shutting down",
filled, mFrameCount);
mIsShutdown = true;
}
if (mIsShutdown) {
goto no_init;
}
// don't allow filling pipe beyond the nominal size
size_t availToServer;
if (mIsOut) {
availToServer = filled;
mAvailToClient = mFrameCount - filled;
} else {
availToServer = mFrameCount - filled;
mAvailToClient = filled;
}
// 'availToServer' may be non-contiguous, so return only the first contiguous chunk
size_t part1;
if (mIsOut) {
front &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - front;
} else {
rear &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - rear;
}
if (part1 > availToServer) {
part1 = availToServer;
}
size_t ask = buffer->mFrameCount;
if (part1 > ask) {
part1 = ask;
}
// is assignment redundant in some cases?
buffer->mFrameCount = part1;
buffer->mRaw = part1 > 0 ?
&((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
buffer->mNonContig = availToServer - part1;
// After flush(), allow releaseBuffer() on a previously obtained buffer;
// see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp.
if (!ackFlush) {
mUnreleased = part1;
}
return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
}
no_init:
buffer->mFrameCount = 0;
buffer->mRaw = NULL;
buffer->mNonContig = 0;
mUnreleased = 0;
return NO_INIT;
}
1. 参数说明
Buffer buffer
要获取的buffer描述结构体(in/out),输入时mFrameCount为你申请的帧数,输出时mRaw指向实际可用内存,mFrameCount返回实际可用帧数。
bool ackFlush
是否在flush场景下特殊处理(通常为false,特殊情况下为true)。
2. 函数分步详解
A. 入参校验
LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
"%s: null or zero frame buffer, buffer:%p", __func__, buffer);
buffer指针非空且申请帧数>0,否则crash。
B. 是否已shutdown
if (mIsShutdown) {
goto no_init;
}
如果ServerProxy已被判定为异常(如shm损坏等),直接返回NO_INIT。
C. 读写指针与可用帧数计算
audio_track_cblk_t* cblk = mCblk;
int32_t front;
int32_t rear;
cblk是共享内存控制块,管理buffer的front(读指针)和rear(写指针),这里拿到线程的全局mCblk。
1. 方向判定(mIsOut)
mIsOut==true:AudioTrack(播放流),服务端写入,客户端读取;
mIsOut==false:AudioRecord(录音流),服务端读取,客户端写入。
2. 获取front和rear指针
mIsOut==true(播放)
先调用flushBufferIfNeeded()处理flush场景;
rear = getRear()(服务端写指针),
front = cblk->u.mStreaming.mFront(客户端读指针
mIsOut==false(录音)
front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront)(服务端读指针);
rear = cblk->u.mStreaming.mRear(客户端写指针)。
3. 计算 filled
ssize_t filled = audio_utils::safe_sub_overflow(rear, front);
表示buffer中已有的数据量(帧数)。
播放流:filled=rear-front,表示还有多少数据客户端未消费;
录音流:filled=rear-front,表示客户端写入而服务端还未读取的数据量。
4. 检查buffer完整性
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
ALOGE(...);
mIsShutdown = true;
}
if (mIsShutdown) {
goto no_init;
}
filled必须在[0, mFrameCount]之间,否则buffer指针错乱,判定为损坏、shutdown。
D. 计算服务端和客户端各自可用空间
size_t availToServer;
if (mIsOut) {
availToServer = filled;
mAvailToClient = mFrameCount - filled;
} else {
availToServer = mFrameCount - filled;
mAvailToClient = filled;
}
mIsOut==true(播放流):服务端可用=filled,客户端可用=mFrameCount-filled
mIsOut==false(录音流):服务端可用=mFrameCount-filled,客户端可用=filled
解释
“服务端可用”:服务端本次最多能写/读多少帧
“客户端可用”:客户端还能写/还能读多少帧
E. 计算首段(part1)可用帧数
size_t part1;
if (mIsOut) {
front &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - front;
} else {
rear &= mFrameCountP2 - 1;
part1 = mFrameCountP2 - rear;
}
if (part1 > availToServer) {
part1 = availToServer;
}
size_t ask = buffer->mFrameCount;
if (part1 > ask) {
part1 = ask;
}
环形缓冲区可能“环绕”,首段最大容量为 mFrameCountP2-front/rear 到buffer尾部的距离。
取 min(实际可用, 申请帧数) 作为本次可操作的最大帧数。
part1 表示本次能连续操作(不跨环绕)的最大帧数。
F. 填充输出buffer结构体
buffer->mFrameCount = part1;
buffer->mRaw = part1 > 0 ?
&((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
buffer->mNonContig = availToServer - part1;
mFrameCount:本次可操作的帧数
mRaw:实际内存指针
mNonContig:如果数据分两段(环绕),第二段的帧数
G. flush处理与mUnreleased设置
if (!ackFlush) {
mUnreleased = part1;
}
H. 返回值处理
return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
有数据可操作返回NO_ERROR,否则WOULD_BLOCK(需要等待)。
I. 错误/未初始化
no_init:
buffer->mFrameCount = 0;
buffer->mRaw = NULL;
buffer->mNonContig = 0;
mUnreleased = 0;
return NO_INIT;
如果初始化失败或已shutdown,清空输出buffer并返回NO_INIT。
4. 整体流程总结
1. 校验参数、Shutdown检测
2. 读取共享内存区front/rear指针
3. 计算缓冲区内已用/可用空间
4. 判断缓冲区是否损坏
5. 计算本次可操作的最大帧数和内存指针
6. 输出buffer结构,返回给调用者
7. 错误处理/特殊场景处理
5. 环形缓冲区“首段”原理图
假设buffer容量8,front=6,rear=2(录音流):
[0][1][2][3][4][5][6][7]
^ ^
rear front
此时part1 = 8 - rear = 6
如果availToServer=3,则part1=3
7. 实际用途
对于AudioRecord(录音流),服务端(AudioFlinger)调用 obtainBuffer 获取可读的buffer区域,从mRaw读取数据;
对于AudioTrack(播放流),服务端获取可写buffer区域,把下发给HAL的数据写入mRaw。
8. 小结与重点
obtainBuffer是环形缓冲区多线程安全读写的关键函数。
它保证了读写指针正确、环形结构下的连续/非连续段处理、并发时内存可见性。也是音频系统“为什么有两个环形buffer的重要机制基础。
下一篇重点讲解上层应用是如何一步一步,最后拿到录到的音频数据的。
3万+

被折叠的 条评论
为什么被折叠?



