Merge "Allow frame count regress in AudioStreamOut" into tm-dev am: f8ec839056

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/18230820

Change-Id: Ic52c937d02ace2697e0495eaa44b4e74f9f9ff6f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp
index d8565bd..65ec0e8 100644
--- a/services/audioflinger/AudioStreamOut.cpp
+++ b/services/audioflinger/AudioStreamOut.cpp
@@ -39,6 +39,7 @@
         , mRateMultiplier(1)
         , mHalFormatHasProportionalFrames(false)
         , mHalFrameSize(0)
+        , mExpectRetrograde(false)
 {
 }
 
@@ -69,8 +70,12 @@
     const uint32_t truncatedPosition = (uint32_t)mRenderPosition;
     int32_t deltaHalPosition; // initialization not needed, overwitten by __builtin_sub_overflow()
     (void) __builtin_sub_overflow(halPosition, truncatedPosition, &deltaHalPosition);
+
     if (deltaHalPosition > 0) {
         mRenderPosition += deltaHalPosition;
+    } else if (mExpectRetrograde) {
+        mExpectRetrograde = false;
+        mRenderPosition -= static_cast<uint64_t>(-deltaHalPosition);
     }
     // Scale from HAL sample rate to application rate.
     *frames = mRenderPosition / mRateMultiplier;
@@ -187,6 +192,7 @@
 int AudioStreamOut::flush()
 {
     mRenderPosition = 0;
+    mExpectRetrograde = false;
     mFramesWritten = 0;
     mFramesWrittenAtStandby = 0;
     status_t result = stream->flush();
@@ -196,6 +202,7 @@
 int AudioStreamOut::standby()
 {
     mRenderPosition = 0;
+    mExpectRetrograde = false;
     mFramesWrittenAtStandby = mFramesWritten;
     return stream->standby();
 }
diff --git a/services/audioflinger/AudioStreamOut.h b/services/audioflinger/AudioStreamOut.h
index 565f43a..82fe238 100644
--- a/services/audioflinger/AudioStreamOut.h
+++ b/services/audioflinger/AudioStreamOut.h
@@ -93,13 +93,21 @@
     virtual status_t flush();
     virtual status_t standby();
 
+    // Avoid suppressing retrograde motion in mRenderPosition for gapless offload/direct when
+    // transitioning between tracks.
+    // The HAL resets the frame position without flush/stop being called, but calls back prior to
+    // this event. So, on the next occurrence of retrograde motion, we permit backwards movement of
+    // mRenderPosition.
+    virtual void presentationComplete() { mExpectRetrograde = true; }
+
 protected:
     uint64_t             mFramesWritten; // reset by flush
     uint64_t             mFramesWrittenAtStandby;
-    uint64_t             mRenderPosition; // reset by flush or standby
+    uint64_t             mRenderPosition; // reset by flush, standby, or presentation complete
     int                  mRateMultiplier;
     bool                 mHalFormatHasProportionalFrames;
     size_t               mHalFrameSize;
+    bool                 mExpectRetrograde; // see presentationComplete
 };
 
 } // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 49f6bfc..56ebb6e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6316,9 +6316,13 @@
                     track->isStopping_2() || track->isPaused()) {
                 // We have consumed all the buffers of this track.
                 // Remove it from the list of active tracks.
+                bool presComplete = false;
                 if (mStandby || !last ||
-                        track->presentationComplete(latency_l()) ||
+                        (presComplete = track->presentationComplete(latency_l())) ||
                         track->isPaused() || mHwPaused) {
+                    if (presComplete) {
+                        mOutput->presentationComplete();
+                    }
                     if (track->isStopping_2()) {
                         track->mState = TrackBase::STOPPED;
                     }
@@ -6897,7 +6901,8 @@
                 // Drain has completed or we are in standby, signal presentation complete
                 if (!(mDrainSequence & 1) || !last || mStandby) {
                     track->mState = TrackBase::STOPPED;
-                    track->presentationComplete(latency_l());
+                    mOutput->presentationComplete();
+                    track->presentationComplete(latency_l()); // always returns true
                     track->reset();
                     tracksToRemove->add(track);
                     // OFFLOADED stop resets frame counts.