NuPlayer2: support end position of data source

Test: cts
Bug: 112549021
Change-Id: I5143754eb22fa42c3134474c584d986eb6f2b33d
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 83a10bf..5b7bf4a 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -216,6 +216,7 @@
       mAudioDecoderGeneration(0),
       mVideoDecoderGeneration(0),
       mRendererGeneration(0),
+      mEOSMonitorGeneration(0),
       mLastStartedPlayingTimeNs(0),
       mPreviousSeekTimeUs(0),
       mAudioEOS(false),
@@ -586,6 +587,11 @@
     msg->post();
 }
 
+void NuPlayer2::rewind() {
+    sp<AMessage> msg = new AMessage(kWhatRewind, this);
+    msg->post();
+}
+
 void NuPlayer2::writeTrackInfo(
         PlayerMessage* reply, const sp<AMessage>& format) const {
     if (format == NULL) {
@@ -713,6 +719,22 @@
             break;
         }
 
+        case kWhatEOSMonitor:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            int32_t reason;
+            CHECK(msg->findInt32("reason", &reason));
+
+            if (generation != mEOSMonitorGeneration || reason != MediaClock::TIMER_REASON_REACHED) {
+                break;  // stale or reset
+            }
+
+            ALOGV("kWhatEOSMonitor");
+            notifyListener(mCurrentSourceInfo.mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+            break;
+        }
+
         case kWhatGetBufferingSettings:
         {
             sp<AReplyToken> replyID;
@@ -1522,6 +1544,42 @@
             break;
         }
 
+        case kWhatRewind:
+        {
+            ALOGV("kWhatRewind");
+
+            int64_t seekTimeUs = mCurrentSourceInfo.mStartTimeUs;
+            int32_t mode = MediaPlayer2SeekMode::SEEK_CLOSEST;
+
+            if (!mStarted) {
+                if (!mSourceStarted) {
+                    mSourceStarted = true;
+                    mCurrentSourceInfo.mSource->start();
+                }
+                performSeek(seekTimeUs, (MediaPlayer2SeekMode)mode);
+                break;
+            }
+
+            // seeks can take a while, so we essentially paused
+            notifyListener(mCurrentSourceInfo.mSrcId, MEDIA2_PAUSED, 0, 0);
+
+            mDeferredActions.push_back(
+                    new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
+                                           FLUSH_CMD_FLUSH /* video */));
+
+            mDeferredActions.push_back(
+                    new SeekAction(seekTimeUs, (MediaPlayer2SeekMode)mode));
+
+            // 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 ResumeDecoderAction(false /* needNotify */));
+
+            processDeferredActions();
+            break;
+        }
+
         case kWhatPause:
         {
             if (!mStarted) {
@@ -1675,6 +1733,7 @@
         mRenderer->setVideoFrameRate(rate);
     }
 
+    addEndTimeMonitor();
     // Renderer is created in paused state.
     if (play) {
         mRenderer->resume();
@@ -1693,6 +1752,13 @@
     postScanSources();
 }
 
+void NuPlayer2::addEndTimeMonitor() {
+    sp<AMessage> msg = new AMessage(kWhatEOSMonitor, this);
+    ++mEOSMonitorGeneration;
+    msg->setInt32("generation", mEOSMonitorGeneration);
+    mMediaClock->addTimer(msg, mCurrentSourceInfo.mEndTimeUs);
+}
+
 void NuPlayer2::startPlaybackTimer(const char *where) {
     Mutex::Autolock autoLock(mPlayingTimeLock);
     if (mLastStartedPlayingTimeNs == 0) {
@@ -2473,17 +2539,22 @@
     long previousSrcId;
     {
         Mutex::Autolock autoLock(mSourceLock);
-        mCurrentSourceInfo.mSource = mNextSourceInfo.mSource;
-        mNextSourceInfo.mSource = NULL;
         previousSrcId = mCurrentSourceInfo.mSrcId;
-        mCurrentSourceInfo.mSrcId = mNextSourceInfo.mSrcId;
-        ++mNextSourceInfo.mSrcId;  // to distinguish the two sources.
+
+        mCurrentSourceInfo = mNextSourceInfo;
+        mNextSourceInfo = SourceInfo();
+        mNextSourceInfo.mSrcId = ~mCurrentSourceInfo.mSrcId;  // to distinguish the two sources.
     }
 
     if (mDriver != NULL) {
         sp<NuPlayer2Driver> driver = mDriver.promote();
         if (driver != NULL) {
             notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_END, 0);
+
+            int64_t durationUs;
+            if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
+                driver->notifyDuration(mCurrentSourceInfo.mSrcId, durationUs);
+            }
             notifyListener(
                     mCurrentSourceInfo.mSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_START, 0);
         }
@@ -2494,6 +2565,8 @@
     mResetting = false;
     mSourceStarted = false;
 
+    addEndTimeMonitor();
+
     // Modular DRM
     if (mCrypto != NULL) {
         // decoders will be flushed before this so their mCrypto would go away on their own
@@ -2589,37 +2662,50 @@
     switch (what) {
         case Source::kWhatPrepared:
         {
-            ALOGV("NuPlayer2::onSourceNotify Source::kWhatPrepared source: %p",
-                  mCurrentSourceInfo.mSource.get());
-            if (mCurrentSourceInfo.mSource == NULL) {
-                // This is a stale notification from a source that was
-                // asynchronously preparing when the client called reset().
-                // We handled the reset, the source is gone.
-                break;
-            }
-
-            int32_t err;
-            CHECK(msg->findInt32("err", &err));
-
-            if (err != OK) {
-                // shut down potential secure codecs in case client never calls reset
-                mDeferredActions.push_back(
-                        new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */,
-                                               FLUSH_CMD_SHUTDOWN /* video */));
-                processDeferredActions();
-            } else {
-                mPrepared = true;
-            }
-
-            sp<NuPlayer2Driver> driver = mDriver.promote();
-            if (driver != NULL) {
-                // notify duration first, so that it's definitely set when
-                // the app received the "prepare complete" callback.
-                int64_t durationUs;
-                if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
-                    driver->notifyDuration(srcId, durationUs);
+            ALOGV("NuPlayer2::onSourceNotify Source::kWhatPrepared source:%p, Id(%lld)",
+                  mCurrentSourceInfo.mSource.get(), (long long)srcId);
+            if (srcId == mCurrentSourceInfo.mSrcId) {
+                if (mCurrentSourceInfo.mSource == NULL) {
+                    // This is a stale notification from a source that was
+                    // asynchronously preparing when the client called reset().
+                    // We handled the reset, the source is gone.
+                    break;
                 }
-                driver->notifyPrepareCompleted(srcId, err);
+
+                int32_t err;
+                CHECK(msg->findInt32("err", &err));
+
+                if (err != OK) {
+                    // shut down potential secure codecs in case client never calls reset
+                    mDeferredActions.push_back(
+                            new FlushDecoderAction(FLUSH_CMD_SHUTDOWN /* audio */,
+                                                   FLUSH_CMD_SHUTDOWN /* video */));
+                    processDeferredActions();
+                } else {
+                    mPrepared = true;
+                }
+
+                sp<NuPlayer2Driver> driver = mDriver.promote();
+                if (driver != NULL) {
+                    // notify duration first, so that it's definitely set when
+                    // the app received the "prepare complete" callback.
+                    int64_t durationUs;
+                    if (mCurrentSourceInfo.mSource->getDuration(&durationUs) == OK) {
+                        driver->notifyDuration(srcId, durationUs);
+                    }
+                    driver->notifyPrepareCompleted(srcId, err);
+                }
+            } else if (srcId == mNextSourceInfo.mSrcId) {
+                if (mNextSourceInfo.mSource == NULL) {
+                    break;  // stale
+                }
+
+                sp<NuPlayer2Driver> driver = mDriver.promote();
+                if (driver != NULL) {
+                    int32_t err;
+                    CHECK(msg->findInt32("err", &err));
+                    driver->notifyPrepareCompleted(srcId, err);
+                }
             }
 
             break;
@@ -2665,7 +2751,9 @@
                     driver->notifyListener(
                             srcId, MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
                 }
-                driver->notifyFlagsChanged(srcId, flags);
+                if (srcId == mCurrentSourceInfo.mSrcId) {
+                    driver->notifyFlagsChanged(srcId, flags);
+                }
             }
 
             if (srcId == mCurrentSourceInfo.mSrcId) {
@@ -3131,4 +3219,14 @@
       mEndTimeUs(INT64_MAX) {
 }
 
+NuPlayer2::SourceInfo & NuPlayer2::SourceInfo::operator=(const NuPlayer2::SourceInfo &other) {
+    mSource = other.mSource;
+    mDataSourceType = (DATA_SOURCE_TYPE)other.mDataSourceType;
+    mSrcId = other.mSrcId;
+    mSourceFlags = other.mSourceFlags;
+    mStartTimeUs = other.mStartTimeUs;
+    mEndTimeUs = other.mEndTimeUs;
+    return *this;
+}
+
 }  // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index 4db0cbf..3ecdb01 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -79,6 +79,7 @@
             int64_t seekTimeUs,
             MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC,
             bool needNotify = false);
+    void rewind();
 
     status_t setVideoScalingMode(int32_t mode);
     status_t getTrackInfo(PlayerMessage* reply) const;
@@ -154,6 +155,8 @@
         kWhatSetBufferingSettings       = 'sBuS',
         kWhatPrepareDrm                 = 'pDrm',
         kWhatReleaseDrm                 = 'rDrm',
+        kWhatRewind                     = 'reWd',
+        kWhatEOSMonitor                 = 'eosM',
     };
 
     typedef enum {
@@ -167,6 +170,7 @@
 
     struct SourceInfo {
         SourceInfo();
+        SourceInfo &operator=(const SourceInfo &);
 
         sp<Source> mSource;
         std::atomic<DATA_SOURCE_TYPE> mDataSourceType;
@@ -194,6 +198,7 @@
     int32_t mAudioDecoderGeneration;
     int32_t mVideoDecoderGeneration;
     int32_t mRendererGeneration;
+    int32_t mEOSMonitorGeneration;
 
     Mutex mPlayingTimeLock;
     int64_t mLastStartedPlayingTimeNs;
@@ -302,6 +307,8 @@
 
     void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL);
 
+    void addEndTimeMonitor();
+
     void handleFlushComplete(bool audio, bool isDecoder);
     void finishFlushIfPossible();
 
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index cb4b06d..821dc9f 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -298,7 +298,7 @@
         case STATE_RUNNING:
         {
             if (mAtEOS) {
-                mPlayer->seekToAsync(0);
+                mPlayer->rewind();
                 mAtEOS = false;
                 mPositionUs = -1;
             }
@@ -859,7 +859,7 @@
                         }
                     }
                     if (mLooping || mAutoLoop) {
-                        mPlayer->seekToAsync(0);
+                        mPlayer->rewind();
                         if (mAudioSink != NULL) {
                             // The renderer has stopped the sink at the end in order to play out
                             // the last little bit of audio. In looping mode, we need to restart it.
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
index 452b781..652cc89 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
@@ -1563,6 +1563,7 @@
         } else {
             notifyComplete = mNotifyCompleteVideo;
             mNotifyCompleteVideo = false;
+            mVideoRenderingStarted = false;
         }
 
         // If we're currently syncing the queues, i.e. dropping audio while