diff options
author | Andreas Huber <andih@google.com> | 2010-10-14 23:22:21 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-10-14 23:22:21 -0700 |
commit | e449fceaa4b1e1c76d95a4255d23ac508bd75751 (patch) | |
tree | 8b31fb1768f6a13521faefe47d6bdd6b439944ce | |
parent | af95d12b12056b4f88cbb35e61209a953739009b (diff) | |
parent | 28b93967d0e702304c7edec854a29d5ab88bfce0 (diff) | |
download | frameworks_base-e449fceaa4b1e1c76d95a4255d23ac508bd75751.zip frameworks_base-e449fceaa4b1e1c76d95a4255d23ac508bd75751.tar.gz frameworks_base-e449fceaa4b1e1c76d95a4255d23ac508bd75751.tar.bz2 |
am 28b93967: am 165c6579: Merge "HTTP Live content that are tagged as complete are now seekable." into gingerbread
Merge commit '28b93967d0e702304c7edec854a29d5ab88bfce0'
* commit '28b93967d0e702304c7edec854a29d5ab88bfce0':
HTTP Live content that are tagged as complete are now seekable.
-rw-r--r-- | include/media/stagefright/DataSource.h | 1 | ||||
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 12 | ||||
-rw-r--r-- | media/libstagefright/NuCachedSource2.cpp | 48 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSource.cpp | 70 | ||||
-rw-r--r-- | media/libstagefright/httplive/M3UParser.cpp | 9 | ||||
-rw-r--r-- | media/libstagefright/include/LiveSource.h | 7 | ||||
-rw-r--r-- | media/libstagefright/include/M3UParser.h | 2 | ||||
-rw-r--r-- | media/libstagefright/include/MPEG2TSExtractor.h | 12 | ||||
-rw-r--r-- | media/libstagefright/include/NuCachedSource2.h | 6 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 61 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.h | 2 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.cpp | 11 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.h | 2 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ESQueue.cpp | 1 | ||||
-rw-r--r-- | 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 fcc3e81..7b4dd1c 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" @@ -509,8 +510,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)) { @@ -1365,14 +1366,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 f6f7dbd..90f3d6d 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( |