/ prepareTracks_l() must be called with ThreadBase::mLock held
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
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;
if (mFastMixer != 0) {
sq = mFastMixer->sq();
state = sq->begin();
}
mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found.
mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i].promote();
if (t == 0) {
continue;
}
// this const just means the local variable doesn't change
Track* const track = t.get();
// process fast tracks
if (track->isFastTrack()) {
// 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
if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
recentUnderruns > 0) {
// FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
} else {
track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
// 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;
}
// fall through
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
{
size_t audioHALFrames =
(mOutput->stream->get_latency(mOutput->stream)*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", 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->mGeneration++;
state->mTrackMask |= 1 << j;
didModify = true;
// no acknowledgement required for newly active tracks
}
// 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
track->mCachedVolume = masterVolume * mStreamTypes[track->streamType()].volume;
++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 {
LOG_ALWAYS_FATAL("fast track %d should have been active; "
"mState=%d, mTrackMask=%#x, recentUnderruns=%u, isShared=%d",
j, track->mState, state->mTrackMask, recentUnderruns,
track->sharedBuffer() != 0);
}
tracksToRemove->add(track);
// Avoids a misleading display in dumpsys
track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
}
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
int name = track->name();
// 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(track->name());
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}
size_t framesReady = track->framesReady();
if (ATRACE_ENABLED()) {
// I wish we had formatted trace names
char traceName[16];
strcpy(traceName, "nRdy");
int name = track->name();
if (AudioMixer::TRACK0 <= name &&
name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) {
name -= AudioMixer::TRACK0;
traceName[4] = (name / 10) + '0';
traceName[5] = (name % 10) + '0';
} else {
traceName[4] = '?';
traceName[5] = '?';
}
traceName[6] = '\0';
ATRACE_INT(traceName, framesReady);
}
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
ALOGVV("track %d s=%08x [OK] on thread %p", name, 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",
name, track->sessionId());
}
}
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;
param = AudioMixer::RAMP_VOLUME;
}
mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
// 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
if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
vl = vr = 0;
vlf = vrf = vaf = 0.;
if (track->isPausing()) {
track->setPaused();
}
} else {
// read original volumes with volume control
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
//add for boot video:sync audio for boot
char value[PROPERTY_VALUE_MAX] = "";
property_get("persist.sys.bootvideo.enable", value, "false");
if(!strcmp(value,"true")){
property_get("sys.bootvideo.closed", value, "1");
if (atoi(value) == 0){
ALOGV("bootvideo running now,audioflinger no need to control volume");
v = 1.0;
}
}
AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
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
vlf *= v;
vrf *= v;
// 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);
}
// 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(name, track);
mAudioMixer->enable(name);
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::FORMAT, (void *)track->format());
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask);
// 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 = track->mAudioTrackServerProxy->getSampleRate();
if (reqSampleRate == 0) {
reqSampleRate = mSampleRate;
} else if (reqSampleRate > maxSampleRate) {
reqSampleRate = maxSampleRate;
}
mAudioMixer->setParameter(
name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
mAudioMixer->setParameter(
name,
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)) {
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
// TODO: override track->mainBuffer()?
mMixerBufferValid = true;
} else {
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)AUDIO_FORMAT_PCM_16_BIT);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
}
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
// 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;
}
} else {
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
track, framesReady, desiredFrames);
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
} else {
track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
}
// 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", name, 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", name, 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(name);
}
} // local variable scope to avoid goto warning
}
// 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);
sq->push(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> t = mActiveTracks[i].promote();
if (t == 0) {
continue;
}
Track* track = t.get();
ALOG_ASSERT(track->isFastTrack() && track->isStopped());
track->reset();
}
// remove all the tracks that need to be...
removeTracks_l(*tracksToRemove);
if (getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX) != 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);
}
// 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
if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
(mixedTracks == 0 && fastTracks > 0))) {
// 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;
}
根据上下文优化这段代码
最新发布