Loading include/media/stagefright/Utils.h +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ struct HLSTime { sp<AMessage> mMeta; HLSTime(const sp<AMessage> &meta = NULL); int64_t getSegmentTimeUs(bool midpoint = false) const; int64_t getSegmentTimeUs() const; }; bool operator <(const HLSTime &t0, const HLSTime &t1); Loading media/libstagefright/Utils.cpp +21 −3 Original line number Diff line number Diff line Loading @@ -852,14 +852,32 @@ HLSTime::HLSTime(const sp<AMessage>& 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; Loading media/libstagefright/httplive/LiveSession.cpp +50 −8 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -81,6 +81,9 @@ private: Mutex mLock; List<BandwidthEntry> mBandwidthHistory; List<int32_t> mPrevEstimates; bool mHasNewSample; bool mIsStable; int64_t mTotalTransferTimeUs; size_t mTotalTransferBytes; Loading @@ -88,6 +91,8 @@ private: }; LiveSession::BandwidthEstimator::BandwidthEstimator() : mHasNewSample(false), mIsStable(true), mTotalTransferTimeUs(0), mTotalTransferBytes(0) { } Loading @@ -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. Loading @@ -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<int32_t>::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; } Loading Loading @@ -1930,7 +1965,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &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 Loading @@ -1945,7 +1980,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { sources[kSubtitleIndex], getMetadataSource(sources, mNewStreamMask, switching), startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs, startTime.getSegmentTimeUs(true /* midpoint */), startTime.getSegmentTimeUs(), startTime.mSeq, seekMode); } Loading Loading @@ -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 { Loading @@ -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 Loading @@ -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( Loading media/libstagefright/httplive/M3UParser.cpp +32 −5 Original line number Diff line number Diff line Loading @@ -250,6 +250,9 @@ M3UParser::M3UParser( mIsVariantPlaylist(false), mIsComplete(false), mIsEvent(false), mFirstSeqNumber(-1), mLastSeqNumber(-1), mTargetDurationUs(-1ll), mDiscontinuitySeq(0), mDiscontinuityCount(0), mSelectedIndex(-1) { Loading Loading @@ -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<AMessage> M3UParser::meta() { return mMeta; } Loading Loading @@ -664,12 +680,23 @@ 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") // (currently only checking "target-duration"), and // initialization of playlist properties (eg. mTargetDurationUs) if (!mIsVariantPlaylist) { int32_t targetDurationSecs; if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32( "target-duration", &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; } Loading media/libstagefright/httplive/M3UParser.h +6 −0 Original line number Diff line number Diff line Loading @@ -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<AMessage> meta(); Loading Loading @@ -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; Loading Loading
include/media/stagefright/Utils.h +1 −1 Original line number Diff line number Diff line Loading @@ -71,7 +71,7 @@ struct HLSTime { sp<AMessage> mMeta; HLSTime(const sp<AMessage> &meta = NULL); int64_t getSegmentTimeUs(bool midpoint = false) const; int64_t getSegmentTimeUs() const; }; bool operator <(const HLSTime &t0, const HLSTime &t1); Loading
media/libstagefright/Utils.cpp +21 −3 Original line number Diff line number Diff line Loading @@ -852,14 +852,32 @@ HLSTime::HLSTime(const sp<AMessage>& 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; Loading
media/libstagefright/httplive/LiveSession.cpp +50 −8 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -81,6 +81,9 @@ private: Mutex mLock; List<BandwidthEntry> mBandwidthHistory; List<int32_t> mPrevEstimates; bool mHasNewSample; bool mIsStable; int64_t mTotalTransferTimeUs; size_t mTotalTransferBytes; Loading @@ -88,6 +91,8 @@ private: }; LiveSession::BandwidthEstimator::BandwidthEstimator() : mHasNewSample(false), mIsStable(true), mTotalTransferTimeUs(0), mTotalTransferBytes(0) { } Loading @@ -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. Loading @@ -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<int32_t>::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; } Loading Loading @@ -1930,7 +1965,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &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 Loading @@ -1945,7 +1980,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { sources[kSubtitleIndex], getMetadataSource(sources, mNewStreamMask, switching), startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs, startTime.getSegmentTimeUs(true /* midpoint */), startTime.getSegmentTimeUs(), startTime.mSeq, seekMode); } Loading Loading @@ -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 { Loading @@ -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 Loading @@ -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( Loading
media/libstagefright/httplive/M3UParser.cpp +32 −5 Original line number Diff line number Diff line Loading @@ -250,6 +250,9 @@ M3UParser::M3UParser( mIsVariantPlaylist(false), mIsComplete(false), mIsEvent(false), mFirstSeqNumber(-1), mLastSeqNumber(-1), mTargetDurationUs(-1ll), mDiscontinuitySeq(0), mDiscontinuityCount(0), mSelectedIndex(-1) { Loading Loading @@ -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<AMessage> M3UParser::meta() { return mMeta; } Loading Loading @@ -664,12 +680,23 @@ 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") // (currently only checking "target-duration"), and // initialization of playlist properties (eg. mTargetDurationUs) if (!mIsVariantPlaylist) { int32_t targetDurationSecs; if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32( "target-duration", &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; } Loading
media/libstagefright/httplive/M3UParser.h +6 −0 Original line number Diff line number Diff line Loading @@ -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<AMessage> meta(); Loading Loading @@ -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; Loading