分享

红狼博客 ? Android源码分析:AudioFlinger中的线程

 arm_embed 2012-12-07

Android源码分析:AudioFlinger中的线程

2011-11-14 10:05 星期一    浏览: 694    绿 发表评论 阅读评论

 

Track相关类概述

下图是其继承关系图,继承在AudioBufferProvider之后,各种Track可以作为AudioBufferProvider的一种为AudioMixer提供音频数据缓冲。TrackBase是基类,Track作为普通的音轨类,用于音频播放;OutputTrack用于复制线程,相当于将声音同时输出到两个输出设备中。

TrackBase

在它的构造函数中,在ashm上为audio_track_cblk_t分配共享内存(即成员变量mCblk所指向的内存),用于应用程序和audioflinger所在的mediaserver进程进行通讯。若是在AudioTrack中指定了Stream mode(需要不断提供音频数据),那么还要为要写进来的数据分配共享内存,这块共享内存紧接在audio_track_cblk_t的后面,大小为:

size_t bufferSize = frameCount*channelCount*sizeof(int16_t);

下图是Stream模式下的共享内存示意图:

 

bufferSize

 

audio_track_cblk_t

mBuffer

mCblk

若是Static mdoe(如音频资源文件直接装载入,效率较高,如用于时间短的、多次使用的提示音),则使用传递进来的IMemory作为缓冲区,不需要在共享缓冲区上分配内存。成员变量mBuffer则指向这个缓冲区IMemory所代表的缓冲区。此种情况下,mCblk仍在构造函数中分配位于ashm中的内存。

应用程序和AudioFlinger通过mBuffer指向的缓冲区来共享音频数据。应用程序作为“生产者”写入音频数据到mBuffer中,同时改变mCblk里的成员值;AudioFlinger作为“消费者”消耗mBuffer里的音频数据。

releaseBufferaudioflinger侧的释放缓冲区函数。得到缓冲区中的桢数后,调用step将计数server向前移动缓冲区所包含的桢数。

step:调用audio_track_cblk_tstepServer将计数器向前移动指定的桢数。

reset:重置audio_track_cblk_t中的userBaseuserserverBaseserver等计数为0

getBuffer:参数offset是基于serverBase的偏移桢数,参数frames是从开始offset计算的桢数,返回值是offset处的指针值。调用该函数表示需要获取offset处开始的、包含frames桢的一块缓冲区。

 

Track

构造函数:从所存在的线程中获取track的名称放置于mName中,注意此处的名称是一个整数,也是个索引,来自于AudioMixerAudioMixer支持高达32路混音,这个名称就是track在这32个元素数组中的索引。在构造函数中,另一个要完成的是让TrackmMainBuffer指向track所在播放线程的mMixBuffer。在构造函数后面,计算一个桢的大小赋值给audio_track_cblk_t中的frameSize

为了以示强调,下图示意出:TrackmMainBuffer指向track所在播放线程的mMixBuffer

 

PlaybackThread

 

Track

 

mMainBuffer

mMixBuffer

 

 

若是有音效链,则被修改为指向音效链的输入buffer,见函数PlaybackThread::createTrack_l

 

 

destroy:销毁自身。若不是OutputTrack(用于复制线程),则调用AudioSystem::stopOutput停止输出。如果是播放线程中的最后一个track,则还要调用AudioSystem::releaseOutput释放输出。

getNextBuffer:参照参数AudioBufferProvider::Buffer* buffer的请求中的桢数,尽可能以audio_track_cblk_tframesReady为准,得到mBuffer中已经准备好(Ready)的音频数据缓冲区,让参数bufferraw指针指向该准备好的数据块缓冲区。注意:根据偏移和请求的桢数,其调用了父类的getBuffer函数来获得缓冲区指针。

start函数:在进行状态检查和设置后,调用AudioSystem::startOutput开始播放,然后将自己添加到播放线程的mTracks列表中。开始由播放线程开始调度操作缓冲区。

stop/pause:调用AudioSystem::stopOutput停掉播放,二者状态被设置的不同

attachAuxEffect:参数EffectIdEffectid号,它调用播放线程的同名函数,后者会在其编号为0AudioSystem::SESSION_OUTPUT_MIX)的session中去查找具有该id号的Effect,并且要求是Auxiliary Effect,最后让TrackmAuxBuffer指向Effect的输入bufferinBuffer

 

OutputTrack

Track的子类,为DuplicatingThread线程所用,DuplicatingThread将混音好的数据写入到它里面,然后输出到两个output设备对应的mixer线程中,实现复制输出。

构造函数:为audio_track_cblk_t指定CBLK_DIRECTION_OUT标志,让mCblk中的buffers指向audio_track_cblk_t后面的缓冲区(因OutputTrack中不支持指定一个IMemory,所以mBuffer指向mCblk后面的缓冲区,即Stream模式);最后将自己添加到播放线程中的mTracks中。

成员函数obtainBuffer: 获取mBuffer中一块空闲的缓冲区。缓冲区信息将填充到第一个参数AudioBufferProvider::Buffer* buffer中,填入的信息包括缓冲区地址和桢数。请求获得的桢数是第一个参数AudioBufferProvider::Buffer* buffer中的frameCount,但数量还决定于mBuffer中是否已有这么多空闲未用的缓冲区、以及截止mBuffer末尾处的最大桢数。第二个参数waitTimeMs表示当没有可用缓冲区时需要等待的超时时间。

成员函数write:将要写入的数据首先写到一个限定不超过10个节点的待写缓冲队列mBufferQueue上,再从待写缓冲队列上再将数据copymCblk后面的缓冲区即mBuffer中。每次拷贝操作,表面上是拷贝到mOutBuffer中。实际上mOutBuffer指向的是mBuffer中的空闲区域。当待写队列为空时,才会跳过待写队列,直接让write传递过来的数据写到mOutBuffer中。可见write的过程本质是将数据写到mBuffer中,但是添加了一个不超过10个节点的缓冲队列来临时存放write要写的数据,然后再copy过去。

函数write代码较多,分四部分来分析,第一部分代码让inBuffer指向参数传递进来的要写的数据,然后分配缓冲区内存(大小为mCblk->frameCount – frames桢),添加到队列mBufferQueue

bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
{
Buffer *pInBuffer;
Buffer inBuffer;
uint32_t channelCount = mCblk->channelCount;
bool outputBufferFull = false;
inBuffer.frameCount = frames;//
此行及下面一行:inBuffer代表着要写入的数据
inBuffer.i16 = data;

uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();

if (!mActive && frames != 0) {//若不为active状态且要写入非空数据
start();//
开始
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {//
获取mixer线程
MixerThread *mixerThread = (MixerThread *)thread.get();
if (mCblk->frameCount > frames){//
要写的frmaes数量不超过mCblk后面的缓冲区大小
if (mBufferQueue.size() < kMaxOverFlowBuffers) {//
缓冲队列还没超限(10个)
uint32_t startFrames = (mCblk->frameCount – frames);
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[startFrames * channelCount];//
分配内存
pInBuffer->frameCount = startFrames;
pInBuffer->i16 = pInBuffer->mBuffer;
memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));//
分配的内存清零
mBufferQueue.add(pInBuffer);//
添加到队列中
} else {//
若是缓冲队列超限
LOGW (“OutputTrack::write() %p no more buffers in queue”, this);
}
}
}
}

第二部分代码是一个while循环,在剩下的等待时间里,将待写缓冲队列上的数据写到mOutBuffermCblk后面的mBuffer所对应的空闲缓冲区中

while (waitTimeLeftMs) {//若还有等待时间
// First write pending buffers, then new data
if (mBufferQueue.size()) {//
检查缓冲队列是否为空
pInBuffer = mBufferQueue.itemAt(0);//
从队列上取下第一个节点,作为待写入的数据
} else {
pInBuffer = &inBuffer;//
没有的话,直接使用参数传递来的作为待写数据
}
if (pInBuffer->frameCount == 0) {
break;//
都为空,退出while循环
}

if (mOutBuffer.frameCount == 0) {//mOutBuffermCblk后面没有对应的缓冲区,即要写入的目的地
mOutBuffer.frameCount = pInBuffer->frameCount;//
待写入的数据作为其大小
nsecs_t startTime = systemTime();
if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) {//
调用obtainBuffer获取在mCblk后的空闲缓冲区,即mOutBuffer在其上有对应的内存缓冲区
LOGV (“OutputTrack::write() %p thread %p no more output buffers”, this, mThread.unsafe_get());
outputBufferFull = true;
break;//
调用obtainBuffer失败,退出循环
}

uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() – startTime);
if (waitTimeLeftMs >= waitTimeMs) {
waitTimeLeftMs -= waitTimeMs;
} else {//若没有时间了,下次循环时退出
waitTimeLeftMs = 0;
}
}
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount;//
取最小者,即可以写入这么多数据

memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));//将待写入的数据(队列上第一个节点或实参传递过来的数据)copymOutBuffer所对应的缓冲区(即mCblk后面的空闲缓冲区内存),完成写入过程

mCblk->stepUser(outFrames);//表示有outFrames写到mCblk后面的缓冲区mBuffer上,user计数向后迈进outFrames
pInBuffer->frameCount -= outFrames;//
待写数据计数减少
pInBuffer->i16 += outFrames * channelCount;//
指向下一个待写数据块
mOutBuffer.frameCount -= outFrames;//
可能为0
mOutBuffer.i16 += outFrames * channelCount;//
指针指向向前累加

if (pInBuffer->frameCount == 0) {//待写的数据都写完
if (mBufferQueue.size()) {
mBufferQueue.removeAt(0);//
释放它
delete [] pInBuffer->mBuffer;
delete pInBuffer;
LOGV(“OutputTrack::write() %p thread %p released overflow buffer %d”, this, mThread.unsafe_get(), mBufferQueue.size());
} else {
break;
}
}
}

可见,第二部分代码是在线程允许的时间内,从mCblk后面的缓冲区mBuffer中获取一块空闲的输出内存(即调用obtainBuffer后得到的mOutBuffer),然后从缓冲队列mBufferQueue上取下第一项(若没有则直接是write要写的数据)取出一定数量的桢,copymOutBuffer中,也就是mCblk后面的缓冲区mBuffer中,相应地调整mCblk中的计数。

第三部分则是将write要写的数据(可能是剩下的部分地数据)放置到待写缓冲队列上:

// If we could not write all frames, allocate a buffer and queue it for next time.
if (inBuffer.frameCount) {//
如果传递进来的还有待写的数据(可能是原封未动的数据,也可能是上面没有写完剩下的),则分配内存,然后将其copy到新分配的内存块上,再将其添加到待写队列上
sp<ThreadBase> thread = mThread.promote();
if (thread != 0 && !thread->standby()) {
if (mBufferQueue.size() < kMaxOverFlowBuffers) {
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount];//
分配内存
pInBuffer->frameCount = inBuffer.frameCount;
pInBuffer->i16 = pInBuffer->mBuffer;
memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * sizeof(int16_t));//copy
到新分配的内存块上
mBufferQueue.add(pInBuffer);//
添加到队列上,这样每次write要写的数据可能被添加到待写队列mBufferQueue
LOGV(“OutputTrack::write() %p thread %p adding overflow buffer %d”, this, mThread.unsafe_get(), mBufferQueue.size());
} else {
LOGW(“OutputTrack::write() %p thread %p no more overflow buffers”, mThread.unsafe_get(), this);
}
}
}

第四部分则是当调用者无数据可写时的情况,即要写入的frames0,分配缓冲区添加一个节点,准备有数据到达时可先送往待写缓冲队列:

// Calling write() with a 0 length buffer, means that no more data will be written: If no more buffers are pending, fill output track buffer to make sure it is started by output mixer.
if (frames == 0 && mBufferQueue.size() == 0) {//
无数据要写入,待写缓冲队列也为空
if (mCblk->user < mCblk->frameCount) {//
有缓冲区空间,则将它们作为一块内存缓冲区添加到队列上
frames = mCblk->frameCount – mCblk->user;
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[frames * channelCount];
pInBuffer->frameCount = frames;
pInBuffer->i16 = pInBuffer->mBuffer;
memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
} else if (mActive) {
stop();
}
}
return outputBufferFull;
}

 

线程相关类概述

线程类从Thread继承过来,ThreadBasePlaybackThread都因为有纯虚函数,所以都是抽象类。在系统中真正运行的线程实体主要是MixerThread类的对象,DuplicatingThread用于复制输出,DirectOutputThread用于将声音直接送往HAL音频硬件,不经过混音处理。下图是其类继承关系图:

 

ThreadBase

ThreadBase类继承自Thread类,在其里面,嵌套定义了ConfigEvent(包含了Event类型和一个整型参数)用于通知audio配置信息的变化:当有配置信息变化时,调用ThreadBasesendConfigEvent可以将某个ConfigEvent发送到队列上,然后在某个线程循环里调用ThreadBaseprocessConfigEvents来处理这些event事件。处理这些事件的过程如下:调用AudioFlingeraudioConfigChanged_l将配置事件通知到客户端,AudioFlinger是通过IAudioFlingerClient接口跨进程调用到应用程序侧的AudioFlingerClient类,见AudioSystem.cpp文件中的AudioSystem::AudioFlingerClient::ioConfigChanged函数(该函数也仅仅是缓存audio的配置信息)。通过IAudioFlingerClient接口跨进程调用的类继承关系图如下:

 

其中BpAudioFlingerClient运行在mediaserver进程中的AudioFlinger这个service中,嵌套类AudioFlingerClient运行在应用程序进程中。

 

PlaybackThread

CreateTrack_l

addTrack_l

 

 

MixerThread

对于MixerThread线程,有三样东西不得不提:一是其成员变量mAudioMixer,它是一个AudioMixer类的对象,在创建MixerThread对象时随其构造函数一起创建,线程中的所有活动的PCM音轨数据都是由它来进行混音的;二是线程函数threadLoop,它是线程的执行内容,在未要求退出的情况下,由它调度来实现声音的混合;三是成员函数prepareTracks_l,它为AudioMixer准备需要混合的音轨(track)及其它所需信息,以便在threadLoop中进行混合,还将非活动的音轨从活动列表中删除。

由于代码很多,也牵涉到很多音频处理方面的细节,这儿只摘录部分代码来分析其主体执行流程。在线程函数threadLoopwhile循环中,调用了prepareTracks_l准备音轨之后,调用了AudioMixerprocess进行混音处理,最后将这些混过音和经过音效处理后的数据写到HAL(硬件适配层)中的音频硬件中,实现声音的输出:

//…省略了代码

mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);//准备音轨信息,为混音做准备

// prevent any changes in effect chain list and in each effect //chain during mixing and effect process as the audio buffers could //be deleted or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
}
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers…
mAudioMixer->process();//
混音处理
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
//TODO: delay standby when effects have a tail
} else

//…省略了代码
if (sleepTime == 0) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();//
音效处理
}
//…
省略了代码

int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);//写入HAL音频硬件

//…
省略了代码

混音处理,是在AudioMixer中进行的,它将根据不同的情况调用AudioMixer【注释:下面这段描述得到了ZJY的帮助】

process__genericNoResampling:多于2Track的处理,不重采样

process__genericResampling:多于2Track的处理,重采样

process__OneTrack16BitsStereoNoResampling:处理一路Track的情况,多数都是这种情况,只进行音量的调整处理。

process__TwoTracks16BitsStereoNoResampling:将两路声音处理为一路,处理完数据存放在MAIN_BUFFER中,亦即播放线程中的mMixerBuffer中(若有音效链则是音效链的inBuffer)。它不进行重采样。它使用累计乘法器将两路混合为一路。

 

再来看下prepareTracks_l,只摘录出设置缓冲区这块代码:


mAudioMixer->setParameter(
AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)
track->mainBuffer());//
也就是将mMainBuffer设置过去,其实也就是播放线程中的mMixBuffer

mAudioMixer->setParameter(
AudioMixer::TRACK,
AudioMixer::AUX_BUFFER, (void *)
track->auxBuffer());//
也就是Aux Effect的输入缓冲区

 

DuplicatingThread

成员函数updateWaitTime:因为声音都是按一定的采样率编码的,所以在播放时,也应该按其采样率去复现原声音,也就是说按一定频率将数据写入音频硬件。该函数就是检查线程中的各个track在写一次数据后该等待的时间,得最小者,赋值给成员变量mWaitTimeMs

如果需要让声音同时在两个设备里输出,需要调用到AudioFlinger中的openDuplicateOutput打开复制输出。该函数根据两个输出设备号查找对应的mixer输出线程,然后创建一个DuplicatingThread复制线程,进而创建一个OutputTrack对象添加到主线程thread1中;接着再调用addOutputTrack创建一个OutputTrack对象,添加到线程thread2中:

int AudioFlinger::openDuplicateOutput(int output1, int output2)
{
Mutex::Autolock _l(mLock);
MixerThread *thread1 = checkMixerThread_l(output1);//
得到对应的线程1
MixerThread *thread2 = checkMixerThread_l(output2);//
得到对应的线程2
if (thread1 == NULL || thread2 == NULL) {
LOGW(“openDuplicateOutput() wrong output mixer type for output %d or %d”, output1, output2);
return 0;
}
int id = nextUniqueId();
DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);//
创建一个复制输出线程,当然也创建添加一个OutputTrack
thread->addOutputTrack(thread2);//
为线程2添加一个OutputTrack
mPlaybackThreads.add(id, thread);//
将自身(复制输出线程)添加到audioflinger
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);//
通知配置改变
return id;
}

DuplicatingThread的构造函数中,设置完线程为类型为DUPLICATING后,调用addOutputTrack创建一个OutputTrack(也就是将自己添加到mixer主线程的mTracks中),然后将其添加到DuplicatingThread自身的mOutputTracks列表中。

成员函数addOutputTrack用于创建一个OutputTrack对象,然后将其添加到DuplicatingThread自身的Tracks列表mOutputTracks中。在OutputTrack构造函数中,它会将自己添加到其第一个参数的线程中的Tracks列表中。也就是说,addOutputTrack会创建一个OutputTrack对象,将其添加到其参数指定的线程threadTracks列表中;同时,该OutputTrack对象也会被添加到DuplicatingThread自身的Tracks列表中。

void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
{
int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, this, mSampleRate, mFormat, mChannelCount, frameCount);//
OutputTrack构造函数中,会将自己添加到其第一个参数(即mixer线程)维护的Tracks列表中
if (outputTrack->cblk() != NULL) {
thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
mOutputTracks.add(outputTrack);//
添加到DuplicatingThread自身维护的列表中
LOGV(“addOutputTrack() track %p, on thread %p”, outputTrack, thread);
updateWaitTime();
}
}

再来看看DuplicatingThread的线程函数threadLoop,首先是准备好activeTracks,然后使用AudioMixer进行混音,再接着音效处理,最后就是将处理过的数据写入到OutputTrack对应的缓冲区。因为这Output也位于mixer线程的tracks列表中,所以它们最终在mixer线程中输出到HAL音频设备中。

其流程示意图如下,因为DuplicatingThread也继承自MixerThread,在threadLoop中如同MixerThread所要做的,首先将各活动音轨(activeTracks)进行混音和音效处理,然后将这些处理过的数据写入到OuputTrack中,而不是像MixerThread线程中写入到HAL音频硬件中。因为OuputTrack同时又位于个mixer输出线程的tracks中,所以它们由mixer线程输出到HAL硬件。

 

track1

ouput1

 

AudioMixer

 

track2

 

output2

 

DuplicatingThread

MixerThread

OutputTrack

AudioStreamOut

 

下面是threadLoop的主体代码:

mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);

//省略部分代码

if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers…
if (outputsReady(outputTracks)) {
mAudioMixer->process();//
混音处理
} else {
memset(mMixBuffer, 0, mixBufferSize);
}
sleepTime = 0;
writeFrames = mFrameCount;
} else {

//省略部分代码

if (sleepTime == 0) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();//
音效处理
}
// enable changes in effect chain
unlockEffectChains(effectChains);

//省略部分代码

for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);//
将数据写入OutputTrack的缓冲队列或缓冲区
}

 

DirectOutputThread

 

下面的代码是其while循环内的代码,受上锁保护:

第一部分:检查配置是否变化

if (checkForNewParameters_l()) {

mixBufferSize = mFrameCount*mFrameSize;

activeSleepTime = activeSleepTimeUs();

idleSleepTime = idleSleepTimeUs();

standbyDelay = microseconds(activeSleepTime*2);

}

 

第二部分,无活动的track或需进入standby状态:

if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) || mSuspended) {//发生的可能性不大,UNLIKELY用于执行效率优化
// wait until we have something to do…
if (!mStandby) {//
进入standby状态
LOGV(“Audio hardware entering standby, mixer %pn”, this);
mOutput->standby();
mStandby = true;
mBytesWritten = 0;
}

if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {//无活动tracks,睡眠等待,直至被外界唤醒
// we’re about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
if (exitPending()) break;
LOGV(“DirectOutputThread %p TID %d going to sleepn”, this, gettid());
mWaitWorkCV.wait(mLock);
LOGV(“DirectOutputThread %p TID %d waking up in active moden”, this, gettid());
if (mMasterMute == false) {//
检查设置静音
char value[PROPERTY_VALUE_MAX];
property_get(“ro.audio.silent”, value, “0″);
if (atoi(value)) {
LOGD(“Silence is golden”);
setMasterMute(true);
}
}
standbyTime = systemTime() + standbyDelay;
sleepTime = idleSleepTime;
continue;//
继续循环
}
}

 

直接输出线程DirectOutputThread,不将音频数据送往AudioMixer送往混音器混音,而是直接输出到HAL音频硬件。线程循环函数threadLoop中,反映其主要执行流程的函数代码如下:

if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
curBuf = (int8_t *)mMixBuffer;
// output audio to hardware
while (frameCount) {
buffer.frameCount = frameCount;
activeTrack->getNextBuffer(&buffer);//
获取mCblk后面mBuffer中的数据已经ready的缓冲区
if (UNLIKELY(buffer.raw == 0)) {
memset(curBuf, 0, frameCount * mFrameSize);
break;
}
memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);//
拷贝到mMixBuffer
frameCount -= buffer.frameCount;
curBuf += buffer.frameCount * mFrameSize;
activeTrack->releaseBuffer(&buffer);//
释放用过的缓冲区
}
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;

}

它将mCblk后面的缓冲区中的数据copymMixBuffer后,再经(过下面的代码)音效链的音效处理后,直接输出到音频HAL音频硬件中(通过AudioStreamOutwrite函数):

if (sleepTime == 0) {
if (mixerStatus == MIXER_TRACKS_READY) {
applyVolume(leftVol, rightVol, rampVolume);
}
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();//
调用音效链上的音效进行处理
}
unlockEffectChains(effectChains);
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);//
完成音频数据到HAL音频硬件的输出
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
mStandby = false;
} else {
unlockEffectChains(effectChains);
usleep(sleepTime);
}

 

若要复制输出,则使用函数openDuplicateOutput;若要直接输出,不进行混音,则在调用AudioFlinger::openOutput函数时,在其最后一个参数flags指定OUTPUT_FLAG_DIRECT标志。下面是部分代码摘录:

int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
{
…//
省略部分代码
AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices, (int *)&format, &channels, &samplingRate,&status);//
调用HAL音频硬件打开一个输出流
…//
省略部分代码

if (output != 0) {
int id = nextUniqueId();
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||//
直接输出标志
(format != AudioSystem::PCM_16_BIT) || //
16
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {//
非立体声
thread = new DirectOutputThread(this, output, id, *pDevices);//
在直接指定输出标志、非16位数据或非立体声输出时,创建直接输出线程
LOGV(“openOutput() created direct output: ID %d thread %p”, id, thread);
} else {
thread = new MixerThread(this, output, id, *pDevices);//
创建混音线程进行混音输出
LOGV(“openOutput() created mixer output: ID %d thread %p”, id, thread);
…//
省略部分代码

可见,在使用标志或一定的参数的情况去打开一个输出流时,Audioflinger创建对应的输出线程,要么是直接输出线程,要么是混音线程。此处也可同时看出,系统中运行的播放线程只有混音线程、直接输出线程和复制线程三个类的实例对象。

本文链接地址: http://www./?p=976

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多