AudioFlinger Thread中的几个buffer

简述

AudioFlinger是实现上层跟Hal层进行交互的重要的一层,它主要就是通过Thread进行沟通,不管是数据流还是控制流都是需要经由AudioFlinger Thread往下分发的。这两天有空梳理了一下Thread中几个重要的buffer,在这边做个总结,东西很浅,留作备忘。

内容

主要是下面三个buffer:

  1. mSinkBuffer,这个buffer的数据最后是要写到hal去的,但是你会发现一开始都没有对它操作,只有在写之前最后一步的时候会进行拷贝操作,将之前处理过的数据的buffer拷贝到该buffer中来。
  2. mMixerBuffer 在没有效果的情况下,使用这个buffer进行前期处理
  3. 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()

  1. 常规检查自己看代码(对于mixer超过2个通道的将被cut掉,全局音效只用于offload和mixer的线程等等)
  2. 通过id去尝试获取chain,发现获取到的id为0,所以必须自己创建chain,并且添加到线程的chain vector中
  3. 接下来是创建effect,会先将该效果注册到AudioPolicyManager中,然后创建Effect(其实就是EffectModule)
  4. 将该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.