PlaybackThread类继承ThreadBase,而ThreadBase又继承Thread ,在调用PlaybackThread.run的方法由父类的父类Thread时序,在Thread的run方法中会创建一个线程,并且允许其_threadLoop方法,在_threadLoop方法中会允许其子类的threadLoop方法,PlaybackThread 的threadLoop方法如下:
//frameworks/av/services/audioflinger/Threads.cpp
bool AudioFlinger::PlaybackThread::threadLoop()
{
tlNBLogWriter = mNBLogWriter.get();
Vector< sp<Track> > tracksToRemove;
mStandbyTimeNs = systemTime();
int64_t lastLoopCountWritten = -2; // never matches "previous" loop, when loopCount = 0.
// MIXER
nsecs_t lastWarning = 0;
// DUPLICATING
// FIXME could this be made local to while loop?
writeFrames = 0;
cacheParameters_l();
mSleepTimeUs = mIdleSleepTimeUs;
if (mType == MIXER || mType == SPATIALIZER) {
sleepTimeShift = 0;
}
CpuStats cpuStats;
const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid()));
acquireWakeLock();
// mNBLogWriter logging APIs can only be called by a single thread, typically the
// thread associated with this PlaybackThread.
// If you want to share the mNBLogWriter with other threads (for example, binder threads)
// then all such threads must agree to hold a common mutex before logging.
// So if you need to log when mutex is unlocked, set logString to a non-NULL string,
// and then that string will be logged at the next convenient opportunity.
// See reference to logString below.
const char *logString = NULL;
// Estimated time for next buffer to be written to hal. This is used only on
// suspended mode (for now) to help schedule the wait time until next iteration.
nsecs_t timeLoopNextNs = 0;
checkSilentMode_l();
audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
sendCheckOutputStageEffectsEvent();
// loopCount is used for statistics and diagnostics.
for (int64_t loopCount = 0; !exitPending(); ++loopCount)
{
// Log merge requests are performed during AudioFlinger binder transactions, but
// that does not cover audio playback. It's requested here for that reason.
mAudioFlinger->requestLogMerge();
cpuStats.sample(myName);
Vector< sp<EffectChain> > effectChains;
audio_session_t activeHapticSessionId = AUDIO_SESSION_NONE;
bool isHapticSessionSpatialized = false;
std::vector<sp<Track>> activeTracks;
// If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency.
//
// Note: we access outDeviceTypes() outside of mLock.
if (isMsdDevice() && outDeviceTypes().count(AUDIO_DEVICE_OUT_BUS) != 0) {
// Here, we try for the AF lock, but do not block on it as the latency
// is more informational.
if (mAudioFlinger->mLock.tryLock() == NO_ERROR) {
std::vector<PatchPanel::SoftwarePatch> swPatches;
double latencyMs;
status_t status = INVALID_OPERATION;
audio_patch_handle_t downstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
if (mAudioFlinger->mPatchPanel.getDownstreamSoftwarePatches(id(), &swPatches) == OK
&& swPatches.size() > 0) {
status = swPatches[0].getLatencyMs_l(&latencyMs);
downstreamPatchHandle = swPatches[0].getPatchHandle();
}
if (downstreamPatchHandle != lastDownstreamPatchHandle) {
mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = downstreamPatchHandle;
}
if (status == OK) {
// verify downstream latency (we assume a max reasonable
// latency of 5 seconds).
const double minLatency = 0., maxLatency = 5000.;
if (latencyMs >= minLatency && latencyMs <= maxLatency) {
ALOGVV("new downstream latency %lf ms", latencyMs);
} else {
ALOGD("out of range downstream latency %lf ms", latencyMs);
if (latencyMs < minLatency) latencyMs = minLatency;
else if (latencyMs > maxLatency) latencyMs = maxLatency;
}
mDownstreamLatencyStatMs.add(latencyMs);
}
mAudioFlinger->mLock.unlock();
}
} else {
if (lastDownstreamPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
// our device is no longer AUDIO_DEVICE_OUT_BUS, reset patch handle and stats.
mDownstreamLatencyStatMs.reset();
lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
}
}
if (mCheckOutputStageEffects.exchange(false)) {
checkOutputStageEffects();
}
{ // scope for mLock
Mutex::Autolock _l(mLock);
processConfigEvents_l();
if (mCheckOutputStageEffects.load()) {
continue;
}
// See comment at declaration of logString for why this is done under mLock
if (logString != NULL) {
mNBLogWriter->logTimestamp();
mNBLogWriter->log(logString);
logString = NULL;
}
collectTimestamps_l();
saveOutputTracks();
if (mSignalPending) {
// A signal was raised while we were unlocked
mSignalPending = false;
} else if (waitingAsyncCallback_l()) {
if (exitPending()) {
break;
}
bool released = false;
if (!keepWakeLock()) {
releaseWakeLock_l();
released = true;
}
const int64_t waitNs = computeWaitTimeNs_l();
ALOGV("wait async completion (wait time: %lld)", (long long)waitNs);
status_t status = mWaitWorkCV.waitRelative(mLock, waitNs); //然后Thread会在这里睡眠等待,直到AudioTrack:: start发送广播唤醒或超时
if (status == TIMED_OUT) {
mSignalPending = true; // if timeout recheck everything
}
ALOGV("async completion/wake");
if (released) {
acquireWakeLock_l();
}
mStandbyTimeNs = systemTime() + mStandbyDelayNs;
mSleepTimeUs = 0;
continue;
}
if ((mActiveTracks.isEmpty() && systemTime() > mStandbyTimeNs) ||
isSuspended()) {
// put audio hardware into standby after short delay
if (shouldStandby_l()) {
threadLoop_standby();
// This is where we go into standby
if (!mStandby) {
LOG_AUDIO_STATE();
mThreadMetrics.logEndInterval();
mThreadSnapshot.onEnd();
mStandby = true;
}
sendStatistics(false /* force */);
}
if (mActiveTracks.isEmpty() && mConfigEvents.isEmpty()) {
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
clearOutputTracks();
if (exitPending()) {
break;
}
releaseWakeLock_l();
// wait until we have something to do...
ALOGV("%s going to sleep", myName.string());
mWaitWorkCV.wait(mLock); //然后Thread会在这里睡眠等待,直到AudioTrack:: start发送广播唤醒
ALOGV("%s waking up", myName.string());
acquireWakeLock_l();
mMixerStatus = MIXER_IDLE;
mMixerStatusIgnoringFastTracks = MIXER_IDLE;
mBytesWritten = 0;
mBytesRemaining = 0;
checkSilentMode_l();
mStandbyTimeNs = systemTime() + mStandbyDelayNs;
mSleepTimeUs = mIdleSleepTimeUs;
if (mType == MIXER || mType == SPATIALIZER) {
sleepTimeShift = 0;
}
continue;
}
}
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove); //track预处理
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().
// The haptic data from the effect is at a higher priority than the one from track.
// TODO: Write haptic data directly to sink buffer when mixing.
if (mHapticChannelCount > 0) {
for (const auto& track : mActiveTracks) {
sp<EffectChain> effectChain = getEffectChain_l(track->sessionId());
if (effectChain != nullptr
&& effectChain->containsHapticGeneratingEffect_l()) {
activeHapticSessionId = track->sessionId();
isHapticSessionSpatialized =
mType == SPATIALIZER && track->isSpatialized();
break;
}
if (activeHapticSessionId == AUDIO_SESSION_NONE
&& track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
isHapticSessionSpatialized =
mType == SPATIALIZER && track->isSpatialized();
}
}
}
// 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->isStopped()
&& !track->isPaused()
&& !track->isTerminated()) {
ALOGV("%s: track(%d) %s underrun due to thread sleep of %zu frames",
__func__, track->id(), track->getTrackStateAsString(),
mNormalFrameCount);
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.
uint32_t mixerChannelCount = mEffectBufferValid ?
audio_channel_count_from_out_mask(mMixerChannelMask) : mChannelCount;
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);
}
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
mNormalFrameCount * (mixerChannelCount + 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.
uint32_t hapticSessionChannelCount = mEffectBufferValid ?
audio_channel_count_from_out_mask(mMixerChannelMask) :
mChannelCount;
if (mType == SPATIALIZER && !isHapticSessionSpatialized) {
hapticSessionChannelCount = mChannelCount;
}
const size_t audioBufferSize = mNormalFrameCount
* audio_bytes_per_frame(hapticSessionChannelCount,
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);
void *effectBuffer = (mType == SPATIALIZER) ? mPostSpatializerBuffer : mEffectBuffer;
if (requireMonoBlend()) {
mono_blend(effectBuffer, 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 *)effectBuffer, mNormalFrameCount);
}
// for SPATIALIZER thread, Move haptics channels from mEffectBuffer to
// mPostSpatializerBuffer if the haptics track is spatialized.
// Otherwise, the haptics channels are already in mPostSpatializerBuffer.
// For other thread types, the haptics channels are already in mEffectBuffer.
if (mType == SPATIALIZER && isHapticSessionSpatialized) {
const size_t srcBufferSize = mNormalFrameCount *
audio_bytes_per_frame(audio_channel_count_from_out_mask(mMixerChannelMask),
mEffectBufferFormat);
const size_t dstBufferSize = mNormalFrameCount
* audio_bytes_per_frame(mChannelCount, mEffectBufferFormat);
memcpy_by_audio_format((uint8_t*)mPostSpatializerBuffer + dstBufferSize,
mEffectBufferFormat,
(uint8_t*)mEffectBuffer + srcBufferSize,
mEffectBufferFormat,
mNormalFrameCount * mHapticChannelCount);
}
memcpy_by_audio_format(mSinkBuffer, mFormat, effectBuffer, 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(); //音频输出(向硬件抽象层写数据,写入HAL)
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);
if (mPipeSink.get() != nullptr) {
// Using the Monopipe availableToWrite, we estimate the current
// buffer size.
MonoPipe* monoPipe = static_cast<MonoPipe*>(mPipeSink.get());
const ssize_t
availableToWrite = mPipeSink->availableToWrite();
const size_t pipeFrames = monoPipe->maxFrames();
const size_t
remainingFrames = pipeFrames - max(availableToWrite, 0);
mMonopipePipeDepthStats.add(remainingFrames);
}
}
// write blocked detection
const int64_t deltaWriteNs = lastIoEndNs - lastIoBeginNs;
if ((mType == MIXER || mType == SPATIALIZER)
&& 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 || mType == SPATIALIZER) && !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) {
mThreadMetrics.logThrottleMs((double)throttleMs);
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(!isSingleDeviceType(
outDeviceTypes(), audio_is_a2dp_out_device) &&
!isSingleDeviceType(
outDeviceTypes(), audio_is_hearing_aid_out_device),
"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)); //然后Thread会在这里睡眠等待,直到AudioTrack:: start发送广播唤醒或超时
}
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); //移除相关track
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).
}
threadLoop_exit();
if (!mStandby) {
threadLoop_standby();
mStandby = true;
}
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
以上函数主要进行3步处理:
1、调用子类MixerThread的prepareTracks_l函数进行track预处理。
2、调用子类MixerThread的threadLoop_mix进行混音处理。
3、调用threadLoop_write函数,向硬件抽象层写数据,写入HAL。
AudioFlinger MixerThread prepareTracks_l
我们先看MixerThread的prepareTracks_l:
//frameworks/av/services/audioflinger/Threads.cpp
//重新准备音频流和混音器:ACTIVE 状态的 Track 会添加到 mActiveTracks,此外的Track 会从 mActiveTracks 上移除出来,然后重新准备 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
size_t count = mActiveTracks.size();
size_t mixedTracks = 0;
size_t tracksWithEffect = 0;
// counts only _active_ fast tracks
size_t fastTracks = 0;
uint32_t resetMask = 0; // bit mask of fast tracks that need to be reset
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
if (masterMute) {
masterVolume = 0;
}
// Delegate master volume control to effect in output mix effect chain if needed
sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
if (chain != 0) {
uint32_t v = (uint32_t)(masterVolume * (1 << 24));
chain->setVolume_l(&v, &v);
masterVolume = (float)((v + (1 << 23)) >> 24);
chain.clear();
}
// prepare a new state to push
FastMixerStateQueue *sq = NULL;
FastMixerState *state = NULL;
bool didModify = false;
FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;
bool coldIdle = false;
if (mFastMixer != 0) {
sq = mFastMixer->sq();
state = sq->begin();
coldIdle = state->mCommand == FastMixerState::COLD_IDLE;
}
mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found.
mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.
// DeferredOperations handles statistics after setting mixerStatus.
class DeferredOperations {
public:
DeferredOperations(mixer_state *mixerStatus, ThreadMetrics *threadMetrics)
: mMixerStatus(mixerStatus)
, mThreadMetrics(threadMetrics) {}
// when leaving scope, tally frames properly.
~DeferredOperations() {
// Tally underrun frames only if we are actually mixing (MIXER_TRACKS_READY)
// because that is when the underrun occurs.
// We do not distinguish between FastTracks and NormalTracks here.
size_t maxUnderrunFrames = 0;
if (*mMixerStatus == MIXER_TRACKS_READY && mUnderrunFrames.size() > 0) {
for (const auto &underrun : mUnderrunFrames) {
underrun.first->tallyUnderrunFrames(underrun.second);
maxUnderrunFrames = max(underrun.second, maxUnderrunFrames);
}
}
// send the max underrun frames for this mixer period
mThreadMetrics->logUnderrunFrames(maxUnderrunFrames);
}
// tallyUnderrunFrames() is called to update the track counters
// with the number of underrun frames for a particular mixer period.
// We defer tallying until we know the final mixer status.
void tallyUnderrunFrames(sp<Track> track, size_t underrunFrames) {
mUnderrunFrames.emplace_back(track, underrunFrames);
}
private:
const mixer_state * const mMixerStatus;
ThreadMetrics * const mThreadMetrics;
std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
} deferredOperations(&mixerStatus, &mThreadMetrics);
// implicit nested scope for variable capture
bool noFastHapticTrack = true;
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();
// process fast tracks
if (track->isFastTrack()) {
LOG_ALWAYS_FATAL_IF(mFastMixer.get() == nullptr,
"%s(%d): FastTrack(%d) present without FastMixer",
__func__, id(), track->id());
if (track->getHapticPlaybackEnabled()) {
noFastHapticTrack = false;
}
// It's theoretically possible (though unlikely) for a fast track to be created
// and then removed within the same normal mix cycle. This is not a problem, as
// the track never becomes active so it's fast mixer slot is never touched.
// The converse, of removing an (active) track and then creating a new track
// at the identical fast mixer slot within the same normal mix cycle,
// is impossible because the slot isn't marked available until the end of each cycle.
int j = track->mFastIndex;
ALOG_ASSERT(0 < j && j < (int)FastMixerState::sMaxFastTracks);
ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));
FastTrack *fastTrack = &state->mFastTracks[j];
// Determine whether the track is currently in underrun condition,
// and whether it had a recent underrun.
FastTrackDump *ftDump = &mFastMixerDumpState.mTracks[j];
FastTrackUnderruns underruns = ftDump->mUnderruns;
uint32_t recentFull = (underruns.mBitFields.mFull -
track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
uint32_t recentPartial = (underruns.mBitFields.mPartial -
track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;
uint32_t recentEmpty = (underruns.mBitFields.mEmpty -
track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;
uint32_t recentUnderruns = recentPartial + recentEmpty;
track->mObservedUnderruns = underruns;
// don't count underruns that occur while stopping or pausing
// or stopped which can occur when flush() is called while active
size_t underrunFrames = 0;
if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
recentUnderruns > 0) {
// FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
underrunFrames = recentUnderruns * mFrameCount;
}
// Immediately account for FastTrack underruns.
track->mAudioTrackServerProxy->tallyUnderrunFrames(underrunFrames);
// This is similar to the state machine for normal tracks,
// with a few modifications for fast tracks.
bool isActive = true;
switch (track->mState) {
case TrackBase::STOPPING_1:
// track stays active in STOPPING_1 state until first underrun
if (recentUnderruns > 0 || track->isTerminated()) {
track->mState = TrackBase::STOPPING_2;
}
break;
case TrackBase::PAUSING:
// ramp down is not yet implemented
track->setPaused();
break;
case TrackBase::RESUMING:
// ramp up is not yet implemented
track->mState = TrackBase::ACTIVE;
break;
case TrackBase::ACTIVE:
if (recentFull > 0 || recentPartial > 0) {
// track has provided at least some frames recently: reset retry count
track->mRetryCount = kMaxTrackRetries;
}
if (recentUnderruns == 0) {
// no recent underruns: stay active
break;
}
// there has recently been an underrun of some kind
if (track->sharedBuffer() == 0) {
// were any of the recent underruns "empty" (no frames available)?
if (recentEmpty == 0) {
// no, then ignore the partial underruns as they are allowed indefinitely
break;
}
// there has recently been an "empty" underrun: decrement the retry counter
if (--(track->mRetryCount) > 0) {
break;
}
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
track->disable();
// remove from active list, but state remains ACTIVE [confusing but true]
isActive = false;
break;
}
FALLTHROUGH_INTENDED;
case TrackBase::STOPPING_2:
case TrackBase::PAUSED:
case TrackBase::STOPPED:
case TrackBase::FLUSHED: // flush() while active
// Check for presentation complete if track is inactive
// We have consumed all the buffers of this track.
// This would be incomplete if we auto-paused on underrun
{
uint32_t latency = 0;
status_t result = mOutput->stream->getLatency(&latency);
ALOGE_IF(result != OK,
"Error when retrieving output stream latency: %d", result);
size_t audioHALFrames = (latency * mSampleRate) / 1000;
int64_t framesWritten = mBytesWritten / mFrameSize;
if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
// track stays in active list until presentation is complete
break;
}
}
if (track->isStopping_2()) {
track->mState = TrackBase::STOPPED;
}
if (track->isStopped()) {
// Can't reset directly, as fast mixer is still polling this track
// track->reset();
// So instead mark this track as needing to be reset after push with ack
resetMask |= 1 << i;
}
isActive = false;
break;
case TrackBase::IDLE:
default:
LOG_ALWAYS_FATAL("unexpected track state %d", (int)track->mState);
}
if (isActive) {
// was it previously inactive?
if (!(state->mTrackMask & (1 << j))) {
ExtendedAudioBufferProvider *eabp = track;
VolumeProvider *vp = track;
fastTrack->mBufferProvider = eabp;
fastTrack->mVolumeProvider = vp;
fastTrack->mChannelMask = track->mChannelMask;
fastTrack->mFormat = track->mFormat;
fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled();
fastTrack->mHapticIntensity = track->getHapticIntensity();
fastTrack->mHapticMaxAmplitude = track->getHapticMaxAmplitude();
fastTrack->mGeneration++;
state->mTrackMask |= 1 << j;
didModify = true;
// no acknowledgement required for newly active tracks
}
sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
float volume;
if (track->isPlaybackRestricted() || mStreamTypes[track->streamType()].mute) {
volume = 0.f;
} else {
volume = masterVolume * mStreamTypes[track->streamType()].volume;
}
handleVoipVolume_l(&volume);
// cache the combined master volume and stream type volume for fast mixer; this
// lacks any synchronization or barrier so VolumeProvider may read a stale value
const float vh = track->getVolumeHandler()->getVolume(
proxy->framesReleased()).first;
volume *= vh;
track->mCachedVolume = volume;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr));
track->setFinalVolume((vlf + vrf) / 2.f);
++fastTracks;
} else {
// was it previously active?
if (state->mTrackMask & (1 << j)) {
fastTrack->mBufferProvider = NULL;
fastTrack->mGeneration++;
state->mTrackMask &= ~(1 << j);
didModify = true;
// If any fast tracks were removed, we must wait for acknowledgement
// because we're about to decrement the last sp<> on those tracks.
block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
} else {
// ALOGW rather than LOG_ALWAYS_FATAL because it seems there are cases where an
// AudioTrack may start (which may not be with a start() but with a write()
// after underrun) and immediately paused or released. In that case the
// FastTrack state hasn't had time to update.
// TODO Remove the ALOGW when this theory is confirmed.
ALOGW("fast track %d should have been active; "
"mState=%d, mTrackMask=%#x, recentUnderruns=%u, isShared=%d",
j, (int)track->mState, state->mTrackMask, recentUnderruns,
track->sharedBuffer() != 0);
// Since the FastMixer state already has the track inactive, do nothing here.
}
tracksToRemove->add(track);
// Avoids a misleading display in dumpsys
track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
}
if (fastTrack->mHapticPlaybackEnabled != track->getHapticPlaybackEnabled()) {
fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled();
didModify = true;
}
continue;
}
{ // 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();
// if an active track doesn't exist in the AudioMixer, create it.
// use the trackId as the AudioMixer name.
if (!mAudioMixer->exists(trackId)) {
status_t status = mAudioMixer->create(
trackId,
track->mChannelMask,
track->mFormat,
track->mSessionId);
if (status != OK) {
ALOGW("%s(): AudioMixer cannot create track(%d)"
" mask %#x, format %#x, sessionId %d",
__func__, trackId,
track->mChannelMask, track->mFormat, track->mSessionId);
tracksToRemove->add(track);
track->invalidate(); // consider it dead.
continue;
}
}
// make sure that we have enough frames to mix one full buffer.
// enforce this condition only once to enable draining the buffer in case the client
// app does not call stop() and relies on underrun to stop:
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
size_t desiredFrames;
const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
desiredFrames = sourceFramesNeededWithTimestretch(
sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
// TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
// add frames already consumed but not yet released by the resampler
// because mAudioTrackServerProxy->framesReady() will include these frames
desiredFrames += mAudioMixer->getUnreleasedFrames(trackId);
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}
size_t framesReady = track->framesReady(); //数据准备完毕,设置音量、设置一些参数(混音的状态下,frameReady = 1,那么会进入下面的条件,进行AudioMixer参数设置)
if (ATRACE_ENABLED()) {
// I wish we had formatted trace names
std::string traceName("nRdy");
traceName += std::to_string(trackId);
ATRACE_INT(traceName.c_str(), framesReady);
}
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
ALOGVV("track(%d) s=%08x [OK] on thread %p", trackId, cblk->mServer, this);
mixedTracks++;
// track->mainBuffer() != mSinkBuffer or mMixerBuffer means
// there is an effect chain connected to the track
chain.clear();
if (track->mainBuffer() != mSinkBuffer &&
track->mainBuffer() != mMixerBuffer) {
if (mEffectBufferEnabled) {
mEffectBufferValid = true; // Later can set directly.
}
chain = getEffectChain_l(track->sessionId());
// Delegate volume control to effect in track effect chain if needed
if (chain != 0) {
tracksWithEffect++;
} else {
ALOGW("prepareTracks_l(): track(%d) attached to effect but no chain found on "
"session %d",
trackId, track->sessionId());
}
}
//音量参数设置:设置AudioMixer参数、音量、音轨、音频格式、重采样、混音(设置混音所需要的参数,包括:音量,混音的源buffer,目的buffer,音频格式,是否重采样等)
int param = AudioMixer::VOLUME;
if (track->mFillingUpStatus == Track::FS_FILLED) {
// no ramp for the first volume setting
track->mFillingUpStatus = Track::FS_ACTIVE;
if (track->mState == TrackBase::RESUMING) {
track->mState = TrackBase::ACTIVE;
// If a new track is paused immediately after start, do not ramp on resume.
if (cblk->mServer != 0) {
param = AudioMixer::RAMP_VOLUME;
}
}
mAudioMixer->setParameter(trackId, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
mLeftVolFloat = -1.0;
// FIXME should not make a decision based on mServer
} else if (cblk->mServer != 0) {
// If the track is stopped before the first frame was mixed,
// do not apply ramp
param = AudioMixer::RAMP_VOLUME;
}
// compute volume for this track
uint32_t vl, vr; // in U8.24 integer format
float vlf, vrf, vaf; // in [0.0, 1.0] float format
// read original volumes with volume control
float v = masterVolume * mStreamTypes[track->streamType()].volume;
// Always fetch volumeshaper volume to ensure state is updated.
const sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
const float vh = track->getVolumeHandler()->getVolume(
track->mAudioTrackServerProxy->framesReleased()).first;
if (mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
v = 0;
}
handleVoipVolume_l(&v);
if (track->isPausing()) {
vl = vr = 0;
vlf = vrf = vaf = 0.;
track->setPaused();
} else {
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
// track volumes come from shared memory, so can't be trusted and must be clamped
if (vlf > GAIN_FLOAT_UNITY) {
ALOGV("Track left volume out of range: %.3g", vlf);
vlf = GAIN_FLOAT_UNITY;
}
if (vrf > GAIN_FLOAT_UNITY) {
ALOGV("Track right volume out of range: %.3g", vrf);
vrf = GAIN_FLOAT_UNITY;
}
// now apply the master volume and stream type volume and shaper volume
vlf *= v * vh;
vrf *= v * vh;
// assuming master volume and stream type volume each go up to 1.0,
// then derive vl and vr as U8.24 versions for the effect chain
const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;
vl = (uint32_t) (scaleto8_24 * vlf);
vr = (uint32_t) (scaleto8_24 * vrf);
// vl and vr are now in U8.24 format
uint16_t sendLevel = proxy->getSendLevel_U4_12();
// send level comes from shared memory and so may be corrupt
if (sendLevel > MAX_GAIN_INT) {
ALOGV("Track send level out of range: %04X", sendLevel);
sendLevel = MAX_GAIN_INT;
}
// vaf is represented as [0.0, 1.0] float by rescaling sendLevel
vaf = v * sendLevel * (1. / MAX_GAIN_INT);
}
track->setFinalVolume((vrf + vlf) / 2.f);
// Delegate volume control to effect in track effect chain if needed
if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
// Do not ramp volume if volume is controlled by effect
param = AudioMixer::VOLUME;
// Update remaining floating point volume levels
vlf = (float)vl / (1 << 24);
vrf = (float)vr / (1 << 24);
track->mHasVolumeController = true;
} else {
// force no volume ramp when volume controller was just disabled or removed
// from effect chain to avoid volume spike
if (track->mHasVolumeController) {
param = AudioMixer::VOLUME;
}
track->mHasVolumeController = false;
}
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(trackId, track);
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());
if (mType == SPATIALIZER && !track->isSpatialized()) {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_CHANNEL_MASK,
(void *)(uintptr_t)(mChannelMask | mHapticChannelMask));
} else {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_CHANNEL_MASK,
(void *)(uintptr_t)(mMixerChannelMask | mHapticChannelMask));
}
// limit track sample rate to 2 x output sample rate, which changes at re-configuration
uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;
uint32_t reqSampleRate = proxy->getSampleRate();
if (reqSampleRate == 0) {
reqSampleRate = mSampleRate;
} else if (reqSampleRate > maxSampleRate) {
reqSampleRate = maxSampleRate;
}
mAudioMixer->setParameter(
trackId,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
AudioPlaybackRate playbackRate = proxy->getPlaybackRate();
mAudioMixer->setParameter(
trackId,
AudioMixer::TIMESTRETCH,
AudioMixer::PLAYBACK_RATE,
&playbackRate);
/*
* Select the appropriate output buffer for the track.
*
* Tracks with effects go into their own effects chain buffer
* and from there into either mEffectBuffer or mSinkBuffer.
*
* Other tracks can use mMixerBuffer for higher precision
* channel accumulation. If this buffer is enabled
* (mMixerBufferEnabled true), then selected tracks will accumulate
* into it.
*
*/
if (mMixerBufferEnabled
&& (track->mainBuffer() == mSinkBuffer
|| track->mainBuffer() == mMixerBuffer)) {
if (mType == SPATIALIZER && !track->isSpatialized()) {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)mEffectBufferFormat);
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)mPostSpatializerBuffer);
} else {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
// TODO: override track->mainBuffer()?
mMixerBufferValid = true;
}
} else {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)EFFECT_BUFFER_FORMAT);
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
}
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_MAX_AMPLITUDE, (void *)(&(track->mHapticMaxAmplitude)));
// reset retry count
track->mRetryCount = kMaxTrackRetries;
// If one track is ready, set the mixer ready if:
// - the mixer was not ready during previous round OR
// - no other track is not ready
if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
mixerStatus != MIXER_TRACKS_ENABLED) {
mixerStatus = MIXER_TRACKS_READY;
}
// Enable the next few lines to instrument a test for underrun log handling.
// TODO: Remove when we have a better way of testing the underrun log.
#if 0
static int i;
if ((++i & 0xf) == 0) {
deferredOperations.tallyUnderrunFrames(track, 10 /* underrunFrames */);
}
#endif
} else {
size_t underrunFrames = 0;
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
ALOGV("track(%d) underrun, track state %s framesReady(%zu) < framesDesired(%zd)",
trackId, track->getTrackStateAsString(), framesReady, desiredFrames);
underrunFrames = desiredFrames;
}
deferredOperations.tallyUnderrunFrames(track, underrunFrames);
// clear effect chain input buffer if an active track underruns to avoid sending
// previous audio buffer again to effects
chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
chain->clearInputBuffer();
}
ALOGVV("track(%d) s=%08x [NOT READY] on thread %p", trackId, cblk->mServer, this);
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()) {
track->reset();
}
tracksToRemove->add(track);
}
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
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;
}
}
mAudioMixer->disable(trackId);
}
} // local variable scope to avoid goto warning
}
if (mHapticChannelMask != AUDIO_CHANNEL_NONE && sq != NULL) {
// When there is no fast track playing haptic and FastMixer exists,
// enabling the first FastTrack, which provides mixed data from normal
// tracks, to play haptic data.
FastTrack *fastTrack = &state->mFastTracks[0];
if (fastTrack->mHapticPlaybackEnabled != noFastHapticTrack) {
fastTrack->mHapticPlaybackEnabled = noFastHapticTrack;
didModify = true;
}
}
// Push the new FastMixer state if necessary
bool pauseAudioWatchdog = false;
if (didModify) {
state->mFastTracksGen++;
// if the fast mixer was active, but now there are no fast tracks, then put it in cold idle
if (kUseFastMixer == FastMixer_Dynamic &&
state->mCommand == FastMixerState::MIX_WRITE && state->mTrackMask <= 1) {
state->mCommand = FastMixerState::COLD_IDLE;
state->mColdFutexAddr = &mFastMixerFutex;
state->mColdGen++;
mFastMixerFutex = 0;
if (kUseFastMixer == FastMixer_Dynamic) {
mNormalSink = mOutputSink;
}
// If we go into cold idle, need to wait for acknowledgement
// so that fast mixer stops doing I/O.
block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
pauseAudioWatchdog = true;
}
}
if (sq != NULL) {
sq->end(didModify);
// No need to block if the FastMixer is in COLD_IDLE as the FastThread
// is not active. (We BLOCK_UNTIL_ACKED when entering COLD_IDLE
// when bringing the output sink into standby.)
//
// We will get the latest FastMixer state when we come out of COLD_IDLE.
//
// This occurs with BT suspend when we idle the FastMixer with
// active tracks, which may be added or removed.
sq->push(coldIdle ? FastMixerStateQueue::BLOCK_NEVER : block);
}
#ifdef AUDIO_WATCHDOG
if (pauseAudioWatchdog && mAudioWatchdog != 0) {
mAudioWatchdog->pause();
}
#endif
// Now perform the deferred reset on fast tracks that have stopped
while (resetMask != 0) {
size_t i = __builtin_ctz(resetMask);
ALOG_ASSERT(i < count);
resetMask &= ~(1 << i);
sp<Track> track = mActiveTracks[i];
ALOG_ASSERT(track->isFastTrack() && track->isStopped());
track->reset();
}
// Track destruction may occur outside of threadLoop once it is removed from active tracks.
// Ensure the AudioMixer doesn't have a raw "buffer provider" pointer to the track if
// it ceases to be active, to allow safe removal from the AudioMixer at the start
// of prepareTracks_l(); this releases any outstanding buffer back to the track.
// See also the implementation of destroyTrack_l().
for (const auto &track : *tracksToRemove) {
const int trackId = track->id();
if (mAudioMixer->exists(trackId)) { // Normal tracks here, fast tracks in FastMixer.
mAudioMixer->setBufferProvider(trackId, nullptr /* bufferProvider */);
}
}
// remove all the tracks that need to be...
removeTracks_l(*tracksToRemove); //从mActiveTracks删除需要移除的track
if (getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX) != 0 ||
getEffectChain_l(AUDIO_SESSION_OUTPUT_STAGE) != 0) {
mEffectBufferValid = true;
}
if (mEffectBufferValid) {
// as long as there are effects we should clear the effects buffer, to avoid
// passing a non-clean buffer to the effect chain
memset(mEffectBuffer, 0, mEffectBufferSize);
if (mType == SPATIALIZER) {
memset(mPostSpatializerBuffer, 0, mPostSpatializerBufferSize);
}
}
// sink or mix buffer must be cleared if all tracks are connected to an
// effect chain as in this case the mixer will not write to the sink or mix buffer
// and track effects will accumulate into it
// always clear sink buffer for spatializer output as the output of the spatializer
// effect will be accumulated into it
if ((mBytesRemaining == 0) && (((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
(mixedTracks == 0 && fastTracks > 0)) || (mType == SPATIALIZER))) {
// FIXME as a performance optimization, should remember previous zero status
if (mMixerBufferValid) {
memset(mMixerBuffer, 0, mMixerBufferSize);
// TODO: In testing, mSinkBuffer below need not be cleared because
// the PlaybackThread::threadLoop() copies mMixerBuffer into mSinkBuffer
// after mixing.
//
// To enforce this guarantee:
// ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
// (mixedTracks == 0 && fastTracks > 0))
// must imply MIXER_TRACKS_READY.
// Later, we may clear buffers regardless, and skip much of this logic.
}
// FIXME as a performance optimization, should remember previous zero status
memset(mSinkBuffer, 0, mNormalFrameCount * mFrameSize);
}
// if any fast tracks, then status is ready
mMixerStatusIgnoringFastTracks = mixerStatus;
if (fastTracks > 0) {
mixerStatus = MIXER_TRACKS_READY;
}
return mixerStatus; //返回当前状态mMixerStatus
}
AudioFlinger MixerThread threadLoop_mix
然后是调用子类MixerThread的threadLoop_mix进行混音处理:
//frameworks/av/services/audioflinger/Threads.cpp
//混音处理(有了prepareTrack_l设置的参数,在threadLoop_mix中需要做的就是调用AudioMixer的process方法进行混音)
void AudioFlinger::MixerThread::threadLoop_mix()
{
// mix buffers...
mAudioMixer->process(); //调用AudioMixer的process方法
mCurrentWriteLength = mSinkBufferSize;//混音了多少数据
// increase sleep time progressively when application underrun condition clears.
// Only increase sleep time if the mixer is ready for two consecutive times to avoid
// that a steady state of alternating ready/not ready conditions keeps the sleep time
// such that we would underrun the audio HAL.
if ((mSleepTimeUs == 0) && (sleepTimeShift > 0)) {
sleepTimeShift--;
}
mSleepTimeUs = 0;
mStandbyTimeNs = systemTime() + mStandbyDelayNs; //待机时间更新
//TODO: delay standby when effects have a tail
}
AudioFlinger PlaybackThread threadLoop_write
调用threadLoop_write函数,向硬件抽象层写数据,写入HAL:
//frameworks/av/services/audioflinger/Threads.h
AudioStreamOut *mOutput;
//frameworks/av/services/audioflinger/Threads.cpp
// shared by MIXER and DIRECT, overridden by DUPLICATING
ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
LOG_HIST_TS();
mInWrite = true;
ssize_t bytesWritten;
const size_t offset = mCurrentWriteLength - mBytesRemaining;
// If an NBAIO sink is present, use it to write the normal mixer's submix
if (mNormalSink != 0) {
const size_t count = mBytesRemaining / mFrameSize;
ATRACE_BEGIN("write");
// update the setpoint when AudioFlinger::mScreenState changes
uint32_t screenState = AudioFlinger::mScreenState;
if (screenState != mScreenState) {
mScreenState = screenState;
MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
if (pipe != NULL) {
pipe->setAvgFrames((mScreenState & 1) ?
(pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
}
}
ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count);
ATRACE_END();
if (framesWritten > 0) {
bytesWritten = framesWritten * mFrameSize;
#ifdef TEE_SINK
mTee.write((char *)mSinkBuffer + offset, framesWritten);
#endif
} else {
bytesWritten = framesWritten;
}
// otherwise use the HAL / AudioStreamOut directly
} else {
// Direct output and offload threads
if (mUseAsyncWrite) {
ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
mWriteAckSequence += 2;
mWriteAckSequence |= 1;
ALOG_ASSERT(mCallbackThread != 0);
mCallbackThread->setWriteBlocked(mWriteAckSequence);
}
ATRACE_BEGIN("write");
// FIXME We should have an implementation of timestamps for direct output threads.
// They are used e.g for multichannel PCM playback over HDMI.
bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining); //调用AudioStreamOut的write函数
ATRACE_END();
if (mUseAsyncWrite &&
((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
// do not wait for async callback in case of error of full write
mWriteAckSequence &= ~1;
ALOG_ASSERT(mCallbackThread != 0);
mCallbackThread->setWriteBlocked(mWriteAckSequence);
}
}
mNumWrites++;
mInWrite = false;
if (mStandby) {
mThreadMetrics.logBeginInterval();
mThreadSnapshot.onBegin();
mStandby = false;
}
return bytesWritten;
}
在threadLoop_write中调用AudioStreamOut的write函数:
//frameworks/av/services/audioflinger/AudioStreamOut.h
sp<StreamOutHalInterface> stream;
//frameworks/av/services/audioflinger/AudioStreamOut.cpp
ssize_t AudioStreamOut::write(const void *buffer, size_t numBytes)
{
size_t bytesWritten;
status_t result = stream->write(buffer, numBytes, &bytesWritten);
if (result == OK && bytesWritten > 0 && mHalFrameSize > 0) {
mFramesWritten += bytesWritten / mHalFrameSize;
}
return result == OK ? bytesWritten : result;
}
在AudioStreamOut的write函数中调用StreamOutHalInterface的write函数,StreamOutHalInterface是一个接口,它的具体实现为StreamOutHalHidl:
//frameworks/av/media/libaudiohal/impl/StreamHalHidl.cpp
status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
TIME_CHECK();
if (mStream == 0) return NO_INIT;
*written = 0;
if (bytes == 0 && !mDataMQ) {
// Can't determine the size for the MQ buffer. Wait for a non-empty write request.
ALOGW_IF(mCallback.load().unsafe_get(), "First call to async write with 0 bytes");
return OK;
}
status_t status;
if (!mDataMQ) {
// In case if playback starts close to the end of a compressed track, the bytes
// that need to be written is less than the actual buffer size. Need to use
// full buffer size for the MQ since otherwise after seeking back to the middle
// data will be truncated.
size_t bufferSize;
if ((status = getCachedBufferSize(&bufferSize)) != OK) {
return status;
}
if (bytes > bufferSize) bufferSize = bytes;
if ((status = prepareForWriting(bufferSize)) != OK) {
return status;
}
}
status = callWriterThread(
WriteCommand::WRITE, "write", static_cast<const uint8_t*>(buffer), bytes,
[&] (const WriteStatus& writeStatus) {
*written = writeStatus.reply.written;
// Diagnostics of the cause of b/35813113.
ALOGE_IF(*written > bytes,
"hal reports more bytes written than asked for: %lld > %lld",
(long long)*written, (long long)bytes);
});
mStreamPowerLog.log(buffer, *written);
return status;
}
在StreamOutHalHidl的write函数中调用callWriterThread函数:
//frameworks/av/media/libaudiohal/impl/StreamHalHidl.h
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
std::atomic<pid_t> mWriterClient;
EventFlag* mEfGroup;
//frameworks/av/media/libaudiohal/impl/StreamHalHidl.cpp
status_t StreamOutHalHidl::callWriterThread(
WriteCommand cmd, const char* cmdName,
const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) {
if (!mCommandMQ->write(&cmd)) {
ALOGE("command message queue write failed for \"%s\"", cmdName);
return -EAGAIN;
}
if (data != nullptr) {
size_t availableToWrite = mDataMQ->availableToWrite();
if (dataSize > availableToWrite) {
ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",
(long long)dataSize, (long long)availableToWrite);
dataSize = availableToWrite;
}
if (!mDataMQ->write(data, dataSize)) { //向数据消息队列中写入数据
ALOGE("data message queue write failed for \"%s\"", cmdName);
}
}
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)); //唤醒数据写入线程
// TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
uint32_t efState = 0;
retry:
status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {
WriteStatus writeStatus;
writeStatus.retval = Result::NOT_INITIALIZED;
if (!mStatusMQ->read(&writeStatus)) {
ALOGE("status message read failed for \"%s\"", cmdName);
}
if (writeStatus.retval == Result::OK) {
ret = OK;
callback(writeStatus);
} else {
ret = processReturn(cmdName, writeStatus.retval);
}
return ret;
}
if (ret == -EAGAIN || ret == -EINTR) {
// Spurious wakeup. This normally retries no more than once.
goto retry;
}
return ret;
}