Loading media/libstagefright/httplive/LiveSession.cpp +133 −14 Original line number Original line Diff line number Diff line Loading @@ -58,15 +58,21 @@ struct LiveSession::BandwidthEstimator : public RefBase { BandwidthEstimator(); BandwidthEstimator(); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL); bool estimateBandwidth( int32_t *bandwidth, bool *isStable = NULL, int32_t *shortTermBps = NULL); private: private: // Bandwidth estimation parameters // Bandwidth estimation parameters static const int32_t kShortTermBandwidthItems = 3; static const int32_t kMinBandwidthHistoryItems = 20; static const int32_t kMinBandwidthHistoryItems = 20; static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec static const int64_t kMaxBandwidthHistoryAgeUs = 60000000ll; // 60 sec struct BandwidthEntry { struct BandwidthEntry { int64_t mTimestampUs; int64_t mDelayUs; int64_t mDelayUs; size_t mNumBytes; size_t mNumBytes; }; }; Loading @@ -74,6 +80,7 @@ private: Mutex mLock; Mutex mLock; List<BandwidthEntry> mBandwidthHistory; List<BandwidthEntry> mBandwidthHistory; List<int32_t> mPrevEstimates; List<int32_t> mPrevEstimates; int32_t mShortTermEstimate; bool mHasNewSample; bool mHasNewSample; bool mIsStable; bool mIsStable; int64_t mTotalTransferTimeUs; int64_t mTotalTransferTimeUs; Loading @@ -83,6 +90,7 @@ private: }; }; LiveSession::BandwidthEstimator::BandwidthEstimator() : LiveSession::BandwidthEstimator::BandwidthEstimator() : mShortTermEstimate(0), mHasNewSample(false), mHasNewSample(false), mIsStable(true), mIsStable(true), mTotalTransferTimeUs(0), mTotalTransferTimeUs(0), Loading @@ -93,7 +101,9 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( size_t numBytes, int64_t delayUs) { size_t numBytes, int64_t delayUs) { AutoMutex autoLock(mLock); AutoMutex autoLock(mLock); int64_t nowUs = ALooper::GetNowUs(); BandwidthEntry entry; BandwidthEntry entry; entry.mTimestampUs = nowUs; entry.mDelayUs = delayUs; entry.mDelayUs = delayUs; entry.mNumBytes = numBytes; entry.mNumBytes = numBytes; mTotalTransferTimeUs += delayUs; mTotalTransferTimeUs += delayUs; Loading @@ -115,7 +125,10 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( // and total transfer time at least kMaxBandwidthHistoryWindowUs. // and total transfer time at least kMaxBandwidthHistoryWindowUs. while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) { while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) { List<BandwidthEntry>::iterator it = mBandwidthHistory.begin(); List<BandwidthEntry>::iterator it = mBandwidthHistory.begin(); if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) { // remove sample if either absolute age or total transfer time is // over kMaxBandwidthHistoryWindowUs if (nowUs - it->mTimestampUs < kMaxBandwidthHistoryAgeUs && mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) { break; break; } } mTotalTransferTimeUs -= it->mDelayUs; mTotalTransferTimeUs -= it->mDelayUs; Loading @@ -125,7 +138,7 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( } } bool LiveSession::BandwidthEstimator::estimateBandwidth( bool LiveSession::BandwidthEstimator::estimateBandwidth( int32_t *bandwidthBps, bool *isStable) { int32_t *bandwidthBps, bool *isStable, int32_t *shortTermBps) { AutoMutex autoLock(mLock); AutoMutex autoLock(mLock); if (mBandwidthHistory.size() < 2) { if (mBandwidthHistory.size() < 2) { Loading @@ -137,6 +150,9 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( if (isStable) { if (isStable) { *isStable = mIsStable; *isStable = mIsStable; } } if (shortTermBps) { *shortTermBps = mShortTermEstimate; } return true; return true; } } Loading @@ -147,6 +163,21 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( } } mHasNewSample = false; mHasNewSample = false; int64_t totalTimeUs = 0; size_t totalBytes = 0; if (mBandwidthHistory.size() >= kShortTermBandwidthItems) { List<BandwidthEntry>::iterator it = --mBandwidthHistory.end(); for (size_t i = 0; i < kShortTermBandwidthItems; i++, it--) { totalTimeUs += it->mDelayUs; totalBytes += it->mNumBytes; } } mShortTermEstimate = totalTimeUs > 0 ? (totalBytes * 8E6 / totalTimeUs) : *bandwidthBps; if (shortTermBps) { *shortTermBps = mShortTermEstimate; } int32_t minEstimate = -1, maxEstimate = -1; int32_t minEstimate = -1, maxEstimate = -1; List<int32_t>::iterator it; List<int32_t>::iterator it; for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) { for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) { Loading @@ -158,10 +189,14 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( maxEstimate = estimate; maxEstimate = estimate; } } } } mIsStable = (maxEstimate <= minEstimate * 4 / 3); // consider it stable if long-term average is not jumping a lot // and short-term average is not much lower than long-term average mIsStable = (maxEstimate <= minEstimate * 4 / 3) && mShortTermEstimate > minEstimate * 7 / 10; if (isStable) { if (isStable) { *isStable = mIsStable; *isStable = mIsStable; } } #if 0 #if 0 { { char dumpStr[1024] = {0}; char dumpStr[1024] = {0}; Loading Loading @@ -251,6 +286,7 @@ LiveSession::LiveSession( mCurBandwidthIndex(-1), mCurBandwidthIndex(-1), mOrigBandwidthIndex(-1), mOrigBandwidthIndex(-1), mLastBandwidthBps(-1ll), mLastBandwidthBps(-1ll), mLastBandwidthStable(false), mBandwidthEstimator(new BandwidthEstimator()), mBandwidthEstimator(new BandwidthEstimator()), mMaxWidth(720), mMaxWidth(720), mMaxHeight(480), mMaxHeight(480), Loading Loading @@ -713,6 +749,20 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { } } } } // remember the failure index (as mCurBandwidthIndex will be restored // after cancelBandwidthSwitch()), and record last fail time size_t failureIndex = mCurBandwidthIndex; mBandwidthItems.editItemAt( failureIndex).mLastFailureUs = ALooper::GetNowUs(); if (mSwitchInProgress) { // if error happened when we switch to a variant, try fallback // to other variant to save the session if (tryBandwidthFallback()) { break; } } if (mInPreparationPhase) { if (mInPreparationPhase) { postPrepared(err); postPrepared(err); } } Loading Loading @@ -886,6 +936,13 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { } } } } // static bool LiveSession::isBandwidthValid(const BandwidthItem &item) { static const int64_t kBlacklistWindowUs = 300 * 1000000ll; return item.mLastFailureUs < 0 || ALooper::GetNowUs() - item.mLastFailureUs > kBlacklistWindowUs; } // static // static int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { if (a->mBandwidth < b->mBandwidth) { if (a->mBandwidth < b->mBandwidth) { Loading Loading @@ -986,6 +1043,7 @@ void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) { BandwidthItem item; BandwidthItem item; item.mPlaylistIndex = i; item.mPlaylistIndex = i; item.mLastFailureUs = -1ll; sp<AMessage> meta; sp<AMessage> meta; AString uri; AString uri; Loading Loading @@ -1223,6 +1281,13 @@ float LiveSession::getAbortThreshold( X/T < bw1 / (bw1 + bw0 - bw) X/T < bw1 / (bw1 + bw0 - bw) */ */ // abort old bandwidth immediately if bandwidth is fluctuating a lot. // our estimate could be far off, and fetching old bandwidth could // take too long. if (!mLastBandwidthStable) { return 0.0f; } // Taking the measured current bandwidth at 50% face value only, // Taking the measured current bandwidth at 50% face value only, // as our bandwidth estimation is a lagging indicator. Being // as our bandwidth estimation is a lagging indicator. Being // conservative on this, we prefer switching to lower bandwidth // conservative on this, we prefer switching to lower bandwidth Loading Loading @@ -1250,6 +1315,16 @@ void LiveSession::addBandwidthMeasurement(size_t numBytes, int64_t delayUs) { mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs); mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs); } } ssize_t LiveSession::getLowestValidBandwidthIndex() const { for (size_t index = 0; index < mBandwidthItems.size(); index++) { if (isBandwidthValid(mBandwidthItems[index])) { return index; } } // if playlists are all blacklisted, return 0 and hope it's alive return 0; } size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { if (mBandwidthItems.size() < 2) { if (mBandwidthItems.size() < 2) { // shouldn't be here if we only have 1 bandwidth, check // shouldn't be here if we only have 1 bandwidth, check Loading Loading @@ -1284,14 +1359,18 @@ size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { } } } } // Pick the highest bandwidth stream below or equal to estimated bandwidth. // Pick the highest bandwidth stream that's not currently blacklisted // below or equal to estimated bandwidth. index = mBandwidthItems.size() - 1; index = mBandwidthItems.size() - 1; while (index > 0) { ssize_t lowestBandwidth = getLowestValidBandwidthIndex(); while (index > lowestBandwidth) { // be conservative (70%) to avoid overestimating and immediately // be conservative (70%) to avoid overestimating and immediately // switching down again. // switching down again. size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) { const BandwidthItem &item = mBandwidthItems[index]; if (item.mBandwidth <= adjustedBandwidthBps && isBandwidthValid(item)) { break; break; } } --index; --index; Loading Loading @@ -2172,21 +2251,57 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) { notify->post(); notify->post(); } } bool LiveSession::tryBandwidthFallback() { if (mInPreparationPhase || mReconfigurationInProgress) { // Don't try fallback during prepare or reconfig. // If error happens there, it's likely unrecoverable. return false; } if (mCurBandwidthIndex > mOrigBandwidthIndex) { // if we're switching up, simply cancel and resume old variant cancelBandwidthSwitch(true /* resume */); return true; } else { // if we're switching down, we're likely about to underflow (if // not already underflowing). try the lowest viable bandwidth if // not on that variant already. ssize_t lowestValid = getLowestValidBandwidthIndex(); if (mCurBandwidthIndex > lowestValid) { cancelBandwidthSwitch(); changeConfiguration(-1ll, lowestValid); return true; } } // return false if we couldn't find any fallback return false; } /* /* * returns true if a bandwidth switch is actually needed (and started), * returns true if a bandwidth switch is actually needed (and started), * returns false otherwise * returns false otherwise */ */ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // no need to check bandwidth if we only have 1 bandwidth settings // no need to check bandwidth if we only have 1 bandwidth settings if (mSwitchInProgress || mBandwidthItems.size() < 2) { if (mBandwidthItems.size() < 2) { return false; } if (mSwitchInProgress) { if (mBuffering) { tryBandwidthFallback(); } return false; return false; } } int32_t bandwidthBps; int32_t bandwidthBps, shortTermBps; bool isStable; bool isStable; if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) { if (mBandwidthEstimator->estimateBandwidth( ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); &bandwidthBps, &isStable, &shortTermBps)) { ALOGV("bandwidth estimated at %.2f kbps, " "stable %d, shortTermBps %.2f kbps", bandwidthBps / 1024.0f, isStable, shortTermBps / 1024.0f); mLastBandwidthBps = bandwidthBps; mLastBandwidthBps = bandwidthBps; mLastBandwidthStable = isStable; } else { } else { ALOGV("no bandwidth estimate."); ALOGV("no bandwidth estimate."); return false; return false; Loading @@ -2203,9 +2318,13 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { if (canSwitchDown || canSwitchUp) { if (canSwitchDown || canSwitchUp) { // bandwidth estimating has some delay, if we have to downswitch when // bandwidth estimating has some delay, if we have to downswitch when // it hasn't stabilized, be very conservative on bandwidth. // it hasn't stabilized, use the short term to guess real bandwidth, // since it may be dropping too fast. // (note this doesn't apply to upswitch, always use longer average there) if (!isStable && canSwitchDown) { if (!isStable && canSwitchDown) { bandwidthBps /= 2; if (shortTermBps < bandwidthBps) { bandwidthBps = shortTermBps; } } } ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); Loading media/libstagefright/httplive/LiveSession.h +5 −0 Original line number Original line Diff line number Diff line Loading @@ -146,6 +146,7 @@ private: struct BandwidthItem { struct BandwidthItem { size_t mPlaylistIndex; size_t mPlaylistIndex; unsigned long mBandwidth; unsigned long mBandwidth; int64_t mLastFailureUs; }; }; struct FetcherInfo { struct FetcherInfo { Loading Loading @@ -199,6 +200,7 @@ private: ssize_t mCurBandwidthIndex; ssize_t mCurBandwidthIndex; ssize_t mOrigBandwidthIndex; ssize_t mOrigBandwidthIndex; int32_t mLastBandwidthBps; int32_t mLastBandwidthBps; bool mLastBandwidthStable; sp<BandwidthEstimator> mBandwidthEstimator; sp<BandwidthEstimator> mBandwidthEstimator; sp<M3UParser> mPlaylist; sp<M3UParser> mPlaylist; Loading Loading @@ -268,8 +270,10 @@ private: ssize_t currentBWIndex, ssize_t targetBWIndex) const; ssize_t currentBWIndex, ssize_t targetBWIndex) const; void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); size_t getBandwidthIndex(int32_t bandwidthBps); size_t getBandwidthIndex(int32_t bandwidthBps); ssize_t getLowestValidBandwidthIndex() const; HLSTime latestMediaSegmentStartTime() const; HLSTime latestMediaSegmentStartTime() const; static bool isBandwidthValid(const BandwidthItem &item); static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); static StreamType indexToType(int idx); static StreamType indexToType(int idx); static ssize_t typeToIndex(int32_t type); static ssize_t typeToIndex(int32_t type); Loading @@ -287,6 +291,7 @@ private: sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); bool tryBandwidthFallback(); void schedulePollBuffering(); void schedulePollBuffering(); void cancelPollBuffering(); void cancelPollBuffering(); Loading media/libstagefright/httplive/PlaylistFetcher.cpp +7 −0 Original line number Original line Diff line number Diff line Loading @@ -511,6 +511,13 @@ void PlaylistFetcher::startAsync( msg->post(); msg->post(); } } /* * pauseAsync * * threshold: 0.0f - pause after current fetch block (default 47Kbytes) * -1.0f - pause after finishing current segment * 0.0~1.0f - pause if remaining of current segment exceeds threshold */ void PlaylistFetcher::pauseAsync( void PlaylistFetcher::pauseAsync( float thresholdRatio, bool disconnect) { float thresholdRatio, bool disconnect) { setStoppingThreshold(thresholdRatio, disconnect); setStoppingThreshold(thresholdRatio, disconnect); Loading Loading
media/libstagefright/httplive/LiveSession.cpp +133 −14 Original line number Original line Diff line number Diff line Loading @@ -58,15 +58,21 @@ struct LiveSession::BandwidthEstimator : public RefBase { BandwidthEstimator(); BandwidthEstimator(); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL); bool estimateBandwidth( int32_t *bandwidth, bool *isStable = NULL, int32_t *shortTermBps = NULL); private: private: // Bandwidth estimation parameters // Bandwidth estimation parameters static const int32_t kShortTermBandwidthItems = 3; static const int32_t kMinBandwidthHistoryItems = 20; static const int32_t kMinBandwidthHistoryItems = 20; static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec static const int64_t kMaxBandwidthHistoryAgeUs = 60000000ll; // 60 sec struct BandwidthEntry { struct BandwidthEntry { int64_t mTimestampUs; int64_t mDelayUs; int64_t mDelayUs; size_t mNumBytes; size_t mNumBytes; }; }; Loading @@ -74,6 +80,7 @@ private: Mutex mLock; Mutex mLock; List<BandwidthEntry> mBandwidthHistory; List<BandwidthEntry> mBandwidthHistory; List<int32_t> mPrevEstimates; List<int32_t> mPrevEstimates; int32_t mShortTermEstimate; bool mHasNewSample; bool mHasNewSample; bool mIsStable; bool mIsStable; int64_t mTotalTransferTimeUs; int64_t mTotalTransferTimeUs; Loading @@ -83,6 +90,7 @@ private: }; }; LiveSession::BandwidthEstimator::BandwidthEstimator() : LiveSession::BandwidthEstimator::BandwidthEstimator() : mShortTermEstimate(0), mHasNewSample(false), mHasNewSample(false), mIsStable(true), mIsStable(true), mTotalTransferTimeUs(0), mTotalTransferTimeUs(0), Loading @@ -93,7 +101,9 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( size_t numBytes, int64_t delayUs) { size_t numBytes, int64_t delayUs) { AutoMutex autoLock(mLock); AutoMutex autoLock(mLock); int64_t nowUs = ALooper::GetNowUs(); BandwidthEntry entry; BandwidthEntry entry; entry.mTimestampUs = nowUs; entry.mDelayUs = delayUs; entry.mDelayUs = delayUs; entry.mNumBytes = numBytes; entry.mNumBytes = numBytes; mTotalTransferTimeUs += delayUs; mTotalTransferTimeUs += delayUs; Loading @@ -115,7 +125,10 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( // and total transfer time at least kMaxBandwidthHistoryWindowUs. // and total transfer time at least kMaxBandwidthHistoryWindowUs. while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) { while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) { List<BandwidthEntry>::iterator it = mBandwidthHistory.begin(); List<BandwidthEntry>::iterator it = mBandwidthHistory.begin(); if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) { // remove sample if either absolute age or total transfer time is // over kMaxBandwidthHistoryWindowUs if (nowUs - it->mTimestampUs < kMaxBandwidthHistoryAgeUs && mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) { break; break; } } mTotalTransferTimeUs -= it->mDelayUs; mTotalTransferTimeUs -= it->mDelayUs; Loading @@ -125,7 +138,7 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement( } } bool LiveSession::BandwidthEstimator::estimateBandwidth( bool LiveSession::BandwidthEstimator::estimateBandwidth( int32_t *bandwidthBps, bool *isStable) { int32_t *bandwidthBps, bool *isStable, int32_t *shortTermBps) { AutoMutex autoLock(mLock); AutoMutex autoLock(mLock); if (mBandwidthHistory.size() < 2) { if (mBandwidthHistory.size() < 2) { Loading @@ -137,6 +150,9 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( if (isStable) { if (isStable) { *isStable = mIsStable; *isStable = mIsStable; } } if (shortTermBps) { *shortTermBps = mShortTermEstimate; } return true; return true; } } Loading @@ -147,6 +163,21 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( } } mHasNewSample = false; mHasNewSample = false; int64_t totalTimeUs = 0; size_t totalBytes = 0; if (mBandwidthHistory.size() >= kShortTermBandwidthItems) { List<BandwidthEntry>::iterator it = --mBandwidthHistory.end(); for (size_t i = 0; i < kShortTermBandwidthItems; i++, it--) { totalTimeUs += it->mDelayUs; totalBytes += it->mNumBytes; } } mShortTermEstimate = totalTimeUs > 0 ? (totalBytes * 8E6 / totalTimeUs) : *bandwidthBps; if (shortTermBps) { *shortTermBps = mShortTermEstimate; } int32_t minEstimate = -1, maxEstimate = -1; int32_t minEstimate = -1, maxEstimate = -1; List<int32_t>::iterator it; List<int32_t>::iterator it; for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) { for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) { Loading @@ -158,10 +189,14 @@ bool LiveSession::BandwidthEstimator::estimateBandwidth( maxEstimate = estimate; maxEstimate = estimate; } } } } mIsStable = (maxEstimate <= minEstimate * 4 / 3); // consider it stable if long-term average is not jumping a lot // and short-term average is not much lower than long-term average mIsStable = (maxEstimate <= minEstimate * 4 / 3) && mShortTermEstimate > minEstimate * 7 / 10; if (isStable) { if (isStable) { *isStable = mIsStable; *isStable = mIsStable; } } #if 0 #if 0 { { char dumpStr[1024] = {0}; char dumpStr[1024] = {0}; Loading Loading @@ -251,6 +286,7 @@ LiveSession::LiveSession( mCurBandwidthIndex(-1), mCurBandwidthIndex(-1), mOrigBandwidthIndex(-1), mOrigBandwidthIndex(-1), mLastBandwidthBps(-1ll), mLastBandwidthBps(-1ll), mLastBandwidthStable(false), mBandwidthEstimator(new BandwidthEstimator()), mBandwidthEstimator(new BandwidthEstimator()), mMaxWidth(720), mMaxWidth(720), mMaxHeight(480), mMaxHeight(480), Loading Loading @@ -713,6 +749,20 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { } } } } // remember the failure index (as mCurBandwidthIndex will be restored // after cancelBandwidthSwitch()), and record last fail time size_t failureIndex = mCurBandwidthIndex; mBandwidthItems.editItemAt( failureIndex).mLastFailureUs = ALooper::GetNowUs(); if (mSwitchInProgress) { // if error happened when we switch to a variant, try fallback // to other variant to save the session if (tryBandwidthFallback()) { break; } } if (mInPreparationPhase) { if (mInPreparationPhase) { postPrepared(err); postPrepared(err); } } Loading Loading @@ -886,6 +936,13 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { } } } } // static bool LiveSession::isBandwidthValid(const BandwidthItem &item) { static const int64_t kBlacklistWindowUs = 300 * 1000000ll; return item.mLastFailureUs < 0 || ALooper::GetNowUs() - item.mLastFailureUs > kBlacklistWindowUs; } // static // static int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { if (a->mBandwidth < b->mBandwidth) { if (a->mBandwidth < b->mBandwidth) { Loading Loading @@ -986,6 +1043,7 @@ void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) { BandwidthItem item; BandwidthItem item; item.mPlaylistIndex = i; item.mPlaylistIndex = i; item.mLastFailureUs = -1ll; sp<AMessage> meta; sp<AMessage> meta; AString uri; AString uri; Loading Loading @@ -1223,6 +1281,13 @@ float LiveSession::getAbortThreshold( X/T < bw1 / (bw1 + bw0 - bw) X/T < bw1 / (bw1 + bw0 - bw) */ */ // abort old bandwidth immediately if bandwidth is fluctuating a lot. // our estimate could be far off, and fetching old bandwidth could // take too long. if (!mLastBandwidthStable) { return 0.0f; } // Taking the measured current bandwidth at 50% face value only, // Taking the measured current bandwidth at 50% face value only, // as our bandwidth estimation is a lagging indicator. Being // as our bandwidth estimation is a lagging indicator. Being // conservative on this, we prefer switching to lower bandwidth // conservative on this, we prefer switching to lower bandwidth Loading Loading @@ -1250,6 +1315,16 @@ void LiveSession::addBandwidthMeasurement(size_t numBytes, int64_t delayUs) { mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs); mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs); } } ssize_t LiveSession::getLowestValidBandwidthIndex() const { for (size_t index = 0; index < mBandwidthItems.size(); index++) { if (isBandwidthValid(mBandwidthItems[index])) { return index; } } // if playlists are all blacklisted, return 0 and hope it's alive return 0; } size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { if (mBandwidthItems.size() < 2) { if (mBandwidthItems.size() < 2) { // shouldn't be here if we only have 1 bandwidth, check // shouldn't be here if we only have 1 bandwidth, check Loading Loading @@ -1284,14 +1359,18 @@ size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { } } } } // Pick the highest bandwidth stream below or equal to estimated bandwidth. // Pick the highest bandwidth stream that's not currently blacklisted // below or equal to estimated bandwidth. index = mBandwidthItems.size() - 1; index = mBandwidthItems.size() - 1; while (index > 0) { ssize_t lowestBandwidth = getLowestValidBandwidthIndex(); while (index > lowestBandwidth) { // be conservative (70%) to avoid overestimating and immediately // be conservative (70%) to avoid overestimating and immediately // switching down again. // switching down again. size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) { const BandwidthItem &item = mBandwidthItems[index]; if (item.mBandwidth <= adjustedBandwidthBps && isBandwidthValid(item)) { break; break; } } --index; --index; Loading Loading @@ -2172,21 +2251,57 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) { notify->post(); notify->post(); } } bool LiveSession::tryBandwidthFallback() { if (mInPreparationPhase || mReconfigurationInProgress) { // Don't try fallback during prepare or reconfig. // If error happens there, it's likely unrecoverable. return false; } if (mCurBandwidthIndex > mOrigBandwidthIndex) { // if we're switching up, simply cancel and resume old variant cancelBandwidthSwitch(true /* resume */); return true; } else { // if we're switching down, we're likely about to underflow (if // not already underflowing). try the lowest viable bandwidth if // not on that variant already. ssize_t lowestValid = getLowestValidBandwidthIndex(); if (mCurBandwidthIndex > lowestValid) { cancelBandwidthSwitch(); changeConfiguration(-1ll, lowestValid); return true; } } // return false if we couldn't find any fallback return false; } /* /* * returns true if a bandwidth switch is actually needed (and started), * returns true if a bandwidth switch is actually needed (and started), * returns false otherwise * returns false otherwise */ */ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // no need to check bandwidth if we only have 1 bandwidth settings // no need to check bandwidth if we only have 1 bandwidth settings if (mSwitchInProgress || mBandwidthItems.size() < 2) { if (mBandwidthItems.size() < 2) { return false; } if (mSwitchInProgress) { if (mBuffering) { tryBandwidthFallback(); } return false; return false; } } int32_t bandwidthBps; int32_t bandwidthBps, shortTermBps; bool isStable; bool isStable; if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) { if (mBandwidthEstimator->estimateBandwidth( ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); &bandwidthBps, &isStable, &shortTermBps)) { ALOGV("bandwidth estimated at %.2f kbps, " "stable %d, shortTermBps %.2f kbps", bandwidthBps / 1024.0f, isStable, shortTermBps / 1024.0f); mLastBandwidthBps = bandwidthBps; mLastBandwidthBps = bandwidthBps; mLastBandwidthStable = isStable; } else { } else { ALOGV("no bandwidth estimate."); ALOGV("no bandwidth estimate."); return false; return false; Loading @@ -2203,9 +2318,13 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { if (canSwitchDown || canSwitchUp) { if (canSwitchDown || canSwitchUp) { // bandwidth estimating has some delay, if we have to downswitch when // bandwidth estimating has some delay, if we have to downswitch when // it hasn't stabilized, be very conservative on bandwidth. // it hasn't stabilized, use the short term to guess real bandwidth, // since it may be dropping too fast. // (note this doesn't apply to upswitch, always use longer average there) if (!isStable && canSwitchDown) { if (!isStable && canSwitchDown) { bandwidthBps /= 2; if (shortTermBps < bandwidthBps) { bandwidthBps = shortTermBps; } } } ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps); Loading
media/libstagefright/httplive/LiveSession.h +5 −0 Original line number Original line Diff line number Diff line Loading @@ -146,6 +146,7 @@ private: struct BandwidthItem { struct BandwidthItem { size_t mPlaylistIndex; size_t mPlaylistIndex; unsigned long mBandwidth; unsigned long mBandwidth; int64_t mLastFailureUs; }; }; struct FetcherInfo { struct FetcherInfo { Loading Loading @@ -199,6 +200,7 @@ private: ssize_t mCurBandwidthIndex; ssize_t mCurBandwidthIndex; ssize_t mOrigBandwidthIndex; ssize_t mOrigBandwidthIndex; int32_t mLastBandwidthBps; int32_t mLastBandwidthBps; bool mLastBandwidthStable; sp<BandwidthEstimator> mBandwidthEstimator; sp<BandwidthEstimator> mBandwidthEstimator; sp<M3UParser> mPlaylist; sp<M3UParser> mPlaylist; Loading Loading @@ -268,8 +270,10 @@ private: ssize_t currentBWIndex, ssize_t targetBWIndex) const; ssize_t currentBWIndex, ssize_t targetBWIndex) const; void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); void addBandwidthMeasurement(size_t numBytes, int64_t delayUs); size_t getBandwidthIndex(int32_t bandwidthBps); size_t getBandwidthIndex(int32_t bandwidthBps); ssize_t getLowestValidBandwidthIndex() const; HLSTime latestMediaSegmentStartTime() const; HLSTime latestMediaSegmentStartTime() const; static bool isBandwidthValid(const BandwidthItem &item); static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); static StreamType indexToType(int idx); static StreamType indexToType(int idx); static ssize_t typeToIndex(int32_t type); static ssize_t typeToIndex(int32_t type); Loading @@ -287,6 +291,7 @@ private: sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); bool tryBandwidthFallback(); void schedulePollBuffering(); void schedulePollBuffering(); void cancelPollBuffering(); void cancelPollBuffering(); Loading
media/libstagefright/httplive/PlaylistFetcher.cpp +7 −0 Original line number Original line Diff line number Diff line Loading @@ -511,6 +511,13 @@ void PlaylistFetcher::startAsync( msg->post(); msg->post(); } } /* * pauseAsync * * threshold: 0.0f - pause after current fetch block (default 47Kbytes) * -1.0f - pause after finishing current segment * 0.0~1.0f - pause if remaining of current segment exceeds threshold */ void PlaylistFetcher::pauseAsync( void PlaylistFetcher::pauseAsync( float thresholdRatio, bool disconnect) { float thresholdRatio, bool disconnect) { setStoppingThreshold(thresholdRatio, disconnect); setStoppingThreshold(thresholdRatio, disconnect); Loading