summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/Threads.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger/Threads.cpp')
-rw-r--r--services/audioflinger/Threads.cpp193
1 files changed, 148 insertions, 45 deletions
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b429cc2..5282ffe 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1197,6 +1197,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
// mLatchD, mLatchQ,
mLatchDValid(false), mLatchQValid(false)
{
@@ -1224,15 +1225,12 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
readOutputParameters_l();
- // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
- // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
+ // ++ operator does not compile
for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
- // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
- // because mAudioFlinger doesn't have one to copy from
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -1625,13 +1623,15 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
if (track->isExternalTrack()) {
TrackBase::track_state state = track->mState;
mLock.unlock();
- status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
+ status = AudioSystem::startOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
mLock.lock();
// abort track was stopped/paused while we released the lock
if (state != track->mState) {
if (status == NO_ERROR) {
mLock.unlock();
- AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ AudioSystem::stopOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
mLock.lock();
}
return INVALID_OPERATION;
@@ -1848,6 +1848,19 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()
}
}
+ mHwSupportsPause = false;
+ if (mOutput->flags & AUDIO_OUTPUT_FLAG_DIRECT) {
+ if (mOutput->stream->pause != NULL) {
+ if (mOutput->stream->resume != NULL) {
+ mHwSupportsPause = true;
+ } else {
+ ALOGW("direct output implements pause but not resume");
+ }
+ } else if (mOutput->stream->resume != NULL) {
+ ALOGW("direct output implements resume but not pause");
+ }
+ }
+
// Calculate size of normal sink buffer relative to the HAL output buffer size
double multiplier = 1.0;
if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
@@ -2060,13 +2073,15 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
if (track->isExternalTrack()) {
- AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ AudioSystem::stopOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
#ifdef ADD_BATTERY_DATA
// to track the speaker usage
addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
#endif
if (track->isTerminated()) {
- AudioSystem::releaseOutput(mId);
+ AudioSystem::releaseOutput(mId, track->streamType(),
+ (audio_session_t)track->sessionId());
}
}
}
@@ -2179,7 +2194,13 @@ void AudioFlinger::PlaybackThread::threadLoop_drain()
void AudioFlinger::PlaybackThread::threadLoop_exit()
{
- // Default implementation has nothing to do
+ {
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<Track> track = mTracks[i];
+ track->invalidate();
+ }
+ }
}
/*
@@ -2687,7 +2708,8 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
if (mNormalSink != 0) {
return mNormalSink->getTimestamp(timestamp);
}
- if ((mType == OFFLOAD || mType == DIRECT) && mOutput->stream->get_presentation_position) {
+ if ((mType == OFFLOAD || mType == DIRECT)
+ && mOutput != NULL && mOutput->stream->get_presentation_position) {
uint64_t position64;
int ret = mOutput->stream->get_presentation_position(
mOutput->stream, &position64, &timestamp.mTime);
@@ -3070,6 +3092,7 @@ void AudioFlinger::PlaybackThread::threadLoop_standby()
mCallbackThread->setWriteBlocked(mWriteAckSequence);
mCallbackThread->setDraining(mDrainSequence);
}
+ mHwPaused = false;
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
@@ -3982,6 +4005,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
{
size_t count = mActiveTracks.size();
mixer_state mixerStatus = MIXER_IDLE;
+ bool doHwPause = false;
+ bool doHwResume = false;
+ bool flushPending = false;
// find out which tracks need to be processed
for (size_t i = 0; i < count; i++) {
@@ -4000,10 +4026,37 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
sp<Track> l = mLatestActiveTrack.promote();
bool last = l.get() == track;
+ if (mHwSupportsPause && track->isPausing()) {
+ track->setPaused();
+ if (last && !mHwPaused) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
+ tracksToRemove->add(track);
+ } else if (track->isFlushPending()) {
+ track->flushAck();
+ if (last) {
+ flushPending = true;
+ }
+ } else if (mHwSupportsPause && track->isResumePending()){
+ track->resumeAck();
+ if (last) {
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ }
+ }
+ }
+
// The first time a track is added we wait
- // for all its buffers to be filled before processing it
+ // for all its buffers to be filled before processing it.
+ // Allow draining the buffer in case the client
+ // app does not call stop() and relies on underrun to stop:
+ // hence the test on (track->mRetryCount > 1).
+ // If retryCount<=1 then track is about to underrun and be removed.
uint32_t minFrames;
- if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()) {
+ if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()
+ && (track->mRetryCount > 1)) {
minFrames = mNormalFrameCount;
} else {
minFrames = 1;
@@ -4018,8 +4071,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
track->mFillingUpStatus = Track::FS_ACTIVE;
// make sure processVolume_l() will apply new volume even if 0
mLeftVolFloat = mRightVolFloat = -1.0;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
+ if (!mHwSupportsPause) {
+ track->resumeAck();
}
}
@@ -4030,6 +4083,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
track->mRetryCount = kMaxTrackRetriesDirect;
mActiveTrack = t;
mixerStatus = MIXER_TRACKS_READY;
+ if (usesHwAvSync() && mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ }
}
} else {
// clear effect chain input buffer if the last active track started underruns
@@ -4058,9 +4115,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
track->mState = TrackBase::STOPPED;
}
if (track->isStopped()) {
- if (track->mState == TrackBase::FLUSHED) {
- flushHw_l();
- }
track->reset();
}
tracksToRemove->add(track);
@@ -4077,11 +4131,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
+ if (usesHwAvSync() && !mHwPaused && !mStandby) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
}
}
}
}
+ // if an active track did not command a flush, check for pending flush on stopped tracks
+ if (!flushPending) {
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ if (mTracks[i]->isFlushPending()) {
+ mTracks[i]->flushAck();
+ flushPending = true;
+ }
+ }
+ }
+
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (mHwSupportsPause && !mStandby &&
+ (doHwPause || (flushPending && !mHwPaused && (count != 0)))) {
+ mOutput->stream->pause(mOutput->stream);
+ }
+ if (flushPending) {
+ flushHw_l();
+ }
+ if (mHwSupportsPause && !mStandby && doHwResume) {
+ mOutput->stream->resume(mOutput->stream);
+ }
// remove all the tracks that need to be...
removeTracks_l(*tracksToRemove);
@@ -4114,6 +4196,11 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
{
+ // do not write to HAL when paused
+ if (mHwPaused || (usesHwAvSync() && mStandby)) {
+ sleepTime = idleSleepTime;
+ return;
+ }
if (sleepTime == 0) {
if (mMixerStatus == MIXER_TRACKS_ENABLED) {
sleepTime = activeSleepTime;
@@ -4126,6 +4213,38 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
}
}
+void AudioFlinger::DirectOutputThread::threadLoop_exit()
+{
+ {
+ Mutex::Autolock _l(mLock);
+ bool flushPending = false;
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ if (mTracks[i]->isFlushPending()) {
+ mTracks[i]->flushAck();
+ flushPending = true;
+ }
+ }
+ if (flushPending) {
+ flushHw_l();
+ }
+ }
+ PlaybackThread::threadLoop_exit();
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::DirectOutputThread::shouldStandby_l()
+{
+ bool trackPaused = false;
+
+ // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
+ // after a timeout and we will enter standby then.
+ if (mTracks.size() > 0) {
+ trackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ }
+
+ return !mStandby && !(trackPaused || (usesHwAvSync() && mHwPaused));
+}
+
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
audio_format_t format __unused, int sessionId __unused)
@@ -4235,8 +4354,10 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l()
void AudioFlinger::DirectOutputThread::flushHw_l()
{
- if (mOutput->stream->flush != NULL)
+ if (mOutput->stream->flush != NULL) {
mOutput->stream->flush(mOutput->stream);
+ }
+ mHwPaused = false;
}
// ----------------------------------------------------------------------------
@@ -4345,8 +4466,6 @@ void AudioFlinger::AsyncCallbackThread::resetDraining()
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
: DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
- mHwPaused(false),
- mFlushPending(false),
mPausedBytesRemaining(0)
{
//FIXME: mStandby should be set to true by ThreadBase constructor
@@ -4583,21 +4702,6 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
return false;
}
-// must be called with thread mutex locked
-bool AudioFlinger::OffloadThread::shouldStandby_l()
-{
- bool trackPaused = false;
-
- // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
- // after a timeout and we will enter standby then.
- if (mTracks.size() > 0) {
- trackPaused = mTracks[mTracks.size() - 1]->isPaused();
- }
-
- return !mStandby && !trackPaused;
-}
-
-
bool AudioFlinger::OffloadThread::waitingAsyncCallback()
{
Mutex::Autolock _l(mLock);
@@ -4612,7 +4716,6 @@ void AudioFlinger::OffloadThread::flushHw_l()
mBytesRemaining = 0;
mPausedWriteLength = 0;
mPausedBytesRemaining = 0;
- mHwPaused = false;
if (mUseAsyncWrite) {
// discard any pending drain or write ack by incrementing sequence
@@ -4694,15 +4797,15 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
+ // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT
+ // for delivery downstream as needed. This in-place conversion is safe as
+ // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format
+ // (AUDIO_FORMAT_PCM_8_BIT is not allowed here).
+ if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT,
+ mSinkBuffer, mFormat, writeFrames * mChannelCount);
+ }
for (size_t i = 0; i < outputTracks.size(); i++) {
- // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT
- // for delivery downstream as needed. This in-place conversion is safe as
- // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format
- // (AUDIO_FORMAT_PCM_8_BIT is not allowed here).
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT,
- mSinkBuffer, mFormat, writeFrames * mChannelCount);
- }
outputTracks[i]->write(reinterpret_cast<int16_t*>(mSinkBuffer), writeFrames);
}
mStandby = false;
@@ -4746,7 +4849,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
frameCount,
IPCThreadState::self()->getCallingUid());
if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
+ thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
mOutputTracks.add(outputTrack);
ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
updateWaitTime_l();