Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97844998 authored by Chong Zhang's avatar Chong Zhang
Browse files

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
parent 2170233c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
+21 −3
Original line number Diff line number Diff line
@@ -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;
+50 −8
Original line number Diff line number Diff line
@@ -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<BandwidthEntry> mBandwidthHistory;
    List<int32_t> 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<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;
}

@@ -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
@@ -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);
    }
@@ -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(
+32 −5
Original line number Diff line number Diff line
@@ -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<AMessage> M3UParser::meta() {
    return mMeta;
}
@@ -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;
}
+6 −0
Original line number Diff line number Diff line
@@ -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();

@@ -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