From 978449984366946a2e5c9f7cf350746f4306caf8 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Sun, 12 Apr 2015 01:03:51 -0700 Subject: HLS: reduce number of guessed wrong seq numbers - account for playlist age in live streaming when calculating segment time - be more conservative on downswitching if bandwidth is unstable - adjust forward or backward if guessed wrong seq number - code refactor bug: 19567254 Change-Id: I0b61cea888fdffd1b3ee2446747ed10152e9e7d7 --- media/libstagefright/Utils.cpp | 24 +- media/libstagefright/httplive/LiveSession.cpp | 58 +++- media/libstagefright/httplive/M3UParser.cpp | 37 ++- media/libstagefright/httplive/M3UParser.h | 6 + media/libstagefright/httplive/PlaylistFetcher.cpp | 327 ++++++++++------------ media/libstagefright/httplive/PlaylistFetcher.h | 4 +- 6 files changed, 266 insertions(+), 190 deletions(-) (limited to 'media') diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index dfe8ad1..0d8e64a 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -852,14 +852,32 @@ HLSTime::HLSTime(const sp& meta) : } } -int64_t HLSTime::getSegmentTimeUs(bool midpoint) const { +int64_t HLSTime::getSegmentTimeUs() const { int64_t segmentStartTimeUs = -1ll; if (mMeta != NULL) { CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs)); - if (midpoint) { + + int64_t segmentFirstTimeUs; + if (mMeta->findInt64("segmentFirstTimeUs", &segmentFirstTimeUs)) { + segmentStartTimeUs += mTimeUs - segmentFirstTimeUs; + } + + // adjust segment time by playlist age (for live streaming) + int64_t playlistTimeUs; + if (mMeta->findInt64("playlistTimeUs", &playlistTimeUs)) { + int64_t playlistAgeUs = ALooper::GetNowUs() - playlistTimeUs; + int64_t durationUs; CHECK(mMeta->findInt64("segmentDurationUs", &durationUs)); - segmentStartTimeUs += durationUs / 2; + + // round to nearest whole segment + playlistAgeUs = (playlistAgeUs + durationUs / 2) + / durationUs * durationUs; + + segmentStartTimeUs -= playlistAgeUs; + if (segmentStartTimeUs < 0) { + segmentStartTimeUs = 0; + } } } return segmentStartTimeUs; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 203444a..764ff82 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -67,7 +67,7 @@ struct LiveSession::BandwidthEstimator : public RefBase { BandwidthEstimator(); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); - bool estimateBandwidth(int32_t *bandwidth); + bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL); private: // Bandwidth estimation parameters @@ -81,6 +81,9 @@ private: Mutex mLock; List mBandwidthHistory; + List mPrevEstimates; + bool mHasNewSample; + bool mIsStable; int64_t mTotalTransferTimeUs; size_t mTotalTransferBytes; @@ -88,6 +91,8 @@ private: }; LiveSession::BandwidthEstimator::BandwidthEstimator() : + mHasNewSample(false), + mIsStable(true), mTotalTransferTimeUs(0), mTotalTransferBytes(0) { } @@ -102,6 +107,7 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( mTotalTransferTimeUs += delayUs; mTotalTransferBytes += numBytes; mBandwidthHistory.push_back(entry); + mHasNewSample = true; // trim old samples, keeping at least kMaxBandwidthHistoryItems samples, // and total transfer time at least kMaxBandwidthHistoryWindowUs. @@ -116,14 +122,43 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( } } -bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) { +bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps, bool *isStable) { AutoMutex autoLock(mLock); if (mBandwidthHistory.size() < 2) { return false; } + if (!mHasNewSample) { + *bandwidthBps = *(--mPrevEstimates.end()); + if (isStable) { + *isStable = mIsStable; + } + return true; + } + *bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs); + mPrevEstimates.push_back(*bandwidthBps); + while (mPrevEstimates.size() > 3) { + mPrevEstimates.erase(mPrevEstimates.begin()); + } + mHasNewSample = false; + + int32_t minEstimate = -1, maxEstimate = -1; + List::iterator it; + for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) { + int32_t estimate = *it; + if (minEstimate < 0 || minEstimate > estimate) { + minEstimate = estimate; + } + if (maxEstimate < 0 || maxEstimate < estimate) { + maxEstimate = estimate; + } + } + mIsStable = (maxEstimate <= minEstimate * 4 / 3); + if (isStable) { + *isStable = mIsStable; + } return true; } @@ -1930,7 +1965,7 @@ void LiveSession::onChangeConfiguration3(const sp &msg) { fetcher->getFetcherID(), (long long)startTime.mTimeUs, (long long)mLastSeekTimeUs, - (long long)startTime.getSegmentTimeUs(true /* midpoint */), + (long long)startTime.getSegmentTimeUs(), seekMode); // Set the target segment start time to the middle point of the @@ -1945,7 +1980,7 @@ void LiveSession::onChangeConfiguration3(const sp &msg) { sources[kSubtitleIndex], getMetadataSource(sources, mNewStreamMask, switching), startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs, - startTime.getSegmentTimeUs(true /* midpoint */), + startTime.getSegmentTimeUs(), startTime.mSeq, seekMode); } @@ -2296,7 +2331,8 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { } int32_t bandwidthBps; - if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) { + bool isStable; + if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) { ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); mLastBandwidthBps = bandwidthBps; } else { @@ -2308,12 +2344,18 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // canSwithDown and canSwitchUp can't both be true. // we only want to switch up when measured bw is 120% higher than current variant, // and we only want to switch down when measured bw is below current variant. - bool canSwithDown = bufferLow + bool canSwitchDown = bufferLow && (bandwidthBps < (int32_t)curBandwidth); bool canSwitchUp = bufferHigh && (bandwidthBps > (int32_t)curBandwidth * 12 / 10); - if (canSwithDown || canSwitchUp) { + if (canSwitchDown || canSwitchUp) { + // bandwidth estimating has some delay, if we have to downswitch when + // it hasn't stabilized, be very conservative on bandwidth. + if (!isStable && canSwitchDown) { + bandwidthBps /= 2; + } + ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); // it's possible that we're checking for canSwitchUp case, but the returned @@ -2321,7 +2363,7 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // of measured bw. In that case we don't want to do anything, since we have // both enough buffer and enough bw. if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex) - || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) { + || (canSwitchDown && bandwidthIndex < mCurBandwidthIndex)) { // if not yet prepared, just restart again with new bw index. // this is faster and playback experience is cleaner. changeConfiguration( diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index ef9145c..ff2bb27 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -250,6 +250,9 @@ M3UParser::M3UParser( mIsVariantPlaylist(false), mIsComplete(false), mIsEvent(false), + mFirstSeqNumber(-1), + mLastSeqNumber(-1), + mTargetDurationUs(-1ll), mDiscontinuitySeq(0), mDiscontinuityCount(0), mSelectedIndex(-1) { @@ -283,6 +286,19 @@ size_t M3UParser::getDiscontinuitySeq() const { return mDiscontinuitySeq; } +int64_t M3UParser::getTargetDuration() const { + return mTargetDurationUs; +} + +int32_t M3UParser::getFirstSeqNumber() const { + return mFirstSeqNumber; +} + +void M3UParser::getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const { + *firstSeq = mFirstSeqNumber; + *lastSeq = mLastSeqNumber; +} + sp M3UParser::meta() { return mMeta; } @@ -664,11 +680,22 @@ status_t M3UParser::parse(const void *_data, size_t size) { } // error checking of all fields that's required to appear once - // (currently only checking "target-duration") - int32_t targetDurationSecs; - if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32( - "target-duration", &targetDurationSecs))) { - return ERROR_MALFORMED; + // (currently only checking "target-duration"), and + // initialization of playlist properties (eg. mTargetDurationUs) + if (!mIsVariantPlaylist) { + int32_t targetDurationSecs; + if (mMeta == NULL || !mMeta->findInt32( + "target-duration", &targetDurationSecs)) { + ALOGE("Media playlist missing #EXT-X-TARGETDURATION"); + return ERROR_MALFORMED; + } + mTargetDurationUs = targetDurationSecs * 1000000ll; + + mFirstSeqNumber = 0; + if (mMeta != NULL) { + mMeta->findInt32("media-sequence", &mFirstSeqNumber); + } + mLastSeqNumber = mFirstSeqNumber + mItems.size() - 1; } return OK; diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h index fef361f..fa648ed 100644 --- a/media/libstagefright/httplive/M3UParser.h +++ b/media/libstagefright/httplive/M3UParser.h @@ -36,6 +36,9 @@ struct M3UParser : public RefBase { bool isComplete() const; bool isEvent() const; size_t getDiscontinuitySeq() const; + int64_t getTargetDuration() const; + int32_t getFirstSeqNumber() const; + void getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const; sp meta(); @@ -70,6 +73,9 @@ private: bool mIsVariantPlaylist; bool mIsComplete; bool mIsEvent; + int32_t mFirstSeqNumber; + int32_t mLastSeqNumber; + int64_t mTargetDurationUs; size_t mDiscontinuitySeq; int32_t mDiscontinuityCount; diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index a4e523d..949402a 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -160,6 +160,7 @@ PlaylistFetcher::PlaylistFetcher( mDiscontinuitySeq(-1ll), mStartTimeUsRelative(false), mLastPlaylistFetchTimeUs(-1ll), + mPlaylistTimeUs(-1ll), mSeqNumber(-1), mNumRetries(0), mStartup(true), @@ -191,14 +192,9 @@ int32_t PlaylistFetcher::getFetcherID() const { int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const { CHECK(mPlaylist != NULL); - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - - int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist; + mPlaylist->getSeqNumberRange( + &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist); CHECK_GE(seqNumber, firstSeqNumberInPlaylist); CHECK_LE(seqNumber, lastSeqNumberInPlaylist); @@ -222,14 +218,9 @@ int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const { int64_t PlaylistFetcher::getSegmentDurationUs(int32_t seqNumber) const { CHECK(mPlaylist != NULL); - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - - int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist; + mPlaylist->getSeqNumberRange( + &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist); CHECK_GE(seqNumber, firstSeqNumberInPlaylist); CHECK_LE(seqNumber, lastSeqNumberInPlaylist); @@ -257,10 +248,7 @@ int64_t PlaylistFetcher::delayUsToRefreshPlaylist() const { return (~0llu >> 1); } - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - - int64_t targetDurationUs = targetDurationSecs * 1000000ll; + int64_t targetDurationUs = mPlaylist->getTargetDuration(); int64_t minPlaylistAgeUs; @@ -758,16 +746,9 @@ void PlaylistFetcher::onMonitorQueue() { refreshPlaylist(); } - int32_t targetDurationSecs; int64_t targetDurationUs = kMinBufferedDurationUs; if (mPlaylist != NULL) { - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "target-duration", &targetDurationSecs)) { - ALOGE("Playlist is missing required EXT-X-TARGETDURATION tag"); - notifyError(ERROR_MALFORMED); - return; - } - targetDurationUs = targetDurationSecs * 1000000ll; + targetDurationUs = mPlaylist->getTargetDuration(); } int64_t bufferedDurationUs = 0ll; @@ -865,6 +846,7 @@ status_t PlaylistFetcher::refreshPlaylist() { if (!mPlaylist->isComplete()) { updateTargetDuration(); } + mPlaylistTimeUs = ALooper::GetNowUs(); } mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); @@ -884,9 +866,7 @@ bool PlaylistFetcher::shouldPauseDownload() { } // Calculate threshold to abort current download - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - int64_t targetDurationUs = targetDurationSecs * 1000000ll; + int64_t targetDurationUs = mPlaylist->getTargetDuration(); int64_t thresholdUs = -1; { AutoMutex _l(mThresholdLock); @@ -949,12 +929,8 @@ bool PlaylistFetcher::initDownloadState( bool discontinuity = false; if (mPlaylist != NULL) { - if (mPlaylist->meta() != NULL) { - mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist); - } - - lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + mPlaylist->getSeqNumberRange( + &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist); if (mDiscontinuitySeq < 0) { mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq(); @@ -989,11 +965,18 @@ bool PlaylistFetcher::initDownloadState( // to media time 0) is used to determine the start segment; mStartTimeUs (absolute // timestamps coming from the media container) is used to determine the position // inside a segments. - mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs); if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES && mSeekMode != LiveSession::kSeekModeNextSample) { // avoid double fetch/decode - mSeqNumber += 1; + // Use (mSegmentStartTimeUs + 1/2 * targetDurationUs) to search + // for the starting segment in new variant. + // If the two variants' segments are aligned, this gives the + // next segment. If they're not aligned, this gives the segment + // that overlaps no more than 1/2 * targetDurationUs. + mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs + + mPlaylist->getTargetDuration() / 2); + } else { + mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs); } ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq); if (mSeqNumber < minSeq) { @@ -1027,11 +1010,9 @@ bool PlaylistFetcher::initDownloadState( // refresh in increasing fraction (1/2, 1/3, ...) of the // playlist's target duration or 3 seconds, whichever is less int64_t delayUs = kMaxMonitorDelayUs; - if (mPlaylist != NULL && mPlaylist->meta() != NULL) { - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - delayUs = mPlaylist->size() * targetDurationSecs * - 1000000ll / (1 + mNumRetries); + if (mPlaylist != NULL) { + delayUs = mPlaylist->size() * mPlaylist->getTargetDuration() + / (1 + mNumRetries); } if (delayUs > kMaxMonitorDelayUs) { delayUs = kMaxMonitorDelayUs; @@ -1158,7 +1139,7 @@ bool PlaylistFetcher::initDownloadState( } queueDiscontinuity( - ATSParser::DISCONTINUITY_FORMATCHANGE, + ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL /* extra */); if (mStartup && mStartTimeUsRelative && mFirstPTSValid) { @@ -1172,6 +1153,8 @@ bool PlaylistFetcher::initDownloadState( // set mStartTimeUs=0, and take all samples from now on. mStartTimeUs = 0; mFirstPTSValid = false; + mIDRFound = false; + mVideoBuffer->clear(); } } @@ -1408,42 +1391,67 @@ void PlaylistFetcher::onDownloadNext() { } } -int32_t PlaylistFetcher::getSeqNumberWithAnchorTime( - int64_t anchorTimeUs, int64_t targetDiffUs) const { - int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL - || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; +/* + * returns true if we need to adjust mSeqNumber + */ +bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) { + int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber(); + + int64_t minDiffUs, maxDiffUs; + if (mSeekMode == LiveSession::kSeekModeNextSample) { + minDiffUs = -mPlaylist->getTargetDuration(); + maxDiffUs = 0ll; + } else { + minDiffUs = -mPlaylist->getTargetDuration() / 2; + maxDiffUs = mPlaylist->getTargetDuration(); } - lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1; - int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1; - // adjust anchorTimeUs to within targetDiffUs from mStartTimeUs - while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDiffUs) { - sp itemMeta; - CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta)); + int32_t oldSeqNumber = mSeqNumber; + ssize_t index = mSeqNumber - firstSeqNumberInPlaylist; - int64_t itemDurationUs; - CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + // adjust anchorTimeUs to within (minDiffUs, maxDiffUs) from mStartTimeUs + int64_t diffUs = anchorTimeUs - mStartTimeUs; + if (diffUs > maxDiffUs) { + while (index > 0 && diffUs > maxDiffUs) { + --index; + + sp itemMeta; + CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + diffUs -= itemDurationUs; + } + } else if (diffUs < minDiffUs) { + while (index + 1 < (ssize_t) mPlaylist->size() + && diffUs < minDiffUs) { + ++index; + + sp itemMeta; + CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); - anchorTimeUs -= itemDurationUs; - --index; + diffUs += itemDurationUs; + } } - int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1; - if (newSeqNumber <= lastSeqNumberInPlaylist) { - return newSeqNumber; - } else { - return lastSeqNumberInPlaylist; + mSeqNumber = firstSeqNumberInPlaylist + index; + + if (mSeqNumber != oldSeqNumber) { + FLOGV("guessed wrong seg number: diff %lld out of [%lld, %lld]", + (long long) anchorTimeUs - mStartTimeUs, + (long long) minDiffUs, + (long long) maxDiffUs); + return true; } + return false; } int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const { - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } + int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber(); size_t index = 0; while (index < mPlaylist->size()) { @@ -1465,12 +1473,6 @@ int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) c } int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - size_t index = 0; int64_t segmentStartUs = 0; while (index < mPlaylist->size()) { @@ -1493,7 +1495,7 @@ int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const { index = mPlaylist->size() - 1; } - return firstSeqNumberInPlaylist + index; + return mPlaylist->getFirstSeqNumber() + index; } const sp &PlaylistFetcher::setAccessUnitProperties( @@ -1508,14 +1510,13 @@ const sp &PlaylistFetcher::setAccessUnitProperties( accessUnit->meta()->setInt32("discard", discard); } - int32_t targetDurationSecs; - if (mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)) { - accessUnit->meta()->setInt32("targetDuration", targetDurationSecs); - } - accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); + accessUnit->meta()->setInt64("segmentFirstTimeUs", mSegmentFirstPTS); accessUnit->meta()->setInt64("segmentDurationUs", getSegmentDurationUs(mSeqNumber)); + if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { + accessUnit->meta()->setInt64("playlistTimeUs", mPlaylistTimeUs); + } return accessUnit; } @@ -1577,6 +1578,59 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp &bu // setRange to indicate consumed bytes. buffer->setRange(buffer->offset() + offset, buffer->size() - offset); + if (mSegmentFirstPTS < 0ll) { + // get the smallest first PTS from all streams present in this parser + for (size_t i = mPacketSources.size(); i-- > 0;) { + const LiveSession::StreamType stream = mPacketSources.keyAt(i); + if (stream == LiveSession::STREAMTYPE_SUBTITLES) { + ALOGE("MPEG2 Transport streams do not contain subtitles."); + return ERROR_MALFORMED; + } + ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream); + sp source = + static_cast( + mTSParser->getSource(type).get()); + + if (source == NULL) { + continue; + } + sp meta = source->getMetaAfterLastDequeued(0); + if (meta != NULL) { + int64_t timeUs; + CHECK(meta->findInt64("timeUs", &timeUs)); + if (mSegmentFirstPTS < 0ll || timeUs < mSegmentFirstPTS) { + mSegmentFirstPTS = timeUs; + } + } + } + if (mSegmentFirstPTS < 0ll) { + // didn't find any TS packet, can return early + return OK; + } + if (!mStartTimeUsRelative) { + // mStartup + // mStartup is true until we have queued a packet for all the streams + // we are fetching. We queue packets whose timestamps are greater than + // mStartTimeUs. + // mSegmentStartTimeUs >= 0 + // mSegmentStartTimeUs is non-negative when adapting or switching tracks + // adjustSeqNumberWithAnchorTime(timeUs) == true + // we guessed a seq number that's either too large or too small. + // If this happens, we'll adjust mSeqNumber and restart fetching from new + // location. Note that we only want to adjust once, so set mSegmentStartTimeUs + // to -1 so that we don't enter this chunk next time. + if (mStartup && mSegmentStartTimeUs >= 0 + && adjustSeqNumberWithAnchorTime(mSegmentFirstPTS)) { + mStartTimeUsNotify = mNotify->dup(); + mStartTimeUsNotify->setInt32("what", kWhatStartedAt); + mStartTimeUsNotify->setString("uri", mURI); + mIDRFound = false; + mSegmentStartTimeUs = -1; + return -EAGAIN; + } + } + } + status_t err = OK; for (size_t i = mPacketSources.size(); i-- > 0;) { sp packetSource = mPacketSources.valueAt(i); @@ -1611,76 +1665,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp &bu int64_t timeUs; CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - if (mSegmentFirstPTS < 0ll) { - mSegmentFirstPTS = timeUs; - if (!mStartTimeUsRelative) { - int32_t firstSeqNumberInPlaylist; - if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( - "media-sequence", &firstSeqNumberInPlaylist)) { - firstSeqNumberInPlaylist = 0; - } - - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - int64_t targetDurationUs = targetDurationSecs * 1000000ll; - // mStartup - // mStartup is true until we have queued a packet for all the streams - // we are fetching. We queue packets whose timestamps are greater than - // mStartTimeUs. - // mSegmentStartTimeUs >= 0 - // mSegmentStartTimeUs is non-negative when adapting or switching tracks - // mSeqNumber > firstSeqNumberInPlaylist - // don't decrement mSeqNumber if it already points to the 1st segment - // timeUs - mStartTimeUs > targetDurationUs: - // This and the 2 above conditions should only happen when adapting in a live - // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher - // would start fetching after timeUs, which should be greater than mStartTimeUs; - // the old fetcher would then continue fetching data until timeUs. We don't want - // timeUs to be too far ahead of mStartTimeUs because we want the old fetcher to - // stop as early as possible. The definition of being "too far ahead" is - // arbitrary; here we use targetDurationUs as threshold. - int64_t targetDiffUs = (mSeekMode == LiveSession::kSeekModeNextSample - ? 0 : targetDurationUs); - if (mStartup && mSegmentStartTimeUs >= 0 - && mSeqNumber > firstSeqNumberInPlaylist - && timeUs - mStartTimeUs > targetDiffUs) { - // we just guessed a starting timestamp that is too high when adapting in a - // live stream; re-adjust based on the actual timestamp extracted from the - // media segment; if we didn't move backward after the re-adjustment - // (newSeqNumber), start at least 1 segment prior. - int32_t newSeqNumber = getSeqNumberWithAnchorTime( - timeUs, targetDiffUs); - - FLOGV("guessed wrong seq number: timeUs=%lld, mStartTimeUs=%lld, " - "targetDurationUs=%lld, mSeqNumber=%d, newSeq=%d, firstSeq=%d", - (long long)timeUs, - (long long)mStartTimeUs, - (long long)targetDurationUs, - mSeqNumber, - newSeqNumber, - firstSeqNumberInPlaylist); - - if (newSeqNumber >= mSeqNumber) { - --mSeqNumber; - } else { - mSeqNumber = newSeqNumber; - } - mStartTimeUsNotify = mNotify->dup(); - mStartTimeUsNotify->setInt32("what", kWhatStartedAt); - mStartTimeUsNotify->setString("uri", mURI); - mIDRFound = false; - return -EAGAIN; - } - } - } if (mStartup) { bool startTimeReached = isStartTimeReached(timeUs); if (!startTimeReached || (isAvc && !mIDRFound)) { // buffer up to the closest preceding IDR frame in the next segement, // or the closest succeeding IDR frame after the exact position - FSLOGV(stream, "timeUs=%lld, mStartTimeUs=%lld, mIDRFound=%d", - (long long)timeUs, (long long)mStartTimeUs, mIDRFound); + FSLOGV(stream, "timeUs(%lld)-mStartTimeUs(%lld)=%lld, mIDRFound=%d", + (long long)timeUs, + (long long)mStartTimeUs, + (long long)timeUs - mStartTimeUs, + mIDRFound); if (isAvc) { if (IsIDR(accessUnit)) { mVideoBuffer->clear(); @@ -1821,7 +1816,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber)); buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq); buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration); - packetSource->queueAccessUnit(buffer); return OK; } @@ -1932,6 +1926,18 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( mFirstTimeUs = timeUs; } + if (mSegmentFirstPTS < 0ll) { + mSegmentFirstPTS = timeUs; + if (!mStartTimeUsRelative) { + // Duplicated logic from how we handle .ts playlists. + if (mStartup && mSegmentStartTimeUs >= 0 + && adjustSeqNumberWithAnchorTime(timeUs)) { + mSegmentStartTimeUs = -1; + return -EAGAIN; + } + } + } + size_t offset = 0; while (offset < buffer->size()) { const uint8_t *adtsHeader = buffer->data() + offset; @@ -1975,25 +1981,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } if (mStartTimeUsNotify != NULL) { - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - int64_t targetDurationUs = targetDurationSecs * 1000000ll; - - int64_t targetDiffUs =(mSeekMode == LiveSession::kSeekModeNextSample - ? 0 : targetDurationUs); - // Duplicated logic from how we handle .ts playlists. - if (mStartup && mSegmentStartTimeUs >= 0 - && timeUs - mStartTimeUs > targetDiffUs) { - int32_t newSeqNumber = getSeqNumberWithAnchorTime( - timeUs, targetDiffUs); - if (newSeqNumber >= mSeqNumber) { - --mSeqNumber; - } else { - mSeqNumber = newSeqNumber; - } - return -EAGAIN; - } - mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO); mStartup = false; } @@ -2043,13 +2030,9 @@ void PlaylistFetcher::updateDuration() { } void PlaylistFetcher::updateTargetDuration() { - int32_t targetDurationSecs; - CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); - int64_t targetDurationUs = targetDurationSecs * 1000000ll; - sp msg = mNotify->dup(); msg->setInt32("what", kWhatTargetDurationUpdate); - msg->setInt64("targetDurationUs", targetDurationUs); + msg->setInt64("targetDurationUs", mPlaylist->getTargetDuration()); msg->post(); } diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index bb14a0d..cace649 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -138,6 +138,7 @@ private: KeyedVector > mAESKeyForURI; int64_t mLastPlaylistFetchTimeUs; + int64_t mPlaylistTimeUs; sp mPlaylist; int32_t mSeqNumber; int32_t mNumRetries; @@ -238,8 +239,7 @@ private: void queueDiscontinuity( ATSParser::DiscontinuityType type, const sp &extra); - int32_t getSeqNumberWithAnchorTime( - int64_t anchorTimeUs, int64_t targetDurationUs) const; + bool adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs); int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const; int32_t getSeqNumberForTime(int64_t timeUs) const; -- cgit v1.1