-frameworks/av/services/audioflinger/Threads.cpp
void AudioFlinger::PlaybackThread::onFirstRef()
{
run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
}
bool AudioFlinger::PlaybackThread::threadLoop()
{
......
//设置待机时间
mStandbyTimeNs = systemTime();
int64_t lastLoopCountWritten = -2; // never matches "previous" loop, when loopCount = 0.
int64_t lastFramesWritten = -1; // track changes in timestamp server frames written
......
// 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;
std::vector<sp<Track>> activeTracks;
// If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency.
//
// Note: we access outDevice() outside of mLock.
if (isMsdDevice() && (outDevice() & 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) {
ALOGV("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;
}
}
{ // scope for mLock
Mutex::Autolock _l(mLock);
processConfigEvents_l();
// See comment at declaration of logString for why this is done under mLock
if (logString != NULL) {
mNBLogWriter->logTimestamp();
mNBLogWriter->log(logString);
logString = NULL;
}
// Collect timestamp statistics for the Playback Thread types that support it.
if (mType == MIXER
|| mType == DUPLICATING
|| mType == DIRECT
|| mType == OFFLOAD) { // no indentation
// Gather the framesReleased counters for all active tracks,
// and associate with the sink frames written out. We need
// this to convert the sink timestamp to the track timestamp.
bool kernelLocationUpdate = false;
ExtendedTimestamp timestamp; // use private copy to fetch
if (mStandby) {
mTimestampVerifier.discontinuity();
} else if (threadloop_getHalTimestamp_l(×tamp) == OK) {
mTimestampVerifier.add(timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
mSampleRate);
if (isTimestampCorrectionEnabled()) {
ALOGV("TS_BEFORE: %d %lld %lld", id(),
(long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
(long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp();
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
= correctedTimestamp.mFrames;
timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]
= correctedTimestamp.mTimeNs;
ALOGV("TS_AFTER: %d %lld %lld", id(),
(long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
(long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
// Note: Downstream latency only added if timestamp correction enabled.
if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
const int64_t newPosition =
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
// prevent retrograde
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
newPosition,
(mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
- mSuspendedFrames));
}
}
// We always fetch the timestamp here because often the downstream
// sink will block while writing.
// We keep track of the last valid kernel position in case we are in underrun
// and the normal mixer period is the same as the fast mixer period, or there
// is some error from the HAL.
if (mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK] =
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER];
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] =
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER];
}
if (timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
kernelLocationUpdate = true;
} else {
ALOGVV("getTimestamp error - no valid kernel position");
}
// copy over kernel info
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ mSuspendedFrames; // add frames discarded when suspended
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
} else {
mTimestampVerifier.error();
}
// mFramesWritten for non-offloaded tracks are contiguous
// even after standby() is called. This is useful for the track frame
// to sink frame mapping.
bool serverLocationUpdate = false;
if (mFramesWritten != lastFramesWritten) {
serverLocationUpdate = true;
lastFramesWritten = mFramesWritten;
}
// Only update timestamps if there is a meaningful change.
// Either the kernel timestamp must be valid or we have written something.
if (kernelLocationUpdate || serverLocationUpdate) {
if (serverLocationUpdate) {
// use the time before we called the HAL write - it is a bit more accurate
// to when the server last read data than the current time here.
//
// If we haven't written anything, mLastIoBeginNs will be -1
// and we use systemTime().
mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = mFramesWritten;
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastIoBeginNs == -1
? systemTime() : mLastIoBeginNs;
}
for (const sp<Track> &t : mActiveTracks) {
if (!t->isFastTrack()) {
t->updateTrackFrameInfo(
t->mAudioTrackServerProxy->framesReleased(),
mFramesWritten,
mSampleRate,
mTimestamp);
}
}
}
if (audio_has_proportional_frames(mFormat)) {
const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate);
if (latencyMs != 0.) { // note 0. means timestamp is empty.
mLatencyMs.add(latencyMs);
}
}
} // if (mType ... ) { // no indentation
#if 0
// logFormat example
if (z % 100 == 0) {
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
LOGT("This is an integer %d, this is a float %f, this is my "
"pid %p %% %s %t", 42, 3.14, "and this is a timestamp", ts);
LOGT("A deceptive null-terminated string %\0");
}
++z;
#endif
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);
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();
}
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);
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) {
sleepTimeShift = 0;
}
continue;
}
}
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
mActiveTracks.updatePowerState(this);
if (mMixerStatus == MIXER_IDLE && !mActiveTracks.size()) {
onIdleMixer();
}
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);
}
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 && mType != DIRECT) {
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 || mType == DIRECT) {
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);
}
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 (!mStandby && mHwSupportsSuspend) {
mOutput->stream->setParameters(String8("suspend_playback=true"));
}
if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
}
if (!mStandby && mHwSupportsSuspend) {
mOutput->stream->setParameters(String8("suspend_playback=false"));
}
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).
}
threadLoop_exit();
if (!mStandby) {
threadLoop_standby();
mStandby = true;
}
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
prepareTracks_l
void AudioFlinger::MixerThread::threadLoop_mix()
{
// mix buffers...
//调用AudioMixer混音
mAudioMixer->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
}
status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event __unused,
audio_session_t triggerSession __unused)
{
status = playbackThread->addTrack_l(this);
}
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
onAddNewTrack_l();
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
{
ALOGV("signal playback thread");
//发送广播,唤醒线程
broadcast_l();
}
bool AudioFlinger::PlaybackThread::threadLoop(){
...
threadLoop_mix();
mBytesRemaining = mCurrentWriteLength;
...
}
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);
}
// 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);
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;
mStandby = false;
return bytesWritten;
}
ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
// FIXME we should only do one push per cycle; confirm this is true
// Start the fast mixer if it's not already running
if (mFastMixer != 0) {
FastMixerStateQueue *sq = mFastMixer->sq();
FastMixerState *state = sq->begin();
if (state->mCommand != FastMixerState::MIX_WRITE &&
(kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)) {
if (state->mCommand == FastMixerState::COLD_IDLE) {
// FIXME workaround for first HAL write being CPU bound on some devices
ATRACE_BEGIN("write");
mOutput->write((char *)mSinkBuffer, 0);
ATRACE_END();
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
(void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
mAudioWatchdog->resume();
}
#endif
}
state->mCommand = FastMixerState::MIX_WRITE;
#ifdef FAST_THREAD_STATISTICS
mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
FastThreadDumpState::kSamplingNforLowRamDevice : FastThreadDumpState::kSamplingN);
#endif
sq->end();
sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
if (kUseFastMixer == FastMixer_Dynamic) {
mNormalSink = mPipeSink;
}
} else {
sq->end(false /*didModify*/);
}
}
return PlaybackThread::threadLoop_write();
}
bool AudioFlinger::PlaybackThread::threadLoop(){
...
ssize_t ret = threadLoop_write();
mBytesWritten += ret;
mBytesRemaining -= ret;
...
}
bool AudioFlinger::PlaybackThread::threadLoop()
{
...
if(mBytesRemaining == 0){
if(mMixerStatus == MIXER_TRACK_READY){
threadLoop_mix();
}
}
...
}