diff options
Diffstat (limited to 'media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp')
-rw-r--r-- | media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 300 |
1 files changed, 259 insertions, 41 deletions
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index f5c33cf..287cae8 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -21,12 +21,14 @@ #include "include/MPEG2TSExtractor.h" #include "include/NuCachedSource2.h" +#include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/IStreamSource.h> #include <utils/String8.h> #include "AnotherPacketSource.h" @@ -40,7 +42,7 @@ struct MPEG2TSSource : public MediaSource { MPEG2TSSource( const sp<MPEG2TSExtractor> &extractor, const sp<AnotherPacketSource> &impl, - bool seekable); + bool doesSeek); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -54,8 +56,8 @@ private: 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; + // will signal seek on the extractor; otherwise the single stream will seek. + bool mDoesSeek; DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource); }; @@ -63,10 +65,10 @@ private: MPEG2TSSource::MPEG2TSSource( const sp<MPEG2TSExtractor> &extractor, const sp<AnotherPacketSource> &impl, - bool seekable) + bool doesSeek) : mExtractor(extractor), mImpl(impl), - mSeekable(seekable) { + mDoesSeek(doesSeek) { } status_t MPEG2TSSource::start(MetaData *params) { @@ -85,27 +87,18 @@ status_t MPEG2TSSource::read( MediaBuffer **out, const ReadOptions *options) { *out = NULL; - status_t finalResult; - while (!mImpl->hasBufferAvailable(&finalResult)) { - if (finalResult != OK) { - return ERROR_END_OF_STREAM; - } - - status_t err = mExtractor->feedMore(); + int64_t seekTimeUs; + ReadOptions::SeekMode seekMode; + if (mDoesSeek && options && options->getSeekTo(&seekTimeUs, &seekMode)) { + // seek is needed + status_t err = mExtractor->seek(seekTimeUs, seekMode); if (err != OK) { - mImpl->signalEOS(err); + return err; } } - int64_t seekTimeUs; - ReadOptions::SeekMode seekMode; - if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) { - // A seek was requested, but we don't actually support seeking and so can only "seek" to - // the current position - int64_t nextBufTimeUs; - if (mImpl->nextBufferTime(&nextBufTimeUs) != OK || seekTimeUs != nextBufTimeUs) { - return ERROR_UNSUPPORTED; - } + if (mExtractor->feedUntilBufferAvailable(mImpl) != OK) { + return ERROR_END_OF_STREAM; } return mImpl->read(out, options); @@ -129,23 +122,10 @@ sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) { return NULL; } - bool seekable = true; - if (mSourceImpls.size() > 1) { - if (mSourceImpls.size() != 2u) { - ALOGE("Wrong size"); - return NULL; - } - - 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); + // The seek reference track (video if present; audio otherwise) performs + // seek requests, while other tracks ignore requests. + return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), + (mSeekSyncPoints == &mSyncPoints.editItemAt(index))); } sp<MetaData> MPEG2TSExtractor::getTrackMetaData( @@ -178,6 +158,8 @@ void MPEG2TSExtractor::init() { if (impl != NULL) { haveVideo = true; mSourceImpls.push(impl); + mSyncPoints.push(); + mSeekSyncPoints = &mSyncPoints.editTop(); } } @@ -189,6 +171,10 @@ void MPEG2TSExtractor::init() { if (impl != NULL) { haveAudio = true; mSourceImpls.push(impl); + mSyncPoints.push(); + if (!haveVideo) { + mSeekSyncPoints = &mSyncPoints.editTop(); + } } } @@ -197,6 +183,55 @@ void MPEG2TSExtractor::init() { } } + off64_t size; + if (mDataSource->getSize(&size) == OK && (haveAudio || haveVideo)) { + sp<AnotherPacketSource> impl = haveVideo + ? (AnotherPacketSource *)mParser->getSource( + ATSParser::VIDEO).get() + : (AnotherPacketSource *)mParser->getSource( + ATSParser::AUDIO).get(); + int64_t prevBufferedDurationUs = 0; + int64_t durationUs = -1; + List<int64_t> durations; + // Estimate duration --- stabilize until you get <500ms deviation. + for (; feedMore() == OK && numPacketsParsed <= 10000; + ++numPacketsParsed) { + status_t err; + int64_t bufferedDurationUs = impl->getBufferedDurationUs(&err); + if (err != OK) { + break; + } + if (bufferedDurationUs != prevBufferedDurationUs) { + durationUs = size * bufferedDurationUs / mOffset; + durations.push_back(durationUs); + if (durations.size() > 5) { + durations.erase(durations.begin()); + int64_t min = *durations.begin(); + int64_t max = *durations.begin(); + for (List<int64_t>::iterator i = durations.begin(); + i != durations.end(); + ++i) { + if (min > *i) { + min = *i; + } + if (max < *i) { + max = *i; + } + } + if (max - min < 500 * 1000) { + break; + } + } + prevBufferedDurationUs = bufferedDurationUs; + } + } + if (durationUs > 0) { + const sp<MetaData> meta = impl->getFormat(); + meta->setInt64(kKeyDuration, durationUs); + impl->setFormat(meta); + } + } + ALOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo); } @@ -213,12 +248,195 @@ status_t MPEG2TSExtractor::feedMore() { return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; } + ATSParser::SyncEvent event(mOffset); mOffset += n; - return mParser->feedTSPacket(packet, kTSPacketSize); + status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event); + if (event.isInit()) { + for (size_t i = 0; i < mSourceImpls.size(); ++i) { + if (mSourceImpls[i].get() == event.getMediaSource().get()) { + mSyncPoints.editItemAt(i).add( + event.getTimeUs(), event.getOffset()); + break; + } + } + } + return err; } uint32_t MPEG2TSExtractor::flags() const { - return CAN_PAUSE; + return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD; +} + +status_t MPEG2TSExtractor::seek(int64_t seekTimeUs, + const MediaSource::ReadOptions::SeekMode &seekMode) { + if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) { + ALOGW("No sync point to seek to."); + // ... and therefore we have nothing useful to do here. + return OK; + } + + // Determine whether we're seeking beyond the known area. + bool shouldSeekBeyond = + (seekTimeUs > mSeekSyncPoints->keyAt(mSeekSyncPoints->size() - 1)); + + // Determine the sync point to seek. + size_t index = 0; + for (; index < mSeekSyncPoints->size(); ++index) { + int64_t timeUs = mSeekSyncPoints->keyAt(index); + if (timeUs > seekTimeUs) { + break; + } + } + + switch (seekMode) { + case MediaSource::ReadOptions::SEEK_NEXT_SYNC: + if (index == mSeekSyncPoints->size()) { + ALOGW("Next sync not found; starting from the latest sync."); + --index; + } + break; + case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC: + case MediaSource::ReadOptions::SEEK_CLOSEST: + ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC", + seekMode); + // fall-through + case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC: + if (index == 0) { + ALOGW("Previous sync not found; starting from the earliest " + "sync."); + } else { + --index; + } + break; + } + if (!shouldSeekBeyond || mOffset <= mSeekSyncPoints->valueAt(index)) { + int64_t actualSeekTimeUs = mSeekSyncPoints->keyAt(index); + mOffset = mSeekSyncPoints->valueAt(index); + status_t err = queueDiscontinuityForSeek(actualSeekTimeUs); + if (err != OK) { + return err; + } + } + + if (shouldSeekBeyond) { + status_t err = seekBeyond(seekTimeUs); + if (err != OK) { + return err; + } + } + + // Fast-forward to sync frame. + for (size_t i = 0; i < mSourceImpls.size(); ++i) { + const sp<AnotherPacketSource> &impl = mSourceImpls[i]; + status_t err; + feedUntilBufferAvailable(impl); + while (impl->hasBufferAvailable(&err)) { + sp<AMessage> meta = impl->getMetaAfterLastDequeued(0); + sp<ABuffer> buffer; + if (meta == NULL) { + return UNKNOWN_ERROR; + } + int32_t sync; + if (meta->findInt32("isSync", &sync) && sync) { + break; + } + err = impl->dequeueAccessUnit(&buffer); + if (err != OK) { + return err; + } + feedUntilBufferAvailable(impl); + } + } + + return OK; +} + +status_t MPEG2TSExtractor::queueDiscontinuityForSeek(int64_t actualSeekTimeUs) { + // Signal discontinuity + sp<AMessage> extra(new AMessage); + extra->setInt64(IStreamListener::kKeyMediaTimeUs, actualSeekTimeUs); + mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_TIME, extra); + + // After discontinuity, impl should only have discontinuities + // with the last being what we queued. Dequeue them all here. + for (size_t i = 0; i < mSourceImpls.size(); ++i) { + const sp<AnotherPacketSource> &impl = mSourceImpls.itemAt(i); + sp<ABuffer> buffer; + status_t err; + while (impl->hasBufferAvailable(&err)) { + if (err != OK) { + return err; + } + err = impl->dequeueAccessUnit(&buffer); + // If the source contains anything but discontinuity, that's + // a programming mistake. + CHECK(err == INFO_DISCONTINUITY); + } + } + + // Feed until we have a buffer for each source. + for (size_t i = 0; i < mSourceImpls.size(); ++i) { + const sp<AnotherPacketSource> &impl = mSourceImpls.itemAt(i); + sp<ABuffer> buffer; + status_t err = feedUntilBufferAvailable(impl); + if (err != OK) { + return err; + } + } + + return OK; +} + +status_t MPEG2TSExtractor::seekBeyond(int64_t seekTimeUs) { + // If we're seeking beyond where we know --- read until we reach there. + size_t syncPointsSize = mSeekSyncPoints->size(); + + while (seekTimeUs > mSeekSyncPoints->keyAt( + mSeekSyncPoints->size() - 1)) { + status_t err; + if (syncPointsSize < mSeekSyncPoints->size()) { + syncPointsSize = mSeekSyncPoints->size(); + int64_t syncTimeUs = mSeekSyncPoints->keyAt(syncPointsSize - 1); + // Dequeue buffers before sync point in order to avoid too much + // cache building up. + sp<ABuffer> buffer; + for (size_t i = 0; i < mSourceImpls.size(); ++i) { + const sp<AnotherPacketSource> &impl = mSourceImpls[i]; + int64_t timeUs; + while ((err = impl->nextBufferTime(&timeUs)) == OK) { + if (timeUs < syncTimeUs) { + impl->dequeueAccessUnit(&buffer); + } else { + break; + } + } + if (err != OK && err != -EWOULDBLOCK) { + return err; + } + } + } + if (feedMore() != OK) { + return ERROR_END_OF_STREAM; + } + } + + return OK; +} + +status_t MPEG2TSExtractor::feedUntilBufferAvailable( + const sp<AnotherPacketSource> &impl) { + status_t finalResult; + while (!impl->hasBufferAvailable(&finalResult)) { + if (finalResult != OK) { + return finalResult; + } + + status_t err = feedMore(); + if (err != OK) { + impl->signalEOS(err); + } + } + return OK; } //////////////////////////////////////////////////////////////////////////////// |