VolumeShaper: Improve restore
Consider whether VolumeShaper has been started or not when
restoring (position). If the VolumeShaper hasn't been started
we restore in that state. If it has been started already,
we advance to the end assuming the duration has been played out.
Test: CTS and headset / kill audioserver
Bug: 37536598
Change-Id: I4b55dca6f6a859563fd20bad4c8f67d2c92321c0
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index 4ddb8d3..1282124 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -528,6 +528,10 @@
mDelayXOffset = xOffset;
}
+ bool isStarted() const {
+ return mStartFrame >= 0;
+ }
+
std::pair<T /* volume */, bool /* active */> getVolume(
int64_t trackFrameCount, double trackSampleRate) {
if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
@@ -752,6 +756,8 @@
return it->getState();
}
+ // getVolume() is not const, as it updates internal state.
+ // Once called, any VolumeShapers not already started begin running.
std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
AutoMutex _l(mLock);
mLastFrame = trackFrameCount;
@@ -768,6 +774,14 @@
return mLastVolume;
}
+ // Used by a client side VolumeHandler to ensure all the VolumeShapers
+ // indicate that they have been started. Upon a change in audioserver
+ // output sink, this information is used for restoration of the server side
+ // VolumeHandler.
+ void setStarted() {
+ (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
+ }
+
std::pair<T /* volume */, bool /* active */> getLastVolume() const {
AutoMutex _l(mLock);
return mLastVolume;
@@ -784,14 +798,12 @@
return ss.str();
}
- void forall(const std::function<VolumeShaper::Status (
- const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation)> &lambda) {
+ void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
AutoMutex _l(mLock);
VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
for (const auto &shaper : mVolumeShapers) {
- VS_LOG("forall applying lambda");
- (void)lambda(shaper.mConfiguration, shaper.mOperation);
+ VolumeShaper::Status status = lambda(shaper);
+ VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
}
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 3a0ce5e..4baf253 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -652,6 +652,9 @@
get_sched_policy(0, &mPreviousSchedulingGroup);
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
+
+ // Start our local VolumeHandler for restoration purposes.
+ mVolumeHandler->setStarted();
} else {
ALOGE("start() status %d", status);
mState = previousState;
@@ -2254,17 +2257,20 @@
}
}
// restore volume handler
- mVolumeHandler->forall([this](const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
- sp<VolumeShaper::Operation> operationToEnd = new VolumeShaper::Operation(*operation);
+ mVolumeHandler->forall([this](const VolumeShaper &shaper) -> VolumeShaper::Status {
+ sp<VolumeShaper::Operation> operationToEnd =
+ new VolumeShaper::Operation(shaper.mOperation);
// TODO: Ideally we would restore to the exact xOffset position
// as returned by getVolumeShaperState(), but we don't have that
// information when restoring at the client unless we periodically poll
// the server or create shared memory state.
//
- // For now, we simply advance to the end of the VolumeShaper effect.
- operationToEnd->setXOffset(1.f);
- return mAudioTrack->applyVolumeShaper(configuration, operationToEnd);
+ // For now, we simply advance to the end of the VolumeShaper effect
+ // if it has been started.
+ if (shaper.isStarted()) {
+ operationToEnd->setXOffset(1.f);
+ }
+ return mAudioTrack->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
if (mState == STATE_ACTIVE) {
@@ -2334,19 +2340,36 @@
AutoMutex lock(mLock);
mVolumeHandler->setIdIfNecessary(configuration);
VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(configuration, operation);
+
+ if (status == DEAD_OBJECT) {
+ if (restoreTrack_l("applyVolumeShaper") == OK) {
+ status = mAudioTrack->applyVolumeShaper(configuration, operation);
+ }
+ }
if (status >= 0) {
// save VolumeShaper for restore
mVolumeHandler->applyVolumeShaper(configuration, operation);
+ if (mState == STATE_ACTIVE || mState == STATE_STOPPING) {
+ mVolumeHandler->setStarted();
+ }
+ } else {
+ // warn only if not an expected restore failure.
+ ALOGW_IF(!((isOffloadedOrDirect_l() || mDoNotReconnect) && status == DEAD_OBJECT),
+ "applyVolumeShaper failed: %d", status);
}
return status;
}
sp<VolumeShaper::State> AudioTrack::getVolumeShaperState(int id)
{
- // TODO: To properly restore the AudioTrack
- // we will need to save the last state in AudioTrackShared.
AutoMutex lock(mLock);
- return mAudioTrack->getVolumeShaperState(id);
+ sp<VolumeShaper::State> state = mAudioTrack->getVolumeShaperState(id);
+ if (state.get() == nullptr && (mCblk->mFlags & CBLK_INVALID) != 0) {
+ if (restoreTrack_l("getVolumeShaperState") == OK) {
+ state = mAudioTrack->getVolumeShaperState(id);
+ }
+ }
+ return state;
}
status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index b082654..cba5cf5 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -2029,12 +2029,23 @@
ALOGV("setVolume");
t->setVolume(mLeftVolume, mRightVolume);
- // Dispatch any queued VolumeShapers when the track was not open.
- mVolumeHandler->forall([&t](const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
- return t->applyVolumeShaper(configuration, operation);
+ // Restore VolumeShapers for the MediaPlayer in case the track was recreated
+ // due to an output sink error (e.g. offload to non-offload switch).
+ mVolumeHandler->forall([&t](const VolumeShaper &shaper) -> VolumeShaper::Status {
+ sp<VolumeShaper::Operation> operationToEnd =
+ new VolumeShaper::Operation(shaper.mOperation);
+ // TODO: Ideally we would restore to the exact xOffset position
+ // as returned by getVolumeShaperState(), but we don't have that
+ // information when restoring at the client unless we periodically poll
+ // the server or create shared memory state.
+ //
+ // For now, we simply advance to the end of the VolumeShaper effect
+ // if it has been started.
+ if (shaper.isStarted()) {
+ operationToEnd->setXOffset(1.f);
+ }
+ return t->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
- mVolumeHandler->reset(); // After dispatching, clear VolumeShaper queue.
mSampleRateHz = sampleRate;
mFlags = flags;
@@ -2075,7 +2086,11 @@
if (mTrack != 0) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->setAuxEffectSendLevel(mSendLevel);
- return mTrack->start();
+ status_t status = mTrack->start();
+ if (status == NO_ERROR) {
+ mVolumeHandler->setStarted();
+ }
+ return status;
}
return NO_INIT;
}
@@ -2279,13 +2294,20 @@
Mutex::Autolock lock(mLock);
ALOGV("AudioOutput::applyVolumeShaper");
- // We take ownership of the VolumeShaper if set before the track is created.
mVolumeHandler->setIdIfNecessary(configuration);
+
+ VolumeShaper::Status status;
if (mTrack != 0) {
- return mTrack->applyVolumeShaper(configuration, operation);
+ status = mTrack->applyVolumeShaper(configuration, operation);
+ if (status >= 0) {
+ (void)mVolumeHandler->applyVolumeShaper(configuration, operation);
+ // TODO: start on exact AudioTrack state (STATE_ACTIVE || STATE_STOPPING)
+ mVolumeHandler->setStarted();
+ }
} else {
- return mVolumeHandler->applyVolumeShaper(configuration, operation);
+ status = mVolumeHandler->applyVolumeShaper(configuration, operation);
}
+ return status;
}
sp<VolumeShaper::State> MediaPlayerService::AudioOutput::getVolumeShaperState(int id)