diff options
-rw-r--r-- | media/libmediaplayerservice/nuplayer/GenericSource.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.cpp | 279 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.h | 24 | ||||
-rw-r--r-- | media/libstagefright/httplive/M3UParser.cpp | 39 | ||||
-rw-r--r-- | media/libstagefright/httplive/M3UParser.h | 4 | ||||
-rw-r--r-- | media/libstagefright/httplive/PlaylistFetcher.cpp | 322 | ||||
-rw-r--r-- | media/libstagefright/httplive/PlaylistFetcher.h | 25 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.cpp | 21 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.h | 4 |
10 files changed, 546 insertions, 180 deletions
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index a18407f..9df3f53 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -290,7 +290,7 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { int64_t timeUs, actualTimeUs; const bool formatChange = true; - sp<AMessage> latestMeta = track->mPackets->getLatestMeta(); + sp<AMessage> latestMeta = track->mPackets->getLatestEnqueuedMeta(); CHECK(latestMeta != NULL && latestMeta->findInt64("timeUs", &timeUs)); readBuffer(trackType, timeUs, &actualTimeUs, formatChange); readBuffer(counterpartType, -1, NULL, formatChange); diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 10cdde2..8667a6b 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -57,7 +57,7 @@ LiveSession::LiveSession( mHTTPService(httpService), mInPreparationPhase(true), mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())), - mPrevBandwidthIndex(-1), + mCurBandwidthIndex(-1), mStreamMask(0), mNewStreamMask(0), mSwapMask(0), @@ -68,13 +68,17 @@ LiveSession::LiveSession( mReconfigurationInProgress(false), mSwitchInProgress(false), mDisconnectReplyID(0), - mSeekReplyID(0) { + mSeekReplyID(0), + mFirstTimeUsValid(false), + mFirstTimeUs(0), + mLastSeekTimeUs(0) { mStreams[kAudioIndex] = StreamItem("audio"); mStreams[kVideoIndex] = StreamItem("video"); mStreams[kSubtitleIndex] = StreamItem("subtitles"); for (size_t i = 0; i < kMaxStreams; ++i) { + mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */)); } @@ -109,31 +113,65 @@ status_t LiveSession::dequeueAccessUnit( return -EWOULDBLOCK; } + status_t finalResult; + sp<AnotherPacketSource> discontinuityQueue = mDiscontinuities.valueFor(stream); + if (discontinuityQueue->hasBufferAvailable(&finalResult)) { + discontinuityQueue->dequeueAccessUnit(accessUnit); + // seeking, track switching + sp<AMessage> extra; + int64_t timeUs; + if ((*accessUnit)->meta()->findMessage("extra", &extra) + && extra != NULL + && extra->findInt64("timeUs", &timeUs)) { + // seeking only + mLastSeekTimeUs = timeUs; + mDiscontinuityOffsetTimesUs.clear(); + mDiscontinuityAbsStartTimesUs.clear(); + } + return INFO_DISCONTINUITY; + } + sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream); - status_t finalResult; if (!packetSource->hasBufferAvailable(&finalResult)) { return finalResult == OK ? -EAGAIN : finalResult; } + // wait for counterpart + sp<AnotherPacketSource> otherSource; + if (stream == STREAMTYPE_AUDIO && (mStreamMask & STREAMTYPE_VIDEO)) { + otherSource = mPacketSources.valueFor(STREAMTYPE_VIDEO); + } else if (stream == STREAMTYPE_VIDEO && (mStreamMask & STREAMTYPE_AUDIO)) { + otherSource = mPacketSources.valueFor(STREAMTYPE_AUDIO); + } + if (otherSource != NULL && !otherSource->hasBufferAvailable(&finalResult)) { + return finalResult == OK ? -EAGAIN : finalResult; + } + status_t err = packetSource->dequeueAccessUnit(accessUnit); + size_t streamIdx; const char *streamStr; switch (stream) { case STREAMTYPE_AUDIO: + streamIdx = kAudioIndex; streamStr = "audio"; break; case STREAMTYPE_VIDEO: + streamIdx = kVideoIndex; streamStr = "video"; break; case STREAMTYPE_SUBTITLES: + streamIdx = kSubtitleIndex; streamStr = "subs"; break; default: TRESPASS(); } + StreamItem& strm = mStreams[streamIdx]; if (err == INFO_DISCONTINUITY) { + // adaptive streaming, discontinuities in the playlist int32_t type; CHECK((*accessUnit)->meta()->findInt32("discontinuity", &type)); @@ -148,10 +186,7 @@ status_t LiveSession::dequeueAccessUnit( extra == NULL ? "NULL" : extra->debugString().c_str()); int32_t swap; - if (type == ATSParser::DISCONTINUITY_FORMATCHANGE - && (*accessUnit)->meta()->findInt32("swapPacketSource", &swap) - && swap) { - + if ((*accessUnit)->meta()->findInt32("swapPacketSource", &swap) && swap) { int32_t switchGeneration; CHECK((*accessUnit)->meta()->findInt32("switchGeneration", &switchGeneration)); { @@ -164,13 +199,67 @@ status_t LiveSession::dequeueAccessUnit( msg->post(); } } + } else { + size_t seq = strm.mCurDiscontinuitySeq; + int64_t offsetTimeUs; + if (mDiscontinuityOffsetTimesUs.indexOfKey(seq) >= 0) { + offsetTimeUs = mDiscontinuityOffsetTimesUs.valueFor(seq); + } else { + offsetTimeUs = 0; + } + + seq += 1; + if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) { + int64_t firstTimeUs; + firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq); + offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs; + offsetTimeUs += strm.mLastSampleDurationUs; + } else { + offsetTimeUs += strm.mLastSampleDurationUs; + } + + mDiscontinuityOffsetTimesUs.add(seq, offsetTimeUs); } } else if (err == OK) { + if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) { int64_t timeUs; + int32_t discontinuitySeq = 0; CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); - ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs); + (*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq); + strm.mCurDiscontinuitySeq = discontinuitySeq; + + int32_t discard = 0; + int64_t firstTimeUs; + if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) { + int64_t durUs; // approximate sample duration + if (timeUs > strm.mLastDequeuedTimeUs) { + durUs = timeUs - strm.mLastDequeuedTimeUs; + } else { + durUs = strm.mLastDequeuedTimeUs - timeUs; + } + strm.mLastSampleDurationUs = durUs; + firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq); + } else if ((*accessUnit)->meta()->findInt32("discard", &discard) && discard) { + firstTimeUs = timeUs; + } else { + mDiscontinuityAbsStartTimesUs.add(strm.mCurDiscontinuitySeq, timeUs); + firstTimeUs = timeUs; + } + + strm.mLastDequeuedTimeUs = timeUs; + if (timeUs >= firstTimeUs) { + timeUs -= firstTimeUs; + } else { + timeUs = 0; + } + timeUs += mLastSeekTimeUs; + if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) { + timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq); + } + ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs); + (*accessUnit)->meta()->setInt64("timeUs", timeUs); mLastDequeuedTimeUs = timeUs; mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; } else if (stream == STREAMTYPE_SUBTITLES) { @@ -289,7 +378,9 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { break; } - tryToFinishBandwidthSwitch(); + if (mSwitchInProgress) { + tryToFinishBandwidthSwitch(); + } } if (mContinuation != NULL) { @@ -538,8 +629,9 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { mBandwidthItems.push(item); } + mPlaylist->pickRandomMediaItems(); changeConfiguration( - 0ll /* timeUs */, initialBandwidthIndex, true /* pickTrack */); + 0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */); } void LiveSession::finishDisconnect() { @@ -847,20 +939,20 @@ size_t LiveSession::getBandwidthIndex() { // to lowest) const size_t kMinIndex = 0; - static ssize_t mPrevBandwidthIndex = -1; + static ssize_t mCurBandwidthIndex = -1; size_t index; - if (mPrevBandwidthIndex < 0) { + if (mCurBandwidthIndex < 0) { index = kMinIndex; } else if (uniformRand() < 0.5) { - index = (size_t)mPrevBandwidthIndex; + index = (size_t)mCurBandwidthIndex; } else { - index = mPrevBandwidthIndex + 1; + index = mCurBandwidthIndex + 1; if (index == mBandwidthItems.size()) { index = kMinIndex; } } - mPrevBandwidthIndex = index; + mCurBandwidthIndex = index; #elif 0 // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec @@ -937,7 +1029,10 @@ sp<AMessage> LiveSession::getTrackInfo(size_t trackIndex) const { status_t LiveSession::selectTrack(size_t index, bool select) { status_t err = mPlaylist->selectTrack(index, select); if (err == OK) { - (new AMessage(kWhatChangeConfiguration, id()))->post(); + sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id()); + msg->setInt32("bandwidthIndex", mCurBandwidthIndex); + msg->setInt32("pickTrack", select); + msg->post(); } return err; } @@ -964,15 +1059,11 @@ void LiveSession::changeConfiguration( CHECK(!mReconfigurationInProgress); mReconfigurationInProgress = true; - mPrevBandwidthIndex = bandwidthIndex; + mCurBandwidthIndex = bandwidthIndex; ALOGV("changeConfiguration => timeUs:%" PRId64 " us, bwIndex:%zu, pickTrack:%d", timeUs, bandwidthIndex, pickTrack); - if (pickTrack) { - mPlaylist->pickRandomMediaItems(); - } - CHECK_LT(bandwidthIndex, mBandwidthItems.size()); const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex); @@ -995,14 +1086,15 @@ void LiveSession::changeConfiguration( // If we're seeking all current fetchers are discarded. if (timeUs < 0ll) { - // delay fetcher removal - discardFetcher = false; + // delay fetcher removal if not picking tracks + discardFetcher = pickTrack; for (size_t j = 0; j < kMaxStreams; ++j) { StreamType type = indexToType(j); if ((streamMask & type) && uri == URIs[j]) { resumeMask |= type; streamMask &= ~type; + discardFetcher = false; } } } @@ -1016,16 +1108,17 @@ void LiveSession::changeConfiguration( sp<AMessage> msg; if (timeUs < 0ll) { - // skip onChangeConfiguration2 (decoder destruction) if switching. + // skip onChangeConfiguration2 (decoder destruction) if not seeking. msg = new AMessage(kWhatChangeConfiguration3, id()); } else { msg = new AMessage(kWhatChangeConfiguration2, id()); } msg->setInt32("streamMask", streamMask); msg->setInt32("resumeMask", resumeMask); + msg->setInt32("pickTrack", pickTrack); msg->setInt64("timeUs", timeUs); for (size_t i = 0; i < kMaxStreams; ++i) { - if (streamMask & indexToType(i)) { + if ((streamMask | resumeMask) & indexToType(i)) { msg->setString(mStreams[i].uriKey().c_str(), URIs[i].c_str()); } } @@ -1049,7 +1142,10 @@ void LiveSession::changeConfiguration( void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) { if (!mReconfigurationInProgress) { - changeConfiguration(-1ll /* timeUs */, getBandwidthIndex()); + int32_t pickTrack = 0, bandwidthIndex = mCurBandwidthIndex; + msg->findInt32("pickTrack", &pickTrack); + msg->findInt32("bandwidthIndex", &bandwidthIndex); + changeConfiguration(-1ll /* timeUs */, bandwidthIndex, pickTrack); } else { msg->post(1000000ll); // retry in 1 sec } @@ -1060,8 +1156,14 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { // All fetchers are either suspended or have been removed now. - uint32_t streamMask; + uint32_t streamMask, resumeMask; CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask)); + CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask)); + + // currently onChangeConfiguration2 is only called for seeking; + // remove the following CHECK if using it else where. + CHECK_EQ(resumeMask, 0); + streamMask |= resumeMask; AString URIs[kMaxStreams]; for (size_t i = 0; i < kMaxStreams; ++i) { @@ -1125,16 +1227,21 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { } int64_t timeUs; + int32_t pickTrack; bool switching = false; CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("pickTrack", &pickTrack)); if (timeUs < 0ll) { - timeUs = mLastDequeuedTimeUs; - switching = true; + if (!pickTrack) { + switching = true; + } + mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs; + } else { + mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; } - mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; - mNewStreamMask = streamMask; + mNewStreamMask = streamMask | resumeMask; // Of all existing fetchers: // * Resume fetchers that are still needed and assign them original packet sources. @@ -1147,6 +1254,16 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { for (size_t j = 0; j < kMaxStreams; ++j) { if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) { sources[j] = mPacketSources.valueFor(indexToType(j)); + + if (j != kSubtitleIndex) { + ALOGV("queueing dummy discontinuity for stream type %d", indexToType(j)); + sp<AnotherPacketSource> discontinuityQueue; + discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); + discontinuityQueue->queueDiscontinuity( + ATSParser::DISCONTINUITY_NONE, + NULL, + true); + } } } @@ -1180,7 +1297,9 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { CHECK(fetcher != NULL); int32_t latestSeq = -1; - int64_t latestTimeUs = 0ll; + int64_t startTimeUs = -1; + int64_t segmentStartTimeUs = -1ll; + int32_t discontinuitySeq = -1; sp<AnotherPacketSource> sources[kMaxStreams]; // TRICKY: looping from i as earlier streams are already removed from streamMask @@ -1188,29 +1307,65 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { if ((streamMask & indexToType(j)) && uri == mStreams[j].mUri) { sources[j] = mPacketSources.valueFor(indexToType(j)); - if (!switching) { + if (timeUs >= 0) { sources[j]->clear(); + startTimeUs = timeUs; + + sp<AnotherPacketSource> discontinuityQueue; + sp<AMessage> extra = new AMessage; + extra->setInt64("timeUs", timeUs); + discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); + discontinuityQueue->queueDiscontinuity( + ATSParser::DISCONTINUITY_SEEK, extra, true); } else { - int32_t type, seq; - int64_t srcTimeUs; - sp<AMessage> meta = sources[j]->getLatestMeta(); + int32_t type; + int64_t srcSegmentStartTimeUs; + sp<AMessage> meta; + if (pickTrack) { + // selecting + meta = sources[j]->getLatestDequeuedMeta(); + } else { + // adapting + meta = sources[j]->getLatestEnqueuedMeta(); + } if (meta != NULL && !meta->findInt32("discontinuity", &type)) { - CHECK(meta->findInt32("seq", &seq)); - if (seq > latestSeq) { - latestSeq = seq; + int64_t tmpUs; + CHECK(meta->findInt64("timeUs", &tmpUs)); + if (startTimeUs < 0 || tmpUs < startTimeUs) { + startTimeUs = tmpUs; + } + + CHECK(meta->findInt64("segmentStartTimeUs", &tmpUs)); + if (segmentStartTimeUs < 0 || tmpUs < segmentStartTimeUs) { + segmentStartTimeUs = tmpUs; } - CHECK(meta->findInt64("timeUs", &srcTimeUs)); - if (srcTimeUs > latestTimeUs) { - latestTimeUs = srcTimeUs; + + int32_t seq; + CHECK(meta->findInt32("discontinuitySeq", &seq)); + if (discontinuitySeq < 0 || seq < discontinuitySeq) { + discontinuitySeq = seq; } } - sources[j] = mPacketSources2.valueFor(indexToType(j)); - sources[j]->clear(); - uint32_t extraStreams = mNewStreamMask & (~mStreamMask); - if (extraStreams & indexToType(j)) { - sources[j]->queueAccessUnit(createFormatChangeBuffer(/* swap = */ false)); + if (pickTrack) { + // selecting track, queue discontinuities before content + sources[j]->clear(); + if (j == kSubtitleIndex) { + break; + } + sp<AnotherPacketSource> discontinuityQueue; + discontinuityQueue = mDiscontinuities.valueFor(indexToType(j)); + discontinuityQueue->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + } else { + // adapting, queue discontinuities after resume + sources[j] = mPacketSources2.valueFor(indexToType(j)); + sources[j]->clear(); + uint32_t extraStreams = mNewStreamMask & (~mStreamMask); + if (extraStreams & indexToType(j)) { + sources[j]->queueAccessUnit(createFormatChangeBuffer(/*swap*/ false)); + } } } @@ -1222,9 +1377,10 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], - timeUs, - latestTimeUs /* min start time(us) */, - latestSeq >= 0 ? latestSeq + 1 : -1 /* starting sequence number hint */ ); + startTimeUs < 0 ? mLastSeekTimeUs : startTimeUs, + segmentStartTimeUs, + discontinuitySeq, + switching); } // All fetchers have now been started, the configuration change @@ -1236,6 +1392,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { mReconfigurationInProgress = false; if (switching) { mSwitchInProgress = true; + mSwapMask = streamMask; } else { mStreamMask = mNewStreamMask; } @@ -1254,8 +1411,8 @@ void LiveSession::onSwapped(const sp<AMessage> &msg) { int32_t stream; CHECK(msg->findInt32("stream", &stream)); - mSwapMask |= stream; - if (mSwapMask != mStreamMask) { + mSwapMask &= ~stream; + if (mSwapMask != 0) { return; } @@ -1271,9 +1428,12 @@ void LiveSession::onSwapped(const sp<AMessage> &msg) { } // Mark switch done when: -// 1. all old buffers are swapped out, AND -// 2. all old fetchers are removed. +// 1. all old buffers are swapped out void LiveSession::tryToFinishBandwidthSwitch() { + if (!mSwitchInProgress) { + return; + } + bool needToRemoveFetchers = false; for (size_t i = 0; i < mFetcherInfos.size(); ++i) { if (mFetcherInfos.valueAt(i).mToBeRemoved) { @@ -1281,10 +1441,11 @@ void LiveSession::tryToFinishBandwidthSwitch() { break; } } - if (!needToRemoveFetchers && mSwapMask == mStreamMask) { + + if (!needToRemoveFetchers && mSwapMask == 0) { + ALOGI("mSwitchInProgress = false"); mStreamMask = mNewStreamMask; mSwitchInProgress = false; - mSwapMask = 0; } } @@ -1310,13 +1471,13 @@ bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) { return false; } - if (mPrevBandwidthIndex < 0) { + if (mCurBandwidthIndex < 0) { return true; } - if (bandwidthIndex == (size_t)mPrevBandwidthIndex) { + if (bandwidthIndex == (size_t)mCurBandwidthIndex) { return false; - } else if (bandwidthIndex > (size_t)mPrevBandwidthIndex) { + } else if (bandwidthIndex > (size_t)mCurBandwidthIndex) { return canSwitchUp(); } else { return true; diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index ed3818f..5423f0f 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -125,8 +125,19 @@ private: struct StreamItem { const char *mType; AString mUri; - StreamItem() : mType("") {} - StreamItem(const char *type) : mType(type) {} + size_t mCurDiscontinuitySeq; + int64_t mLastDequeuedTimeUs; + int64_t mLastSampleDurationUs; + StreamItem() + : mType(""), + mCurDiscontinuitySeq(0), + mLastDequeuedTimeUs(0), + mLastSampleDurationUs(0) {} + StreamItem(const char *type) + : mType(type), + mCurDiscontinuitySeq(0), + mLastDequeuedTimeUs(0), + mLastSampleDurationUs(0) {} AString uriKey() { AString key(mType); key.append("URI"); @@ -147,7 +158,7 @@ private: AString mMasterURL; Vector<BandwidthItem> mBandwidthItems; - ssize_t mPrevBandwidthIndex; + ssize_t mCurBandwidthIndex; sp<M3UParser> mPlaylist; @@ -163,6 +174,7 @@ private: // we use this to track reconfiguration progress. uint32_t mSwapMask; + KeyedVector<StreamType, sp<AnotherPacketSource> > mDiscontinuities; KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources; // A second set of packet sources that buffer content for the variant we're switching to. KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2; @@ -187,6 +199,12 @@ private: uint32_t mDisconnectReplyID; uint32_t mSeekReplyID; + bool mFirstTimeUsValid; + int64_t mFirstTimeUs; + int64_t mLastSeekTimeUs; + KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs; + KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs; + sp<PlaylistFetcher> addFetcher(const char *uri); void onConnect(const sp<AMessage> &msg); diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index efd852c..1651dee 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -157,8 +157,8 @@ void M3UParser::MediaGroup::pickRandomMediaItems() { } status_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) { - if (mType != TYPE_SUBS) { - ALOGE("only select subtitile tracks for now!"); + if (mType != TYPE_SUBS && mType != TYPE_AUDIO) { + ALOGE("only select subtitile/audio tracks for now!"); return INVALID_OPERATION; } @@ -246,6 +246,7 @@ M3UParser::M3UParser( mIsVariantPlaylist(false), mIsComplete(false), mIsEvent(false), + mDiscontinuitySeq(0), mSelectedIndex(-1) { mInitCheck = parse(data, size); } @@ -273,6 +274,10 @@ bool M3UParser::isEvent() const { return mIsEvent; } +size_t M3UParser::getDiscontinuitySeq() const { + return mDiscontinuitySeq; +} + sp<AMessage> M3UParser::meta() { return mMeta; } @@ -567,6 +572,12 @@ status_t M3UParser::parse(const void *_data, size_t size) { } } else if (line.startsWith("#EXT-X-MEDIA")) { err = parseMedia(line); + } else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) { + size_t seq; + err = parseDiscontinuitySequence(line, &seq); + if (err == OK) { + mDiscontinuitySeq = seq; + } } if (err != OK) { @@ -1110,6 +1121,30 @@ status_t M3UParser::parseMedia(const AString &line) { } // static +status_t M3UParser::parseDiscontinuitySequence(const AString &line, size_t *seq) { + ssize_t colonPos = line.find(":"); + + if (colonPos < 0) { + return ERROR_MALFORMED; + } + + int32_t x; + status_t err = ParseInt32(line.c_str() + colonPos + 1, &x); + if (err != OK) { + return err; + } + + if (x < 0) { + return ERROR_MALFORMED; + } + + if (seq) { + *seq = x; + } + return OK; +} + +// static status_t M3UParser::ParseInt32(const char *s, int32_t *x) { char *end; long lval = strtol(s, &end, 10); diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index fe9fb9d..d588afe 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -34,6 +34,7 @@ struct M3UParser : public RefBase { bool isVariantPlaylist() const; bool isComplete() const; bool isEvent() const; + size_t getDiscontinuitySeq() const; sp<AMessage> meta(); @@ -66,6 +67,7 @@ private: bool mIsVariantPlaylist; bool mIsComplete; bool mIsEvent; + size_t mDiscontinuitySeq; sp<AMessage> mMeta; Vector<Item> mItems; @@ -94,6 +96,8 @@ private: status_t parseMedia(const AString &line); + static status_t parseDiscontinuitySequence(const AString &line, size_t *seq); + static status_t ParseInt32(const char *s, int32_t *x); static status_t ParseDouble(const char *s, double *x); diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 10437c9..80cb2d0 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -49,7 +49,7 @@ namespace android { // static const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll; const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll; -const int32_t PlaylistFetcher::kDownloadBlockSize = 192; +const int32_t PlaylistFetcher::kDownloadBlockSize = 2048; const int32_t PlaylistFetcher::kNumSkipFrames = 10; PlaylistFetcher::PlaylistFetcher( @@ -62,19 +62,21 @@ PlaylistFetcher::PlaylistFetcher( mURI(uri), mStreamTypeMask(0), mStartTimeUs(-1ll), - mMinStartTimeUs(0ll), - mStopParams(NULL), + mSegmentStartTimeUs(-1ll), + mDiscontinuitySeq(-1ll), + mStartTimeUsRelative(false), mLastPlaylistFetchTimeUs(-1ll), mSeqNumber(-1), mNumRetries(0), mStartup(true), + mAdaptive(false), mPrepared(false), - mSkipToFirstIDRAfterConnect(false), mNextPTSTimeUs(-1ll), mMonitorQueueGeneration(0), mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY), mFirstPTSValid(false), - mAbsoluteTimeAnchorUs(0ll) { + mAbsoluteTimeAnchorUs(0ll), + mVideoBuffer(new AnotherPacketSource(NULL)) { memset(mPlaylistHash, 0, sizeof(mPlaylistHash)); mStartTimeUsNotify->setInt32("what", kWhatStartedAt); mStartTimeUsNotify->setInt32("streamMask", 0); @@ -335,8 +337,9 @@ void PlaylistFetcher::startAsync( const sp<AnotherPacketSource> &videoSource, const sp<AnotherPacketSource> &subtitleSource, int64_t startTimeUs, - int64_t minStartTimeUs, - int32_t startSeqNumberHint) { + int64_t segmentStartTimeUs, + int32_t startDiscontinuitySeq, + bool adaptive) { sp<AMessage> msg = new AMessage(kWhatStart, id()); uint32_t streamTypeMask = 0ul; @@ -358,8 +361,9 @@ void PlaylistFetcher::startAsync( msg->setInt32("streamTypeMask", streamTypeMask); msg->setInt64("startTimeUs", startTimeUs); - msg->setInt64("minStartTimeUs", minStartTimeUs); - msg->setInt32("startSeqNumberHint", startSeqNumberHint); + msg->setInt64("segmentStartTimeUs", segmentStartTimeUs); + msg->setInt32("startDiscontinuitySeq", startDiscontinuitySeq); + msg->setInt32("adaptive", adaptive); msg->post(); } @@ -367,9 +371,9 @@ void PlaylistFetcher::pauseAsync() { (new AMessage(kWhatPause, id()))->post(); } -void PlaylistFetcher::stopAsync(bool selfTriggered) { +void PlaylistFetcher::stopAsync(bool clear) { sp<AMessage> msg = new AMessage(kWhatStop, id()); - msg->setInt32("selfTriggered", selfTriggered); + msg->setInt32("clear", clear); msg->post(); } @@ -449,10 +453,13 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask)); int64_t startTimeUs; - int32_t startSeqNumberHint; + int64_t segmentStartTimeUs; + int32_t startDiscontinuitySeq; + int32_t adaptive; CHECK(msg->findInt64("startTimeUs", &startTimeUs)); - CHECK(msg->findInt64("minStartTimeUs", (int64_t *) &mMinStartTimeUs)); - CHECK(msg->findInt32("startSeqNumberHint", &startSeqNumberHint)); + CHECK(msg->findInt64("segmentStartTimeUs", &segmentStartTimeUs)); + CHECK(msg->findInt32("startDiscontinuitySeq", &startDiscontinuitySeq)); + CHECK(msg->findInt32("adaptive", &adaptive)); if (streamTypeMask & LiveSession::STREAMTYPE_AUDIO) { void *ptr; @@ -482,16 +489,16 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) { } mStreamTypeMask = streamTypeMask; + mStartTimeUs = startTimeUs; + mSegmentStartTimeUs = segmentStartTimeUs; + mDiscontinuitySeq = startDiscontinuitySeq; if (mStartTimeUs >= 0ll) { mSeqNumber = -1; mStartup = true; mPrepared = false; - } - - if (startSeqNumberHint >= 0) { - mSeqNumber = startSeqNumberHint; + mAdaptive = adaptive; } postMonitorQueue(); @@ -506,11 +513,9 @@ void PlaylistFetcher::onPause() { void PlaylistFetcher::onStop(const sp<AMessage> &msg) { cancelMonitorQueue(); - int32_t selfTriggered; - CHECK(msg->findInt32("selfTriggered", &selfTriggered)); - if (!selfTriggered) { - // Self triggered stops only happen during switching, in which case we do not want - // to clear the discontinuities queued at the end of packet sources. + int32_t clear; + CHECK(msg->findInt32("clear", &clear)); + if (clear) { for (size_t i = 0; i < mPacketSources.size(); i++) { sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i); packetSource->clear(); @@ -552,15 +557,16 @@ status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) { } // Don't resume if we would stop within a resume threshold. + int32_t discontinuitySeq; int64_t latestTimeUs = 0, stopTimeUs = 0; - sp<AMessage> latestMeta = packetSource->getLatestMeta(); + sp<AMessage> latestMeta = packetSource->getLatestDequeuedMeta(); if (latestMeta != NULL - && (latestMeta->findInt64("timeUs", &latestTimeUs) - && params->findInt64(stopKey, &stopTimeUs))) { - int64_t diffUs = stopTimeUs - latestTimeUs; - if (diffUs < resumeThreshold(latestMeta)) { - stop = true; - } + && latestMeta->findInt32("discontinuitySeq", &discontinuitySeq) + && discontinuitySeq == mDiscontinuitySeq + && latestMeta->findInt64("timeUs", &latestTimeUs) + && params->findInt64(stopKey, &stopTimeUs) + && stopTimeUs - latestTimeUs < resumeThreshold(latestMeta)) { + stop = true; } } @@ -568,7 +574,7 @@ status_t PlaylistFetcher::onResumeUntil(const sp<AMessage> &msg) { for (size_t i = 0; i < mPacketSources.size(); i++) { mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer()); } - stopAsync(/* selfTriggered = */ true); + stopAsync(/* clear = */ false); return OK; } @@ -737,26 +743,47 @@ void PlaylistFetcher::onDownloadNext() { mSeqNumber = lastSeqNumberInPlaylist; } + if (mDiscontinuitySeq < 0) { + mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + } + if (mSeqNumber < 0) { CHECK_GE(mStartTimeUs, 0ll); - if (mPlaylist->isComplete() || mPlaylist->isEvent()) { - mSeqNumber = getSeqNumberForTime(mStartTimeUs); + if (mSegmentStartTimeUs < 0) { + if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { + // If this is a live session, start 3 segments from the end on connect + mSeqNumber = lastSeqNumberInPlaylist - 3; + } else { + mSeqNumber = getSeqNumberForTime(mStartTimeUs); + mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber); + } + mStartTimeUsRelative = true; ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)", mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); } else { - // If this is a live session, start 3 segments from the end. - mSeqNumber = lastSeqNumberInPlaylist - 3; + mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs); + if (mAdaptive) { + // avoid double fetch/decode + mSeqNumber += 1; + } + ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq); + if (mSeqNumber < minSeq) { + mSeqNumber = minSeq; + } + if (mSeqNumber < firstSeqNumberInPlaylist) { mSeqNumber = firstSeqNumberInPlaylist; } + + if (mSeqNumber > lastSeqNumberInPlaylist) { + mSeqNumber = lastSeqNumberInPlaylist; + } ALOGV("Initial sequence number for live event %d from (%d .. %d)", mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); } - - mStartTimeUs = -1ll; } if (mSeqNumber < firstSeqNumberInPlaylist @@ -819,6 +846,7 @@ void PlaylistFetcher::onDownloadNext() { int32_t val; if (itemMeta->findInt32("discontinuity", &val) && val != 0) { + mDiscontinuitySeq++; discontinuity = true; } @@ -850,6 +878,7 @@ void PlaylistFetcher::onDownloadNext() { } // block-wise download + bool startup = mStartup; ssize_t bytesRead; do { bytesRead = mSession->fetchFile( @@ -879,7 +908,7 @@ void PlaylistFetcher::onDownloadNext() { return; } - if (mStartup || discontinuity) { + if (startup || discontinuity) { // Signal discontinuity. if (mPlaylist->isComplete() || mPlaylist->isEvent()) { @@ -898,6 +927,8 @@ void PlaylistFetcher::onDownloadNext() { discontinuity = false; } + + startup = false; } err = OK; @@ -917,24 +948,19 @@ void PlaylistFetcher::onDownloadNext() { } if (err == -EAGAIN) { - // bad starting sequence number hint + // starting sequence number too low mTSParser.clear(); postMonitorQueue(); return; - } - - if (err == ERROR_OUT_OF_RANGE) { + } else if (err == ERROR_OUT_OF_RANGE) { // reached stopping point - stopAsync(/* selfTriggered = */ true); + stopAsync(/* clear = */ false); return; - } - - if (err != OK) { + } else if (err != OK) { notifyError(err); return; } - mStartup = false; } while (bytesRead != 0); if (bufferStartsWithTsSyncByte(buffer)) { @@ -994,11 +1020,44 @@ void PlaylistFetcher::onDownloadNext() { return; } + mStartup = false; ++mSeqNumber; postMonitorQueue(); } +int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const { + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL + || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } + + size_t curDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); + if (discontinuitySeq < curDiscontinuitySeq) { + return firstSeqNumberInPlaylist <= 0 ? 0 : (firstSeqNumberInPlaylist - 1); + } + + size_t index = 0; + while (index < mPlaylist->size()) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt( index, NULL /* uri */, &itemMeta)); + + int64_t discontinuity; + if (itemMeta->findInt64("discontinuity", &discontinuity)) { + curDiscontinuitySeq++; + } + + if (curDiscontinuitySeq == discontinuitySeq) { + return firstSeqNumberInPlaylist + index; + } + + ++index; + } + + return firstSeqNumberInPlaylist + mPlaylist->size(); +} + int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { int32_t firstSeqNumberInPlaylist; if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( @@ -1031,6 +1090,23 @@ int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { return firstSeqNumberInPlaylist + index; } +const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties( + const sp<ABuffer> &accessUnit, const sp<AnotherPacketSource> &source, bool discard) { + sp<MetaData> format = source->getFormat(); + if (format != NULL) { + // for simplicity, store a reference to the format in each unit + accessUnit->meta()->setObject("format", format); + } + + if (discard) { + accessUnit->meta()->setInt32("discard", discard); + } + + accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); + accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); + return accessUnit; +} + status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer) { if (mTSParser == NULL) { // Use TS_TIMESTAMPS_ARE_ABSOLUTE so pts carry over between fetchers. @@ -1046,7 +1122,9 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu mTSParser->signalDiscontinuity( ATSParser::DISCONTINUITY_SEEK, extra); + mAbsoluteTimeAnchorUs = mNextPTSTimeUs; mNextPTSTimeUs = -1ll; + mFirstPTSValid = false; } size_t offset = 0; @@ -1099,46 +1177,30 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu continue; } - if (stream == LiveSession::STREAMTYPE_VIDEO && mVideoMime.empty()) { - const char *mime; - if (source->getFormat()->findCString(kKeyMIMEType, &mime)) { - mVideoMime.setTo(mime); - if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { - mSkipToFirstIDRAfterConnect = true; - } - } - } - int64_t timeUs; sp<ABuffer> accessUnit; status_t finalResult; while (source->hasBufferAvailable(&finalResult) && source->dequeueAccessUnit(&accessUnit) == OK) { - if (stream == LiveSession::STREAMTYPE_VIDEO && mSkipToFirstIDRAfterConnect) { - if (!IsIDR(accessUnit)) { - continue; - } else { - mSkipToFirstIDRAfterConnect = false; - } - } - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - if (mMinStartTimeUs > 0) { - if (timeUs < mMinStartTimeUs) { - // TODO untested path - // try a later ts - int32_t targetDuration; - mPlaylist->meta()->findInt32("target-duration", &targetDuration); - int32_t incr = (mMinStartTimeUs - timeUs) / 1000000 / targetDuration; - if (incr == 0) { - // increment mSeqNumber by at least one - incr = 1; + + if (mStartup) { + if (!mFirstPTSValid) { + mFirstTimeUs = timeUs; + mFirstPTSValid = true; + } + if (mStartTimeUsRelative) { + timeUs -= mFirstTimeUs; + if (timeUs < 0) { + timeUs = 0; + } + } else if (mAdaptive && timeUs > mStartTimeUs) { + int32_t seq; + if (mStartTimeUsNotify != NULL + && !mStartTimeUsNotify->findInt32("discontinuitySeq", &seq)) { + mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq); } - mSeqNumber += incr; - err = -EAGAIN; - break; - } else { int64_t startTimeUs; if (mStartTimeUsNotify != NULL && !mStartTimeUsNotify->findInt64(key, &startTimeUs)) { @@ -1155,12 +1217,51 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu } } } + + if (timeUs < mStartTimeUs) { + if (mAdaptive) { + int32_t targetDuration; + mPlaylist->meta()->findInt32("target-duration", &targetDuration); + int32_t incr = (mStartTimeUs - timeUs) / 1000000 / targetDuration; + if (incr == 0) { + // increment mSeqNumber by at least one + incr = 1; + } + mSeqNumber += incr; + err = -EAGAIN; + break; + } else { + // buffer up to the closest preceding IDR frame + ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us", + timeUs, mStartTimeUs); + const char *mime; + sp<MetaData> format = source->getFormat(); + bool isAvc = false; + if (format != NULL && format->findCString(kKeyMIMEType, &mime) + && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + isAvc = true; + } + if (isAvc && IsIDR(accessUnit)) { + mVideoBuffer->clear(); + } + if (isAvc) { + mVideoBuffer->queueAccessUnit(accessUnit); + } + + continue; + } + } } if (mStopParams != NULL) { // Queue discontinuity in original stream. + int32_t discontinuitySeq; int64_t stopTimeUs; - if (!mStopParams->findInt64(key, &stopTimeUs) || timeUs >= stopTimeUs) { + if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq) + || discontinuitySeq > mDiscontinuitySeq + || !mStopParams->findInt64(key, &stopTimeUs) + || (discontinuitySeq == mDiscontinuitySeq + && timeUs >= stopTimeUs)) { packetSource->queueAccessUnit(mSession->createFormatChangeBuffer()); mStreamTypeMask &= ~stream; mPacketSources.removeItemsAt(i); @@ -1169,15 +1270,18 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu } // Note that we do NOT dequeue any discontinuities except for format change. - - // for simplicity, store a reference to the format in each unit - sp<MetaData> format = source->getFormat(); - if (format != NULL) { - accessUnit->meta()->setObject("format", format); + if (stream == LiveSession::STREAMTYPE_VIDEO) { + const bool discard = true; + status_t status; + while (mVideoBuffer->hasBufferAvailable(&status)) { + sp<ABuffer> videoBuffer; + mVideoBuffer->dequeueAccessUnit(&videoBuffer); + setAccessUnitProperties(videoBuffer, source, discard); + packetSource->queueAccessUnit(videoBuffer); + } } - // Stash the sequence number so we can hint future playlist where to start at. - accessUnit->meta()->setInt32("seq", mSeqNumber); + setAccessUnitProperties(accessUnit, source); packetSource->queueAccessUnit(accessUnit); } @@ -1244,7 +1348,8 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( CHECK(itemMeta->findInt64("durationUs", &durationUs)); buffer->meta()->setInt64("timeUs", getSegmentStartTimeUs(mSeqNumber)); buffer->meta()->setInt64("durationUs", durationUs); - buffer->meta()->setInt32("seq", mSeqNumber); + buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); + buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); packetSource->queueAccessUnit(buffer); return OK; @@ -1310,14 +1415,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( firstID3Tag = false; } - if (!mFirstPTSValid) { - mFirstPTSValid = true; - mFirstPTS = PTS; - } - PTS -= mFirstPTS; - - int64_t timeUs = (PTS * 100ll) / 9ll + mAbsoluteTimeAnchorUs; - if (mStreamTypeMask != LiveSession::STREAMTYPE_AUDIO) { ALOGW("This stream only contains audio data!"); @@ -1360,6 +1457,12 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( int32_t sampleRate; CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate)); + int64_t timeUs = (PTS * 100ll) / 9ll; + if (!mFirstPTSValid) { + mFirstPTSValid = true; + mFirstTimeUs = timeUs; + } + size_t offset = 0; while (offset < buffer->size()) { const uint8_t *adtsHeader = buffer->data() + offset; @@ -1384,19 +1487,32 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( CHECK_LE(offset + aac_frame_length, buffer->size()); - sp<ABuffer> unit = new ABuffer(aac_frame_length); - memcpy(unit->data(), adtsHeader, aac_frame_length); - int64_t unitTimeUs = timeUs + numSamples * 1000000ll / sampleRate; - unit->meta()->setInt64("timeUs", unitTimeUs); + offset += aac_frame_length; // Each AAC frame encodes 1024 samples. numSamples += 1024; - unit->meta()->setInt32("seq", mSeqNumber); - packetSource->queueAccessUnit(unit); + if (mStartup) { + int64_t startTimeUs = unitTimeUs; + if (mStartTimeUsRelative) { + startTimeUs -= mFirstTimeUs; + if (startTimeUs < 0) { + startTimeUs = 0; + } + } + if (startTimeUs < mStartTimeUs) { + continue; + } + } - offset += aac_frame_length; + sp<ABuffer> unit = new ABuffer(aac_frame_length); + memcpy(unit->data(), adtsHeader, aac_frame_length); + + unit->meta()->setInt64("timeUs", unitTimeUs); + unit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); + unit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); + packetSource->queueAccessUnit(unit); } return OK; diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index e4fdbff..daefb26 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -57,13 +57,15 @@ struct PlaylistFetcher : public AHandler { const sp<AnotherPacketSource> &audioSource, const sp<AnotherPacketSource> &videoSource, const sp<AnotherPacketSource> &subtitleSource, - int64_t startTimeUs = -1ll, - int64_t minStartTimeUs = 0ll /* start after this timestamp */, - int32_t startSeqNumberHint = -1 /* try starting at this sequence number */); + int64_t startTimeUs = -1ll, // starting timestamps + int64_t segmentStartTimeUs = -1ll, // starting position within playlist + // startTimeUs!=segmentStartTimeUs only when playlist is live + int32_t startDiscontinuitySeq = 0, + bool adaptive = false); void pauseAsync(); - void stopAsync(bool selfTriggered = false); + void stopAsync(bool clear = true); void resumeUntilAsync(const sp<AMessage> ¶ms); @@ -99,11 +101,12 @@ private: sp<LiveSession> mSession; AString mURI; - AString mVideoMime; uint32_t mStreamTypeMask; int64_t mStartTimeUs; - int64_t mMinStartTimeUs; // start fetching no earlier than this value + int64_t mSegmentStartTimeUs; + ssize_t mDiscontinuitySeq; + bool mStartTimeUsRelative; sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch. KeyedVector<LiveSession::StreamType, sp<AnotherPacketSource> > @@ -116,8 +119,8 @@ private: int32_t mSeqNumber; int32_t mNumRetries; bool mStartup; + bool mAdaptive; bool mPrepared; - bool mSkipToFirstIDRAfterConnect; int64_t mNextPTSTimeUs; int32_t mMonitorQueueGeneration; @@ -136,7 +139,9 @@ private: bool mFirstPTSValid; uint64_t mFirstPTS; + int64_t mFirstTimeUs; int64_t mAbsoluteTimeAnchorUs; + sp<AnotherPacketSource> mVideoBuffer; // Stores the initialization vector to decrypt the next block of cipher text, which can // either be derived from the sequence number, read from the manifest, or copied from @@ -175,6 +180,10 @@ private: // Resume a fetcher to continue until the stopping point stored in msg. status_t onResumeUntil(const sp<AMessage> &msg); + const sp<ABuffer> &setAccessUnitProperties( + const sp<ABuffer> &accessUnit, + const sp<AnotherPacketSource> &source, + bool discard = false); status_t extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer); status_t extractAndQueueAccessUnits( @@ -185,6 +194,8 @@ private: void queueDiscontinuity( ATSParser::DiscontinuityType type, const sp<AMessage> &extra); + int32_t getSeqNumberWithAnchorTime(int64_t anchorTimeUs) const; + int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const; int32_t getSeqNumberForTime(int64_t timeUs) const; void updateDuration(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index eda6387..6d8866a 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -894,6 +894,12 @@ void ATSParser::Stream::onPayloadData( ALOGV("Stream PID 0x%08x of type 0x%02x now has data.", mElementaryPID, mStreamType); + const char *mime; + if (meta->findCString(kKeyMIMEType, &mime) + && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) + && !IsIDR(accessUnit)) { + continue; + } mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); } diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 72c9dae..010063f 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "AnotherPacketSource" + #include "AnotherPacketSource.h" #include <media/stagefright/foundation/ABuffer.h> @@ -38,7 +41,8 @@ AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta) mFormat(NULL), mLastQueuedTimeUs(0), mEOSResult(OK), - mLatestEnqueuedMeta(NULL) { + mLatestEnqueuedMeta(NULL), + mLatestDequeuedMeta(NULL) { setFormat(meta); } @@ -92,7 +96,7 @@ sp<MetaData> AnotherPacketSource::getFormat() { sp<RefBase> object; if (buffer->meta()->findObject("format", &object)) { - return static_cast<MetaData*>(object.get()); + return mFormat = static_cast<MetaData*>(object.get()); } ++it; @@ -121,6 +125,8 @@ status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) { return INFO_DISCONTINUITY; } + mLatestDequeuedMeta = (*buffer)->meta()->dup(); + sp<RefBase> object; if ((*buffer)->meta()->findObject("format", &object)) { mFormat = static_cast<MetaData*>(object.get()); @@ -142,8 +148,10 @@ status_t AnotherPacketSource::read( } if (!mBuffers.empty()) { + const sp<ABuffer> buffer = *mBuffers.begin(); mBuffers.erase(mBuffers.begin()); + mLatestDequeuedMeta = buffer->meta()->dup(); int32_t discontinuity; if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { @@ -202,7 +210,7 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { mBuffers.push_back(buffer); mCondition.signal(); - if (!mLatestEnqueuedMeta.get()) { + if (mLatestEnqueuedMeta == NULL) { mLatestEnqueuedMeta = buffer->meta(); } else { int64_t latestTimeUs = 0; @@ -341,9 +349,14 @@ bool AnotherPacketSource::isFinished(int64_t duration) const { return (mEOSResult != OK); } -sp<AMessage> AnotherPacketSource::getLatestMeta() { +sp<AMessage> AnotherPacketSource::getLatestEnqueuedMeta() { Mutex::Autolock autoLock(mLock); return mLatestEnqueuedMeta; } +sp<AMessage> AnotherPacketSource::getLatestDequeuedMeta() { + Mutex::Autolock autoLock(mLock); + return mLatestDequeuedMeta; +} + } // namespace android diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index f38f9dc..0c717d7 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -64,7 +64,8 @@ struct AnotherPacketSource : public MediaSource { bool isFinished(int64_t duration) const; - sp<AMessage> getLatestMeta(); + sp<AMessage> getLatestEnqueuedMeta(); + sp<AMessage> getLatestDequeuedMeta(); protected: virtual ~AnotherPacketSource(); @@ -80,6 +81,7 @@ private: List<sp<ABuffer> > mBuffers; status_t mEOSResult; sp<AMessage> mLatestEnqueuedMeta; + sp<AMessage> mLatestDequeuedMeta; bool wasFormatChange(int32_t discontinuityType) const; |