转载地址:AudioFlinger Thread中的几个buffer | Thinking
简述
AudioFlinger是实现上层跟Hal层进行交互的重要的一层,它主要就是通过Thread进行沟通,不管是数据流还是控制流都是需要经由AudioFlinger Thread往下分发的。这两天有空梳理了一下Thread中几个重要的buffer,在这边做个总结,东西很浅,留作备忘。
内容
主要是下面三个buffer:
- mSinkBuffer,这个buffer的数据最后是要写到hal去的,但是你会发现一开始都没有对它操作,只有在写之前最后一步的时候会进行拷贝操作,将之前处理过的数据的buffer拷贝到该buffer中来。
- mMixerBuffer 在没有效果的情况下,使用这个buffer进行前期处理
- mEffectBuffer 如果有效果的话,该buffer会直接取代mMixerBuffer进行处理。
AudioPolicyManager在进行初始化的时候会去解析audio_policy.conf文件,会创建Flag为primary的output,在open的过程中会将audio_policy.conf中关于该节点的数据传递给底层streamout对象,每个线程都有自己的streamout对象:
1 2 3 4 5 6 7 8 |
//hwDevHal对应的是hal primary的so,参数config即为audio_policy.conf中该节点的相关参数,包括支持的采样率、format、ChannelMask(表示可以支持到多少channel)等。 status_t status = hwDevHal->open_output_stream(hwDevHal, *output, devices, flags, config, &outStream, address.string()); |
这些参数是后来决定Thread进行初始化的时候相关buffer的大小。
1. 初始化
buffer初始化是在对应Thread中的readOutputParameters_l()完成的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common); mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common); mChannelCount = audio_channel_count_from_out_mask(mChannelMask); mHALFormat = mOutput->stream->common.get_format(&mOutput->stream->common); mFormat = mHALFormat; //确认一帧占多少字节,也是通过format*channel进行转换的。 mFrameSize = audio_stream_out_frame_size(mOutput->stream); //获取底层缓冲区大小 mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common); //换算出Hal层缓冲区容纳的帧数 mFrameCount = mBufferSize / mFrameSize; //这边我们先不管multiplier,这个值跟FastMixer相关。 mNormalFrameCount = multiplier * mFrameCount; //开始初始化SinkBuffer free(mSinkBuffer); mSinkBuffer = NULL; // For sink buffer size, we use the frame size from the downstream sink to avoid problems // with non PCM formats for compressed music, e.g. AAC, and Offload threads. // SinkBuffer 这边使用的是上面计算出来的FrameSize,而下面则是重新计算的,这边谷歌给出了说明: // 为了防止使用了Non PCM Format的一些格式,比如AAC,或者是offload场景下,为了避免存在大小问题,直接从底层获取该帧的位数 const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; (void)posix_memalign((void**)&mSinkBuffer, 32, sinkBufferSize); //mMixerBufferEnabled该变量是否为true,是由AudioFlinger::kEnableExtendedPrecision决定的,并且只存在在MixerThread中,如果有扩展的精度的话,就需要用到该缓冲区,默认情况下是开启的 free(mMixerBuffer); mMixerBuffer = NULL; if (mMixerBufferEnabled) { mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT. mMixerBufferSize = mNormalFrameCount * mChannelCount * audio_bytes_per_sample(mMixerBufferFormat); (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); } free(mEffectBuffer); mEffectBuffer = NULL; //同上面 if (mEffectBufferEnabled) { mEffectBufferFormat = AUDIO_FORMAT_PCM_16_BIT; // Note: Effects support 16b only mEffectBufferSize = mNormalFrameCount * mChannelCount * audio_bytes_per_sample(mEffectBufferFormat); (void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize); } |
2. buffer牵扯到的流程
其实看这几个buffer,主要就是为了想搞清楚这数据流是怎么从audiotrack->audiomixer->audioeffect->audio hal的。
############ 1. audiotrack的创建
我们可以直接看AudioFlinger::PlaybackThread::createTrack_l()
该函数的返回值是一个audiotrack在AudioFlinger这边的对应实例,其实就是实例化了个Track对象,在AudioFlinger里面会封装成trackHandle给用户空间进行操作,并且该track会边添加到AudioFlinger thread中的mTracks中。在创建track的过程中,还需要考虑effect的问题,会通过获取audiotrack提供的sessionid去查看是否有对应的EffectChain,如果有的话,则会将track的mainBuffer设置为chain的InBuffer,我这边先透露下这个InBuffer对于全局的音效而言,其实就是mEffectBuffer了。还有通过代码中我们知道Track其实继承来自BufferProvider的,所以它本身是个BufferProvider,这也符合track的定义,就是提供数据。
对于audiotrack我们只需要知道到这就可以了。
############ 2. audiotrack到audiomixer部分
我们看的这部分需要从AudioFlinger::PlaybackThread::threadLoop()
看起,不过我们可以直接跳到ThreadLoop中的AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l()
中来,这个函数主要为了后面进行mix和write的做准备的,而能否到下一步就得需要有数据,并且状态进入mixer_ready的情况,这边的具体就不说了,我们只看看有激活的audiotrack并且是有数据的情况下,在满足上述条件下,thread会去判断当前的track的mainBuffer是否不为SinkBuffer和MixerBuffer,如果都不是,那就是EffectBuffer,会重新去获取chain,一堆操作之后,会将该track对象给到audiomixer去,并且会设置audiomixer中的mainbuffer为track的mainbuffer,其实就是输出buffer,后面我们看audiomixer的代码的时候会看到的。
1 2 3 4 5 |
mAudioMixer->setBufferProvider(name, track); mAudioMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); |
############ 3. audiomixer如何处理数据部分AudioFlinger::MixerThread::prepareTracks_l()
之后我们就要看AudioFlinger::MixerThread::threadLoop_mix()
的部分了,这边就直接调用了audiomixer的process函数。直接到audiomixer中去看下这部分。这边我们只关注下简单的函数process_NoResampleOneTrack()
的场景就好了,想来想去这部分把代码贴出来吧,流程其实就是从track中获取数据,然后mix之后放到out中,out就是之前在AudioFlinger中设置的track的mainbuffer,如果这个时候你没记错的话,这个buffer其实就是EffectBuffer。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
template <int MIXTYPE, typename TO, typename TI, typename TA> void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts) { ALOGVV("process_NoResampleOneTrack\n"); // CLZ is faster than CTZ on ARM, though really not sure if true after 31 - clz. const int i = 31 - __builtin_clz(state->enabledTracks); ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled"); track_t *t = &state->tracks[i]; const uint32_t channels = t->mMixerChannelCount; //out 对应的 是track的mainBuffer,其实就是EffectBuffer。 TO* out = reinterpret_cast<TO*>(t->mainBuffer); //这个暂且不关心吧,不知道auxiliary effect是哪种类型,以后发现再来补充 TA* aux = reinterpret_cast<TA*>(t->auxBuffer); const bool ramp = t->needsRamp(); for (size_t numFrames = state->frameCount; numFrames; ) { AudioBufferProvider::Buffer& b(t->buffer); // get input buffer b.frameCount = numFrames; const int64_t outputPTS = calculateOutputPTS(*t, pts, state->frameCount - numFrames); //从track中去取数据 t->bufferProvider->getNextBuffer(&b, outputPTS); //in 指向的就是获取到的数据buffer const TI *in = reinterpret_cast<TI*>(b.raw); const size_t outFrames = b.frameCount; //做下音量处理。每种类型的声音,都有各自不同的音量,并且输出到out缓冲区。 volumeMix<MIXTYPE, is_same<TI, float>::value, false> ( out, outFrames, in, aux, ramp, t); out += outFrames * channels; if (aux != NULL) { aux += channels; } numFrames -= b.frameCount; // release buffer t->bufferProvider->releaseBuffer(&b); } if (ramp) { t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value); } } |
ok,到此位置,audiotrack的数据已经经过mix到effectBuffer中来了。
############ 4. audiomixer到audioeffect部分
处理完mix之后,就要处理effect了。也是直接调用EffectChain中的process
1 2 3 4 5 |
if (sleepTime == 0 && mType != OFFLOAD) { for (size_t i = 0; i < effectChains.size(); i ++) { effectChains[i]->process_l(); } } |
我们到Effects.cpp中去一探究竟吧。代码就补贴了,直接遍历EffectChain中的effect,然后一个个调用各自的process函数。这部分我们需要注意下咯。我把一些逻辑无关的东西去掉。其实就一个函数调用mEffectInterface的process,这边我们就不用去care了,具体的实现是在效果的类库中,我们也没有代码的。那为什么贴出这个东西来,因为下面的buffer是我们关心的东西。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void AudioFlinger::EffectModule::process() { if (isProcessEnabled()) { // do the actual processing in the effect engine int ret = (*mEffectInterface)->process(mEffectInterface, &mConfig.inputCfg.buffer, &mConfig.outputCfg.buffer); } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT && mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) { ... } } |
ok,我们来看看这个buffer指向哪里?那我们得从创建全局的效果开始。
在output thread中创建全局音效的流程大致如此(这边重点关注primary mixer)对应代码AudioFlinger::ThreadBase::createEffect_l()
:
- 常规检查自己看代码(对于mixer超过2个通道的将被cut掉,全局音效只用于offload和mixer的线程等等)
- 通过id去尝试获取chain,发现获取到的id为0,所以必须自己创建chain,并且添加到线程的chain vector中
- 接下来是创建effect,会先将该效果注册到AudioPolicyManager中,然后创建Effect(其实就是EffectModule)
- 将该Effect添加到chain来,这里面就需要注意了,因为这边有我们关注的东西,effect的InBuffer是chain的InBuffer,而OutBuffer则这个效果插入到chain的第几位,如果是最后一个的话,则OutBuffer就是chain的OutBuffer,否则是InBuffer,为什么这样?(chain是一节一节处理的,你只需要将buffer指向同一块这样就省去了传递的麻烦。)
那到这边我们还得去寻找EffectChain中InBuffer和OutBuffer。这部分的代码在AudioFlinger::PlaybackThread::addEffectChain_l()
中,
1 2 3 4 5 6 |
int16_t* buffer = reinterpret_cast<int16_t*>(mEffectBufferEnabled ? mEffectBuffer : mSinkBuffer); chain->setThread(this); chain->setInBuffer(buffer, ownsBuffer); chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled ? mEffectBuffer : mSinkBuffer)); |
好吧,这个InBuffer和outBuffer就是mEffectBuffer了,跑不掉了。
这边得稍微总结下了,要不感觉乱乱的,Effect中的In和Out的buffer是会指向EffectChain中的In或者Out的buffer的,而EffectChain的buffer由指向mEffectBuffer,同时Audiotrack的MainBuffer也指向该mEffectBuffer,最为mixer输出的buffer,同时也作为effect输入输出的buffer。
如图。
############ 5. 最后把处理好的数据复制到mSinkBuffer中,到此我们的旅程也结束了。
That’s All.