RTSP seeking is now asynchronous, MediaPlayer is not notified that the seek is complete until it actually is. Ignore seek requests on live streams.

Change-Id: Ie61230cd60dd6c682baf72529100369ad6291189
related-to-bug: 3073955
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 53543b3..ff28f3b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -876,12 +876,19 @@
     return OK;
 }
 
+// static
+void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) {
+    static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone();
+}
+
+void AwesomePlayer::onRTSPSeekDone() {
+    notifyListener_l(MEDIA_SEEK_COMPLETE);
+    mSeekNotificationSent = true;
+}
+
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
     if (mRTSPController != NULL) {
-        mRTSPController->seek(timeUs);
-
-        notifyListener_l(MEDIA_SEEK_COMPLETE);
-        mSeekNotificationSent = true;
+        mRTSPController->seekAsync(timeUs, OnRTSPSeekDoneWrapper, this);
         return OK;
     }
 
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index c2f3090..ce7ffe5 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -33,7 +33,7 @@
     status_t connect(const char *url);
     void disconnect();
 
-    void seek(int64_t timeUs);
+    void seekAsync(int64_t timeUs, void (*seekDoneCb)(void *), void *cookie);
 
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
@@ -61,6 +61,7 @@
     enum {
         kWhatConnectDone    = 'cdon',
         kWhatDisconnectDone = 'ddon',
+        kWhatSeekDone       = 'sdon',
     };
 
     enum State {
@@ -79,6 +80,10 @@
     sp<MyHandler> mHandler;
     sp<AHandlerReflector<ARTSPController> > mReflector;
 
+    void (*mSeekDoneCb)(void *);
+    void *mSeekDoneCookie;
+    int64_t mLastSeekCompletedTimeUs;
+
     DISALLOW_EVIL_CONSTRUCTORS(ARTSPController);
 };
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index bbf482d..079adca 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -251,6 +251,9 @@
 
     static bool ContinuePreparation(void *cookie);
 
+    static void OnRTSPSeekDoneWrapper(void *cookie);
+    void onRTSPSeekDone();
+
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
 };
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 4c53639..a7563ff 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -27,7 +27,10 @@
 
 ARTSPController::ARTSPController(const sp<ALooper> &looper)
     : mState(DISCONNECTED),
-      mLooper(looper) {
+      mLooper(looper),
+      mSeekDoneCb(NULL),
+      mSeekDoneCookie(NULL),
+      mLastSeekCompletedTimeUs(-1) {
     mReflector = new AHandlerReflector<ARTSPController>(this);
     looper->registerHandler(mReflector);
 }
@@ -80,14 +83,31 @@
     mHandler.clear();
 }
 
-void ARTSPController::seek(int64_t timeUs) {
+void ARTSPController::seekAsync(
+        int64_t timeUs,
+        void (*seekDoneCb)(void *), void *cookie) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mState != CONNECTED) {
+    CHECK(seekDoneCb != NULL);
+    CHECK(mSeekDoneCb == NULL);
+
+    // Ignore seek requests that are too soon after the previous one has
+    // completed, we don't want to swamp the server.
+
+    bool tooEarly =
+        mLastSeekCompletedTimeUs >= 0
+            && ALooper::GetNowUs() < mLastSeekCompletedTimeUs + 500000ll;
+
+    if (mState != CONNECTED || tooEarly) {
+        (*seekDoneCb)(cookie);
         return;
     }
 
-    mHandler->seek(timeUs);
+    mSeekDoneCb = seekDoneCb;
+    mSeekDoneCookie = cookie;
+
+    sp<AMessage> msg = new AMessage(kWhatSeekDone, mReflector->id());
+    mHandler->seek(timeUs, msg);
 }
 
 size_t ARTSPController::countTracks() {
@@ -132,6 +152,19 @@
             break;
         }
 
+        case kWhatSeekDone:
+        {
+            LOGI("seek done");
+
+            mLastSeekCompletedTimeUs = ALooper::GetNowUs();
+
+            void (*seekDoneCb)(void *) = mSeekDoneCb;
+            mSeekDoneCb = NULL;
+
+            (*seekDoneCb)(mSeekDoneCookie);
+            break;
+        }
+
         default:
             TRESPASS();
             break;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 2c9cfd3..05dd61b 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -88,7 +88,8 @@
           mCheckPending(false),
           mCheckGeneration(0),
           mTryTCPInterleaving(false),
-          mReceivedFirstRTCPPacket(false) {
+          mReceivedFirstRTCPPacket(false),
+          mSeekable(false) {
         mNetLooper->setName("rtsp net");
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
@@ -115,9 +116,10 @@
         (new AMessage('abor', id()))->post();
     }
 
-    void seek(int64_t timeUs) {
+    void seek(int64_t timeUs, const sp<AMessage> &doneMsg) {
         sp<AMessage> msg = new AMessage('seek', id());
         msg->setInt64("time", timeUs);
+        msg->setMessage("doneMsg", doneMsg);
         msg->post();
     }
 
@@ -379,6 +381,7 @@
                 mFirstAccessUnitNTP = 0;
                 mNumAccessUnitsReceived = 0;
                 mReceivedFirstRTCPPacket = false;
+                mSeekable = false;
 
                 sp<AMessage> reply = new AMessage('tear', id());
 
@@ -551,7 +554,17 @@
 
             case 'seek':
             {
+                sp<AMessage> doneMsg;
+                CHECK(msg->findMessage("doneMsg", &doneMsg));
+
                 if (mSeekPending) {
+                    doneMsg->post();
+                    break;
+                }
+
+                if (!mSeekable) {
+                    LOGW("This is a live stream, ignoring seek request.");
+                    doneMsg->post();
                     break;
                 }
 
@@ -577,6 +590,7 @@
 
                 sp<AMessage> reply = new AMessage('see1', id());
                 reply->setInt64("time", timeUs);
+                reply->setMessage("doneMsg", doneMsg);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -605,7 +619,11 @@
 
                 request.append("\r\n");
 
+                sp<AMessage> doneMsg;
+                CHECK(msg->findMessage("doneMsg", &doneMsg));
+
                 sp<AMessage> reply = new AMessage('see2', id());
+                reply->setMessage("doneMsg", doneMsg);
                 mConn->sendRequest(request.c_str(), reply);
                 break;
             }
@@ -644,6 +662,11 @@
                 }
 
                 mSeekPending = false;
+
+                sp<AMessage> doneMsg;
+                CHECK(msg->findMessage("doneMsg", &doneMsg));
+
+                doneMsg->post();
                 break;
             }
 
@@ -714,6 +737,8 @@
     }
 
     void parsePlayResponse(const sp<ARTSPResponse> &response) {
+        mSeekable = false;
+
         ssize_t i = response->mHeaders.indexOfKey("range");
         if (i < 0) {
             // Server doesn't even tell use what range it is going to
@@ -777,6 +802,8 @@
 
             ++n;
         }
+
+        mSeekable = true;
     }
 
     sp<APacketSource> getPacketSource(size_t index) {
@@ -808,6 +835,7 @@
     int32_t mCheckGeneration;
     bool mTryTCPInterleaving;
     bool mReceivedFirstRTCPPacket;
+    bool mSeekable;
 
     struct TrackInfo {
         AString mURL;