notify seek complete upon first video output frame

Bug: 18541814
Change-Id: Ie4e0976885f26eb253460eab371cb181ea85f2db
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 405278c..c69f74b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -80,6 +80,21 @@
     DISALLOW_EVIL_CONSTRUCTORS(SeekAction);
 };
 
+struct NuPlayer::ResumeDecoderAction : public Action {
+    ResumeDecoderAction(bool needNotify)
+        : mNeedNotify(needNotify) {
+    }
+
+    virtual void execute(NuPlayer *player) {
+        player->performResumeDecoders(mNeedNotify);
+    }
+
+private:
+    bool mNeedNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ResumeDecoderAction);
+};
+
 struct NuPlayer::SetSurfaceAction : public Action {
     SetSurfaceAction(const sp<NativeWindowWrapper> &wrapper)
         : mWrapper(wrapper) {
@@ -163,6 +178,7 @@
       mTimedTextGeneration(0),
       mFlushingAudio(NONE),
       mFlushingVideo(NONE),
+      mResumePending(false),
       mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
       mStarted(false) {
     clearFlushComplete();
@@ -553,11 +569,11 @@
                         new SimpleAction(&NuPlayer::performScanSources));
             }
 
-            // After a flush wihtout shutdown, decoder is paused.
-            // Don't resume it until source is seeked, otherwise it could
+            // After a flush without shutdown, decoder is paused.
+            // Don't resume it until source seek is done, otherwise it could
             // start pulling stale data too soon.
             mDeferredActions.push_back(
-                    new SimpleAction(&NuPlayer::performResumeDecoders));
+                    new ResumeDecoderAction(false /* needNotify */));
 
             processDeferredActions();
             break;
@@ -747,6 +763,8 @@
                 }
 
                 finishFlushIfPossible();
+            } else if (what == DecoderBase::kWhatResumeCompleted) {
+                finishResume();
             } else if (what == DecoderBase::kWhatError) {
                 status_t err;
                 if (!msg->findInt32("err", &err) || err == OK) {
@@ -923,11 +941,11 @@
             mDeferredActions.push_back(
                     new SeekAction(seekTimeUs, needNotify));
 
-            // After a flush wihtout shutdown, decoder is paused.
-            // Don't resume it until source is seeked, otherwise it could
+            // After a flush without shutdown, decoder is paused.
+            // Don't resume it until source seek is done, otherwise it could
             // start pulling stale data too soon.
             mDeferredActions.push_back(
-                    new SimpleAction(&NuPlayer::performResumeDecoders));
+                    new ResumeDecoderAction(needNotify));
 
             processDeferredActions();
             break;
@@ -1499,15 +1517,6 @@
     mSource->seekTo(seekTimeUs);
     ++mTimedTextGeneration;
 
-    if (mDriver != NULL) {
-        sp<NuPlayerDriver> driver = mDriver.promote();
-        if (driver != NULL) {
-            if (needNotify) {
-                driver->notifySeekComplete();
-            }
-        }
-    }
-
     // everything's flushed, continue playback.
 }
 
@@ -1593,13 +1602,39 @@
     }
 }
 
-void NuPlayer::performResumeDecoders() {
+void NuPlayer::performResumeDecoders(bool needNotify) {
+    if (needNotify) {
+        mResumePending = true;
+        if (mVideoDecoder == NULL) {
+            // if audio-only, we can notify seek complete now,
+            // as the resume operation will be relatively fast.
+            finishResume();
+        }
+    }
+
     if (mVideoDecoder != NULL) {
-        mVideoDecoder->signalResume();
+        // When there is continuous seek, MediaPlayer will cache the seek
+        // position, and send down new seek request when previous seek is
+        // complete. Let's wait for at least one video output frame before
+        // notifying seek complete, so that the video thumbnail gets updated
+        // when seekbar is dragged.
+        mVideoDecoder->signalResume(needNotify);
     }
 
     if (mAudioDecoder != NULL) {
-        mAudioDecoder->signalResume();
+        mAudioDecoder->signalResume(false /* needNotify */);
+    }
+}
+
+void NuPlayer::finishResume() {
+    if (mResumePending) {
+        mResumePending = false;
+        if (mDriver != NULL) {
+            sp<NuPlayerDriver> driver = mDriver.promote();
+            if (driver != NULL) {
+                driver->notifySeekComplete();
+            }
+        }
     }
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 6856af1..6be38a4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -93,6 +93,7 @@
     struct Action;
     struct SeekAction;
     struct SetSurfaceAction;
+    struct ResumeDecoderAction;
     struct FlushDecoderAction;
     struct PostMessageAction;
     struct SimpleAction;
@@ -169,6 +170,9 @@
     FlushStatus mFlushingAudio;
     FlushStatus mFlushingVideo;
 
+    // Status of flush responses from the decoder and renderer.
+    bool mResumePending;
+
     int32_t mVideoScalingMode;
 
     bool mStarted;
@@ -205,6 +209,8 @@
 
     void flushDecoder(bool audio, bool needShutdown);
 
+    void finishResume();
+
     void postScanSources();
 
     void schedulePollDuration();
@@ -217,7 +223,7 @@
     void performReset();
     void performScanSources();
     void performSetSurface(const sp<NativeWindowWrapper> &wrapper);
-    void performResumeDecoders();
+    void performResumeDecoders(bool needNotify);
 
     void onSourceNotify(const sp<AMessage> &msg);
     void onClosedCaptionNotify(const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 0439a9a..012d33e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -58,6 +58,7 @@
       mFormatChangePending(false),
       mBufferGeneration(0),
       mPaused(true),
+      mResumePending(false),
       mComponentName("decoder") {
     mCodecLooper = new ALooper;
     mCodecLooper->setName("NPDecoder-CL");
@@ -208,6 +209,7 @@
         requestCodecNotification();
     }
     mPaused = false;
+    mResumePending = false;
 }
 
 void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
@@ -226,8 +228,12 @@
     }
 }
 
-void NuPlayer::Decoder::onResume() {
+void NuPlayer::Decoder::onResume(bool notifyComplete) {
     mPaused = false;
+
+    if (notifyComplete) {
+        mResumePending = true;
+    }
 }
 
 void NuPlayer::Decoder::onFlush(bool notifyComplete) {
@@ -265,6 +271,10 @@
 
 void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
     status_t err = OK;
+
+    // if there is a pending resume request, notify complete now
+    notifyResumeCompleteIfNecessary();
+
     if (mCodec != NULL) {
         err = mCodec->release();
         mCodec = NULL;
@@ -494,6 +504,9 @@
         mSkipRenderingUntilMediaTimeUs = -1;
     }
 
+    // wait until 1st frame comes out to signal resume complete
+    notifyResumeCompleteIfNecessary();
+
     if (mRenderer != NULL) {
         // send the buffer to renderer.
         mRenderer->queueBuffer(mIsAudio, buffer, reply);
@@ -884,5 +897,15 @@
     }
 }
 
+void NuPlayer::Decoder::notifyResumeCompleteIfNecessary() {
+    if (mResumePending) {
+        mResumePending = false;
+
+        sp<AMessage> notify = mNotify->dup();
+        notify->setInt32("what", kWhatResumeCompleted);
+        notify->post();
+    }
+}
+
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 07401b0..2c08f0d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -42,7 +42,7 @@
     virtual void onConfigure(const sp<AMessage> &format);
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
-    virtual void onResume();
+    virtual void onResume(bool notifyComplete);
     virtual void onFlush(bool notifyComplete);
     virtual void onShutdown(bool notifyComplete);
     virtual void doRequestBuffers();
@@ -85,6 +85,7 @@
 
     int32_t mBufferGeneration;
     bool mPaused;
+    bool mResumePending;
     AString mComponentName;
 
     void handleError(int32_t err);
@@ -103,6 +104,8 @@
     bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
     void rememberCodecSpecificData(const sp<AMessage> &format);
 
+    void notifyResumeCompleteIfNecessary();
+
     DISALLOW_EVIL_CONSTRUCTORS(Decoder);
 };
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 6941f77..4164350 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -86,8 +86,10 @@
     (new AMessage(kWhatFlush, id()))->post();
 }
 
-void NuPlayer::DecoderBase::signalResume() {
-    (new AMessage(kWhatResume, id()))->post();
+void NuPlayer::DecoderBase::signalResume(bool notifyComplete) {
+    sp<AMessage> msg = new AMessage(kWhatResume, id());
+    msg->setInt32("notifyComplete", notifyComplete);
+    msg->post();
 }
 
 void NuPlayer::DecoderBase::initiateShutdown() {
@@ -159,7 +161,10 @@
 
         case kWhatResume:
         {
-            onResume();
+            int32_t notifyComplete;
+            CHECK(msg->findInt32("notifyComplete", &notifyComplete));
+
+            onResume(notifyComplete);
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 1b24c4f..5feb6a1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -38,7 +38,7 @@
 
     status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
     void signalFlush();
-    void signalResume();
+    void signalResume(bool notifyComplete);
     void initiateShutdown();
 
     virtual void getStats(
@@ -50,6 +50,7 @@
         kWhatVideoSizeChanged    = 'viSC',
         kWhatFlushCompleted      = 'flsC',
         kWhatShutdownCompleted   = 'shDC',
+        kWhatResumeCompleted     = 'resC',
         kWhatEOS                 = 'eos ',
         kWhatError               = 'err ',
     };
@@ -63,7 +64,7 @@
     virtual void onConfigure(const sp<AMessage> &format) = 0;
     virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
-    virtual void onResume() = 0;
+    virtual void onResume(bool notifyComplete) = 0;
     virtual void onFlush(bool notifyComplete) = 0;
     virtual void onShutdown(bool notifyComplete) = 0;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 3b4c0a7..8112e9f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -354,8 +354,14 @@
     onRequestInputBuffers();
 }
 
-void NuPlayer::DecoderPassThrough::onResume() {
+void NuPlayer::DecoderPassThrough::onResume(bool notifyComplete) {
     onRequestInputBuffers();
+
+    if (notifyComplete) {
+        sp<AMessage> notify = mNotify->dup();
+        notify->setInt32("what", kWhatResumeCompleted);
+        notify->post();
+    }
 }
 
 void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 3fe32b6..d761b07 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -42,7 +42,7 @@
     virtual void onConfigure(const sp<AMessage> &format);
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
-    virtual void onResume();
+    virtual void onResume(bool notifyComplete);
     virtual void onFlush(bool notifyComplete);
     virtual void onShutdown(bool notifyComplete);
     virtual void doRequestBuffers();