文转载自zyuanyun的博客,原文地址为:https://blog.csdn.net/zyuanyun/article/details/60890534 转载请注明作者及原文链接。 4. AudioTrack 实例创建现在我们开始分析 AudioTrack 的创建过程,特别留意 AudioTrack 与 AudioFlinger 如何建立联系、用于 AudioTrack 与 AudioFlinger 交换数据的匿名共享内存如何分配。 4.1. AudioTrack & AudioFlinger 相关类首先看一下 AudioTrack & AudioFlinger 的类图,理一下 AudioFlinger 的主要类及其关系、AudioTrack 与 AudioFlinger 之间的联系,后面将以该图为脉络展开分析。
audio_io_handle_t: 这里再详细说明一下 audio_io_handle_t,它是 AudioTrack/AudioRecord/AudioSystem、AudioFlinger、AudioPolicyManager 之间一个重要的链结点。3.4. AudioFlinger 回放录制线程 小节在 AudioFlinger::openOutput_l() 注释中大致说明了它的来历及其作用,现在回顾下:当打开输出流设备及创建 PlaybackThread 时,系统会分配一个全局唯一的值作为 audio_io_handle_t,并把 audio_io_handle_t 和 PlaybackThread 添加到键值对向量 mPlaybackThreads 中,由于 audio_io_handle_t 和 PlaybackThread 是一一对应的关系,因此拿到一个 audio_io_handle_t,就能遍历键值对向量 mPlaybackThreads 找到它对应的 PlaybackThread,可以简单理解 audio_io_handle_t 为 PlaybackThread 的索引号或线程 id。由于 audio_io_handle_t 具有 PlaybackThread 索引特性,所以应用进程想获取 PlaybackThread 某些信息的话,只需要传入对应的 audio_io_handle_t 即可。例如 AudioFlinger::format(audio_io_handle_t output),这是 AudioFlinger 的一个服务接口,用户进程可以通过该接口获取某个 PlaybackThread 配置的音频格式: audio_format_t AudioFlinger::format(audio_io_handle_t output) const{ Mutex::Autolock _l(mLock); // checkPlaybackThread_l() 根据传入的 audio_io_handle_t,从键值对向量 // mPlaybackThreads 中找到它对应的 PlaybackThread PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { ALOGW("format() unknown thread %d", output); return AUDIO_FORMAT_INVALID; } return thread->format();}AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const{ return mPlaybackThreads.valueFor(output).get();} 4.2. AudioTrack 构造过程当我们构造一个 AudioTrack 实例时(以 MODE_STREAM/TRANSFER_SYNC 模式为例,这也是最常用的模式了,此时 sharedBuffer 为空),系统都发生了什么事?阐述下大致流程: 1. 如果 cbf(audioCallback 回调函数)非空,那么创建 AudioTrackThread 线程处理 audioCallback 回调函数(MODE_STREAM 模式时,cbf 为空); 2. 根据 streamType(流类型)、flags(输出标识)等参数调用 AudioSystem::getOutputForAttr();经过一系列的调用,进入 AudioPolicyManager::getOutputForDevice(): 1. 如果输出标识置了 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 或 AUDIO_OUTPUT_FLAG_DIRECT,那么最终调用 AudioFlinger::openOutput() 打开输出标识对应的输出流设备并创建相应的 PlaybackThread,保存该 PlaybackThread 对应的 audio_io_handle_t 给 AudioTrack; 2. 如果输出标识是其他类型,那么根据策略选择一个输出流设备和 PlaybackThread,并保存该 PlaybackThread 对应的 audio_io_handle_t 给 AudioTrack;别忘了在 3.4. AudioFlinger 回放录制线程 小节中提到:系统启动时,就已经打开 primary_out、low_latency、deep_buffer 这三种输出流设备,并创建对应的 PlaybackThread 了; 3. 通过 Binder 机制调用 AudioFlinger::createTrack()(注意 step2 中 AudioTrack 已经拿到一个 audio_io_handle_t 了,此时把这个 audio_io_handle_t 传入给 createTrack()): 1. 根据传入的 audio_io_handle_t 找到它对应的 PlaybackThread; 2. PlaybackThread 新建一个音频流管理对象 Track;Track 构造时会分配一块匿名共享内存用于 AudioFlinger 与 AudioTrack 的数据交换缓冲区(FIFO)及其控制块(audio_track_cblk_t),并创建一个 AudioTrackServerProxy 对象(PlaybackThread 将使用它从 FIFO 上取得可读数据的位置); 3. 最后新建一个 Track 的通讯代理 TrackHandle,并以 IAudioTrack 作为返回值给 AudioTrack(TrackHandle、BnAudioTrack、BpAudioTrack、IAudioTrack 的关系见上一个小节); 4. 通过 IAudioTrack 接口,取得 AudioFlinger 中的 FIFO 控制块(audio_track_cblk_t),由此再计算得到 FIFO 的首地址; 5. 创建一个 AudioTrackClientProxy 对象(AudioTrack 将使用它从 FIFO 上取得可用空间的位置); AudioTrack 由此建立了和 AudioFlinger 的全部联系工作:
构造 1 个 AudioTrack 实例时,AudioFlinger 会有 1 个 PlaybackThread 实例、1 个 Track 实例、1 个 TrackHandle 实例、1 个 AudioTrackServerProxy 实例、1 块 FIFO 与之对应。 当同时构造 1 个 AudioTrack with AUDIO_OUTPUT_FLAG_PRIMARY、1 个 AudioTrack with AUDIO_OUTPUT_FLAG_FAST、3 个 AudioTrack with AUDIO_OUTPUT_FLAG_DEEP_BUFFER、1 个 AudioTrack with AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD、1 个 AudioTrack with AUDIO_OUTPUT_FLAG_DIRECT 时(事实上,Android 音频策略不允许出现这种情形的),AudioFlinger 拥有的 PlaybackThread、Track、TrackHandle 实例如下图所示: 最后附上相关代码的流程分析,我本意是不多贴代码的,但不上代码总觉得缺点什么,这里我尽量把代码精简,提取主干,忽略细节。 AudioTrack::AudioTrack( audio_stream_type_t streamType, // 音频流类型:如 Music、Voice-Call、DTMF、Alarm 等等 uint32_t sampleRate, // 采样率:如 16KHz、44.1KHz、48KHz 等等 audio_format_t format, // 音频格式:如 PCM、MP3、AAC 等等 audio_channel_mask_t channelMask, // 声道数:如 Mono(单声道)、Stereo(双声道) const sp<IMemory>& sharedBuffer, // 共享内存缓冲区:数据模式是 MODE_STATIC 时使用,数据模式是 MODE_STREAM 时为空 audio_output_flags_t flags, // 输出标识位,详见 AUDIO_OUTPUT_FLAG 描述 callback_t cbf, // 回调函数 void* user, // 回调函数的参数 uint32_t notificationFrames, int sessionId, transfer_type transferType, // 数据传输类型 const audio_offload_info_t *offloadInfo, int uid, pid_t pid, const audio_attributes_t* pAttributes, bool doNotReconnect) : mStatus(NO_INIT), mIsTimed(false), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE){ mStatus = set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, uid, pid, pAttributes, doNotReconnect);}status_t AudioTrack::set( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, audio_output_flags_t flags, callback_t cbf, void* user, uint32_t notificationFrames, const sp<IMemory>& sharedBuffer, bool threadCanCallJava, int sessionId, transfer_type transferType, const audio_offload_info_t *offloadInfo, int uid, pid_t pid, const audio_attributes_t* pAttributes, bool doNotReconnect){ // 参数格式合法性检查、音轨音量初始化 // 如果 cbf 非空,那么创建 AudioTrackThread 线程处理 audioCallback 回调函数 if (cbf != NULL) { mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); // thread begins in paused state, and will not reference us until start() } // create the IAudioTrack status_t status = createTrack_l(); //......}status_t AudioTrack::createTrack_l(){ // 获取 IAudioFlinger,通过 binder 请求 AudioFlinger 服务 const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("Could not get audioflinger"); return NO_INIT; } //...... // AudioSystem::getOutputForAttr() 经过一系列的调用,进入 AudioPolicyManager::getOutputForDevice() // 如果输出标识置了 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 或 AUDIO_OUTPUT_FLAG_DIRECT, // 那么最终调用 AudioFlinger::openOutput() 打开输出标识对应的输出流设备并创建相关的 // PlaybackThread,保存该 PlaybackThread 对应的 audio_io_handle_t 给 AudioTrack; // 如果输出标识是其他类型,那么根据策略选择一个输出流设备和 PlaybackThread,并保存该 // PlaybackThread 对应的 audio_io_handle_t 给 AudioTrack audio_io_handle_t output; status = AudioSystem::getOutputForAttr(attr, &output, (audio_session_t)mSessionId, &streamType, mClientUid, mSampleRate, mFormat, mChannelMask, mFlags, mSelectedDeviceId, mOffloadInfo); //...... // 向 AudioFlinger 发出 createTrack 请求 sp<IAudioTrack> track = audioFlinger->createTrack(streamType, mSampleRate, mFormat, mChannelMask, &temp, &trackFlags, mSharedBuffer, output, tid, &mSessionId, mClientUid, &status); //...... // AudioFlinger 创建 Track 对象时会分配一个 FIFO,这里获取 FIFO 的控制块 sp<IMemory> iMem = track->getCblk(); if (iMem == 0) { ALOGE("Could not get control block"); return NO_INIT; } // 匿名共享内存首地址 void *iMemPointer = iMem->pointer(); if (iMemPointer == NULL) { ALOGE("Could not get control block pointer"); return NO_INIT; } mAudioTrack = track; // 保存 AudioFlinger::PlaybackThread::Track 的代理对象 IAudioTrack mCblkMemory = iMem; // 保存匿名共享内存首地址 // 控制块位于 AudioFlinger 分配的匿名共享内存的首部 audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer); mCblk = cblk; mOutput = output; // 保存返回的 audio_io_handle_t,用它可以找到对应的 PlaybackThread //...... // update proxy if (mSharedBuffer == 0) { // 当 mSharedBuffer 为空,意味着音轨数据模式为 MODE_STREAM,那么创建 AudioTrackClientProxy 对象 mStaticProxy.clear(); mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); } else { // 当 mSharedBuffer 非空,意味着音轨数据模式为 MODE_STATIC,那么创建 StaticAudioTrackClientProxy 对象 mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize); mProxy = mStaticProxy; } //......} AudioFlinger::createTrack(),顾名思义,创建一个 Track 对象,将用于音频流的控制: sp<IAudioTrack> AudioFlinger::createTrack( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t *frameCount, IAudioFlinger::track_flags_t *flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, pid_t tid, int *sessionId, int clientUid, status_t *status){ sp<PlaybackThread::Track> track; sp<TrackHandle> trackHandle; sp<Client> client; status_t lStatus; int lSessionId; //...... { Mutex::Autolock _l(mLock); // 根据传入来的 audio_io_handle_t,找到对应的 PlaybackThread PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { ALOGE("no playback thread found for output handle %d", output); lStatus = BAD_VALUE; goto Exit; } //...... // 在 PlaybackThread 上创建一个音频流管理对象 Track track = thread->createTrack_l(client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus); //...... setAudioHwSyncForSession_l(thread, (audio_session_t)lSessionId); } //...... // 创建 Track 的通讯代理 TrackHandle 并返回它 trackHandle = new TrackHandle(track);Exit: *status = lStatus; return trackHandle;}sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( const sp<AudioFlinger::Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t *pFrameCount, const sp<IMemory>& sharedBuffer, int sessionId, IAudioFlinger::track_flags_t *flags, pid_t tid, int uid, status_t *status){ size_t frameCount = *pFrameCount; sp<Track> track; status_t lStatus; bool isTimed = (*flags & IAudioFlinger::TRACK_TIMED) != 0; // ...... { // scope for mLock Mutex::Autolock _l(mLock); // ...... if (!isTimed) { // 创建 Track,等会再看看 Track 构造函数干些啥 track = new Track(this, client, streamType, sampleRate, format, channelMask, frameCount, NULL, sharedBuffer, sessionId, uid, *flags, TrackBase::TYPE_DEFAULT); } else { // 创建 TimedTrack,带时间戳的 Track?这里不深究 track = TimedTrack::create(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, uid); } // ...... // 把创建的 Track 添加到 mTracks 向量中,方便 PlaybackThread 统一管理 mTracks.add(track); // ...... } lStatus = NO_ERROR;Exit: *status = lStatus; return track;}// ----------------------------------------------------------------------------// 如下是 TrackHandle 的相关代码,可以看到,TrackHandle 其实就是一个壳子,是 Track 的包装类// 所有 TrackHandle 接口都是调向 Track 的// Google 为什么要搞这么一则?Track 是 PlaybackThread 内部使用的,不适宜对外暴露,但应用进程// 又确实需要控制音频流的状态(start、stop、pause),所以就采取这么一种方式实现AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) : BnAudioTrack(), mTrack(track){}AudioFlinger::TrackHandle::~TrackHandle() { // just stop the track on deletion, associated resources // will be freed from the main thread once all pending buffers have // been played. Unless it's not in the active track list, in which // case we free everything now... mTrack->destroy();}sp<IMemory> AudioFlinger::TrackHandle::getCblk() const { return mTrack->getCblk();}status_t AudioFlinger::TrackHandle::start() { return mTrack->start();}void AudioFlinger::TrackHandle::stop() { mTrack->stop();}void AudioFlinger::TrackHandle::flush() { mTrack->flush();}void AudioFlinger::TrackHandle::pause() { mTrack->pause();}// ---------------------------------------------------------------------------- 最后,我们看看 Track 的构造过程,主要分析数据 FIFO 及它的控制块是如何分配的: AudioFlinger::PlaybackThread::Track::Track( PlaybackThread *thread, const sp<Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, void *buffer, const sp<IMemory>& sharedBuffer, int sessionId, int uid, IAudioFlinger::track_flags_t flags, track_type type) : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer, sessionId, uid, flags, true /*isOut*/, (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK, type), mFillingUpStatus(FS_INVALID), // mRetryCount initialized later when needed mSharedBuffer(sharedBuffer), mStreamType(streamType), mName(-1), // see note below mMainBuffer(thread->mixBuffer()), mAuxBuffer(NULL), mAuxEffectId(0), mHasVolumeController(false), mPresentationCompleteFrames(0), mFastIndex(-1), mCachedVolume(1.0), mIsInvalid(false), mAudioTrackServerProxy(NULL), mResumeToStopping(false), mFlushHwPending(false){ // client == 0 implies sharedBuffer == 0 ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); // 检查 FIFO 控制块(audio_track_cblk_t)是否分配好了,上面代码并未分配 audio_track_cblk_t // 因此只可能是构造 TrackBase 时分配的,等下再看看 TrackBase 的构造函数 if (mCblk == NULL) { return; } if (sharedBuffer == 0) { // 数据传输模式为 MODE_STREAM 模式,创建一个 AudioTrackServerProxy 对象 // PlaybackThread 将持续使用它从 FIFO 上取得可读数据的位置 mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, mFrameSize, !isExternalTrack(), sampleRate); } else { // 数据传输模式为 MODE_STATIC 模式,创建一个 StaticAudioTrackServerProxy 对象 mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, mFrameSize); } mServerProxy = mAudioTrackServerProxy; // 为 Track 分配一个名称,AudioMixer 会根据 TrackName 找到对应的 Track mName = thread->getTrackName_l(channelMask, format, sessionId); if (mName < 0) { ALOGE("no more track names available"); return; } // ......}AudioFlinger::ThreadBase::TrackBase::TrackBase( ThreadBase *thread, const sp<Client>& client, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, void *buffer, int sessionId, int clientUid, IAudioFlinger::track_flags_t flags, bool isOut, alloc_type alloc, track_type type) : RefBase(), mThread(thread), mClient(client), mCblk(NULL), // mBuffer mState(IDLE), mSampleRate(sampleRate), mFormat(format), mChannelMask(channelMask), mChannelCount(isOut ? audio_channel_count_from_out_mask(channelMask) : audio_channel_count_from_in_mask(channelMask)), mFrameSize(audio_is_linear_pcm(format) ? mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)), mFrameCount(frameCount), mSessionId(sessionId), mFlags(flags), mIsOut(isOut), mServerProxy(NULL), mId(android_atomic_inc(&nextTrackId)), mTerminated(false), mType(type), mThreadIoHandle(thread->id()){ // ...... // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); size_t size = sizeof(audio_track_cblk_t); size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize; if (buffer == NULL && alloc == ALLOC_CBLK) { // 这个 size 将是分配的匿名共享内存的大小 // 等于控制块的大小(sizeof(audio_track_cblk_t)加上数据 FIFO的大小(bufferSize) // 待会看到这块内存的结构,就明白这样分配的意义了 size += bufferSize; } if (client != 0) { // 分配一块匿名共享内存 mCblkMemory = client->heap()->allocate(size); if (mCblkMemory == 0 || (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) { ALOGE("not enough memory for AudioTrack size=%u", size); client->heap()->dump("AudioTrack"); mCblkMemory.clear(); return; } } else { // this syntax avoids calling the audio_track_cblk_t constructor twice mCblk = (audio_track_cblk_t *) new uint8_t[size]; // assume mCblk != NULL } // construct the shared structure in-place. if (mCblk != NULL) { // 这是 C++ 的 placement new(定位创建对象)语法:new(@BUFFER) @CLASS(); // 可以在特定内存位置上构造一个对象 // 这里,在匿名共享内存首地址上构造了一个 audio_track_cblk_t 对象 // 这样 AudioTrack 与 AudioFlinger 都能访问这个 audio_track_cblk_t 对象了 new(mCblk) audio_track_cblk_t(); // 如下分配数据 FIFO,将用于 AudioTrack 与 AudioFlinger 的数据交换 switch (alloc) { // ...... case ALLOC_CBLK: // clear all buffers if (buffer == NULL) { // 数据传输模式为 MODE_STREAM/TRANSFER_SYNC 时,数据 FIFO 的分配 // 数据 FIFO 的首地址紧靠控制块(audio_track_cblk_t)之后 // | | // | -------------------> mCblkMemory <--------------------- | // | | // +--------------------+------------------------------------+ // | audio_track_cblk_t | Buffer | // +--------------------+------------------------------------+ // ^ ^ // | | // mCblk mBuffer mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, bufferSize); } else { // 数据传输模式为 MODE_STATIC/TRANSFER_SHARED 时,直接指向 sharedBuffer // sharedBuffer 是应用进程分配的匿名共享内存,应用进程已经一次性把数据 // 写到 sharedBuffer 来了,AudioFlinger 可以直接从这里读取 // +--------------------+ +-----------------------------------+ // | audio_track_cblk_t | | sharedBuffer | // +--------------------+ +-----------------------------------+ // ^ ^ // | | // mCblk mBuffer mBuffer = buffer; } break; // ...... } // ...... }} 5. AudioTrack 数据写入AudioTrack 实例构造后,应用程序接着可以写入音频数据了。如之前所描述:AudioTrack 与 AudioFlinger 是 生产者-消费者 的关系:
上面的过程中,如果 AudioTrack 总能及时生产数据,并且 AudioFlinger 总能及时消耗掉这些数据,那么整个过程将是非常和谐的;但系统可能会发生异常,出现如下的状态:
5.1. AudioTrack 写数据流程我们看一下 AudioTrack 写数据的代码,流程很简单:obtainBuffer() 在 FIFO 中找到一块可用区间,memcpy() 把用户传入的音频数据拷贝到这个可用区间上,releaseBuffer() 更新写位置。 ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking){ if (mTransfer != TRANSFER_SYNC) { return INVALID_OPERATION; } if (isDirect()) { AutoMutex lock(mLock); int32_t flags = android_atomic_and( ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->mFlags); if (flags & CBLK_INVALID) { return DEAD_OBJECT; } } if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // Sanity-check: user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("AudioTrack::write(buffer=%p, size=%zu (%zd)", buffer, userSize, userSize); return BAD_VALUE; } size_t written = 0; Buffer audioBuffer; while (userSize >= mFrameSize) { // 单帧数据量 frameSize = channelCount * bytesPerSample // 对于双声道,16位采样的音频数据来说,frameSize = 2 * 2 = 4(bytes) // 用户传入的数据帧数 frameCount = userSize / frameSize audioBuffer.frameCount = userSize / mFrameSize; // obtainBuffer() 从 FIFO 上得到一块可用区间 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); } // toWrite 是 FIFO 可用区间的大小,可能比 userSize(用户传入数据的大小)要小 // 因此用户传入的数据可能要拆分多次拷贝到 FIFO 上 // 注意:AudioTrack 和 AudioFlinger 是不同的进程,AudioFlinger 同时也在不停地 // 消耗数据,所以 FIFO 可用区间是在不停变化的 size_t toWrite = audioBuffer.size; memcpy(audioBuffer.i8, buffer, toWrite); // 把用户数据拷贝到 FIFO 可用区间 buffer = ((const char *) buffer) + toWrite; // 未拷贝数据的位置 userSize -= toWrite; // 未拷贝数据的大小 written += toWrite; // 已拷贝数据的大小 // releaseBuffer() 更新 FIFO 写位置 // 对于 AudioFinger 来说,意味 FIFO 上有更多的可读数据 releaseBuffer(&audioBuffer); } if (written > 0) { mFramesWritten += written / mFrameSize; } return written;} 5.2. AudioFlinger 读数据流程AudioFlinger 消费数据的流程稍微复杂一点,3.4. AudioFlinger 回放录制线程 小节中描述了 AudioFlinger::PlaybackThread::threadLoop() 工作流程,这里不累述了,我们把焦点放在“如何从 FIFO 读取数据”节点上。 我们以 DirectOutputThread/OffloadThread 为例说明(MixerThread 读数据也是类似的过程,只不过是在 AudioMixer 中进行的,3.7. AudioFlinger 混音器处理 小节中有相关描述)。 void AudioFlinger::DirectOutputThread::threadLoop_mix(){ // mFrameCount 是硬件设备(PCM 设备)处理单个数据块的帧数(周期大小) // 上层必须积累了足够多(mFrameCount)的数据,才写入到 PCM 设备 // 所以 mFrameCount 也就是 AudioFlinger 预期的数据量 size_t frameCount = mFrameCount; // mSinkBuffer 目的缓冲区,threadLoop_write() 会把 mSinkBuffer 上的数据写到 PCM 设备 int8_t *curBuf = (int8_t *)mSinkBuffer; // output audio to hardware // FIFO 上可读的数据量可能要比预期的要小,因此可能需要多次读取才能积累足够的数据量 // 注意:AudioTrack 和 AudioFlinger 是不同的进程,AudioTrack 同时也在不停地生产数据 // 所以 FIFO 可读的数据量是在不停变化的 while (frameCount) { AudioBufferProvider::Buffer buffer; buffer.frameCount = frameCount; // getNextBuffer() 从 FIFO 上获取可读数据块 status_t status = mActiveTrack->getNextBuffer(&buffer); if (status != NO_ERROR || buffer.raw == NULL) { memset(curBuf, 0, frameCount * mFrameSize); break; } // memcpy() 把 FIFO 可读数据拷贝到 mSinkBuffer 目的缓冲区 memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); frameCount -= buffer.frameCount; curBuf += buffer.frameCount * mFrameSize; // releaseBuffer() 更新 FIFO 读位置 // 对于 AudioTrack 来说,意味着 FIFO 上有更多的可用空间 mActiveTrack->releaseBuffer(&buffer); } mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer; mSleepTimeUs = 0; mStandbyTimeNs = systemTime() + mStandbyDelayNs; mActiveTrack.clear();} 5.3. 环形 FIFO 管理在上述过程中,不知大家有无意识到:整个过程中,最难的是如何协调生产者与消费者之间的步调。上文所说的 FIFO 是环形 FIFO,AudioTrack 写指针、AudioFlinger 读指针都是基于 FIFO 当前的读写位置来计算的。
我们回顾下创建 AudioTrack 对象时,FIFO 及其控制块的结构如下所示:
| | | -------------------> mCblkMemory <--------------------- | | | +--------------------+------------------------------------+ | audio_track_cblk_t | FIFO | +--------------------+------------------------------------+ ^ ^ | | mCblk mBuffer mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); new(mCblk) audio_track_cblk_t(); mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+--------------------+ +-----------------------------------+ | audio_track_cblk_t | | FIFO (sharedBuffer) | +--------------------+ +-----------------------------------+ ^ ^ | | mCblk mBuffer mCblk = (audio_track_cblk_t *) new uint8_t[size]; new(mCblk) audio_track_cblk_t(); mBuffer = sharedBuffer->pointer() FIFO 管理相关的类图:
到这里,我决定结束本文了。环形 FIFO 管理是 Android 音频系统的精髓,一个小节并不足以描述其原理及实现细节;Android 环形 FIFO 的实现可说得上精妙绝伦,其他项目如果要用到环形 FIFO,不妨多借鉴它。因此我想另写一篇博文详细分析 Android 环形 FIFO 的原理及实现,初定提纲如下,以作备忘: 1. 传统环形 FIFO 的原理 2.Android 环形 FIFO 的原理 3.读写指针的线程安全 4.Futex 进程同步机制 5.Android 环形 FIFO 的实现 |
|