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
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
index ec3a10e..f17ee88 100644
--- a/include/media/stagefright/Utils.h
+++ b/include/media/stagefright/Utils.h
@@ -71,7 +71,7 @@
     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);
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 @@
     }
 }
 
-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 @@
     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 @@
 
     Mutex mLock;
     List<BandwidthEntry> mBandwidthHistory;
+    List<int32_t> mPrevEstimates;
+    bool mHasNewSample;
+    bool mIsStable;
     int64_t mTotalTransferTimeUs;
     size_t mTotalTransferBytes;
 
@@ -88,6 +91,8 @@
 };
 
 LiveSession::BandwidthEstimator::BandwidthEstimator() :
+    mHasNewSample(false),
+    mIsStable(true),
     mTotalTransferTimeUs(0),
     mTotalTransferBytes(0) {
 }
@@ -102,6 +107,7 @@
     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 @@
     }
 }
 
-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 @@
                 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 @@
                 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 @@
     }
 
     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 @@
     // 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 @@
         // 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 @@
       mIsVariantPlaylist(false),
       mIsComplete(false),
       mIsEvent(false),
+      mFirstSeqNumber(-1),
+      mLastSeqNumber(-1),
+      mTargetDurationUs(-1ll),
       mDiscontinuitySeq(0),
       mDiscontinuityCount(0),
       mSelectedIndex(-1) {
@@ -283,6 +286,19 @@
     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,11 +680,22 @@
     }
 
     // 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 @@
     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 @@
     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 @@
       mDiscontinuitySeq(-1ll),
       mStartTimeUsRelative(false),
       mLastPlaylistFetchTimeUs(-1ll),
+      mPlaylistTimeUs(-1ll),
       mSeqNumber(-1),
       mNumRetries(0),
       mStartup(true),
@@ -191,14 +192,9 @@
 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::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 @@
         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 @@
         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 @@
             if (!mPlaylist->isComplete()) {
                 updateTargetDuration();
             }
+            mPlaylistTimeUs = ALooper::GetNowUs();
         }
 
         mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
@@ -884,9 +866,7 @@
     }
 
     // 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 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 @@
             // 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 @@
                 // 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 @@
         }
 
         queueDiscontinuity(
-                ATSParser::DISCONTINUITY_FORMATCHANGE,
+                ATSParser::DISCONTINUITY_FORMAT_ONLY,
                 NULL /* extra */);
 
         if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
@@ -1172,6 +1153,8 @@
             // set mStartTimeUs=0, and take all samples from now on.
             mStartTimeUs = 0;
             mFirstPTSValid = false;
+            mIDRFound = false;
+            mVideoBuffer->clear();
         }
     }
 
@@ -1408,42 +1391,67 @@
     }
 }
 
-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;
-    }
-    lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
+/*
+ * returns true if we need to adjust mSeqNumber
+ */
+bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) {
+    int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
 
-    int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
-    // adjust anchorTimeUs to within targetDiffUs from mStartTimeUs
-    while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDiffUs) {
-        sp<AMessage> itemMeta;
-        CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
-
-        int64_t itemDurationUs;
-        CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
-
-        anchorTimeUs -= itemDurationUs;
-        --index;
-    }
-
-    int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1;
-    if (newSeqNumber <= lastSeqNumberInPlaylist) {
-        return newSeqNumber;
+    int64_t minDiffUs, maxDiffUs;
+    if (mSeekMode == LiveSession::kSeekModeNextSample) {
+        minDiffUs = -mPlaylist->getTargetDuration();
+        maxDiffUs = 0ll;
     } else {
-        return lastSeqNumberInPlaylist;
+        minDiffUs = -mPlaylist->getTargetDuration() / 2;
+        maxDiffUs = mPlaylist->getTargetDuration();
     }
+
+    int32_t oldSeqNumber = mSeqNumber;
+    ssize_t index = mSeqNumber - firstSeqNumberInPlaylist;
+
+    // adjust anchorTimeUs to within (minDiffUs, maxDiffUs) from mStartTimeUs
+    int64_t diffUs = anchorTimeUs - mStartTimeUs;
+    if (diffUs > maxDiffUs) {
+        while (index > 0 && diffUs > maxDiffUs) {
+            --index;
+
+            sp<AMessage> 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<AMessage> itemMeta;
+            CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+            int64_t itemDurationUs;
+            CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+            diffUs += itemDurationUs;
+        }
+    }
+
+    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::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 @@
         index = mPlaylist->size() - 1;
     }
 
-    return firstSeqNumberInPlaylist + index;
+    return mPlaylist->getFirstSeqNumber() + index;
 }
 
 const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
@@ -1508,14 +1510,13 @@
         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 @@
     // 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<AnotherPacketSource> source =
+                static_cast<AnotherPacketSource *>(
+                        mTSParser->getSource(type).get());
+
+            if (source == NULL) {
+                continue;
+            }
+            sp<AMessage> 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<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
@@ -1611,76 +1665,17 @@
             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 @@
         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 @@
         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 @@
             }
 
             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::updateTargetDuration() {
-    int32_t targetDurationSecs;
-    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
     sp<AMessage> 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 @@
     KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
 
     int64_t mLastPlaylistFetchTimeUs;
+    int64_t mPlaylistTimeUs;
     sp<M3UParser> mPlaylist;
     int32_t mSeqNumber;
     int32_t mNumRetries;
@@ -238,8 +239,7 @@
     void queueDiscontinuity(
             ATSParser::DiscontinuityType type, const sp<AMessage> &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;