From 0c46b69f612da61ed39b32823d2d6baf2e8215e9 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 8 Oct 2010 15:21:08 -0700 Subject: [PATCH] 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 --- media/libstagefright/AwesomePlayer.cpp | 15 +++++-- .../libstagefright/include/ARTSPController.h | 7 +++- media/libstagefright/include/AwesomePlayer.h | 3 ++ media/libstagefright/rtsp/ARTSPController.cpp | 41 +++++++++++++++++-- media/libstagefright/rtsp/MyHandler.h | 32 ++++++++++++++- 5 files changed, 87 insertions(+), 11 deletions(-) diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 53543b374c0f4..ff28f3b86769c 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -876,12 +876,19 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { return OK; } +// static +void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) { + static_cast(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 c2f3090b03a43..ce7ffe5c23520 100644 --- a/media/libstagefright/include/ARTSPController.h +++ b/media/libstagefright/include/ARTSPController.h @@ -33,7 +33,7 @@ struct ARTSPController : public MediaExtractor { 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 getTrack(size_t index); @@ -61,6 +61,7 @@ private: enum { kWhatConnectDone = 'cdon', kWhatDisconnectDone = 'ddon', + kWhatSeekDone = 'sdon', }; enum State { @@ -79,6 +80,10 @@ private: sp mHandler; sp > 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 bbf482d48f66b..079adcabca807 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -251,6 +251,9 @@ private: 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 4c53639a9e8ec..a7563ff93b3d0 100644 --- a/media/libstagefright/rtsp/ARTSPController.cpp +++ b/media/libstagefright/rtsp/ARTSPController.cpp @@ -27,7 +27,10 @@ namespace android { ARTSPController::ARTSPController(const sp &looper) : mState(DISCONNECTED), - mLooper(looper) { + mLooper(looper), + mSeekDoneCb(NULL), + mSeekDoneCookie(NULL), + mLastSeekCompletedTimeUs(-1) { mReflector = new AHandlerReflector(this); looper->registerHandler(mReflector); } @@ -80,14 +83,31 @@ void ARTSPController::disconnect() { 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 msg = new AMessage(kWhatSeekDone, mReflector->id()); + mHandler->seek(timeUs, msg); } size_t ARTSPController::countTracks() { @@ -132,6 +152,19 @@ void ARTSPController::onMessageReceived(const sp &msg) { 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 2c9cfd3be81ee..05dd61b9ac860 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -88,7 +88,8 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { (new AMessage('abor', id()))->post(); } - void seek(int64_t timeUs) { + void seek(int64_t timeUs, const sp &doneMsg) { sp msg = new AMessage('seek', id()); msg->setInt64("time", timeUs); + msg->setMessage("doneMsg", doneMsg); msg->post(); } @@ -379,6 +381,7 @@ struct MyHandler : public AHandler { mFirstAccessUnitNTP = 0; mNumAccessUnitsReceived = 0; mReceivedFirstRTCPPacket = false; + mSeekable = false; sp reply = new AMessage('tear', id()); @@ -551,7 +554,17 @@ struct MyHandler : public AHandler { case 'seek': { + sp 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 @@ struct MyHandler : public AHandler { sp reply = new AMessage('see1', id()); reply->setInt64("time", timeUs); + reply->setMessage("doneMsg", doneMsg); mConn->sendRequest(request.c_str(), reply); break; } @@ -605,7 +619,11 @@ struct MyHandler : public AHandler { request.append("\r\n"); + sp doneMsg; + CHECK(msg->findMessage("doneMsg", &doneMsg)); + sp reply = new AMessage('see2', id()); + reply->setMessage("doneMsg", doneMsg); mConn->sendRequest(request.c_str(), reply); break; } @@ -644,6 +662,11 @@ struct MyHandler : public AHandler { } mSeekPending = false; + + sp doneMsg; + CHECK(msg->findMessage("doneMsg", &doneMsg)); + + doneMsg->post(); break; } @@ -714,6 +737,8 @@ struct MyHandler : public AHandler { } void parsePlayResponse(const sp &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 @@ struct MyHandler : public AHandler { ++n; } + + mSeekable = true; } sp getPacketSource(size_t index) { @@ -808,6 +835,7 @@ private: int32_t mCheckGeneration; bool mTryTCPInterleaving; bool mReceivedFirstRTCPPacket; + bool mSeekable; struct TrackInfo { AString mURL;