AudioTrack的共享内存
AudioTrack的两种模式
APP给AudioTrack提供音频数据有2种模式:
MODE_STATIC模式
:一次性提前提供数据。使用APP创建共享内存, APP一次性填充数据。playbackthread等数据构造好,取出数据就可以直接使用了,不存在同步问题。对应的playbackThread工作是:获得含有数据的obtainBuffer(APP一次性提交共享内存的数据有可能很多,playbackThread需要进行多次播放)。完成后释放buffer。
MODE_STREAM模式
:边播放边提供。使用playbackThread创建共享内存。APP使用obtainBuffer获得buffer, 填充数据后使用releaseBuffer释放buffer。playbackThread一样需要进行多次播放。只不过这里使用的是环形缓冲区机制来,不断传递数据。完成后释放buffer。
从AudioTrack构造看内存共享
# Java::AudioTrack->Java::native_setup->JNI转换->android_media_AudioTrack_setup
static jint android_media_AudioTrack_setup(....) {
// lyh 两种模式
switch (memoryMode) {
// lyh 写一段播一段
case MODE_STREAM:
status = lpTrack->set(AUDIO_STREAM_DEFAULT,...
// shared mem = 0 内存后面由playbackthread来申请
0,....);
break;
// lyh 一次写完播放
case MODE_STATIC:
// lyh 应用端申请共享内存
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
goto native_init_failure;
}
status = lpTrack->set(AUDIO_STREAM_DEFAULT,...
// shared mem = 应用端创建的共享内存
lpJniStorage->mMemBase,....);
break;
}
}
复制代码
# AudioTrack::set-> AudioTrack::createTrack_l-> Track的创建
//lyh 构建aduiotrack的流程
status_t AudioTrack::createTrack_l() {
sp<IAudioTrack> track = audioFlinger->createTrack(input,
output,
&status);
/* lyh
*获取track变量 的共享内存 buffer
*当PlaybackThread创建一个PlaybackThread::Track对象时,所需的缓冲区空间
*就已经分配了,这块空间是可以跨进程共享的,所以AudioTrack可以通过track->getCblk
*
**/
sp<IMemory> iMem = track->getCblk();
void *iMemPointer = iMem->unsecurePointer();
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
// mSharedBuffer = App端创建的共享内存 = MODE_STATIC
void* buffers;
if (mSharedBuffer == 0) {
buffers = cblk + 1;
} else {
buffers = mSharedBuffer->unsecurePointer();
}
// 创建对应的client代理
if (mSharedBuffer == 0) {
// MODE_STREAM模式
// 指向 playbackthread提供的Buffer
mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
} else {
// MODE_STATIC模式(看名字也知道)
// 指向应用端APP提供的Buffer
mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
mProxy = mStaticProxy;
}
}
复制代码
PlaybackThread的Track建立共享内存
AudioFlinger::ThreadBase::TrackBase::TrackBase(
void *buffer, // 如果应用端创建了内存共享块,就是这个值
size_t bufferSize,
) {
size_t minBufferSize = buffer == NULL ? roundup(frameCount) : frameCount;
if (buffer == nullptr) {
bufferSize = minBufferSize;
}
// lyh 计算结构体的
size_t size = sizeof(audio_track_cblk_t);
if (buffer == NULL && alloc == ALLOC_CBLK) {
size += bufferSize;
}
// lyh 从之前文章流程来看,client是一定有值的
if (client != 0) {
// lyh 分配一块匿名共享内存
mCblkMemory = client->heap()->allocate(size);
} else {
mCblk = (audio_track_cblk_t *) malloc(size);
}
if (mCblk != NULL) {
// lyh 定位创建对象,可以在特定位置创建对象
// lyh 这里就是在匿名共享内存的首地址创建一个audio_track_cblk_t对象
new(mCblk) audio_track_cblk_t();
switch (alloc) {
case ....
case ALLOC_CBLK:
// clear all buffers
// lyh buffer的初始化
if (buffer == NULL) {
// lyh 指向playbackthread提供的Buffer
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
} else {
// lyh 指向APP提供的Buffer
mBuffer = buffer;
}
case ....
}
mBufferSize = bufferSize;
}
复制代码
AudioFlinger::PlaybackThread::Track::Track(...){
// sharedBuffer == 0 -> MODE_STREAM
if (sharedBuffer == 0) {
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
} else {
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, sampleRate);
}
mServerProxy = mAudioTrackServerProxy;
}
复制代码
总的来说
上面的过程就是这两句话
- AudioTrack中使用AudioTrackClientProxy对象 和 StaticAudioTrackClientProxy对象来管理共享内存。
- Track中使用AudioTrackServerProxy对象 和 StaticAudioTrackServerProxy对象 来管理共享内存。
- 环形缓冲区感兴趣可以了解一下,个人感觉就是一个双指针的算法题
工作流程
-
AudioTrack(APP应用端)通过mClientProxy向共享buffer写入数据, AudioFlinger(server端)通过 mServerProxy从共享内存中读出数据。 这样client、server通过proxy对共享内存形成了生产者——消费者模式
-
Client端: AudioTrackClientProxy:: obtainBuffer()从 audio buffer 获取连续的空buffer; AudioTrackClientProxy:: releaseBuffer ()将填充了数据的 buffer 放回 audio buffer。
-
Server端: AudioTrackServerProxy:: obtainBuffer()从 audio buffer 获取连续的填充了数据的 buffer; AudioTrackServerProxy:: releaseBuffer()将使用完的空buffer 放回 audio buffer。
AudioTrack数据的write和play流程
wrtie
obtainBuffer最终是调用AudioTrackClientProxy的方法,主要就是从共享内存中取出空的buffer,将音频数据写入
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
size_t written = 0;
Buffer audioBuffer;
// lyh userSize 用户可用空间
while (userSize >= mFrameSize) {
audioBuffer.frameCount = userSize / mFrameSize;
// lyh 获取buffer中的空余部分
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
if (err < 0) {
if (written > 0) {
break;
}
if (err == TIMED_OUT || err == -EINTR) {
err = WOULD_BLOCK;
}
return ssize_t(err);
}
size_t toWrite = audioBuffer.size;
// lyh 做数据拷贝 保存在i8变量中
memcpy(audioBuffer.i8, buffer, toWrite);
buffer = ((const char *) buffer) + toWrite;
// lyh 剩余可用和已经写入
userSize -= toWrite;
written += toWrite;
// lyh 释放
releaseBuffer(&audioBuffer);
}
return written;
}
复制代码
Play
- 调用流程:从Java层调用Play -> JNI的native_start -> NAtive层面AudioTrack的start -> Binder通信 -> Track的start
- 总结:拿到到playbackThread回放线程,然后添加Track,开启Server端去管理共享内存块,接着通过广播环形Playback::threadloop方法,
// lyh AudioTrack.cpp#mAudioTrack->start方法最后流入这
status_t AudioFlinger::PlaybackThread::Track::start(...)
{
// lyh
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
if (state == PAUSED || state == PAUSING) {
...
} else {
// lyh 状态修改
mState = TrackBase::ACTIVE;
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
// lyh
status = playbackThread->addTrack_l(this);
if (status == NO_ERROR || status == ALREADY_EXISTS) {
// for streaming tracks, remove the buffer read stop limit.
// lyh Server端的Proxy开始管理共享内存
mAudioTrackServerProxy->start();
}
}
return status;
}
复制代码
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
status_t status = ALREADY_EXISTS;
// lyh 是新增的track
if (mActiveTracks.indexOf(track) < 0) {
if (track->isExternalTrack()) {
// lyh 最后调用到AudioPolicyManager方法中
status = AudioSystem::startOutput(track->portId());
// lyh 加入到活跃Track的数组中
mActiveTracks.add(track);
}
// lyh 通过广播唤醒PlaybackThread.threadLoop()
onAddNewTrack_l();
return status;
}
复制代码