NuPlayer: wait for renderer flush before decoder shutdown

Bug: 17679341
Change-Id: Ie3883686891e7ee6fb45ceb01af1eb60b559d3a0
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 53eec91..1c73995 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -174,6 +174,7 @@
       mNumFramesDropped(0ll),
       mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
       mStarted(false) {
+    clearFlushComplete();
 }
 
 NuPlayer::~NuPlayer() {
@@ -333,25 +334,6 @@
     msg->post();
 }
 
-// static
-bool NuPlayer::IsFlushingState(FlushStatus state, bool *needShutdown) {
-    switch (state) {
-        case FLUSHING_DECODER:
-            if (needShutdown != NULL) {
-                *needShutdown = false;
-            }
-            return true;
-
-        case FLUSHING_DECODER_SHUTDOWN:
-            if (needShutdown != NULL) {
-                *needShutdown = true;
-            }
-            return true;
-
-        default:
-            return false;
-    }
-}
 
 void NuPlayer::writeTrackInfo(
         Parcel* reply, const sp<AMessage> format) const {
@@ -773,38 +755,9 @@
 
                 mRenderer->queueEOS(audio, err);
             } else if (what == Decoder::kWhatFlushCompleted) {
-                bool needShutdown;
-
-                if (audio) {
-                    CHECK(IsFlushingState(mFlushingAudio, &needShutdown));
-                    mFlushingAudio = FLUSHED;
-                } else {
-                    CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
-                    mFlushingVideo = FLUSHED;
-
-                    mVideoLateByUs = 0;
-                }
-
                 ALOGV("decoder %s flush completed", audio ? "audio" : "video");
 
-                if (needShutdown) {
-                    ALOGV("initiating %s decoder shutdown",
-                         audio ? "audio" : "video");
-
-                    // Widevine source reads must stop before releasing the video decoder.
-                    if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
-                        mSource->stop();
-                    }
-
-                    getDecoder(audio)->initiateShutdown();
-
-                    if (audio) {
-                        mFlushingAudio = SHUTTING_DOWN_DECODER;
-                    } else {
-                        mFlushingVideo = SHUTTING_DOWN_DECODER;
-                    }
-                }
-
+                handleFlushComplete(audio, true /* isDecoder */);
                 finishFlushIfPossible();
             } else if (what == Decoder::kWhatOutputFormatChanged) {
                 sp<AMessage> format;
@@ -957,6 +910,8 @@
                 CHECK(msg->findInt32("audio", &audio));
 
                 ALOGV("renderer %s flush completed.", audio ? "audio" : "video");
+                handleFlushComplete(audio, false /* isDecoder */);
+                finishFlushIfPossible();
             } else if (what == Renderer::kWhatVideoRenderingStart) {
                 notifyListener(MEDIA_INFO, MEDIA_INFO_RENDERING_START, 0);
             } else if (what == Renderer::kWhatMediaRenderingStart) {
@@ -1084,6 +1039,50 @@
     return ((mFlushingAudio != SHUT_DOWN) && (mFlushingAudio != SHUTTING_DOWN_DECODER));
 }
 
+void NuPlayer::handleFlushComplete(bool audio, bool isDecoder) {
+    // We wait for both the decoder flush and the renderer flush to complete
+    // before entering either the FLUSHED or the SHUTTING_DOWN_DECODER state.
+
+    mFlushComplete[audio][isDecoder] = true;
+    if (!mFlushComplete[audio][!isDecoder]) {
+        return;
+    }
+
+    FlushStatus *state = audio ? &mFlushingAudio : &mFlushingVideo;
+    switch (*state) {
+        case FLUSHING_DECODER:
+        {
+            *state = FLUSHED;
+
+            if (!audio) {
+                mVideoLateByUs = 0;
+            }
+            break;
+        }
+
+        case FLUSHING_DECODER_SHUTDOWN:
+        {
+            *state = SHUTTING_DOWN_DECODER;
+
+            ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video");
+            if (!audio) {
+                mVideoLateByUs = 0;
+                // Widevine source reads must stop before releasing the video decoder.
+                if (mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+                    mSource->stop();
+                }
+            }
+            getDecoder(audio)->initiateShutdown();
+            break;
+        }
+
+        default:
+            // decoder flush completes only occur in a flushing state.
+            LOG_ALWAYS_FATAL_IF(isDecoder, "decoder flush in invalid state %d", *state);
+            break;
+    }
+}
+
 void NuPlayer::finishFlushIfPossible() {
     if (mFlushingAudio != NONE && mFlushingAudio != FLUSHED
             && mFlushingAudio != SHUT_DOWN) {
@@ -1116,6 +1115,8 @@
     mFlushingAudio = NONE;
     mFlushingVideo = NONE;
 
+    clearFlushComplete();
+
     processDeferredActions();
 }
 
@@ -1720,6 +1721,8 @@
     FlushStatus newStatus =
         needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
 
+    mFlushComplete[audio][false /* isDecoder */] = false;
+    mFlushComplete[audio][true /* isDecoder */] = false;
     if (audio) {
         ALOGE_IF(mFlushingAudio != NONE,
                 "audio flushDecoder() is called in state %d", mFlushingAudio);