App应用播放时,每个APP端都会创建AudioTrack,每个AudioTrack都会通过共享内存与播放线程的Track传递数据,每个应用发送的音频数据格式可能都不相同,而声卡仅支持固定的几种格式,除非发送的格式就是 声卡本身支持的格式,否则这里都需要进行重采样/混音,Playbackthread中使用mAudioMixer的一些操作把硬件不支持的音频格式转化为硬件支持的音频格式,这个过程叫做重采样。
xref: /frameworks/av/services/audioflinger/Threads.cpp
bool AudioFlinger::PlaybackThread::threadLoop()
{
.......................................................................................
// loopCount is used for statistics and diagnostics.
for (int64_t loopCount = 0; !exitPending(); ++loopCount)
{
{ // scope for mLock
...............................................................................
// mMixerStatusIgnoringFastTracks is also updated internally
// 混音前的准备工作
mMixerStatus = prepareTracks_l(&tracksToRemove);
mActiveTracks.updatePowerState(this);
updateMetadata_l();
// 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);
// Determine which session to pick up haptic data.
// This must be done under the same lock as prepareTracks_l().
// TODO: Write haptic data directly to sink buffer when mixing.
if (mHapticChannelCount > 0 && effectChains.size() > 0) {
for (const auto& track : mActiveTracks) {
if (track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
break;
}
}
}
// Acquire a local copy of active tracks with lock (release w/o lock).
//
// Control methods on the track acquire the ThreadBase lock (e.g. start()
// stop(), pause(), etc.), but the threadLoop is entitled to call audio
// data / buffer methods on tracks from activeTracks without the ThreadBase lock.
activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end());
} // mLock scope ends
if (mBytesRemaining == 0) {
mCurrentWriteLength = 0;
if (mMixerStatus == MIXER_TRACKS_READY) {
// threadLoop_mix() sets mCurrentWriteLength
// 混音
threadLoop_mix();
} else if ((mMixerStatus != MIXER_DRAIN_TRACK)
&& (mMixerStatus != MIXER_DRAIN_ALL)) {
// threadLoop_sleepTime sets mSleepTimeUs to 0 if data
// must be written to HAL
threadLoop_sleepTime();
if (mSleepTimeUs == 0) {
mCurrentWriteLength = mSinkBufferSize;
// Tally underrun frames as we are inserting 0s here.
for (const auto& track : activeTracks) {
if (track->mFillingUpStatus == Track::FS_ACTIVE) {
track->mAudioTrackServerProxy->tallyUnderrunFrames(mNormalFrameCount);
}
}
}
}
// Either threadLoop_mix() or threadLoop_sleepTime() should have set
// mMixerBuffer with data if mMixerBufferValid is true and mSleepTimeUs == 0.
// Merge mMixerBuffer data into mEffectBuffer (if any effects are valid)
// or mSinkBuffer (if there are no effects).
//
// This is done pre-effects computation; if effects change to
// support higher precision, this needs to move.
//
// mMixerBufferValid is only set true by MixerThread::prepareTracks_l().
// TODO use mSleepTimeUs == 0 as an additional condition.
if (mMixerBufferValid) {
void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
// mono blend occurs for mixer threads only (not direct or offloaded)
// and is handled here if we're going directly to the sink.
if (requireMonoBlend() && !mEffectBufferValid) {
mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount, mNormalFrameCount,
true /*limit*/);
}
if (!hasFastMixer()) {
// Balance must take effect after mono conversion.
// We do it here if there is no FastMixer.
// mBalance detects zero balance within the class for speed (not needed here).
mBalance.setBalance(mMasterBalance.load());
mBalance.process((float *)mMixerBuffer, mNormalFrameCount);
}
// 将数据从mMixerBuffer复制到mSinkBuffer
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
mNormalFrameCount * (mChannelCount + mHapticChannelCount));
// If we're going directly to the sink and there are haptic channels,
// we should adjust channels as the sample data is partially interleaved
// in this case.
if (!mEffectBufferValid && mHapticChannelCount > 0) {
adjust_channels_non_destructive(buffer, mChannelCount, buffer,
mChannelCount + mHapticChannelCount,
audio_bytes_per_sample(format),
audio_bytes_per_frame(mChannelCount, format) * mNormalFrameCount);
}
}
mBytesRemaining = mCurrentWriteLength;
if (isSuspended()) {
// Simulate write to HAL when suspended (e.g. BT SCO phone call).
mSleepTimeUs = suspendSleepTimeUs(); // assumes full buffer.
const size_t framesRemaining = mBytesRemaining / mFrameSize;
mBytesWritten += mBytesRemaining;
mFramesWritten += framesRemaining;
mSuspendedFrames += framesRemaining; // to adjust kernel HAL position
mBytesRemaining = 0;
}
// only process effects if we're going to write
if (mSleepTimeUs == 0 && mType != OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
// TODO: Write haptic data directly to sink buffer when mixing.
if (activeHapticSessionId != AUDIO_SESSION_NONE
&& activeHapticSessionId == effectChains[i]->sessionId()) {
// Haptic data is active in this case, copy it directly from
// in buffer to out buffer.
const size_t audioBufferSize = mNormalFrameCount
* audio_bytes_per_frame(mChannelCount, EFFECT_BUFFER_FORMAT);
memcpy_by_audio_format(
(uint8_t*)effectChains[i]->outBuffer() + audioBufferSize,
EFFECT_BUFFER_FORMAT,
(const uint8_t*)effectChains[i]->inBuffer() + audioBufferSize,
EFFECT_BUFFER_FORMAT, mNormalFrameCount * mHapticChannelCount);
}
}
}
}
// Process effect chains for offloaded thread even if no audio
// was read from audio track: process only updates effect state
// and thus does have to be synchronized with audio writes but may have
// to be called while waiting for async write callback
if (mType == OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
}
// Only if the Effects buffer is enabled and there is data in the
// Effects buffer (buffer valid), we need to
// copy into the sink buffer.
// TODO use mSleepTimeUs == 0 as an additional condition.
if (mEffectBufferValid) {
//ALOGV("writing effect buffer to sink buffer format %#x", mFormat);
if (requireMonoBlend()) {
mono_blend(mEffectBuffer, mEffectBufferFormat, mChannelCount, mNormalFrameCount,
true /*limit*/);
}
if (!hasFastMixer()) {
// Balance must take effect after mono conversion.
// We do it here if there is no FastMixer.
// mBalance detects zero balance within the class for speed (not needed here).
mBalance.setBalance(mMasterBalance.load());
mBalance.process((float *)mEffectBuffer, mNormalFrameCount);
}
// 将数据从mEffectBuffer拷贝到mSinkBuffer
memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
mNormalFrameCount * (mChannelCount + mHapticChannelCount));
// The sample data is partially interleaved when haptic channels exist,
// we need to adjust channels here.
if (mHapticChannelCount > 0) {
adjust_channels_non_destructive(mSinkBuffer, mChannelCount, mSinkBuffer,
mChannelCount + mHapticChannelCount,
audio_bytes_per_sample(mFormat),
audio_bytes_per_frame(mChannelCount, mFormat) * mNormalFrameCount);
}
}
// enable changes in effect chain
unlockEffectChains(effectChains);
if (!waitingAsyncCallback()) {
// mSleepTimeUs == 0 means we must write to audio hardware
if (mSleepTimeUs == 0) {
ssize_t ret = 0;
// writePeriodNs is updated >= 0 when ret > 0.
int64_t writePeriodNs = -1;
if (mBytesRemaining) {
// FIXME rewrite to reduce number of system calls
const int64_t lastIoBeginNs = systemTime();
// 将混合后的数据写入硬件
ret = threadLoop_write();
const int64_t lastIoEndNs = systemTime();
if (ret < 0) {
mBytesRemaining = 0;
} else if (ret > 0) {
mBytesWritten += ret;
mBytesRemaining -= ret;
const int64_t frames = ret / mFrameSize;
mFramesWritten += frames;
writePeriodNs = lastIoEndNs - mLastIoEndNs;
// process information relating to write time.
if (audio_has_proportional_frames(mFormat)) {
// we are in a continuous mixing cycle
if (mMixerStatus == MIXER_TRACKS_READY &&
loopCount == lastLoopCountWritten + 1) {
const double jitterMs =
TimestampVerifier<int64_t, int64_t>::computeJitterMs(
{frames, writePeriodNs},
{0, 0} /* lastTimestamp */, mSampleRate);
const double processMs =
(lastIoBeginNs - mLastIoEndNs) * 1e-6;
Mutex::Autolock _l(mLock);
mIoJitterMs.add(jitterMs);
mProcessTimeMs.add(processMs);
}
// write blocked detection
const int64_t deltaWriteNs = lastIoEndNs - lastIoBeginNs;
if (mType == MIXER && deltaWriteNs > maxPeriod) {
mNumDelayedWrites++;
if ((lastIoEndNs - lastWarning) > kWarningThrottleNs) {
ATRACE_NAME("underrun");
ALOGW("write blocked for %lld msecs, "
"%d delayed writes, thread %d",
(long long)deltaWriteNs / NANOS_PER_MILLISECOND,
mNumDelayedWrites, mId);
lastWarning = lastIoEndNs;
}
}
}
// update timing info.
mLastIoBeginNs = lastIoBeginNs;
mLastIoEndNs = lastIoEndNs;
lastLoopCountWritten = loopCount;
}
} else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
(mMixerStatus == MIXER_DRAIN_ALL)) {
threadLoop_drain();
}
if (mType == MIXER && !mStandby) {
if (mThreadThrottle
&& mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)
&& writePeriodNs > 0) { // we have write period info
// Limit MixerThread data processing to no more than twice the
// expected processing rate.
//
// This helps prevent underruns with NuPlayer and other applications
// which may set up buffers that are close to the minimum size, or use
// deep buffers, and rely on a double-buffering sleep strategy to fill.
//
// The throttle smooths out sudden large data drains from the device,
// e.g. when it comes out of standby, which often causes problems with
// (1) mixer threads without a fast mixer (which has its own warm-up)
// (2) minimum buffer sized tracks (even if the track is full,
// the app won't fill fast enough to handle the sudden draw).
//
// Total time spent in last processing cycle equals time spent in
// 1. threadLoop_write, as well as time spent in
// 2. threadLoop_mix (significant for heavy mixing, especially
// on low tier processors)
// it's OK if deltaMs is an overestimate.
const int32_t deltaMs = writePeriodNs / NANOS_PER_MILLISECOND;
const int32_t throttleMs = (int32_t)mHalfBufferMs - deltaMs;
if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
usleep(throttleMs * 1000);
// notify of throttle start on verbose log
ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,
"mixer(%p) throttle begin:"
" ret(%zd) deltaMs(%d) requires sleep %d ms",
this, ret, deltaMs, throttleMs);
mThreadThrottleTimeMs += throttleMs;
// Throttle must be attributed to the previous mixer loop's write time
// to allow back-to-back throttling.
// This also ensures proper timing statistics.
mLastIoEndNs = systemTime(); // we fetch the write end time again.
} else {
uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs;
if (diff > 0) {
// notify of throttle end on debug log
// but prevent spamming for bluetooth
ALOGD_IF(!audio_is_a2dp_out_device(outDevice()) &&
!audio_is_hearing_aid_out_device(outDevice()),
"mixer(%p) throttle end: throttle time(%u)", this, diff);
mThreadThrottleEndMs = mThreadThrottleTimeMs;
}
}
}
}
} else {
ATRACE_BEGIN("sleep");
Mutex::Autolock _l(mLock);
// suspended requires accurate metering of sleep time.
if (isSuspended()) {
// advance by expected sleepTime
timeLoopNextNs += microseconds((nsecs_t)mSleepTimeUs);
const nsecs_t nowNs = systemTime();
// compute expected next time vs current time.
// (negative deltas are treated as delays).
nsecs_t deltaNs = timeLoopNextNs - nowNs;
if (deltaNs < -kMaxNextBufferDelayNs) {
// Delays longer than the max allowed trigger a reset.
ALOGV("DelayNs: %lld, resetting timeLoopNextNs", (long long) deltaNs);
deltaNs = microseconds((nsecs_t)mSleepTimeUs);
timeLoopNextNs = nowNs + deltaNs;
} else if (deltaNs < 0) {
// Delays within the max delay allowed: zero the delta/sleepTime
// to help the system catch up in the next iteration(s)
ALOGV("DelayNs: %lld, catching-up", (long long) deltaNs);
deltaNs = 0;
}
// update sleep time (which is >= 0)
mSleepTimeUs = deltaNs / 1000;
}
if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
}
ATRACE_END();
}
}
// Finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
// same lock. This will also mutate and push a new fast mixer state.
threadLoop_removeTracks(tracksToRemove);
tracksToRemove.clear();
// FIXME I don't understand the need for this here;
// it was in the original code but maybe the
// assignment in saveOutputTracks() makes this unnecessary?
clearOutputTracks();
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
effectChains.clear();
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
}
............................................................................................................
return false;
}
所有的传过来的数据会在PlaybackThread的AudioMixer里面进行处理, 创建不同的线程。
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
// clean up deleted track ids in AudioMixer before allocating new tracks
(void)mTracks.processDeletedTrackIds([this](int trackId) {
// for each trackId, destroy it in the AudioMixer
if (mAudioMixer->exists(trackId)) {
mAudioMixer->destroy(trackId);
}
});
mTracks.clearDeletedTrackIds();
mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
// 激活Track的个数
size_t count = mActiveTracks.size();
............................................................................................
// 依次查询这些Track的情况
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i];
// this const just means the local variable doesn't change
Track* const track = t.get();
..............................................................................................
{ // local variable scope to avoid goto warning
// 拿到数据控制块
audio_track_cblk_t* cblk = track->cblk();
// The first time a track is added we wait
// for all its buffers to be filled before processing it
const int trackId = track->id();
..............................................................................................
size_t framesReady = track->framesReady();
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
..........................................................................................
// XXX: these things DON'T need to be done each time
// 数据准备,将track设置到mAudioMixer
mAudioMixer->setBufferProvider(trackId, track);
// 调用enable方法将track的enabled设置为true,然后调用invalidate()方法进行局部刷新
mAudioMixer->enable(trackId);
// 设置混音器的左右声道音量和音效的水平
mAudioMixer->setParameter(trackId, param, AudioMixer::VOLUME0, &vlf);
mAudioMixer->setParameter(trackId, param, AudioMixer::VOLUME1, &vrf);
mAudioMixer->setParameter(trackId, param, AudioMixer::AUXLEVEL, &vaf);
//设置音频数据格式
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::FORMAT, (void *)track->format());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_CHANNEL_MASK,
(void *)(uintptr_t)(mChannelMask | mHapticChannelMask));
........................................................................................
} else {
.......................................................................................
// 如果处于这4种状态之一,则加入移除队列
if ((track->sharedBuffer() != 0) || track->isTerminated() ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
// TODO: use actual buffer filling status instead of latency when available from
// audio HAL
size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
// reset会清零读写位置,表示没有可读数据
track->reset();
}
//再加入移除队列
tracksToRemove->add(track);
}
} else {
// 不处于上面三种状态时,表示暂时没有可读数据,则重试mRetryCount次
if (--(track->mRetryCount) <= 0) {
ALOGI("BUFFER TIMEOUT: remove(%d) from active list on thread %p",
trackId, this);
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
track->disable();
// If one track is not ready, mark the mixer also not ready if:
// - the mixer was ready during previous round OR
// - no other track is ready
} else if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY ||
mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
}
//禁止这个Track的混音
mAudioMixer->disable(trackId);
}
} // local variable scope to avoid goto warning
}
................................................................................................
return mixerStatus;
}
prepareTracks_l():参数处理的主要函数
audio_track_cblk控制块:
AudioTrack与AudioFlinger之间的数据传输分为两种方式,MODE_STATIC与MODE_STREAM
1. MODE_STATIC: static方式适用于数据较小,实时性比较高的情形,比如ring、系统铃声等。在这种模式下,是在AT端创建共享内存,一次性将数据copy到buffer中,然后传递到AF端。
2. MODE_STREAM:stream方式适用于数据较大,media播放等更多其他的情况,也比较复杂。在这种模式下,共享内存是由AF创建的,然后通过生产-消费者的模式进行数据的传输。
即AT是数据的生产者,AF是数据的消费者。这个数据读写的控制是由struct audio_track_cblk来实现的
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(trackId, track);
mAudioMixer->enable(trackId);
为每一个准备好的Track设置混音标志,通过mAudioMixer->enable混音
xref: /frameworks/av/media/libaudioprocessing/AudioMixer.cpp
void AudioMixer::enable(int name)
{
LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (!track->enabled) {
track->enabled = true;
ALOGV("enable(%d)", name);
invalidate();
}
}
xref: /frameworks/av/include/media/AudioMixer.h
void invalidate() {
mHook = &AudioMixer::process__validate;
}
将mHook设置为AudioMixer::process__validate
xref: /frameworks/av/media/libaudioprocessing/AudioMixer.cpp
void AudioMixer::process__validate()
{
bool all16BitsStereoNoResample = true;
bool resampling = false;
bool volumeRamp = false;
mEnabled.clear();
mGroups.clear();
for (const auto &pair : mTracks) {
const int name = pair.first;
const std::shared_ptr<Track> &t = pair.second;
if (!t->enabled) continue;
mEnabled.emplace_back(name); // we add to mEnabled in order of name.
mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.
uint32_t n = 0;
// FIXME can overflow (mask is only 3 bits)
n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
if (t->doesResample()) {
n |= NEEDS_RESAMPLE;
}
if (t->auxLevel != 0 && t->auxBuffer != NULL) {
n |= NEEDS_AUX;
}
if (t->volumeInc[0]|t->volumeInc[1]) {
volumeRamp = true;
} else if (!t->doesResample() && t->volumeRL == 0) {
n |= NEEDS_MUTE;
}
t->needs = n;
if (n & NEEDS_MUTE) {
t->hook = &Track::track__nop;
} else {
if (n & NEEDS_AUX) {
all16BitsStereoNoResample = false;
}
if (n & NEEDS_RESAMPLE) {
all16BitsStereoNoResample = false;
resampling = true;
t->hook = Track::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix + resample", name);
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t->hook = Track::getTrackHook(
(t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
&& t->channelMask == AUDIO_CHANNEL_OUT_MONO)
? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
all16BitsStereoNoResample = false;
}
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
t->hook = Track::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
t->mMixerInFormat, t->mMixerFormat);
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
"Track %d needs downmix", name);
}
}
}
}
// select the processing hooks
mHook = &AudioMixer::process__nop;
if (mEnabled.size() > 0) {
if (resampling) {
if (mOutputTemp.get() == nullptr) {
mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
}
if (mResampleTemp.get() == nullptr) {
mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
}
// 如果需要重采样,则选择process__genericResampling
mHook = &AudioMixer::process__genericResampling;
} else {
// 不需要重采样
mHook = &AudioMixer::process__genericNoResampling;
if (all16BitsStereoNoResample && !volumeRamp) {
if (mEnabled.size() == 1) {
const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
if ((t->needs & NEEDS_MUTE) == 0) {
mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
}
}
}
}
}
....................................................................................
}
....................................................................................
AudioMixer::process_hook_t AudioMixer::getProcessHook(
int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
{
if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
LOG_ALWAYS_FATAL("bad processType: %d", processType);
return NULL;
}
if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
// 当只有一路track的时候调用process__oneTrack16BitsStereoNoResampling
return &AudioMixer::process__oneTrack16BitsStereoNoResampling;
}
LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
switch (mixerInFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixer::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixer::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
case AUDIO_FORMAT_PCM_16_BIT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
return &AudioMixer::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
case AUDIO_FORMAT_PCM_16_BIT:
return &AudioMixer::process__noResampleOneTrack<
MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
break;
}
break;
default:
LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
break;
}
return NULL;
}
mHook是总的hook,它根据Track的个数和它的音频数据格式(采样率等)等情况,使用不同的处理函数。
process__validate:根据Track的格式、数量等信息选择其他的处理函数。
process__nop:什么都不做。
process__genericNoResampling:普通无需重采样。
process__genericResampling:普通需重采样。
process__OneTrack16BitsStereoNoResampling:一路音频流,双声道,PCM16格式,无需重采样。
当所有Track准备就绪后,最重要的工作就是混音,混音对象的process就派上了用场。hook最初的值为process__nop,当调用mAudioMixer->enable是能混音后,hook就会指向process__validate。