Properly shutdown the decoders on a reset() in NuPlayer's implementation.

related-to-bug: 3321470
Change-Id: Ida6d2171c5a3a407188d4633602b764f8fe7086a
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 88b0c3e..178f0db 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -36,6 +36,7 @@
     MEDIA_BUFFERING_UPDATE  = 3,
     MEDIA_SEEK_COMPLETE     = 4,
     MEDIA_SET_VIDEO_SIZE    = 5,
+    MEDIA_RESET_COMPLETE    = 6,  // not visible on java side
     MEDIA_ERROR             = 100,
     MEDIA_INFO              = 200,
 };
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index c656e69..29e4971 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -87,7 +87,7 @@
             break;
         } else if (n < 0) {
             LOGI("input data EOS reached.");
-            mTSParser->signalEOS(ERROR_END_OF_STREAM);
+            mTSParser->signalEOS(n);
             mEOS = true;
             break;
         } else {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 24efa35..9a8cff3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -45,8 +45,11 @@
     : mAudioEOS(false),
       mVideoEOS(false),
       mScanSourcesPending(false),
+      mScanSourcesGeneration(0),
       mFlushingAudio(NONE),
-      mFlushingVideo(NONE) {
+      mFlushingVideo(NONE),
+      mResetInProgress(false),
+      mResetPostponed(false) {
 }
 
 NuPlayer::~NuPlayer() {
@@ -87,18 +90,22 @@
     (new AMessage(kWhatStart, id()))->post();
 }
 
+void NuPlayer::resetAsync() {
+    (new AMessage(kWhatReset, id()))->post();
+}
+
 // static
-bool NuPlayer::IsFlushingState(FlushStatus state, bool *formatChange) {
+bool NuPlayer::IsFlushingState(FlushStatus state, bool *needShutdown) {
     switch (state) {
         case FLUSHING_DECODER:
-            if (formatChange != NULL) {
-                *formatChange = false;
+            if (needShutdown != NULL) {
+                *needShutdown = false;
             }
             return true;
 
-        case FLUSHING_DECODER_FORMATCHANGE:
-            if (formatChange != NULL) {
-                *formatChange = true;
+        case FLUSHING_DECODER_SHUTDOWN:
+            if (needShutdown != NULL) {
+                *needShutdown = true;
             }
             return true;
 
@@ -111,7 +118,7 @@
     switch (msg->what()) {
         case kWhatSetDataSource:
         {
-            LOGI("kWhatSetDataSource");
+            LOGV("kWhatSetDataSource");
 
             CHECK(mSource == NULL);
 
@@ -124,7 +131,7 @@
 
         case kWhatSetVideoSurface:
         {
-            LOGI("kWhatSetVideoSurface");
+            LOGV("kWhatSetVideoSurface");
 
             sp<RefBase> obj;
             CHECK(msg->findObject("surface", &obj));
@@ -135,7 +142,7 @@
 
         case kWhatSetAudioSink:
         {
-            LOGI("kWhatSetAudioSink");
+            LOGV("kWhatSetAudioSink");
 
             sp<RefBase> obj;
             CHECK(msg->findObject("sink", &obj));
@@ -146,6 +153,9 @@
 
         case kWhatStart:
         {
+            mAudioEOS = false;
+            mVideoEOS = false;
+
             mSource->start();
 
             mRenderer = new Renderer(
@@ -154,13 +164,19 @@
 
             looper()->registerHandler(mRenderer);
 
-            (new AMessage(kWhatScanSources, id()))->post();
-            mScanSourcesPending = true;
+            postScanSources();
             break;
         }
 
         case kWhatScanSources:
         {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mScanSourcesGeneration) {
+                // Drop obsolete msg.
+                break;
+            }
+
             mScanSourcesPending = false;
 
             instantiateDecoder(false, &mVideoDecoder);
@@ -170,6 +186,11 @@
             }
 
             if (!mSource->feedMoreTSData()) {
+                if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
+                    // We're not currently decoding anything (no audio or
+                    // video tracks found) and we just ran out of input data.
+                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
+                }
                 break;
             }
 
@@ -203,20 +224,20 @@
             } else if (what == ACodec::kWhatEOS) {
                 mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
             } else if (what == ACodec::kWhatFlushCompleted) {
-                bool formatChange;
+                bool needShutdown;
 
                 if (audio) {
-                    CHECK(IsFlushingState(mFlushingAudio, &formatChange));
+                    CHECK(IsFlushingState(mFlushingAudio, &needShutdown));
                     mFlushingAudio = FLUSHED;
                 } else {
-                    CHECK(IsFlushingState(mFlushingVideo, &formatChange));
+                    CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
                     mFlushingVideo = FLUSHED;
                 }
 
-                LOGI("decoder %s flush completed", audio ? "audio" : "video");
+                LOGV("decoder %s flush completed", audio ? "audio" : "video");
 
-                if (formatChange) {
-                    LOGI("initiating %s decoder shutdown",
+                if (needShutdown) {
+                    LOGV("initiating %s decoder shutdown",
                          audio ? "audio" : "video");
 
                     (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
@@ -238,7 +259,7 @@
                 int32_t sampleRate;
                 CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
 
-                LOGI("Audio output format changed to %d Hz, %d channels",
+                LOGV("Audio output format changed to %d Hz, %d channels",
                      sampleRate, numChannels);
 
                 mAudioSink->close();
@@ -247,7 +268,7 @@
 
                 mRenderer->signalAudioSinkChanged();
             } else if (what == ACodec::kWhatShutdownCompleted) {
-                LOGI("%s shutdown completed", audio ? "audio" : "video");
+                LOGV("%s shutdown completed", audio ? "audio" : "video");
                 if (audio) {
                     mAudioDecoder.clear();
 
@@ -285,7 +306,7 @@
                     mVideoEOS = true;
                 }
 
-                LOGI("reached %s EOS", audio ? "audio" : "video");
+                LOGV("reached %s EOS", audio ? "audio" : "video");
 
                 if ((mAudioEOS || mAudioDecoder == NULL)
                         && (mVideoEOS || mVideoDecoder == NULL)) {
@@ -297,7 +318,7 @@
                 int32_t audio;
                 CHECK(msg->findInt32("audio", &audio));
 
-                LOGI("renderer %s flush completed.", audio ? "audio" : "video");
+                LOGV("renderer %s flush completed.", audio ? "audio" : "video");
             }
             break;
         }
@@ -307,6 +328,37 @@
             break;
         }
 
+        case kWhatReset:
+        {
+            LOGV("kWhatReset");
+
+            if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
+                // We're currently flushing, postpone the reset until that's
+                // completed.
+
+                LOGV("postponing reset");
+
+                mResetPostponed = true;
+                break;
+            }
+
+            if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
+                finishReset();
+                break;
+            }
+
+            if (mAudioDecoder != NULL) {
+                flushDecoder(true /* audio */, true /* needShutdown */);
+            }
+
+            if (mVideoDecoder != NULL) {
+                flushDecoder(false /* audio */, true /* needShutdown */);
+            }
+
+            mResetInProgress = true;
+            break;
+        }
+
         default:
             TRESPASS();
             break;
@@ -322,7 +374,7 @@
         return;
     }
 
-    LOGI("both audio and video are flushed now.");
+    LOGV("both audio and video are flushed now.");
 
     mRenderer->signalTimeDiscontinuity();
 
@@ -343,12 +395,41 @@
     mFlushingAudio = NONE;
     mFlushingVideo = NONE;
 
-    if (scanSourcesAgain && !mScanSourcesPending) {
-        mScanSourcesPending = true;
-        (new AMessage(kWhatScanSources, id()))->post();
+    if (mResetInProgress) {
+        LOGV("reset completed");
+
+        mResetInProgress = false;
+        finishReset();
+    } else if (mResetPostponed) {
+        (new AMessage(kWhatReset, id()))->post();
+        mResetPostponed = false;
+    } else if (scanSourcesAgain) {
+        postScanSources();
     }
 }
 
+void NuPlayer::finishReset() {
+    CHECK(mAudioDecoder == NULL);
+    CHECK(mVideoDecoder == NULL);
+
+    mRenderer.clear();
+    mSource.clear();
+
+    notifyListener(MEDIA_RESET_COMPLETE, 0, 0);
+}
+
+void NuPlayer::postScanSources() {
+    if (mScanSourcesPending) {
+        return;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatScanSources, id());
+    msg->setInt32("generation", mScanSourcesGeneration);
+    msg->post();
+
+    mScanSourcesPending = true;
+}
+
 status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
     if (*decoder != NULL) {
         return OK;
@@ -396,37 +477,10 @@
             bool formatChange =
                 type == ATSParser::DISCONTINUITY_FORMATCHANGE;
 
-            LOGI("%s discontinuity (formatChange=%d)",
+            LOGV("%s discontinuity (formatChange=%d)",
                  audio ? "audio" : "video", formatChange);
 
-            (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
-            mRenderer->flush(audio);
-
-            if (audio) {
-                CHECK(mFlushingAudio == NONE
-                        || mFlushingAudio == AWAITING_DISCONTINUITY);
-
-                mFlushingAudio = formatChange
-                    ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
-
-                if (mFlushingVideo == NONE) {
-                    mFlushingVideo = (mVideoDecoder != NULL)
-                        ? AWAITING_DISCONTINUITY
-                        : FLUSHED;
-                }
-            } else {
-                CHECK(mFlushingVideo == NONE
-                        || mFlushingVideo == AWAITING_DISCONTINUITY);
-
-                mFlushingVideo = formatChange
-                    ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
-
-                if (mFlushingAudio == NONE) {
-                    mFlushingAudio = (mAudioDecoder != NULL)
-                        ? AWAITING_DISCONTINUITY
-                        : FLUSHED;
-                }
-            }
+            flushDecoder(audio, formatChange);
         }
 
         reply->setInt32("err", err);
@@ -439,7 +493,7 @@
 #if 0
     int64_t mediaTimeUs;
     CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
-    LOGI("feeding %s input buffer at media time %.2f secs",
+    LOGV("feeding %s input buffer at media time %.2f secs",
          audio ? "audio" : "video",
          mediaTimeUs / 1E6);
 #endif
@@ -478,4 +532,39 @@
     listener->sendEvent(msg, ext1, ext2);
 }
 
+void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
+    // Make sure we don't continue to scan sources until we finish flushing.
+    ++mScanSourcesGeneration;
+
+    (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+    mRenderer->flush(audio);
+
+    FlushStatus newStatus =
+        needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
+
+    if (audio) {
+        CHECK(mFlushingAudio == NONE
+                || mFlushingAudio == AWAITING_DISCONTINUITY);
+
+        mFlushingAudio = newStatus;
+
+        if (mFlushingVideo == NONE) {
+            mFlushingVideo = (mVideoDecoder != NULL)
+                ? AWAITING_DISCONTINUITY
+                : FLUSHED;
+        }
+    } else {
+        CHECK(mFlushingVideo == NONE
+                || mFlushingVideo == AWAITING_DISCONTINUITY);
+
+        mFlushingVideo = newStatus;
+
+        if (mFlushingAudio == NONE) {
+            mFlushingAudio = (mAudioDecoder != NULL)
+                ? AWAITING_DISCONTINUITY
+                : FLUSHED;
+        }
+    }
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 172a962..1cf2f60 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -40,6 +40,10 @@
     void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
     void start();
 
+    // Will notify the listener that reset() has completed
+    // with code MEDIA_RESET_COMPLETE.
+    void resetAsync();
+
 protected:
     virtual ~NuPlayer();
 
@@ -63,6 +67,7 @@
         kWhatVideoNotify,
         kWhatAudioNotify,
         kWhatRendererNotify,
+        kWhatReset,
     };
 
     wp<MediaPlayerBase> mListener;
@@ -73,17 +78,17 @@
     sp<Decoder> mAudioDecoder;
     sp<Renderer> mRenderer;
 
-    bool mEOS;
     bool mAudioEOS;
     bool mVideoEOS;
 
     bool mScanSourcesPending;
+    int32_t mScanSourcesGeneration;
 
     enum FlushStatus {
         NONE,
         AWAITING_DISCONTINUITY,
         FLUSHING_DECODER,
-        FLUSHING_DECODER_FORMATCHANGE,
+        FLUSHING_DECODER_SHUTDOWN,
         SHUTTING_DOWN_DECODER,
         FLUSHED,
         SHUT_DOWN,
@@ -91,6 +96,8 @@
 
     FlushStatus mFlushingAudio;
     FlushStatus mFlushingVideo;
+    bool mResetInProgress;
+    bool mResetPostponed;
 
     status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
 
@@ -101,7 +108,12 @@
 
     void finishFlushIfPossible();
 
-    static bool IsFlushingState(FlushStatus state, bool *formatChange = NULL);
+    void flushDecoder(bool audio, bool needShutdown);
+
+    static bool IsFlushingState(FlushStatus state, bool *needShutdown = NULL);
+
+    void finishReset();
+    void postScanSources();
 
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index d21d4ff..4988d24 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -22,12 +22,14 @@
 
 #include "NuPlayer.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 
 namespace android {
 
 NuPlayerDriver::NuPlayerDriver()
-    : mLooper(new ALooper),
+    : mResetInProgress(false),
+      mLooper(new ALooper),
       mPlayer(false) {
     mLooper->setName("NuPlayerDriver Looper");
 
@@ -121,6 +123,15 @@
 }
 
 status_t NuPlayerDriver::reset() {
+    Mutex::Autolock autoLock(mLock);
+    mResetInProgress = true;
+
+    mPlayer->resetAsync();
+
+    while (mResetInProgress) {
+        mCondition.wait(mLock);
+    }
+
     return OK;
 }
 
@@ -145,4 +156,16 @@
     return INVALID_OPERATION;
 }
 
+void NuPlayerDriver::sendEvent(int msg, int ext1, int ext2) {
+    if (msg != MEDIA_RESET_COMPLETE) {
+        MediaPlayerInterface::sendEvent(msg, ext1, ext2);
+        return;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    CHECK(mResetInProgress);
+    mResetInProgress = false;
+    mCondition.broadcast();
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 44ae3bf..f153af4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -54,10 +54,16 @@
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
 
+    virtual void sendEvent(int msg, int ext1 = 0, int ext2 = 0);
+
 protected:
     virtual ~NuPlayerDriver();
 
 private:
+    Mutex mLock;
+    Condition mCondition;
+    bool mResetInProgress;
+
     sp<ALooper> mLooper;
     sp<NuPlayer> mPlayer;
     bool mPlaying;