httplive: timestamp reporting, track selection

Bug: 15153976
Bug: 15763638
Bug: 16351654
Change-Id: I4462276d4b7342647286a0ca4be11692ce52ff6d
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 2f5b0f1..ebc3d08 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -308,7 +308,7 @@
 
           int64_t timeUs, actualTimeUs;
           const bool formatChange = true;
-          sp<AMessage> latestMeta = track->mPackets->getLatestMeta();
+          sp<AMessage> latestMeta = track->mPackets->getLatestEnqueuedMeta();
           CHECK(latestMeta != NULL && latestMeta->findInt64("timeUs", &timeUs));
           readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
           readBuffer(counterpartType, -1, NULL, formatChange);
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 10cdde2..8667a6b 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -57,7 +57,7 @@
       mHTTPService(httpService),
       mInPreparationPhase(true),
       mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
-      mPrevBandwidthIndex(-1),
+      mCurBandwidthIndex(-1),
       mStreamMask(0),
       mNewStreamMask(0),
       mSwapMask(0),
@@ -68,13 +68,17 @@
       mReconfigurationInProgress(false),
       mSwitchInProgress(false),
       mDisconnectReplyID(0),
-      mSeekReplyID(0) {
+      mSeekReplyID(0),
+      mFirstTimeUsValid(false),
+      mFirstTimeUs(0),
+      mLastSeekTimeUs(0) {
 
     mStreams[kAudioIndex] = StreamItem("audio");
     mStreams[kVideoIndex] = StreamItem("video");
     mStreams[kSubtitleIndex] = StreamItem("subtitles");
 
     for (size_t i = 0; i < kMaxStreams; ++i) {
+        mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
     }
@@ -109,31 +113,65 @@
         return -EWOULDBLOCK;
     }
 
+    status_t finalResult;
+    sp<AnotherPacketSource> discontinuityQueue  = mDiscontinuities.valueFor(stream);
+    if (discontinuityQueue->hasBufferAvailable(&finalResult)) {
+        discontinuityQueue->dequeueAccessUnit(accessUnit);
+        // seeking, track switching
+        sp<AMessage> extra;
+        int64_t timeUs;
+        if ((*accessUnit)->meta()->findMessage("extra", &extra)
+                && extra != NULL
+                && extra->findInt64("timeUs", &timeUs)) {
+            // seeking only
+            mLastSeekTimeUs = timeUs;
+            mDiscontinuityOffsetTimesUs.clear();
+            mDiscontinuityAbsStartTimesUs.clear();
+        }
+        return INFO_DISCONTINUITY;
+    }
+
     sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
 
-    status_t finalResult;
     if (!packetSource->hasBufferAvailable(&finalResult)) {
         return finalResult == OK ? -EAGAIN : finalResult;
     }
 
+    // wait for counterpart
+    sp<AnotherPacketSource> otherSource;
+    if (stream == STREAMTYPE_AUDIO && (mStreamMask & STREAMTYPE_VIDEO)) {
+        otherSource = mPacketSources.valueFor(STREAMTYPE_VIDEO);
+    } else if (stream == STREAMTYPE_VIDEO && (mStreamMask & STREAMTYPE_AUDIO)) {
+        otherSource = mPacketSources.valueFor(STREAMTYPE_AUDIO);
+    }
+    if (otherSource != NULL && !otherSource->hasBufferAvailable(&finalResult)) {
+        return finalResult == OK ? -EAGAIN : finalResult;
+    }
+
     status_t err = packetSource->dequeueAccessUnit(accessUnit);
 
+    size_t streamIdx;
     const char *streamStr;
     switch (stream) {
         case STREAMTYPE_AUDIO:
+            streamIdx = kAudioIndex;
             streamStr = "audio";
             break;
         case STREAMTYPE_VIDEO:
+            streamIdx = kVideoIndex;
             streamStr = "video";
             break;
         case STREAMTYPE_SUBTITLES:
+            streamIdx = kSubtitleIndex;
             streamStr = "subs";
             break;
         default:
             TRESPASS();
     }
 
+    StreamItem& strm = mStreams[streamIdx];
     if (err == INFO_DISCONTINUITY) {
+        // adaptive streaming, discontinuities in the playlist
         int32_t type;
         CHECK((*accessUnit)->meta()->findInt32("discontinuity", &type));
 
@@ -148,10 +186,7 @@
               extra == NULL ? "NULL" : extra->debugString().c_str());
 
         int32_t swap;
-        if (type == ATSParser::DISCONTINUITY_FORMATCHANGE
-                && (*accessUnit)->meta()->findInt32("swapPacketSource", &swap)
-                && swap) {
-
+        if ((*accessUnit)->meta()->findInt32("swapPacketSource", &swap) && swap) {
             int32_t switchGeneration;
             CHECK((*accessUnit)->meta()->findInt32("switchGeneration", &switchGeneration));
             {
@@ -164,13 +199,67 @@
                     msg->post();
                 }
             }
+        } else {
+            size_t seq = strm.mCurDiscontinuitySeq;
+            int64_t offsetTimeUs;
+            if (mDiscontinuityOffsetTimesUs.indexOfKey(seq) >= 0) {
+                offsetTimeUs = mDiscontinuityOffsetTimesUs.valueFor(seq);
+            } else {
+                offsetTimeUs = 0;
+            }
+
+            seq += 1;
+            if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+                int64_t firstTimeUs;
+                firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
+                offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
+                offsetTimeUs += strm.mLastSampleDurationUs;
+            } else {
+                offsetTimeUs += strm.mLastSampleDurationUs;
+            }
+
+            mDiscontinuityOffsetTimesUs.add(seq, offsetTimeUs);
         }
     } else if (err == OK) {
+
         if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
             int64_t timeUs;
+            int32_t discontinuitySeq = 0;
             CHECK((*accessUnit)->meta()->findInt64("timeUs",  &timeUs));
-            ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs);
+            (*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq);
+            strm.mCurDiscontinuitySeq = discontinuitySeq;
 
+            int32_t discard = 0;
+            int64_t firstTimeUs;
+            if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+                int64_t durUs; // approximate sample duration
+                if (timeUs > strm.mLastDequeuedTimeUs) {
+                    durUs = timeUs - strm.mLastDequeuedTimeUs;
+                } else {
+                    durUs = strm.mLastDequeuedTimeUs - timeUs;
+                }
+                strm.mLastSampleDurationUs = durUs;
+                firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
+            } else if ((*accessUnit)->meta()->findInt32("discard", &discard) && discard) {
+                firstTimeUs = timeUs;
+            } else {
+                mDiscontinuityAbsStartTimesUs.add(strm.mCurDiscontinuitySeq, timeUs);
+                firstTimeUs = timeUs;
+            }
+
+            strm.mLastDequeuedTimeUs = timeUs;
+            if (timeUs >= firstTimeUs) {
+                timeUs -= firstTimeUs;
+            } else {
+                timeUs = 0;
+            }
+            timeUs += mLastSeekTimeUs;
+            if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) {
+                timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
+            }
+
+            ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs);
+            (*accessUnit)->meta()->setInt64("timeUs",  timeUs);
             mLastDequeuedTimeUs = timeUs;
             mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
         } else if (stream == STREAMTYPE_SUBTITLES) {
@@ -289,7 +378,9 @@
                             break;
                         }
 
-                        tryToFinishBandwidthSwitch();
+                        if (mSwitchInProgress) {
+                            tryToFinishBandwidthSwitch();
+                        }
                     }
 
                     if (mContinuation != NULL) {
@@ -538,8 +629,9 @@
         mBandwidthItems.push(item);
     }
 
+    mPlaylist->pickRandomMediaItems();
     changeConfiguration(
-            0ll /* timeUs */, initialBandwidthIndex, true /* pickTrack */);
+            0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */);
 }
 
 void LiveSession::finishDisconnect() {
@@ -847,20 +939,20 @@
     // to lowest)
     const size_t kMinIndex = 0;
 
-    static ssize_t mPrevBandwidthIndex = -1;
+    static ssize_t mCurBandwidthIndex = -1;
 
     size_t index;
-    if (mPrevBandwidthIndex < 0) {
+    if (mCurBandwidthIndex < 0) {
         index = kMinIndex;
     } else if (uniformRand() < 0.5) {
-        index = (size_t)mPrevBandwidthIndex;
+        index = (size_t)mCurBandwidthIndex;
     } else {
-        index = mPrevBandwidthIndex + 1;
+        index = mCurBandwidthIndex + 1;
         if (index == mBandwidthItems.size()) {
             index = kMinIndex;
         }
     }
-    mPrevBandwidthIndex = index;
+    mCurBandwidthIndex = index;
 #elif 0
     // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec
 
@@ -937,7 +1029,10 @@
 status_t LiveSession::selectTrack(size_t index, bool select) {
     status_t err = mPlaylist->selectTrack(index, select);
     if (err == OK) {
-        (new AMessage(kWhatChangeConfiguration, id()))->post();
+        sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id());
+        msg->setInt32("bandwidthIndex", mCurBandwidthIndex);
+        msg->setInt32("pickTrack", select);
+        msg->post();
     }
     return err;
 }
@@ -964,15 +1059,11 @@
     CHECK(!mReconfigurationInProgress);
     mReconfigurationInProgress = true;
 
-    mPrevBandwidthIndex = bandwidthIndex;
+    mCurBandwidthIndex = bandwidthIndex;
 
     ALOGV("changeConfiguration => timeUs:%" PRId64 " us, bwIndex:%zu, pickTrack:%d",
           timeUs, bandwidthIndex, pickTrack);
 
-    if (pickTrack) {
-        mPlaylist->pickRandomMediaItems();
-    }
-
     CHECK_LT(bandwidthIndex, mBandwidthItems.size());
     const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex);
 
@@ -995,14 +1086,15 @@
 
         // If we're seeking all current fetchers are discarded.
         if (timeUs < 0ll) {
-            // delay fetcher removal
-            discardFetcher = false;
+            // delay fetcher removal if not picking tracks
+            discardFetcher = pickTrack;
 
             for (size_t j = 0; j < kMaxStreams; ++j) {
                 StreamType type = indexToType(j);
                 if ((streamMask & type) && uri == URIs[j]) {
                     resumeMask |= type;
                     streamMask &= ~type;
+                    discardFetcher = false;
                 }
             }
         }
@@ -1016,16 +1108,17 @@
 
     sp<AMessage> msg;
     if (timeUs < 0ll) {
-        // skip onChangeConfiguration2 (decoder destruction) if switching.
+        // skip onChangeConfiguration2 (decoder destruction) if not seeking.
         msg = new AMessage(kWhatChangeConfiguration3, id());
     } else {
         msg = new AMessage(kWhatChangeConfiguration2, id());
     }
     msg->setInt32("streamMask", streamMask);
     msg->setInt32("resumeMask", resumeMask);
+    msg->setInt32("pickTrack", pickTrack);
     msg->setInt64("timeUs", timeUs);
     for (size_t i = 0; i < kMaxStreams; ++i) {
-        if (streamMask & indexToType(i)) {
+        if ((streamMask | resumeMask) & indexToType(i)) {
             msg->setString(mStreams[i].uriKey().c_str(), URIs[i].c_str());
         }
     }
@@ -1049,7 +1142,10 @@
 
 void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
     if (!mReconfigurationInProgress) {
-        changeConfiguration(-1ll /* timeUs */, getBandwidthIndex());
+        int32_t pickTrack = 0, bandwidthIndex = mCurBandwidthIndex;
+        msg->findInt32("pickTrack", &pickTrack);
+        msg->findInt32("bandwidthIndex", &bandwidthIndex);
+        changeConfiguration(-1ll /* timeUs */, bandwidthIndex, pickTrack);
     } else {
         msg->post(1000000ll); // retry in 1 sec
     }
@@ -1060,8 +1156,14 @@
 
     // All fetchers are either suspended or have been removed now.
 
-    uint32_t streamMask;
+    uint32_t streamMask, resumeMask;
     CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
+    CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
+
+    // currently onChangeConfiguration2 is only called for seeking;
+    // remove the following CHECK if using it else where.
+    CHECK_EQ(resumeMask, 0);
+    streamMask |= resumeMask;
 
     AString URIs[kMaxStreams];
     for (size_t i = 0; i < kMaxStreams; ++i) {
@@ -1125,16 +1227,21 @@
     }
 
     int64_t timeUs;
+    int32_t pickTrack;
     bool switching = false;
     CHECK(msg->findInt64("timeUs", &timeUs));
+    CHECK(msg->findInt32("pickTrack", &pickTrack));
 
     if (timeUs < 0ll) {
-        timeUs = mLastDequeuedTimeUs;
-        switching = true;
+        if (!pickTrack) {
+            switching = true;
+        }
+        mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs;
+    } else {
+        mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
     }
-    mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
 
-    mNewStreamMask = streamMask;
+    mNewStreamMask = streamMask | resumeMask;
 
     // Of all existing fetchers:
     // * Resume fetchers that are still needed and assign them original packet sources.
@@ -1147,6 +1254,16 @@
         for (size_t j = 0; j < kMaxStreams; ++j) {
             if ((resumeMask & indexToType(j)) && uri == mStreams[j].mUri) {
                 sources[j] = mPacketSources.valueFor(indexToType(j));
+
+                if (j != kSubtitleIndex) {
+                    ALOGV("queueing dummy discontinuity for stream type %d", indexToType(j));
+                    sp<AnotherPacketSource> discontinuityQueue;
+                    discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
+                    discontinuityQueue->queueDiscontinuity(
+                            ATSParser::DISCONTINUITY_NONE,
+                            NULL,
+                            true);
+                }
             }
         }
 
@@ -1180,7 +1297,9 @@
         CHECK(fetcher != NULL);
 
         int32_t latestSeq = -1;
-        int64_t latestTimeUs = 0ll;
+        int64_t startTimeUs = -1;
+        int64_t segmentStartTimeUs = -1ll;
+        int32_t discontinuitySeq = -1;
         sp<AnotherPacketSource> sources[kMaxStreams];
 
         // TRICKY: looping from i as earlier streams are already removed from streamMask
@@ -1188,29 +1307,65 @@
             if ((streamMask & indexToType(j)) && uri == mStreams[j].mUri) {
                 sources[j] = mPacketSources.valueFor(indexToType(j));
 
-                if (!switching) {
+                if (timeUs >= 0) {
                     sources[j]->clear();
+                    startTimeUs = timeUs;
+
+                    sp<AnotherPacketSource> discontinuityQueue;
+                    sp<AMessage> extra = new AMessage;
+                    extra->setInt64("timeUs", timeUs);
+                    discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
+                    discontinuityQueue->queueDiscontinuity(
+                            ATSParser::DISCONTINUITY_SEEK, extra, true);
                 } else {
-                    int32_t type, seq;
-                    int64_t srcTimeUs;
-                    sp<AMessage> meta = sources[j]->getLatestMeta();
+                    int32_t type;
+                    int64_t srcSegmentStartTimeUs;
+                    sp<AMessage> meta;
+                    if (pickTrack) {
+                        // selecting
+                        meta = sources[j]->getLatestDequeuedMeta();
+                    } else {
+                        // adapting
+                        meta = sources[j]->getLatestEnqueuedMeta();
+                    }
 
                     if (meta != NULL && !meta->findInt32("discontinuity", &type)) {
-                        CHECK(meta->findInt32("seq", &seq));
-                        if (seq > latestSeq) {
-                            latestSeq = seq;
+                        int64_t tmpUs;
+                        CHECK(meta->findInt64("timeUs", &tmpUs));
+                        if (startTimeUs < 0 || tmpUs < startTimeUs) {
+                            startTimeUs = tmpUs;
                         }
-                        CHECK(meta->findInt64("timeUs", &srcTimeUs));
-                        if (srcTimeUs > latestTimeUs) {
-                            latestTimeUs = srcTimeUs;
+
+                        CHECK(meta->findInt64("segmentStartTimeUs", &tmpUs));
+                        if (segmentStartTimeUs < 0 || tmpUs < segmentStartTimeUs) {
+                            segmentStartTimeUs = tmpUs;
+                        }
+
+                        int32_t seq;
+                        CHECK(meta->findInt32("discontinuitySeq", &seq));
+                        if (discontinuitySeq < 0 || seq < discontinuitySeq) {
+                            discontinuitySeq = seq;
                         }
                     }
 
-                    sources[j] = mPacketSources2.valueFor(indexToType(j));
-                    sources[j]->clear();
-                    uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
-                    if (extraStreams & indexToType(j)) {
-                        sources[j]->queueAccessUnit(createFormatChangeBuffer(/* swap = */ false));
+                    if (pickTrack) {
+                        // selecting track, queue discontinuities before content
+                        sources[j]->clear();
+                        if (j == kSubtitleIndex) {
+                            break;
+                        }
+                        sp<AnotherPacketSource> discontinuityQueue;
+                        discontinuityQueue = mDiscontinuities.valueFor(indexToType(j));
+                        discontinuityQueue->queueDiscontinuity(
+                                ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+                    } else {
+                        // adapting, queue discontinuities after resume
+                        sources[j] = mPacketSources2.valueFor(indexToType(j));
+                        sources[j]->clear();
+                        uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
+                        if (extraStreams & indexToType(j)) {
+                            sources[j]->queueAccessUnit(createFormatChangeBuffer(/*swap*/ false));
+                        }
                     }
                 }
 
@@ -1222,9 +1377,10 @@
                 sources[kAudioIndex],
                 sources[kVideoIndex],
                 sources[kSubtitleIndex],
-                timeUs,
-                latestTimeUs /* min start time(us) */,
-                latestSeq >= 0 ? latestSeq + 1 : -1 /* starting sequence number hint */ );
+                startTimeUs < 0 ? mLastSeekTimeUs : startTimeUs,
+                segmentStartTimeUs,
+                discontinuitySeq,
+                switching);
     }
 
     // All fetchers have now been started, the configuration change
@@ -1236,6 +1392,7 @@
     mReconfigurationInProgress = false;
     if (switching) {
         mSwitchInProgress = true;
+        mSwapMask = streamMask;
     } else {
         mStreamMask = mNewStreamMask;
     }
@@ -1254,8 +1411,8 @@
 
     int32_t stream;
     CHECK(msg->findInt32("stream", &stream));
-    mSwapMask |= stream;
-    if (mSwapMask != mStreamMask) {
+    mSwapMask &= ~stream;
+    if (mSwapMask != 0) {
         return;
     }
 
@@ -1271,9 +1428,12 @@
 }
 
 // Mark switch done when:
-//   1. all old buffers are swapped out, AND
-//   2. all old fetchers are removed.
+//   1. all old buffers are swapped out
 void LiveSession::tryToFinishBandwidthSwitch() {
+    if (!mSwitchInProgress) {
+        return;
+    }
+
     bool needToRemoveFetchers = false;
     for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
         if (mFetcherInfos.valueAt(i).mToBeRemoved) {
@@ -1281,10 +1441,11 @@
             break;
         }
     }
-    if (!needToRemoveFetchers && mSwapMask == mStreamMask) {
+
+    if (!needToRemoveFetchers && mSwapMask == 0) {
+        ALOGI("mSwitchInProgress = false");
         mStreamMask = mNewStreamMask;
         mSwitchInProgress = false;
-        mSwapMask = 0;
     }
 }
 
@@ -1310,13 +1471,13 @@
         return false;
     }
 
-    if (mPrevBandwidthIndex < 0) {
+    if (mCurBandwidthIndex < 0) {
         return true;
     }
 
-    if (bandwidthIndex == (size_t)mPrevBandwidthIndex) {
+    if (bandwidthIndex == (size_t)mCurBandwidthIndex) {
         return false;
-    } else if (bandwidthIndex > (size_t)mPrevBandwidthIndex) {
+    } else if (bandwidthIndex > (size_t)mCurBandwidthIndex) {
         return canSwitchUp();
     } else {
         return true;
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index ed3818f..5423f0f 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -125,8 +125,19 @@
     struct StreamItem {
         const char *mType;
         AString mUri;
-        StreamItem() : mType("") {}
-        StreamItem(const char *type) : mType(type) {}
+        size_t mCurDiscontinuitySeq;
+        int64_t mLastDequeuedTimeUs;
+        int64_t mLastSampleDurationUs;
+        StreamItem()
+            : mType(""),
+              mCurDiscontinuitySeq(0),
+              mLastDequeuedTimeUs(0),
+              mLastSampleDurationUs(0) {}
+        StreamItem(const char *type)
+            : mType(type),
+              mCurDiscontinuitySeq(0),
+              mLastDequeuedTimeUs(0),
+              mLastSampleDurationUs(0) {}
         AString uriKey() {
             AString key(mType);
             key.append("URI");
@@ -147,7 +158,7 @@
     AString mMasterURL;
 
     Vector<BandwidthItem> mBandwidthItems;
-    ssize_t mPrevBandwidthIndex;
+    ssize_t mCurBandwidthIndex;
 
     sp<M3UParser> mPlaylist;
 
@@ -163,6 +174,7 @@
     // we use this to track reconfiguration progress.
     uint32_t mSwapMask;
 
+    KeyedVector<StreamType, sp<AnotherPacketSource> > mDiscontinuities;
     KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources;
     // A second set of packet sources that buffer content for the variant we're switching to.
     KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2;
@@ -187,6 +199,12 @@
     uint32_t mDisconnectReplyID;
     uint32_t mSeekReplyID;
 
+    bool mFirstTimeUsValid;
+    int64_t mFirstTimeUs;
+    int64_t mLastSeekTimeUs;
+    KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
+    KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
+
     sp<PlaylistFetcher> addFetcher(const char *uri);
 
     void onConnect(const sp<AMessage> &msg);
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index efd852c..1651dee 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -157,8 +157,8 @@
 }
 
 status_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) {
-    if (mType != TYPE_SUBS) {
-        ALOGE("only select subtitile tracks for now!");
+    if (mType != TYPE_SUBS && mType != TYPE_AUDIO) {
+        ALOGE("only select subtitile/audio tracks for now!");
         return INVALID_OPERATION;
     }
 
@@ -246,6 +246,7 @@
       mIsVariantPlaylist(false),
       mIsComplete(false),
       mIsEvent(false),
+      mDiscontinuitySeq(0),
       mSelectedIndex(-1) {
     mInitCheck = parse(data, size);
 }
@@ -273,6 +274,10 @@
     return mIsEvent;
 }
 
+size_t M3UParser::getDiscontinuitySeq() const {
+    return mDiscontinuitySeq;
+}
+
 sp<AMessage> M3UParser::meta() {
     return mMeta;
 }
@@ -567,6 +572,12 @@
                 }
             } else if (line.startsWith("#EXT-X-MEDIA")) {
                 err = parseMedia(line);
+            } else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {
+                size_t seq;
+                err = parseDiscontinuitySequence(line, &seq);
+                if (err == OK) {
+                    mDiscontinuitySeq = seq;
+                }
             }
 
             if (err != OK) {
@@ -1110,6 +1121,30 @@
 }
 
 // static
+status_t M3UParser::parseDiscontinuitySequence(const AString &line, size_t *seq) {
+    ssize_t colonPos = line.find(":");
+
+    if (colonPos < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    int32_t x;
+    status_t err = ParseInt32(line.c_str() + colonPos + 1, &x);
+    if (err != OK) {
+        return err;
+    }
+
+    if (x < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (seq) {
+        *seq = x;
+    }
+    return OK;
+}
+
+// static
 status_t M3UParser::ParseInt32(const char *s, int32_t *x) {
     char *end;
     long lval = strtol(s, &end, 10);
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index fe9fb9d..d588afe 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -34,6 +34,7 @@
     bool isVariantPlaylist() const;
     bool isComplete() const;
     bool isEvent() const;
+    size_t getDiscontinuitySeq() const;
 
     sp<AMessage> meta();
 
@@ -66,6 +67,7 @@
     bool mIsVariantPlaylist;
     bool mIsComplete;
     bool mIsEvent;
+    size_t mDiscontinuitySeq;
 
     sp<AMessage> mMeta;
     Vector<Item> mItems;
@@ -94,6 +96,8 @@
 
     status_t parseMedia(const AString &line);
 
+    static status_t parseDiscontinuitySequence(const AString &line, size_t *seq);
+
     static status_t ParseInt32(const char *s, int32_t *x);
     static status_t ParseDouble(const char *s, double *x);
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 10437c9..80cb2d0 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -49,7 +49,7 @@
 // static
 const int64_t PlaylistFetcher::kMinBufferedDurationUs = 10000000ll;
 const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll;
-const int32_t PlaylistFetcher::kDownloadBlockSize = 192;
+const int32_t PlaylistFetcher::kDownloadBlockSize = 2048;
 const int32_t PlaylistFetcher::kNumSkipFrames = 10;
 
 PlaylistFetcher::PlaylistFetcher(
@@ -62,19 +62,21 @@
       mURI(uri),
       mStreamTypeMask(0),
       mStartTimeUs(-1ll),
-      mMinStartTimeUs(0ll),
-      mStopParams(NULL),
+      mSegmentStartTimeUs(-1ll),
+      mDiscontinuitySeq(-1ll),
+      mStartTimeUsRelative(false),
       mLastPlaylistFetchTimeUs(-1ll),
       mSeqNumber(-1),
       mNumRetries(0),
       mStartup(true),
+      mAdaptive(false),
       mPrepared(false),
-      mSkipToFirstIDRAfterConnect(false),
       mNextPTSTimeUs(-1ll),
       mMonitorQueueGeneration(0),
       mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
       mFirstPTSValid(false),
-      mAbsoluteTimeAnchorUs(0ll) {
+      mAbsoluteTimeAnchorUs(0ll),
+      mVideoBuffer(new AnotherPacketSource(NULL)) {
     memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
     mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
     mStartTimeUsNotify->setInt32("streamMask", 0);
@@ -335,8 +337,9 @@
         const sp<AnotherPacketSource> &videoSource,
         const sp<AnotherPacketSource> &subtitleSource,
         int64_t startTimeUs,
-        int64_t minStartTimeUs,
-        int32_t startSeqNumberHint) {
+        int64_t segmentStartTimeUs,
+        int32_t startDiscontinuitySeq,
+        bool adaptive) {
     sp<AMessage> msg = new AMessage(kWhatStart, id());
 
     uint32_t streamTypeMask = 0ul;
@@ -358,8 +361,9 @@
 
     msg->setInt32("streamTypeMask", streamTypeMask);
     msg->setInt64("startTimeUs", startTimeUs);
-    msg->setInt64("minStartTimeUs", minStartTimeUs);
-    msg->setInt32("startSeqNumberHint", startSeqNumberHint);
+    msg->setInt64("segmentStartTimeUs", segmentStartTimeUs);
+    msg->setInt32("startDiscontinuitySeq", startDiscontinuitySeq);
+    msg->setInt32("adaptive", adaptive);
     msg->post();
 }
 
@@ -367,9 +371,9 @@
     (new AMessage(kWhatPause, id()))->post();
 }
 
-void PlaylistFetcher::stopAsync(bool selfTriggered) {
+void PlaylistFetcher::stopAsync(bool clear) {
     sp<AMessage> msg = new AMessage(kWhatStop, id());
-    msg->setInt32("selfTriggered", selfTriggered);
+    msg->setInt32("clear", clear);
     msg->post();
 }
 
@@ -449,10 +453,13 @@
     CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask));
 
     int64_t startTimeUs;
-    int32_t startSeqNumberHint;
+    int64_t segmentStartTimeUs;
+    int32_t startDiscontinuitySeq;
+    int32_t adaptive;
     CHECK(msg->findInt64("startTimeUs", &startTimeUs));
-    CHECK(msg->findInt64("minStartTimeUs", (int64_t *) &mMinStartTimeUs));
-    CHECK(msg->findInt32("startSeqNumberHint", &startSeqNumberHint));
+    CHECK(msg->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
+    CHECK(msg->findInt32("startDiscontinuitySeq", &startDiscontinuitySeq));
+    CHECK(msg->findInt32("adaptive", &adaptive));
 
     if (streamTypeMask & LiveSession::STREAMTYPE_AUDIO) {
         void *ptr;
@@ -482,16 +489,16 @@
     }
 
     mStreamTypeMask = streamTypeMask;
+
     mStartTimeUs = startTimeUs;
+    mSegmentStartTimeUs = segmentStartTimeUs;
+    mDiscontinuitySeq = startDiscontinuitySeq;
 
     if (mStartTimeUs >= 0ll) {
         mSeqNumber = -1;
         mStartup = true;
         mPrepared = false;
-    }
-
-    if (startSeqNumberHint >= 0) {
-        mSeqNumber = startSeqNumberHint;
+        mAdaptive = adaptive;
     }
 
     postMonitorQueue();
@@ -506,11 +513,9 @@
 void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
     cancelMonitorQueue();
 
-    int32_t selfTriggered;
-    CHECK(msg->findInt32("selfTriggered", &selfTriggered));
-    if (!selfTriggered) {
-        // Self triggered stops only happen during switching, in which case we do not want
-        // to clear the discontinuities queued at the end of packet sources.
+    int32_t clear;
+    CHECK(msg->findInt32("clear", &clear));
+    if (clear) {
         for (size_t i = 0; i < mPacketSources.size(); i++) {
             sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
             packetSource->clear();
@@ -552,15 +557,16 @@
         }
 
         // Don't resume if we would stop within a resume threshold.
+        int32_t discontinuitySeq;
         int64_t latestTimeUs = 0, stopTimeUs = 0;
-        sp<AMessage> latestMeta = packetSource->getLatestMeta();
+        sp<AMessage> latestMeta = packetSource->getLatestDequeuedMeta();
         if (latestMeta != NULL
-                && (latestMeta->findInt64("timeUs", &latestTimeUs)
-                && params->findInt64(stopKey, &stopTimeUs))) {
-            int64_t diffUs = stopTimeUs - latestTimeUs;
-            if (diffUs < resumeThreshold(latestMeta)) {
-                stop = true;
-            }
+                && latestMeta->findInt32("discontinuitySeq", &discontinuitySeq)
+                && discontinuitySeq == mDiscontinuitySeq
+                && latestMeta->findInt64("timeUs", &latestTimeUs)
+                && params->findInt64(stopKey, &stopTimeUs)
+                && stopTimeUs - latestTimeUs < resumeThreshold(latestMeta)) {
+            stop = true;
         }
     }
 
@@ -568,7 +574,7 @@
         for (size_t i = 0; i < mPacketSources.size(); i++) {
             mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer());
         }
-        stopAsync(/* selfTriggered = */ true);
+        stopAsync(/* clear = */ false);
         return OK;
     }
 
@@ -737,26 +743,47 @@
         mSeqNumber = lastSeqNumberInPlaylist;
     }
 
+    if (mDiscontinuitySeq < 0) {
+        mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
+    }
+
     if (mSeqNumber < 0) {
         CHECK_GE(mStartTimeUs, 0ll);
 
-        if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
-            mSeqNumber = getSeqNumberForTime(mStartTimeUs);
+        if (mSegmentStartTimeUs < 0) {
+            if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) {
+                // If this is a live session, start 3 segments from the end on connect
+                mSeqNumber = lastSeqNumberInPlaylist - 3;
+            } else {
+                mSeqNumber = getSeqNumberForTime(mStartTimeUs);
+                mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber);
+            }
+            mStartTimeUsRelative = true;
             ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)",
                     mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
                     lastSeqNumberInPlaylist);
         } else {
-            // If this is a live session, start 3 segments from the end.
-            mSeqNumber = lastSeqNumberInPlaylist - 3;
+            mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
+            if (mAdaptive) {
+                // avoid double fetch/decode
+                mSeqNumber += 1;
+            }
+            ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq);
+            if (mSeqNumber < minSeq) {
+                mSeqNumber = minSeq;
+            }
+
             if (mSeqNumber < firstSeqNumberInPlaylist) {
                 mSeqNumber = firstSeqNumberInPlaylist;
             }
+
+            if (mSeqNumber > lastSeqNumberInPlaylist) {
+                mSeqNumber = lastSeqNumberInPlaylist;
+            }
             ALOGV("Initial sequence number for live event %d from (%d .. %d)",
                     mSeqNumber, firstSeqNumberInPlaylist,
                     lastSeqNumberInPlaylist);
         }
-
-        mStartTimeUs = -1ll;
     }
 
     if (mSeqNumber < firstSeqNumberInPlaylist
@@ -819,6 +846,7 @@
 
     int32_t val;
     if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
+        mDiscontinuitySeq++;
         discontinuity = true;
     }
 
@@ -850,6 +878,7 @@
     }
 
     // block-wise download
+    bool startup = mStartup;
     ssize_t bytesRead;
     do {
         bytesRead = mSession->fetchFile(
@@ -879,7 +908,7 @@
             return;
         }
 
-        if (mStartup || discontinuity) {
+        if (startup || discontinuity) {
             // Signal discontinuity.
 
             if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
@@ -898,6 +927,8 @@
 
                 discontinuity = false;
             }
+
+            startup = false;
         }
 
         err = OK;
@@ -917,24 +948,19 @@
         }
 
         if (err == -EAGAIN) {
-            // bad starting sequence number hint
+            // starting sequence number too low
             mTSParser.clear();
             postMonitorQueue();
             return;
-        }
-
-        if (err == ERROR_OUT_OF_RANGE) {
+        } else if (err == ERROR_OUT_OF_RANGE) {
             // reached stopping point
-            stopAsync(/* selfTriggered = */ true);
+            stopAsync(/* clear = */ false);
             return;
-        }
-
-        if (err != OK) {
+        } else if (err != OK) {
             notifyError(err);
             return;
         }
 
-        mStartup = false;
     } while (bytesRead != 0);
 
     if (bufferStartsWithTsSyncByte(buffer)) {
@@ -994,11 +1020,44 @@
         return;
     }
 
+    mStartup = false;
     ++mSeqNumber;
 
     postMonitorQueue();
 }
 
+int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
+    int32_t firstSeqNumberInPlaylist;
+    if (mPlaylist->meta() == NULL
+            || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
+        firstSeqNumberInPlaylist = 0;
+    }
+
+    size_t curDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
+    if (discontinuitySeq < curDiscontinuitySeq) {
+        return firstSeqNumberInPlaylist <= 0 ? 0 : (firstSeqNumberInPlaylist - 1);
+    }
+
+    size_t index = 0;
+    while (index < mPlaylist->size()) {
+        sp<AMessage> itemMeta;
+        CHECK(mPlaylist->itemAt( index, NULL /* uri */, &itemMeta));
+
+        int64_t discontinuity;
+        if (itemMeta->findInt64("discontinuity", &discontinuity)) {
+            curDiscontinuitySeq++;
+        }
+
+        if (curDiscontinuitySeq == discontinuitySeq) {
+            return firstSeqNumberInPlaylist + index;
+        }
+
+        ++index;
+    }
+
+    return firstSeqNumberInPlaylist + mPlaylist->size();
+}
+
 int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const {
     int32_t firstSeqNumberInPlaylist;
     if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
@@ -1031,6 +1090,23 @@
     return firstSeqNumberInPlaylist + index;
 }
 
+const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
+        const sp<ABuffer> &accessUnit, const sp<AnotherPacketSource> &source, bool discard) {
+    sp<MetaData> format = source->getFormat();
+    if (format != NULL) {
+        // for simplicity, store a reference to the format in each unit
+        accessUnit->meta()->setObject("format", format);
+    }
+
+    if (discard) {
+        accessUnit->meta()->setInt32("discard", discard);
+    }
+
+    accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
+    accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+    return accessUnit;
+}
+
 status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer) {
     if (mTSParser == NULL) {
         // Use TS_TIMESTAMPS_ARE_ABSOLUTE so pts carry over between fetchers.
@@ -1046,7 +1122,9 @@
         mTSParser->signalDiscontinuity(
                 ATSParser::DISCONTINUITY_SEEK, extra);
 
+        mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
         mNextPTSTimeUs = -1ll;
+        mFirstPTSValid = false;
     }
 
     size_t offset = 0;
@@ -1099,46 +1177,30 @@
             continue;
         }
 
-        if (stream == LiveSession::STREAMTYPE_VIDEO && mVideoMime.empty()) {
-            const char *mime;
-            if (source->getFormat()->findCString(kKeyMIMEType, &mime)) {
-                mVideoMime.setTo(mime);
-                if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-                    mSkipToFirstIDRAfterConnect = true;
-                }
-            }
-        }
-
         int64_t timeUs;
         sp<ABuffer> accessUnit;
         status_t finalResult;
         while (source->hasBufferAvailable(&finalResult)
                 && source->dequeueAccessUnit(&accessUnit) == OK) {
 
-            if (stream == LiveSession::STREAMTYPE_VIDEO && mSkipToFirstIDRAfterConnect) {
-                if (!IsIDR(accessUnit)) {
-                    continue;
-                } else {
-                    mSkipToFirstIDRAfterConnect = false;
-                }
-            }
-
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-            if (mMinStartTimeUs > 0) {
-                if (timeUs < mMinStartTimeUs) {
-                    // TODO untested path
-                    // try a later ts
-                    int32_t targetDuration;
-                    mPlaylist->meta()->findInt32("target-duration", &targetDuration);
-                    int32_t incr = (mMinStartTimeUs - timeUs) / 1000000 / targetDuration;
-                    if (incr == 0) {
-                        // increment mSeqNumber by at least one
-                        incr = 1;
+
+            if (mStartup) {
+                if (!mFirstPTSValid) {
+                    mFirstTimeUs = timeUs;
+                    mFirstPTSValid = true;
+                }
+                if (mStartTimeUsRelative) {
+                    timeUs -= mFirstTimeUs;
+                    if (timeUs < 0) {
+                        timeUs = 0;
                     }
-                    mSeqNumber += incr;
-                    err = -EAGAIN;
-                    break;
-                } else {
+                } else if (mAdaptive && timeUs > mStartTimeUs) {
+                    int32_t seq;
+                    if (mStartTimeUsNotify != NULL
+                            && !mStartTimeUsNotify->findInt32("discontinuitySeq", &seq)) {
+                        mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq);
+                    }
                     int64_t startTimeUs;
                     if (mStartTimeUsNotify != NULL
                             && !mStartTimeUsNotify->findInt64(key, &startTimeUs)) {
@@ -1155,12 +1217,51 @@
                         }
                     }
                 }
+
+                if (timeUs < mStartTimeUs) {
+                    if (mAdaptive) {
+                        int32_t targetDuration;
+                        mPlaylist->meta()->findInt32("target-duration", &targetDuration);
+                        int32_t incr = (mStartTimeUs - timeUs) / 1000000 / targetDuration;
+                        if (incr == 0) {
+                            // increment mSeqNumber by at least one
+                            incr = 1;
+                        }
+                        mSeqNumber += incr;
+                        err = -EAGAIN;
+                        break;
+                    } else {
+                        // buffer up to the closest preceding IDR frame
+                        ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
+                                timeUs, mStartTimeUs);
+                        const char *mime;
+                        sp<MetaData> format  = source->getFormat();
+                        bool isAvc = false;
+                        if (format != NULL && format->findCString(kKeyMIMEType, &mime)
+                                && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+                            isAvc = true;
+                        }
+                        if (isAvc && IsIDR(accessUnit)) {
+                            mVideoBuffer->clear();
+                        }
+                        if (isAvc) {
+                            mVideoBuffer->queueAccessUnit(accessUnit);
+                        }
+
+                        continue;
+                    }
+                }
             }
 
             if (mStopParams != NULL) {
                 // Queue discontinuity in original stream.
+                int32_t discontinuitySeq;
                 int64_t stopTimeUs;
-                if (!mStopParams->findInt64(key, &stopTimeUs) || timeUs >= stopTimeUs) {
+                if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
+                        || discontinuitySeq > mDiscontinuitySeq
+                        || !mStopParams->findInt64(key, &stopTimeUs)
+                        || (discontinuitySeq == mDiscontinuitySeq
+                                && timeUs >= stopTimeUs)) {
                     packetSource->queueAccessUnit(mSession->createFormatChangeBuffer());
                     mStreamTypeMask &= ~stream;
                     mPacketSources.removeItemsAt(i);
@@ -1169,15 +1270,18 @@
             }
 
             // Note that we do NOT dequeue any discontinuities except for format change.
-
-            // for simplicity, store a reference to the format in each unit
-            sp<MetaData> format = source->getFormat();
-            if (format != NULL) {
-                accessUnit->meta()->setObject("format", format);
+            if (stream == LiveSession::STREAMTYPE_VIDEO) {
+                const bool discard = true;
+                status_t status;
+                while (mVideoBuffer->hasBufferAvailable(&status)) {
+                    sp<ABuffer> videoBuffer;
+                    mVideoBuffer->dequeueAccessUnit(&videoBuffer);
+                    setAccessUnitProperties(videoBuffer, source, discard);
+                    packetSource->queueAccessUnit(videoBuffer);
+                }
             }
 
-            // Stash the sequence number so we can hint future playlist where to start at.
-            accessUnit->meta()->setInt32("seq", mSeqNumber);
+            setAccessUnitProperties(accessUnit, source);
             packetSource->queueAccessUnit(accessUnit);
         }
 
@@ -1244,7 +1348,8 @@
         CHECK(itemMeta->findInt64("durationUs", &durationUs));
         buffer->meta()->setInt64("timeUs", getSegmentStartTimeUs(mSeqNumber));
         buffer->meta()->setInt64("durationUs", durationUs);
-        buffer->meta()->setInt32("seq", mSeqNumber);
+        buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+        buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
 
         packetSource->queueAccessUnit(buffer);
         return OK;
@@ -1310,14 +1415,6 @@
         firstID3Tag = false;
     }
 
-    if (!mFirstPTSValid) {
-        mFirstPTSValid = true;
-        mFirstPTS = PTS;
-    }
-    PTS -= mFirstPTS;
-
-    int64_t timeUs = (PTS * 100ll) / 9ll + mAbsoluteTimeAnchorUs;
-
     if (mStreamTypeMask != LiveSession::STREAMTYPE_AUDIO) {
         ALOGW("This stream only contains audio data!");
 
@@ -1360,6 +1457,12 @@
     int32_t sampleRate;
     CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
 
+    int64_t timeUs = (PTS * 100ll) / 9ll;
+    if (!mFirstPTSValid) {
+        mFirstPTSValid = true;
+        mFirstTimeUs = timeUs;
+    }
+
     size_t offset = 0;
     while (offset < buffer->size()) {
         const uint8_t *adtsHeader = buffer->data() + offset;
@@ -1384,19 +1487,32 @@
 
         CHECK_LE(offset + aac_frame_length, buffer->size());
 
-        sp<ABuffer> unit = new ABuffer(aac_frame_length);
-        memcpy(unit->data(), adtsHeader, aac_frame_length);
-
         int64_t unitTimeUs = timeUs + numSamples * 1000000ll / sampleRate;
-        unit->meta()->setInt64("timeUs", unitTimeUs);
+        offset += aac_frame_length;
 
         // Each AAC frame encodes 1024 samples.
         numSamples += 1024;
 
-        unit->meta()->setInt32("seq", mSeqNumber);
-        packetSource->queueAccessUnit(unit);
+        if (mStartup) {
+            int64_t startTimeUs = unitTimeUs;
+            if (mStartTimeUsRelative) {
+                startTimeUs -= mFirstTimeUs;
+                if (startTimeUs  < 0) {
+                    startTimeUs = 0;
+                }
+            }
+            if (startTimeUs < mStartTimeUs) {
+                continue;
+            }
+        }
 
-        offset += aac_frame_length;
+        sp<ABuffer> unit = new ABuffer(aac_frame_length);
+        memcpy(unit->data(), adtsHeader, aac_frame_length);
+
+        unit->meta()->setInt64("timeUs", unitTimeUs);
+        unit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+        unit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
+        packetSource->queueAccessUnit(unit);
     }
 
     return OK;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index e4fdbff..daefb26 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -57,13 +57,15 @@
             const sp<AnotherPacketSource> &audioSource,
             const sp<AnotherPacketSource> &videoSource,
             const sp<AnotherPacketSource> &subtitleSource,
-            int64_t startTimeUs = -1ll,
-            int64_t minStartTimeUs = 0ll /* start after this timestamp */,
-            int32_t startSeqNumberHint = -1 /* try starting at this sequence number */);
+            int64_t startTimeUs = -1ll,         // starting timestamps
+            int64_t segmentStartTimeUs = -1ll, // starting position within playlist
+            // startTimeUs!=segmentStartTimeUs only when playlist is live
+            int32_t startDiscontinuitySeq = 0,
+            bool adaptive = false);
 
     void pauseAsync();
 
-    void stopAsync(bool selfTriggered = false);
+    void stopAsync(bool clear = true);
 
     void resumeUntilAsync(const sp<AMessage> &params);
 
@@ -99,11 +101,12 @@
 
     sp<LiveSession> mSession;
     AString mURI;
-    AString mVideoMime;
 
     uint32_t mStreamTypeMask;
     int64_t mStartTimeUs;
-    int64_t mMinStartTimeUs; // start fetching no earlier than this value
+    int64_t mSegmentStartTimeUs;
+    ssize_t mDiscontinuitySeq;
+    bool mStartTimeUsRelative;
     sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
 
     KeyedVector<LiveSession::StreamType, sp<AnotherPacketSource> >
@@ -116,8 +119,8 @@
     int32_t mSeqNumber;
     int32_t mNumRetries;
     bool mStartup;
+    bool mAdaptive;
     bool mPrepared;
-    bool mSkipToFirstIDRAfterConnect;
     int64_t mNextPTSTimeUs;
 
     int32_t mMonitorQueueGeneration;
@@ -136,7 +139,9 @@
 
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
+    int64_t mFirstTimeUs;
     int64_t mAbsoluteTimeAnchorUs;
+    sp<AnotherPacketSource> mVideoBuffer;
 
     // Stores the initialization vector to decrypt the next block of cipher text, which can
     // either be derived from the sequence number, read from the manifest, or copied from
@@ -175,6 +180,10 @@
     // Resume a fetcher to continue until the stopping point stored in msg.
     status_t onResumeUntil(const sp<AMessage> &msg);
 
+    const sp<ABuffer> &setAccessUnitProperties(
+            const sp<ABuffer> &accessUnit,
+            const sp<AnotherPacketSource> &source,
+            bool discard = false);
     status_t extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer);
 
     status_t extractAndQueueAccessUnits(
@@ -185,6 +194,8 @@
     void queueDiscontinuity(
             ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
 
+    int32_t getSeqNumberWithAnchorTime(int64_t anchorTimeUs) const;
+    int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const;
     int32_t getSeqNumberForTime(int64_t timeUs) const;
 
     void updateDuration();
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index eda6387..6d8866a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -894,6 +894,12 @@
                 ALOGV("Stream PID 0x%08x of type 0x%02x now has data.",
                      mElementaryPID, mStreamType);
 
+                const char *mime;
+                if (meta->findCString(kKeyMIMEType, &mime)
+                        && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
+                        && !IsIDR(accessUnit)) {
+                    continue;
+                }
                 mSource = new AnotherPacketSource(meta);
                 mSource->queueAccessUnit(accessUnit);
             }
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 72c9dae..010063f 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AnotherPacketSource"
+
 #include "AnotherPacketSource.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -38,7 +41,8 @@
       mFormat(NULL),
       mLastQueuedTimeUs(0),
       mEOSResult(OK),
-      mLatestEnqueuedMeta(NULL) {
+      mLatestEnqueuedMeta(NULL),
+      mLatestDequeuedMeta(NULL) {
     setFormat(meta);
 }
 
@@ -92,7 +96,7 @@
 
         sp<RefBase> object;
         if (buffer->meta()->findObject("format", &object)) {
-            return static_cast<MetaData*>(object.get());
+            return mFormat = static_cast<MetaData*>(object.get());
         }
 
         ++it;
@@ -121,6 +125,8 @@
             return INFO_DISCONTINUITY;
         }
 
+        mLatestDequeuedMeta = (*buffer)->meta()->dup();
+
         sp<RefBase> object;
         if ((*buffer)->meta()->findObject("format", &object)) {
             mFormat = static_cast<MetaData*>(object.get());
@@ -142,8 +148,10 @@
     }
 
     if (!mBuffers.empty()) {
+
         const sp<ABuffer> buffer = *mBuffers.begin();
         mBuffers.erase(mBuffers.begin());
+        mLatestDequeuedMeta = buffer->meta()->dup();
 
         int32_t discontinuity;
         if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
@@ -202,7 +210,7 @@
     mBuffers.push_back(buffer);
     mCondition.signal();
 
-    if (!mLatestEnqueuedMeta.get()) {
+    if (mLatestEnqueuedMeta == NULL) {
         mLatestEnqueuedMeta = buffer->meta();
     } else {
         int64_t latestTimeUs = 0;
@@ -341,9 +349,14 @@
     return (mEOSResult != OK);
 }
 
-sp<AMessage> AnotherPacketSource::getLatestMeta() {
+sp<AMessage> AnotherPacketSource::getLatestEnqueuedMeta() {
     Mutex::Autolock autoLock(mLock);
     return mLatestEnqueuedMeta;
 }
 
+sp<AMessage> AnotherPacketSource::getLatestDequeuedMeta() {
+    Mutex::Autolock autoLock(mLock);
+    return mLatestDequeuedMeta;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index f38f9dc..0c717d7 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -64,7 +64,8 @@
 
     bool isFinished(int64_t duration) const;
 
-    sp<AMessage> getLatestMeta();
+    sp<AMessage> getLatestEnqueuedMeta();
+    sp<AMessage> getLatestDequeuedMeta();
 
 protected:
     virtual ~AnotherPacketSource();
@@ -80,6 +81,7 @@
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
     sp<AMessage> mLatestEnqueuedMeta;
+    sp<AMessage> mLatestDequeuedMeta;
 
     bool wasFormatChange(int32_t discontinuityType) const;