summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2014-12-15 14:33:13 -0800
committerRachad Alao <rachad@google.com>2014-12-16 01:38:34 +0000
commitd1f69b0b17acbd96987ecb2f3378abd394d05903 (patch)
treee3061c609dfaa2c217bb79a8d85e4571e8bd0d3d /services
parent8ddb02230d02968d471aa41b22433957c38827c0 (diff)
downloadframeworks_av-d1f69b0b17acbd96987ecb2f3378abd394d05903.zip
frameworks_av-d1f69b0b17acbd96987ecb2f3378abd394d05903.tar.gz
frameworks_av-d1f69b0b17acbd96987ecb2f3378abd394d05903.tar.bz2
audioflinger: implement pause/resume for direct outputs
Extend pause/resume support to direct output threads (was only for offload threads). If the HAL implements pause/resume, track pause/resume is forwarded to the HAL. Pause, flush, resume sequence is respected by executing the HAL calls in the playback thread (same as offload). Make sure the track flags on client side are consistent with the flags on server side. Bug: 17883772. Change-Id: I89b360d69818f7a9204bd36e3ec63a79e106ecf1
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Threads.cpp127
-rw-r--r--services/audioflinger/Threads.h9
-rw-r--r--services/audioflinger/Tracks.cpp9
3 files changed, 115 insertions, 30 deletions
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 87f636c..dacb12c 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)
{
@@ -1847,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 ||
@@ -3078,6 +3092,7 @@ void AudioFlinger::PlaybackThread::threadLoop_standby()
mCallbackThread->setWriteBlocked(mWriteAckSequence);
mCallbackThread->setDraining(mDrainSequence);
}
+ mHwPaused = false;
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
@@ -3990,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++) {
@@ -4008,6 +4026,28 @@ 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.
// Allow draining the buffer in case the client
@@ -4031,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();
}
}
@@ -4095,6 +4135,30 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
}
}
+ // 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);
@@ -4127,6 +4191,11 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
{
+ // do not write to HAL when paused
+ if (mHwPaused) {
+ sleepTime = idleSleepTime;
+ return;
+ }
if (sleepTime == 0) {
if (mMixerStatus == MIXER_TRACKS_ENABLED) {
sleepTime = activeSleepTime;
@@ -4139,6 +4208,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;
+}
+
// 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)
@@ -4248,8 +4349,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;
}
// ----------------------------------------------------------------------------
@@ -4358,8 +4461,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
@@ -4596,21 +4697,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);
@@ -4625,7 +4711,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
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 119e495..f5d0e27 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -809,7 +809,9 @@ public:
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
-
+ bool mHwSupportsPause;
+ bool mHwPaused;
+ bool mFlushPending;
private:
// timestamp latch:
// D input is written by threadLoop_write while mutex is unlocked, and read while locked
@@ -910,6 +912,8 @@ protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
+ virtual void threadLoop_exit();
+ virtual bool shouldStandby_l();
// volumes last sent to audio HAL with stream->set_volume()
float mLeftVolFloat;
@@ -940,12 +944,9 @@ protected:
virtual bool waitingAsyncCallback();
virtual bool waitingAsyncCallback_l();
- virtual bool shouldStandby_l();
virtual void onAddNewTrack_l();
private:
- bool mHwPaused;
- bool mFlushPending;
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
wp<Track> mPreviousTrack; // used to detect track switch
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index fcbf8f8..e970036 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -823,12 +823,11 @@ void AudioFlinger::PlaybackThread::Track::flush()
// this will be done by prepareTracks_l() when the track is stopped.
// prepareTracks_l() will see mState == FLUSHED, then
// remove from active track list, reset(), and trigger presentation complete
+ if (isDirect()) {
+ mFlushHwPending = true;
+ }
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
reset();
- if (thread->type() == ThreadBase::DIRECT) {
- DirectOutputThread *t = (DirectOutputThread *)playbackThread;
- t->flushHw_l();
- }
}
}
// Prevent flush being lost if the track is flushed and then resumed
@@ -841,7 +840,7 @@ void AudioFlinger::PlaybackThread::Track::flush()
// must be called with thread lock held
void AudioFlinger::PlaybackThread::Track::flushAck()
{
- if (!isOffloaded())
+ if (!isOffloaded() && !isDirect())
return;
mFlushHwPending = false;