From bff07d0b22a5ee2d9f044f6cb5e4be1532017ab0 Mon Sep 17 00:00:00 2001
From: Andreas Huber <andih@google.com>
Date: Tue, 12 Oct 2010 11:34:37 -0700
Subject: HTTP Live content that are tagged as complete are now seekable.

Change-Id: I9d0d2f009f883e5baf3e9de8c5c0aa05760e4bde
related-to-bug: 2368598
---
 include/media/stagefright/DataSource.h             |  1 +
 media/libstagefright/AwesomePlayer.cpp             | 12 ++-
 media/libstagefright/NuCachedSource2.cpp           | 48 +++++++++++-
 media/libstagefright/httplive/LiveSource.cpp       | 70 ++++++++++++++++-
 media/libstagefright/httplive/M3UParser.cpp        |  9 ++-
 media/libstagefright/include/LiveSource.h          |  7 ++
 media/libstagefright/include/M3UParser.h           |  2 +
 media/libstagefright/include/MPEG2TSExtractor.h    | 12 ++-
 media/libstagefright/include/NuCachedSource2.h     |  6 ++
 media/libstagefright/mpeg2ts/ATSParser.cpp         | 61 +++++++++++----
 media/libstagefright/mpeg2ts/ATSParser.h           |  2 +-
 .../libstagefright/mpeg2ts/AnotherPacketSource.cpp | 11 +++
 media/libstagefright/mpeg2ts/AnotherPacketSource.h |  2 +
 media/libstagefright/mpeg2ts/ESQueue.cpp           |  1 +
 media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp  | 87 ++++++++++++++++++++--
 15 files changed, 298 insertions(+), 33 deletions(-)

diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 9d2cff6..a3da3ed 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -36,6 +36,7 @@ public:
     enum Flags {
         kWantsPrefetching      = 1,
         kStreamedFromLocalHost = 2,
+        kIsCachingDataSource   = 4,
     };
 
     static sp<DataSource> CreateFromURI(
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ff28f3b..69389f5 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -26,6 +26,7 @@
 #include "include/SoftwareRenderer.h"
 #include "include/NuCachedSource2.h"
 #include "include/ThrottledSource.h"
+#include "include/MPEG2TSExtractor.h"
 
 #include "ARTPSession.h"
 #include "APacketSource.h"
@@ -506,8 +507,8 @@ void AwesomePlayer::onBufferingUpdate() {
                 // We don't know the bitrate of the stream, use absolute size
                 // limits to maintain the cache.
 
-                const size_t kLowWaterMarkBytes = 400000;
-                const size_t kHighWaterMarkBytes = 1000000;
+                const size_t kLowWaterMarkBytes = 40000;
+                const size_t kHighWaterMarkBytes = 200000;
 
                 if ((mFlags & PLAYING) && !eos
                         && (cachedDataRemaining < kLowWaterMarkBytes)) {
@@ -1343,14 +1344,17 @@ status_t AwesomePlayer::finishSetDataSource_l() {
         String8 uri("http://");
         uri.append(mUri.string() + 11);
 
-        dataSource = new LiveSource(uri.string());
+        sp<LiveSource> liveSource = new LiveSource(uri.string());
 
-        mCachedSource = new NuCachedSource2(dataSource);
+        mCachedSource = new NuCachedSource2(liveSource);
         dataSource = mCachedSource;
 
         sp<MediaExtractor> extractor =
             MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
+        static_cast<MPEG2TSExtractor *>(extractor.get())
+            ->setLiveSource(liveSource);
+
         return setDataSource_l(extractor);
     } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
         if (mLooper == NULL) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 3a0fc41..b67002d 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -179,7 +179,8 @@ NuCachedSource2::NuCachedSource2(const sp<DataSource> &source)
       mFinalStatus(OK),
       mLastAccessPos(0),
       mFetching(true),
-      mLastFetchTimeUs(-1) {
+      mLastFetchTimeUs(-1),
+      mSuspended(false) {
     mLooper->setName("NuCachedSource2");
     mLooper->registerHandler(mReflector);
     mLooper->start();
@@ -205,7 +206,7 @@ status_t NuCachedSource2::getSize(off_t *size) {
 }
 
 uint32_t NuCachedSource2::flags() {
-    return mSource->flags();
+    return (mSource->flags() & ~kWantsPrefetching) | kIsCachingDataSource;
 }
 
 void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
@@ -222,6 +223,12 @@ void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
             break;
         }
 
+        case kWhatSuspend:
+        {
+            onSuspend();
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -263,6 +270,7 @@ void NuCachedSource2::onFetch() {
 
     bool keepAlive =
         !mFetching
+            && !mSuspended
             && mFinalStatus == OK
             && ALooper::GetNowUs() >= mLastFetchTimeUs + kKeepAliveIntervalUs;
 
@@ -279,7 +287,7 @@ void NuCachedSource2::onFetch() {
             LOGI("Cache full, done prefetching for now");
             mFetching = false;
         }
-    } else {
+    } else if (!mSuspended) {
         Mutex::Autolock autoLock(mLock);
         restartPrefetcherIfNecessary_l();
     }
@@ -468,5 +476,39 @@ status_t NuCachedSource2::seekInternal_l(off_t offset) {
     return OK;
 }
 
+void NuCachedSource2::clearCacheAndResume() {
+    LOGV("clearCacheAndResume");
+
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mSuspended);
+
+    mCacheOffset = 0;
+    mFinalStatus = OK;
+    mLastAccessPos = 0;
+    mLastFetchTimeUs = -1;
+
+    size_t totalSize = mCache->totalSize();
+    CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
+
+    mFetching = true;
+    mSuspended = false;
+}
+
+void NuCachedSource2::suspend() {
+    (new AMessage(kWhatSuspend, mReflector->id()))->post();
+
+    while (!mSuspended) {
+        usleep(10000);
+    }
+}
+
+void NuCachedSource2::onSuspend() {
+    Mutex::Autolock autoLock(mLock);
+
+    mFetching = false;
+    mSuspended = true;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 943a0fc..4124571 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -31,6 +31,7 @@ namespace android {
 LiveSource::LiveSource(const char *url)
     : mMasterURL(url),
       mInitCheck(NO_INIT),
+      mDurationUs(-1),
       mPlaylistIndex(0),
       mLastFetchTimeUs(-1),
       mSource(new NuHTTPDataSource),
@@ -40,6 +41,8 @@ LiveSource::LiveSource(const char *url)
       mPrevBandwidthIndex(-1) {
     if (switchToNext()) {
         mInitCheck = OK;
+
+        determineSeekability();
     }
 }
 
@@ -139,7 +142,7 @@ bool LiveSource::loadPlaylist(bool fetchMaster) {
         }
 #else
         // Stay on the lowest bandwidth available.
-        size_t index = 0;  // Lowest bandwidth stream
+        size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
 #endif
 
         mURL = mBandwidthItems.editItemAt(index).mURI;
@@ -336,4 +339,69 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
     return OK;
 }
 
+bool LiveSource::seekTo(int64_t seekTimeUs) {
+    LOGV("seek to %lld us", seekTimeUs);
+
+    if (!mPlaylist->isComplete()) {
+        return false;
+    }
+
+    int32_t targetDuration;
+    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
+        return false;
+    }
+
+    int64_t seekTimeSecs = (seekTimeUs + 500000ll) / 1000000ll;
+
+    int64_t index = seekTimeSecs / targetDuration;
+
+    if (index < 0 || index >= mPlaylist->size()) {
+        return false;
+    }
+
+    size_t newPlaylistIndex = mFirstItemSequenceNumber + index;
+
+    if (newPlaylistIndex == mPlaylistIndex) {
+        return false;
+    }
+
+    mPlaylistIndex = newPlaylistIndex;
+
+    switchToNext();
+    mOffsetBias = 0;
+
+    LOGV("seeking to index %lld", index);
+
+    return true;
+}
+
+bool LiveSource::getDuration(int64_t *durationUs) const {
+    if (mDurationUs >= 0) {
+        *durationUs = mDurationUs;
+        return true;
+    }
+
+    *durationUs = 0;
+    return false;
+}
+
+bool LiveSource::isSeekable() const {
+    return mDurationUs >= 0;
+}
+
+void LiveSource::determineSeekability() {
+    mDurationUs = -1;
+
+    if (!mPlaylist->isComplete()) {
+        return;
+    }
+
+    int32_t targetDuration;
+    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
+        return;
+    }
+
+    mDurationUs = targetDuration * 1000000ll * mPlaylist->size();
+}
+
 }  // namespace android
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 0d7daa9..a68c641 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -27,7 +27,8 @@ M3UParser::M3UParser(
     : mInitCheck(NO_INIT),
       mBaseURI(baseURI),
       mIsExtM3U(false),
-      mIsVariantPlaylist(false) {
+      mIsVariantPlaylist(false),
+      mIsComplete(false) {
     mInitCheck = parse(data, size);
 }
 
@@ -46,6 +47,10 @@ bool M3UParser::isVariantPlaylist() const {
     return mIsVariantPlaylist;
 }
 
+bool M3UParser::isComplete() const {
+    return mIsComplete;
+}
+
 sp<AMessage> M3UParser::meta() {
     return mMeta;
 }
@@ -153,6 +158,8 @@ status_t M3UParser::parse(const void *_data, size_t size) {
                     return ERROR_MALFORMED;
                 }
                 err = parseMetaData(line, &mMeta, "media-sequence");
+            } else if (line.startsWith("#EXT-X-ENDLIST")) {
+                mIsComplete = true;
             } else if (line.startsWith("#EXTINF")) {
                 if (mIsVariantPlaylist) {
                     return ERROR_MALFORMED;
diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h
index 5e89581..55dd45e 100644
--- a/media/libstagefright/include/LiveSource.h
+++ b/media/libstagefright/include/LiveSource.h
@@ -40,6 +40,11 @@ struct LiveSource : public DataSource {
         return kWantsPrefetching;
     }
 
+    bool getDuration(int64_t *durationUs) const;
+
+    bool isSeekable() const;
+    bool seekTo(int64_t seekTimeUs);
+
 protected:
     virtual ~LiveSource();
 
@@ -53,6 +58,7 @@ private:
     AString mMasterURL;
     AString mURL;
     status_t mInitCheck;
+    int64_t mDurationUs;
 
     sp<M3UParser> mPlaylist;
     int32_t mFirstItemSequenceNumber;
@@ -72,6 +78,7 @@ private:
 
     bool switchToNext();
     bool loadPlaylist(bool fetchMaster);
+    void determineSeekability();
 
     DISALLOW_EVIL_CONSTRUCTORS(LiveSource);
 };
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 69199ab..bd9eebe 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -32,6 +32,7 @@ struct M3UParser : public RefBase {
 
     bool isExtM3U() const;
     bool isVariantPlaylist() const;
+    bool isComplete() const;
 
     sp<AMessage> meta();
 
@@ -52,6 +53,7 @@ private:
     AString mBaseURI;
     bool mIsExtM3U;
     bool mIsVariantPlaylist;
+    bool mIsComplete;
 
     sp<AMessage> mMeta;
     Vector<Item> mItems;
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 1bf4cd1..d83b538 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -15,6 +15,7 @@ struct ATSParser;
 struct DataSource;
 struct MPEG2TSSource;
 struct String8;
+struct LiveSource;
 
 struct MPEG2TSExtractor : public MediaExtractor {
     MPEG2TSExtractor(const sp<DataSource> &source);
@@ -25,16 +26,19 @@ struct MPEG2TSExtractor : public MediaExtractor {
 
     virtual sp<MetaData> getMetaData();
 
-    virtual uint32_t flags() const {
-        return CAN_PAUSE;
-    }
+    virtual uint32_t flags() const;
+
+    void setLiveSource(const sp<LiveSource> &liveSource);
+    void seekTo(int64_t seekTimeUs);
 
 private:
     friend struct MPEG2TSSource;
 
-    Mutex mLock;
+    mutable Mutex mLock;
 
     sp<DataSource> mDataSource;
+    sp<LiveSource> mLiveSource;
+
     sp<ATSParser> mParser;
 
     Vector<sp<AnotherPacketSource> > mSourceImpls;
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 3a20c16..1fb2088 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -42,6 +42,9 @@ struct NuCachedSource2 : public DataSource {
     size_t cachedSize();
     size_t approxDataRemaining(bool *eos);
 
+    void suspend();
+    void clearCacheAndResume();
+
 protected:
     virtual ~NuCachedSource2();
 
@@ -61,6 +64,7 @@ private:
     enum {
         kWhatFetchMore  = 'fetc',
         kWhatRead       = 'read',
+        kWhatSuspend    = 'susp',
     };
 
     sp<DataSource> mSource;
@@ -78,10 +82,12 @@ private:
     sp<AMessage> mAsyncResult;
     bool mFetching;
     int64_t mLastFetchTimeUs;
+    bool mSuspended;
 
     void onMessageReceived(const sp<AMessage> &msg);
     void onFetch();
     void onRead(const sp<AMessage> &msg);
+    void onSuspend();
 
     void fetchInternal();
     ssize_t readInternal(off_t offset, void *data, size_t size);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 7c9b83a..c88c6c1 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -49,13 +49,17 @@ struct ATSParser::Program : public RefBase {
             unsigned pid, unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity();
+    void signalDiscontinuity(bool isASeek);
 
     sp<MediaSource> getSource(SourceType type);
 
+    int64_t convertPTSToTimestamp(uint64_t PTS);
+
 private:
     unsigned mProgramMapPID;
     KeyedVector<unsigned, sp<Stream> > mStreams;
+    bool mFirstPTSValid;
+    uint64_t mFirstPTS;
 
     void parseProgramMap(ABitReader *br);
 
@@ -63,13 +67,13 @@ private:
 };
 
 struct ATSParser::Stream : public RefBase {
-    Stream(unsigned elementaryPID, unsigned streamType);
+    Stream(Program *program, unsigned elementaryPID, unsigned streamType);
 
     void parse(
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity();
+    void signalDiscontinuity(bool isASeek);
 
     sp<MediaSource> getSource(SourceType type);
 
@@ -77,6 +81,7 @@ protected:
     virtual ~Stream();
 
 private:
+    Program *mProgram;
     unsigned mElementaryPID;
     unsigned mStreamType;
 
@@ -101,7 +106,9 @@ private:
 ////////////////////////////////////////////////////////////////////////////////
 
 ATSParser::Program::Program(unsigned programMapPID)
-    : mProgramMapPID(programMapPID) {
+    : mProgramMapPID(programMapPID),
+      mFirstPTSValid(false),
+      mFirstPTS(0) {
 }
 
 bool ATSParser::Program::parsePID(
@@ -128,9 +135,9 @@ bool ATSParser::Program::parsePID(
     return true;
 }
 
-void ATSParser::Program::signalDiscontinuity() {
+void ATSParser::Program::signalDiscontinuity(bool isASeek) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        mStreams.editValueAt(i)->signalDiscontinuity();
+        mStreams.editValueAt(i)->signalDiscontinuity(isASeek);
     }
 }
 
@@ -213,10 +220,12 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
         ssize_t index = mStreams.indexOfKey(elementaryPID);
 #if 0  // XXX revisit
         CHECK_LT(index, 0);
-        mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+        mStreams.add(elementaryPID,
+                     new Stream(this, elementaryPID, streamType));
 #else
         if (index < 0) {
-            mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
+            mStreams.add(elementaryPID,
+                         new Stream(this, elementaryPID, streamType));
         }
 #endif
 
@@ -239,10 +248,26 @@ sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
     return NULL;
 }
 
+int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
+    if (!mFirstPTSValid) {
+        mFirstPTSValid = true;
+        mFirstPTS = PTS;
+        PTS = 0;
+    } else if (PTS < mFirstPTS) {
+        PTS = 0;
+    } else {
+        PTS -= mFirstPTS;
+    }
+
+    return (PTS * 100) / 9;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::Stream::Stream(unsigned elementaryPID, unsigned streamType)
-    : mElementaryPID(elementaryPID),
+ATSParser::Stream::Stream(
+        Program *program, unsigned elementaryPID, unsigned streamType)
+    : mProgram(program),
+      mElementaryPID(elementaryPID),
       mStreamType(streamType),
       mBuffer(new ABuffer(128 * 1024)),
       mPayloadStarted(false),
@@ -281,13 +306,21 @@ void ATSParser::Stream::parse(
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
 }
 
-void ATSParser::Stream::signalDiscontinuity() {
+void ATSParser::Stream::signalDiscontinuity(bool isASeek) {
     LOGV("Stream discontinuity");
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
     mQueue.clear();
 
+    if (isASeek) {
+        // This is only a "minor" discontinuity, we stay within the same
+        // bitstream.
+
+        mSource->clear();
+        return;
+    }
+
     if (mStreamType == 0x1b && mSource != NULL) {
         // Don't signal discontinuities on audio streams.
         mSource->queueDiscontinuity();
@@ -467,7 +500,7 @@ void ATSParser::Stream::onPayloadData(
     LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
     CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
-    int64_t timeUs = (PTS * 100) / 9;
+    int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
 
     status_t err = mQueue.appendData(data, size, timeUs);
     CHECK_EQ(err, (status_t)OK);
@@ -515,9 +548,9 @@ void ATSParser::feedTSPacket(const void *data, size_t size) {
     parseTS(&br);
 }
 
-void ATSParser::signalDiscontinuity() {
+void ATSParser::signalDiscontinuity(bool isASeek) {
     for (size_t i = 0; i < mPrograms.size(); ++i) {
-        mPrograms.editItemAt(i)->signalDiscontinuity();
+        mPrograms.editItemAt(i)->signalDiscontinuity(isASeek);
     }
 }
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 9ec6d7b..11b1de4 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -33,7 +33,7 @@ struct ATSParser : public RefBase {
     ATSParser();
 
     void feedTSPacket(const void *data, size_t size);
-    void signalDiscontinuity();
+    void signalDiscontinuity(bool isASeek = false);
 
     enum SourceType {
         AVC_VIDEO,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 3f76820..ea747c8 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -91,6 +91,10 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
         return;
     }
 
+    int64_t timeUs;
+    CHECK(buffer->meta()->findInt64("time", &timeUs));
+    LOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6);
+
     Mutex::Autolock autoLock(mLock);
     mBuffers.push_back(buffer);
     mCondition.signal();
@@ -101,10 +105,17 @@ void AnotherPacketSource::queueDiscontinuity() {
     buffer->meta()->setInt32("discontinuity", true);
 
     Mutex::Autolock autoLock(mLock);
+
     mBuffers.push_back(buffer);
     mCondition.signal();
 }
 
+void AnotherPacketSource::clear() {
+    Mutex::Autolock autoLock(mLock);
+    mBuffers.clear();
+    mEOSResult = OK;
+}
+
 void AnotherPacketSource::signalEOS(status_t result) {
     CHECK(result != OK);
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6b43c4e..6999175 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -43,6 +43,8 @@ struct AnotherPacketSource : public MediaSource {
     void queueDiscontinuity();
     void signalEOS(status_t result);
 
+    void clear();
+
 protected:
     virtual ~AnotherPacketSource();
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index a13287e..b0b9e66 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -42,6 +42,7 @@ sp<MetaData> ElementaryStreamQueue::getFormat() {
 
 void ElementaryStreamQueue::clear() {
     mBuffer->setRange(0, 0);
+    mTimestamps.clear();
     mFormat.clear();
 }
 
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 0d96bd1..3176810 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -19,8 +19,11 @@
 #include <utils/Log.h>
 
 #include "include/MPEG2TSExtractor.h"
+#include "include/LiveSource.h"
+#include "include/NuCachedSource2.h"
 
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
@@ -37,7 +40,8 @@ static const size_t kTSPacketSize = 188;
 struct MPEG2TSSource : public MediaSource {
     MPEG2TSSource(
             const sp<MPEG2TSExtractor> &extractor,
-            const sp<AnotherPacketSource> &impl);
+            const sp<AnotherPacketSource> &impl,
+            bool seekable);
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -50,14 +54,20 @@ private:
     sp<MPEG2TSExtractor> mExtractor;
     sp<AnotherPacketSource> mImpl;
 
+    // If there are both audio and video streams, only the video stream
+    // will be seekable, otherwise the single stream will be seekable.
+    bool mSeekable;
+
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource);
 };
 
 MPEG2TSSource::MPEG2TSSource(
         const sp<MPEG2TSExtractor> &extractor,
-        const sp<AnotherPacketSource> &impl)
+        const sp<AnotherPacketSource> &impl,
+        bool seekable)
     : mExtractor(extractor),
-      mImpl(impl) {
+      mImpl(impl),
+      mSeekable(seekable) {
 }
 
 status_t MPEG2TSSource::start(MetaData *params) {
@@ -69,13 +79,27 @@ status_t MPEG2TSSource::stop() {
 }
 
 sp<MetaData> MPEG2TSSource::getFormat() {
-    return mImpl->getFormat();
+    sp<MetaData> meta = mImpl->getFormat();
+
+    int64_t durationUs;
+    if (mExtractor->mLiveSource != NULL
+            && mExtractor->mLiveSource->getDuration(&durationUs)) {
+        meta->setInt64(kKeyDuration, durationUs);
+    }
+
+    return meta;
 }
 
 status_t MPEG2TSSource::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode seekMode;
+    if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+        mExtractor->seekTo(seekTimeUs);
+    }
+
     status_t finalResult;
     while (!mImpl->hasBufferAvailable(&finalResult)) {
         if (finalResult != OK) {
@@ -109,7 +133,20 @@ sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) {
         return NULL;
     }
 
-    return new MPEG2TSSource(this, mSourceImpls.editItemAt(index));
+    bool seekable = true;
+    if (mSourceImpls.size() > 1) {
+        CHECK_EQ(mSourceImpls.size(), 2u);
+
+        sp<MetaData> meta = mSourceImpls.editItemAt(index)->getFormat();
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+        if (!strncasecmp("audio/", mime, 6)) {
+            seekable = false;
+        }
+    }
+
+    return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable);
 }
 
 sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
@@ -189,6 +226,46 @@ status_t MPEG2TSExtractor::feedMore() {
     return OK;
 }
 
+void MPEG2TSExtractor::setLiveSource(const sp<LiveSource> &liveSource) {
+    Mutex::Autolock autoLock(mLock);
+
+    mLiveSource = liveSource;
+}
+
+void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mLiveSource == NULL) {
+        return;
+    }
+
+    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+        static_cast<NuCachedSource2 *>(mDataSource.get())->suspend();
+    }
+
+    if (mLiveSource->seekTo(seekTimeUs)) {
+        mParser->signalDiscontinuity(true  /* isSeek */);
+        mOffset = 0;
+    }
+
+    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+        static_cast<NuCachedSource2 *>(mDataSource.get())
+            ->clearCacheAndResume();
+    }
+}
+
+uint32_t MPEG2TSExtractor::flags() const {
+    Mutex::Autolock autoLock(mLock);
+
+    uint32_t flags = CAN_PAUSE;
+
+    if (mLiveSource != NULL && mLiveSource->isSeekable()) {
+        flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK;
+    }
+
+    return flags;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffMPEG2TS(
-- 
cgit v1.1