Merge "Remove check if the target video resolution is not supported by CameraSource and clean up Camera is CameraSource could not created."
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 07e506a..7ba5291 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -31,7 +31,7 @@
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include "include/ARTSPController.h"
-#include "include/LiveSource.h"
+#include "include/LiveSession.h"
 #include "include/NuCachedSource2.h"
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
@@ -560,6 +560,7 @@
 
     sp<ALooper> looper;
     sp<ARTSPController> rtspController;
+    sp<LiveSession> liveSession;
 
     int res;
     while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) {
@@ -852,8 +853,15 @@
                 String8 uri("http://");
                 uri.append(filename + 11);
 
-                dataSource = new LiveSource(uri.string());
-                dataSource = new NuCachedSource2(dataSource);
+                if (looper == NULL) {
+                    looper = new ALooper;
+                    looper->start();
+                }
+                liveSession = new LiveSession;
+                looper->registerHandler(liveSession);
+
+                liveSession->connect(uri.string());
+                dataSource = liveSession->getDataSource();
 
                 extractor =
                     MediaExtractor::Create(
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8ebbe6c..2743a3a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -22,7 +22,6 @@
 
 #include "include/ARTSPController.h"
 #include "include/AwesomePlayer.h"
-#include "include/LiveSource.h"
 #include "include/SoftwareRenderer.h"
 #include "include/NuCachedSource2.h"
 #include "include/ThrottledSource.h"
@@ -51,6 +50,7 @@
 #include <surfaceflinger/Surface.h>
 
 #include <media/stagefright/foundation/ALooper.h>
+#include "include/LiveSession.h"
 
 #define USE_SURFACE_ALLOC 1
 
@@ -634,6 +634,11 @@
         mRTSPController.clear();
     }
 
+    if (mLiveSession != NULL) {
+        mLiveSession->disconnect();
+        mLiveSession.clear();
+    }
+
     mRTPPusher.clear();
     mRTCPPusher.clear();
     mRTPSession.clear();
@@ -1659,16 +1664,23 @@
         String8 uri("http://");
         uri.append(mUri.string() + 11);
 
-        sp<LiveSource> liveSource = new LiveSource(uri.string());
+        if (mLooper == NULL) {
+            mLooper = new ALooper;
+            mLooper->setName("httplive");
+            mLooper->start();
+        }
 
-        mCachedSource = new NuCachedSource2(liveSource);
-        dataSource = mCachedSource;
+        mLiveSession = new LiveSession;
+        mLooper->registerHandler(mLiveSession);
+
+        mLiveSession->connect(uri.string());
+        dataSource = mLiveSession->getDataSource();
 
         sp<MediaExtractor> extractor =
             MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
         static_cast<MPEG2TSExtractor *>(extractor.get())
-            ->setLiveSource(liveSource);
+            ->setLiveSession(mLiveSession);
 
         return setDataSource_l(extractor);
     } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 829ab20..110fb03 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -179,8 +179,7 @@
       mFinalStatus(OK),
       mLastAccessPos(0),
       mFetching(true),
-      mLastFetchTimeUs(-1),
-      mSuspended(false) {
+      mLastFetchTimeUs(-1) {
     mLooper->setName("NuCachedSource2");
     mLooper->registerHandler(mReflector);
     mLooper->start();
@@ -223,12 +222,6 @@
             break;
         }
 
-        case kWhatSuspend:
-        {
-            onSuspend();
-            break;
-        }
-
         default:
             TRESPASS();
     }
@@ -270,7 +263,6 @@
 
     bool keepAlive =
         !mFetching
-            && !mSuspended
             && mFinalStatus == OK
             && ALooper::GetNowUs() >= mLastFetchTimeUs + kKeepAliveIntervalUs;
 
@@ -287,7 +279,7 @@
             LOGI("Cache full, done prefetching for now");
             mFetching = false;
         }
-    } else if (!mSuspended) {
+    } else {
         Mutex::Autolock autoLock(mLock);
         restartPrefetcherIfNecessary_l();
     }
@@ -478,40 +470,6 @@
     return OK;
 }
 
-void NuCachedSource2::clearCacheAndResume() {
-    LOGV("clearCacheAndResume");
-
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(mSuspended);
-
-    mCacheOffset = 0;
-    mFinalStatus = OK;
-    mLastAccessPos = 0;
-    mLastFetchTimeUs = -1;
-
-    size_t totalSize = mCache->totalSize();
-    CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
-
-    mFetching = true;
-    mSuspended = false;
-}
-
-void NuCachedSource2::suspend() {
-    (new AMessage(kWhatSuspend, mReflector->id()))->post();
-
-    while (!mSuspended) {
-        usleep(10000);
-    }
-}
-
-void NuCachedSource2::onSuspend() {
-    Mutex::Autolock autoLock(mLock);
-
-    mFetching = false;
-    mSuspended = true;
-}
-
 void NuCachedSource2::resumeFetchingIfNecessary() {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 269b233..4ce7265 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -456,7 +456,7 @@
 bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mNumBandwidthHistoryItems < 10) {
+    if (mNumBandwidthHistoryItems < 2) {
         return false;
     }
 
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 3aabf5f..9225e41 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -2,9 +2,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=       \
-        LiveSource.cpp  \
-        M3UParser.cpp   \
+LOCAL_SRC_FILES:=               \
+        LiveDataSource.cpp      \
+        LiveSession.cpp         \
+        M3UParser.cpp           \
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/httplive/LiveDataSource.cpp b/media/libstagefright/httplive/LiveDataSource.cpp
new file mode 100644
index 0000000..25e2902
--- /dev/null
+++ b/media/libstagefright/httplive/LiveDataSource.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "LiveDataSource"
+#include <utils/Log.h>
+
+#include "LiveDataSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#define SAVE_BACKUP     0
+
+namespace android {
+
+LiveDataSource::LiveDataSource()
+    : mOffset(0),
+      mFinalResult(OK),
+      mBackupFile(NULL) {
+#if SAVE_BACKUP
+    mBackupFile = fopen("/data/misc/backup.ts", "wb");
+    CHECK(mBackupFile != NULL);
+#endif
+}
+
+LiveDataSource::~LiveDataSource() {
+    if (mBackupFile != NULL) {
+        fclose(mBackupFile);
+        mBackupFile = NULL;
+    }
+}
+
+status_t LiveDataSource::initCheck() const {
+    return OK;
+}
+
+size_t LiveDataSource::countQueuedBuffers() {
+    Mutex::Autolock autoLock(mLock);
+
+    return mBufferQueue.size();
+}
+
+ssize_t LiveDataSource::readAt(off64_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset != mOffset) {
+        LOGE("Attempt at reading non-sequentially from LiveDataSource.");
+        return -EPIPE;
+    }
+
+    size_t sizeDone = 0;
+
+    while (sizeDone < size) {
+        while (mBufferQueue.empty() && mFinalResult == OK) {
+            mCondition.wait(mLock);
+        }
+
+        if (mBufferQueue.empty()) {
+            if (sizeDone > 0) {
+                mOffset += sizeDone;
+                return sizeDone;
+            }
+
+            return mFinalResult;
+        }
+
+        sp<ABuffer> buffer = *mBufferQueue.begin();
+
+        size_t copy = size - sizeDone;
+
+        if (copy > buffer->size()) {
+            copy = buffer->size();
+        }
+
+        memcpy((uint8_t *)data + sizeDone, buffer->data(), copy);
+
+        sizeDone += copy;
+
+        buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
+
+        if (buffer->size() == 0) {
+            mBufferQueue.erase(mBufferQueue.begin());
+        }
+    }
+
+    mOffset += sizeDone;
+
+    return sizeDone;
+}
+
+void LiveDataSource::queueBuffer(const sp<ABuffer> &buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFinalResult != OK) {
+        return;
+    }
+
+#if SAVE_BACKUP
+    if (mBackupFile != NULL) {
+        CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mBackupFile),
+                 buffer->size());
+    }
+#endif
+
+    mBufferQueue.push_back(buffer);
+    mCondition.broadcast();
+}
+
+void LiveDataSource::queueEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    Mutex::Autolock autoLock(mLock);
+
+    mFinalResult = finalResult;
+    mCondition.broadcast();
+}
+
+void LiveDataSource::reset() {
+    Mutex::Autolock autoLock(mLock);
+
+    // XXX FIXME: If we've done a partial read and waiting for more buffers,
+    // we'll mix old and new data...
+
+    mFinalResult = OK;
+    mBufferQueue.clear();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/httplive/LiveDataSource.h b/media/libstagefright/httplive/LiveDataSource.h
new file mode 100644
index 0000000..a489ec6
--- /dev/null
+++ b/media/libstagefright/httplive/LiveDataSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIVE_DATA_SOURCE_H_
+
+#define LIVE_DATA_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/DataSource.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct LiveDataSource : public DataSource {
+    LiveDataSource();
+
+    virtual status_t initCheck() const;
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+    void queueBuffer(const sp<ABuffer> &buffer);
+    void queueEOS(status_t finalResult);
+    void reset();
+
+    size_t countQueuedBuffers();
+
+protected:
+    virtual ~LiveDataSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    off64_t mOffset;
+    List<sp<ABuffer> > mBufferQueue;
+    status_t mFinalResult;
+
+    FILE *mBackupFile;
+
+    DISALLOW_EVIL_CONSTRUCTORS(LiveDataSource);
+};
+
+}  // namespace android
+
+#endif  // LIVE_DATA_SOURCE_H_
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
new file mode 100644
index 0000000..62567be
--- /dev/null
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "LiveSession"
+#include <utils/Log.h>
+
+#include "include/LiveSession.h"
+
+#include "LiveDataSource.h"
+
+#include "include/M3UParser.h"
+#include "include/NuHTTPDataSource.h"
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
+#include <openssl/aes.h>
+
+namespace android {
+
+const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;
+
+LiveSession::LiveSession()
+    : mDataSource(new LiveDataSource),
+      mHTTPDataSource(new NuHTTPDataSource),
+      mPrevBandwidthIndex(-1),
+      mLastPlaylistFetchTimeUs(-1),
+      mSeqNumber(-1),
+      mSeekTimeUs(-1),
+      mNumRetries(0),
+      mDurationUs(-1),
+      mSeekDone(false),
+      mMonitorQueueGeneration(0) {
+}
+
+LiveSession::~LiveSession() {
+}
+
+sp<DataSource> LiveSession::getDataSource() {
+    return mDataSource;
+}
+
+void LiveSession::connect(const char *url) {
+    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    msg->setString("url", url);
+    msg->post();
+}
+
+void LiveSession::disconnect() {
+    (new AMessage(kWhatDisconnect, id()))->post();
+}
+
+void LiveSession::seekTo(int64_t timeUs) {
+    Mutex::Autolock autoLock(mLock);
+    mSeekDone = false;
+
+    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    msg->setInt64("timeUs", timeUs);
+    msg->post();
+
+    while (!mSeekDone) {
+        mCondition.wait(mLock);
+    }
+}
+
+void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatConnect:
+            onConnect(msg);
+            break;
+
+        case kWhatDisconnect:
+            onDisconnect();
+            break;
+
+        case kWhatMonitorQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mMonitorQueueGeneration) {
+                // Stale event
+                break;
+            }
+
+            onMonitorQueue();
+            break;
+        }
+
+        case kWhatSeek:
+            onSeek(msg);
+            break;
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+// static
+int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
+    if (a->mBandwidth < b->mBandwidth) {
+        return -1;
+    } else if (a->mBandwidth == b->mBandwidth) {
+        return 0;
+    }
+
+    return 1;
+}
+
+void LiveSession::onConnect(const sp<AMessage> &msg) {
+    AString url;
+    CHECK(msg->findString("url", &url));
+
+    LOGI("onConnect '%s'", url.c_str());
+
+    mMasterURL = url;
+
+    sp<M3UParser> playlist = fetchPlaylist(url.c_str());
+    CHECK(playlist != NULL);
+
+    if (playlist->isVariantPlaylist()) {
+        for (size_t i = 0; i < playlist->size(); ++i) {
+            BandwidthItem item;
+
+            sp<AMessage> meta;
+            playlist->itemAt(i, &item.mURI, &meta);
+
+            unsigned long bandwidth;
+            CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
+
+            mBandwidthItems.push(item);
+        }
+
+        CHECK_GT(mBandwidthItems.size(), 0u);
+
+        mBandwidthItems.sort(SortByBandwidth);
+
+        if (mBandwidthItems.size() > 1) {
+            // XXX Remove the lowest bitrate stream for now...
+            mBandwidthItems.removeAt(0);
+        }
+    }
+
+    postMonitorQueue();
+}
+
+void LiveSession::onDisconnect() {
+    LOGI("onDisconnect");
+
+    mDataSource->queueEOS(ERROR_END_OF_STREAM);
+}
+
+status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
+    *out = NULL;
+
+    sp<DataSource> source;
+
+    if (!strncasecmp(url, "file://", 7)) {
+        source = new FileSource(url + 7);
+    } else {
+        CHECK(!strncasecmp(url, "http://", 7));
+
+        status_t err = mHTTPDataSource->connect(url);
+
+        if (err != OK) {
+            return err;
+        }
+
+        source = mHTTPDataSource;
+    }
+
+    off64_t size;
+    status_t err = source->getSize(&size);
+
+    if (err != OK) {
+        size = 65536;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    buffer->setRange(0, 0);
+
+    for (;;) {
+        size_t bufferRemaining = buffer->capacity() - buffer->size();
+
+        if (bufferRemaining == 0) {
+            bufferRemaining = 32768;
+
+            LOGV("increasing download buffer to %d bytes",
+                 buffer->size() + bufferRemaining);
+
+            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+            memcpy(copy->data(), buffer->data(), buffer->size());
+            copy->setRange(0, buffer->size());
+
+            buffer = copy;
+        }
+
+        ssize_t n = source->readAt(
+                buffer->size(), buffer->data() + buffer->size(),
+                bufferRemaining);
+
+        if (n < 0) {
+            return err;
+        }
+
+        if (n == 0) {
+            break;
+        }
+
+        buffer->setRange(0, buffer->size() + (size_t)n);
+    }
+
+    *out = buffer;
+
+    return OK;
+}
+
+sp<M3UParser> LiveSession::fetchPlaylist(const char *url) {
+    sp<ABuffer> buffer;
+    status_t err = fetchFile(url, &buffer);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    sp<M3UParser> playlist =
+        new M3UParser(url, buffer->data(), buffer->size());
+
+    if (playlist->initCheck() != OK) {
+        return NULL;
+    }
+
+    return playlist;
+}
+
+static double uniformRand() {
+    return (double)rand() / RAND_MAX;
+}
+
+size_t LiveSession::getBandwidthIndex() {
+    if (mBandwidthItems.size() == 0) {
+        return 0;
+    }
+
+#if 1
+    int32_t bandwidthBps;
+    if (mHTTPDataSource != NULL
+            && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) {
+        LOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
+    } else {
+        LOGV("no bandwidth estimate.");
+        return 0;  // Pick the lowest bandwidth stream by default.
+    }
+
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("media.httplive.max-bw", value, NULL)) {
+        char *end;
+        long maxBw = strtoul(value, &end, 10);
+        if (end > value && *end == '\0') {
+            if (maxBw > 0 && bandwidthBps > maxBw) {
+                LOGV("bandwidth capped to %ld bps", maxBw);
+                bandwidthBps = maxBw;
+            }
+        }
+    }
+
+    // Consider only 80% of the available bandwidth usable.
+    bandwidthBps = (bandwidthBps * 8) / 10;
+
+    // Pick the highest bandwidth stream below or equal to estimated bandwidth.
+
+    size_t index = mBandwidthItems.size() - 1;
+    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth
+                            > (size_t)bandwidthBps) {
+        --index;
+    }
+#elif 0
+    // Change bandwidth at random()
+    size_t index = uniformRand() * mBandwidthItems.size();
+#elif 0
+    // There's a 50% chance to stay on the current bandwidth and
+    // a 50% chance to switch to the next higher bandwidth (wrapping around
+    // to lowest)
+    const size_t kMinIndex = 0;
+
+    size_t index;
+    if (mPrevBandwidthIndex < 0) {
+        index = kMinIndex;
+    } else if (uniformRand() < 0.5) {
+        index = (size_t)mPrevBandwidthIndex;
+    } else {
+        index = mPrevBandwidthIndex + 1;
+        if (index == mBandwidthItems.size()) {
+            index = kMinIndex;
+        }
+    }
+#elif 0
+    // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec
+
+    size_t index = mBandwidthItems.size() - 1;
+    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) {
+        --index;
+    }
+#else
+    size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
+#endif
+
+    return index;
+}
+
+void LiveSession::onDownloadNext() {
+    size_t bandwidthIndex = getBandwidthIndex();
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mLastPlaylistFetchTimeUs < 0
+            || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
+            || (!mPlaylist->isComplete()
+                && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) {
+        AString url;
+        if (mBandwidthItems.size() > 0) {
+            url = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
+        } else {
+            url = mMasterURL;
+        }
+
+        bool firstTime = (mPlaylist == NULL);
+
+        mPlaylist = fetchPlaylist(url.c_str());
+        CHECK(mPlaylist != NULL);
+
+        if (firstTime) {
+            Mutex::Autolock autoLock(mLock);
+
+            int32_t targetDuration;
+            if (!mPlaylist->isComplete()
+                    || !mPlaylist->meta()->findInt32(
+                    "target-duration", &targetDuration)) {
+                mDurationUs = -1;
+            } else {
+                mDurationUs = 1000000ll * targetDuration * mPlaylist->size();
+            }
+        }
+
+        mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
+    }
+
+    int32_t firstSeqNumberInPlaylist;
+    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
+                "media-sequence", &firstSeqNumberInPlaylist)) {
+        firstSeqNumberInPlaylist = 0;
+    }
+
+    bool explicitDiscontinuity = false;
+
+    if (mSeekTimeUs >= 0) {
+        int32_t targetDuration;
+        if (mPlaylist->isComplete() &&
+                mPlaylist->meta()->findInt32(
+                    "target-duration", &targetDuration)) {
+            int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll;
+            int64_t index = seekTimeSecs / targetDuration;
+
+            if (index >= 0 && index < mPlaylist->size()) {
+                mSeqNumber = firstSeqNumberInPlaylist + index;
+                mDataSource->reset();
+
+                explicitDiscontinuity = true;
+            }
+        }
+
+        mSeekTimeUs = -1;
+
+        Mutex::Autolock autoLock(mLock);
+        mSeekDone = true;
+        mCondition.broadcast();
+    }
+
+    if (mSeqNumber < 0) {
+        if (mPlaylist->isComplete()) {
+            mSeqNumber = firstSeqNumberInPlaylist;
+        } else {
+            mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2;
+        }
+    }
+
+    int32_t lastSeqNumberInPlaylist =
+        firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+
+    if (mSeqNumber < firstSeqNumberInPlaylist
+            || mSeqNumber > lastSeqNumberInPlaylist) {
+        if (!mPlaylist->isComplete()
+                && mSeqNumber > lastSeqNumberInPlaylist
+                && mNumRetries < kMaxNumRetries) {
+            ++mNumRetries;
+
+            mLastPlaylistFetchTimeUs = -1;
+            postMonitorQueue(1000000ll);
+            return;
+        }
+
+        LOGE("Cannot find sequence number %d in playlist "
+             "(contains %d - %d)",
+             mSeqNumber, firstSeqNumberInPlaylist,
+             firstSeqNumberInPlaylist + mPlaylist->size() - 1);
+
+        mDataSource->queueEOS(ERROR_END_OF_STREAM);
+        return;
+    }
+
+    mNumRetries = 0;
+
+    AString uri;
+    sp<AMessage> itemMeta;
+    CHECK(mPlaylist->itemAt(
+                mSeqNumber - firstSeqNumberInPlaylist,
+                &uri,
+                &itemMeta));
+
+    int32_t val;
+    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
+        explicitDiscontinuity = true;
+    }
+
+    sp<ABuffer> buffer;
+    status_t err = fetchFile(uri.c_str(), &buffer);
+    CHECK_EQ(err, (status_t)OK);
+
+    CHECK_EQ((status_t)OK,
+             decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer));
+
+    if (buffer->size() == 0 || buffer->data()[0] != 0x47) {
+        // Not a transport stream???
+
+        LOGE("This doesn't look like a transport stream...");
+
+        mDataSource->queueEOS(ERROR_UNSUPPORTED);
+        return;
+    }
+
+    if (explicitDiscontinuity
+            || (mPrevBandwidthIndex >= 0
+                && (size_t)mPrevBandwidthIndex != bandwidthIndex)) {
+        // Signal discontinuity.
+
+        sp<ABuffer> tmp = new ABuffer(188);
+        memset(tmp->data(), 0, tmp->size());
+
+        mDataSource->queueBuffer(tmp);
+    }
+
+    mDataSource->queueBuffer(buffer);
+
+    mPrevBandwidthIndex = bandwidthIndex;
+    ++mSeqNumber;
+
+    postMonitorQueue();
+}
+
+void LiveSession::onMonitorQueue() {
+    if (mSeekTimeUs >= 0
+            || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) {
+        onDownloadNext();
+    } else {
+        postMonitorQueue(1000000ll);
+    }
+}
+
+status_t LiveSession::decryptBuffer(
+        size_t playlistIndex, const sp<ABuffer> &buffer) {
+    sp<AMessage> itemMeta;
+    bool found = false;
+    AString method;
+
+    for (ssize_t i = playlistIndex; i >= 0; --i) {
+        AString uri;
+        CHECK(mPlaylist->itemAt(i, &uri, &itemMeta));
+
+        if (itemMeta->findString("cipher-method", &method)) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found) {
+        method = "NONE";
+    }
+
+    if (method == "NONE") {
+        return OK;
+    } else if (!(method == "AES-128")) {
+        LOGE("Unsupported cipher method '%s'", method.c_str());
+        return ERROR_UNSUPPORTED;
+    }
+
+    AString keyURI;
+    if (!itemMeta->findString("cipher-uri", &keyURI)) {
+        LOGE("Missing key uri");
+        return ERROR_MALFORMED;
+    }
+
+    ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
+
+    sp<ABuffer> key;
+    if (index >= 0) {
+        key = mAESKeyForURI.valueAt(index);
+    } else {
+        key = new ABuffer(16);
+
+        sp<NuHTTPDataSource> keySource = new NuHTTPDataSource;
+        status_t err = keySource->connect(keyURI.c_str());
+
+        if (err == OK) {
+            size_t offset = 0;
+            while (offset < 16) {
+                ssize_t n = keySource->readAt(
+                        offset, key->data() + offset, 16 - offset);
+                if (n <= 0) {
+                    err = ERROR_IO;
+                    break;
+                }
+
+                offset += n;
+            }
+        }
+
+        if (err != OK) {
+            LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
+            return ERROR_IO;
+        }
+
+        mAESKeyForURI.add(keyURI, key);
+    }
+
+    AES_KEY aes_key;
+    if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
+        LOGE("failed to set AES decryption key.");
+        return UNKNOWN_ERROR;
+    }
+
+    unsigned char aes_ivec[16];
+
+    AString iv;
+    if (itemMeta->findString("cipher-iv", &iv)) {
+        if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+                || iv.size() != 16 * 2 + 2) {
+            LOGE("malformed cipher IV '%s'.", iv.c_str());
+            return ERROR_MALFORMED;
+        }
+
+        memset(aes_ivec, 0, sizeof(aes_ivec));
+        for (size_t i = 0; i < 16; ++i) {
+            char c1 = tolower(iv.c_str()[2 + 2 * i]);
+            char c2 = tolower(iv.c_str()[3 + 2 * i]);
+            if (!isxdigit(c1) || !isxdigit(c2)) {
+                LOGE("malformed cipher IV '%s'.", iv.c_str());
+                return ERROR_MALFORMED;
+            }
+            uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+            uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+            aes_ivec[i] = nibble1 << 4 | nibble2;
+        }
+    } else {
+        memset(aes_ivec, 0, sizeof(aes_ivec));
+        aes_ivec[15] = mSeqNumber & 0xff;
+        aes_ivec[14] = (mSeqNumber >> 8) & 0xff;
+        aes_ivec[13] = (mSeqNumber >> 16) & 0xff;
+        aes_ivec[12] = (mSeqNumber >> 24) & 0xff;
+    }
+
+    AES_cbc_encrypt(
+            buffer->data(), buffer->data(), buffer->size(),
+            &aes_key, aes_ivec, AES_DECRYPT);
+
+    // hexdump(buffer->data(), buffer->size());
+
+    size_t n = buffer->size();
+    CHECK_GT(n, 0u);
+
+    size_t pad = buffer->data()[n - 1];
+
+    CHECK_GT(pad, 0u);
+    CHECK_LE(pad, 16u);
+    CHECK_GE((size_t)n, pad);
+    for (size_t i = 0; i < pad; ++i) {
+        CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad);
+    }
+
+    n -= pad;
+
+    buffer->setRange(buffer->offset(), n);
+
+    return OK;
+}
+
+void LiveSession::postMonitorQueue(int64_t delayUs) {
+    sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id());
+    msg->setInt32("generation", ++mMonitorQueueGeneration);
+    msg->post(delayUs);
+}
+
+void LiveSession::onSeek(const sp<AMessage> &msg) {
+    int64_t timeUs;
+    CHECK(msg->findInt64("timeUs", &timeUs));
+
+    mSeekTimeUs = timeUs;
+    postMonitorQueue();
+}
+
+status_t LiveSession::getDuration(int64_t *durationUs) {
+    Mutex::Autolock autoLock(mLock);
+    *durationUs = mDurationUs;
+
+    return OK;
+}
+
+bool LiveSession::isSeekable() {
+    int64_t durationUs;
+    return getDuration(&durationUs) == OK && durationUs >= 0;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
deleted file mode 100644
index 4451bd5..0000000
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "LiveSource"
-#include <utils/Log.h>
-
-#include "include/LiveSource.h"
-#include "include/M3UParser.h"
-#include "include/NuHTTPDataSource.h"
-
-#include <cutils/properties.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/FileSource.h>
-
-#include <ctype.h>
-#include <openssl/aes.h>
-
-namespace android {
-
-LiveSource::LiveSource(const char *url)
-    : mMasterURL(url),
-      mInitCheck(NO_INIT),
-      mDurationUs(-1),
-      mPlaylistIndex(0),
-      mLastFetchTimeUs(-1),
-      mSource(new NuHTTPDataSource),
-      mSourceSize(0),
-      mOffsetBias(0),
-      mSignalDiscontinuity(false),
-      mPrevBandwidthIndex(-1),
-      mAESKey((AES_KEY *)malloc(sizeof(AES_KEY))),
-      mStreamEncrypted(false) {
-    if (switchToNext()) {
-        mInitCheck = OK;
-
-        determineSeekability();
-    }
-}
-
-LiveSource::~LiveSource() {
-    free(mAESKey);
-    mAESKey = NULL;
-}
-
-status_t LiveSource::initCheck() const {
-    return mInitCheck;
-}
-
-// static
-int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
-    if (a->mBandwidth < b->mBandwidth) {
-        return -1;
-    } else if (a->mBandwidth == b->mBandwidth) {
-        return 0;
-    }
-
-    return 1;
-}
-
-static double uniformRand() {
-    return (double)rand() / RAND_MAX;
-}
-
-size_t LiveSource::getBandwidthIndex() {
-    if (mBandwidthItems.size() == 0) {
-        return 0;
-    }
-
-#if 1
-    int32_t bandwidthBps;
-    if (mSource != NULL && mSource->estimateBandwidth(&bandwidthBps)) {
-        LOGI("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
-    } else {
-        LOGI("no bandwidth estimate.");
-        return 0;  // Pick the lowest bandwidth stream by default.
-    }
-
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.httplive.max-bw", value, NULL)) {
-        char *end;
-        long maxBw = strtoul(value, &end, 10);
-        if (end > value && *end == '\0') {
-            if (maxBw > 0 && bandwidthBps > maxBw) {
-                LOGV("bandwidth capped to %ld bps", maxBw);
-                bandwidthBps = maxBw;
-            }
-        }
-    }
-
-    // Consider only 80% of the available bandwidth usable.
-    bandwidthBps = (bandwidthBps * 8) / 10;
-
-    // Pick the highest bandwidth stream below or equal to estimated bandwidth.
-
-    size_t index = mBandwidthItems.size() - 1;
-    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth
-                            > (size_t)bandwidthBps) {
-        --index;
-    }
-#elif 0
-    // Change bandwidth at random()
-    size_t index = uniformRand() * mBandwidthItems.size();
-#elif 0
-    // There's a 50% chance to stay on the current bandwidth and
-    // a 50% chance to switch to the next higher bandwidth (wrapping around
-    // to lowest)
-    const size_t kMinIndex = 0;
-
-    size_t index;
-    if (mPrevBandwidthIndex < 0) {
-        index = kMinIndex;
-    } else if (uniformRand() < 0.5) {
-        index = (size_t)mPrevBandwidthIndex;
-    } else {
-        index = mPrevBandwidthIndex + 1;
-        if (index == mBandwidthItems.size()) {
-            index = kMinIndex;
-        }
-    }
-#elif 0
-    // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec
-
-    size_t index = mBandwidthItems.size() - 1;
-    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) {
-        --index;
-    }
-#else
-    size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
-#endif
-
-    return index;
-}
-
-bool LiveSource::loadPlaylist(bool fetchMaster, size_t bandwidthIndex) {
-    mSignalDiscontinuity = false;
-
-    mPlaylist.clear();
-    mPlaylistIndex = 0;
-
-    if (fetchMaster) {
-        mPrevBandwidthIndex = -1;
-
-        sp<ABuffer> buffer;
-        status_t err = fetchM3U(mMasterURL.c_str(), &buffer);
-
-        if (err != OK) {
-            return false;
-        }
-
-        mPlaylist = new M3UParser(
-                mMasterURL.c_str(), buffer->data(), buffer->size());
-
-        if (mPlaylist->initCheck() != OK) {
-            return false;
-        }
-
-        if (mPlaylist->isVariantPlaylist()) {
-            for (size_t i = 0; i < mPlaylist->size(); ++i) {
-                BandwidthItem item;
-
-                sp<AMessage> meta;
-                mPlaylist->itemAt(i, &item.mURI, &meta);
-
-                unsigned long bandwidth;
-                CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
-
-                mBandwidthItems.push(item);
-            }
-            mPlaylist.clear();
-
-            // fall through
-            if (mBandwidthItems.size() == 0) {
-                return false;
-            }
-
-            mBandwidthItems.sort(SortByBandwidth);
-
-#if 1  // XXX
-            if (mBandwidthItems.size() > 1) {
-                // Remove the lowest bandwidth stream, this is sometimes
-                // an AAC program stream, which we don't support at this point.
-                mBandwidthItems.removeItemsAt(0);
-            }
-#endif
-
-            for (size_t i = 0; i < mBandwidthItems.size(); ++i) {
-                const BandwidthItem &item = mBandwidthItems.itemAt(i);
-                LOGV("item #%d: %s", i, item.mURI.c_str());
-            }
-
-            bandwidthIndex = getBandwidthIndex();
-        }
-    }
-
-    if (mBandwidthItems.size() > 0) {
-        mURL = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
-
-        if (mPrevBandwidthIndex >= 0
-                && (size_t)mPrevBandwidthIndex != bandwidthIndex) {
-            // If we switched streams because of bandwidth changes,
-            // we'll signal this discontinuity by inserting a
-            // special transport stream packet into the stream.
-            mSignalDiscontinuity = true;
-        }
-
-        mPrevBandwidthIndex = bandwidthIndex;
-    } else {
-        mURL = mMasterURL;
-    }
-
-    if (mPlaylist == NULL) {
-        sp<ABuffer> buffer;
-        status_t err = fetchM3U(mURL.c_str(), &buffer);
-
-        if (err != OK) {
-            return false;
-        }
-
-        mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size());
-
-        if (mPlaylist->initCheck() != OK) {
-            return false;
-        }
-
-        if (mPlaylist->isVariantPlaylist()) {
-            return false;
-        }
-    }
-
-    if (!mPlaylist->meta()->findInt32(
-                "media-sequence", &mFirstItemSequenceNumber)) {
-        mFirstItemSequenceNumber = 0;
-    }
-
-    return true;
-}
-
-static int64_t getNowUs() {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-
-    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
-}
-
-bool LiveSource::switchToNext() {
-    mSignalDiscontinuity = false;
-
-    mOffsetBias += mSourceSize;
-    mSourceSize = 0;
-
-    size_t bandwidthIndex = getBandwidthIndex();
-
-    if (mLastFetchTimeUs < 0 || getNowUs() >= mLastFetchTimeUs + 15000000ll
-        || mPlaylistIndex == mPlaylist->size()
-        || (ssize_t)bandwidthIndex != mPrevBandwidthIndex) {
-        int32_t nextSequenceNumber =
-            mPlaylistIndex + mFirstItemSequenceNumber;
-
-        if (!loadPlaylist(mLastFetchTimeUs < 0, bandwidthIndex)) {
-            LOGE("failed to reload playlist");
-            return false;
-        }
-
-        if (mLastFetchTimeUs < 0) {
-            if (isSeekable()) {
-                mPlaylistIndex = 0;
-            } else {
-                // This is live streamed content, the first seqnum in the
-                // various bandwidth' streams may be slightly off, so don't
-                // start at the very first entry.
-                // With a segment duration of 6-10secs, this really only
-                // delays playback up to 30secs compared to real time.
-                mPlaylistIndex = 3;
-                if (mPlaylistIndex >= mPlaylist->size()) {
-                    mPlaylistIndex = mPlaylist->size() - 1;
-                }
-            }
-        } else {
-            if (nextSequenceNumber < mFirstItemSequenceNumber
-                    || nextSequenceNumber
-                            >= mFirstItemSequenceNumber + (int32_t)mPlaylist->size()) {
-                LOGE("Cannot find sequence number %d in new playlist",
-                     nextSequenceNumber);
-
-                return false;
-            }
-
-            mPlaylistIndex = nextSequenceNumber - mFirstItemSequenceNumber;
-        }
-
-        mLastFetchTimeUs = getNowUs();
-    }
-
-    if (!setupCipher()) {
-        return false;
-    }
-
-    AString uri;
-    sp<AMessage> itemMeta;
-    CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
-    LOGV("switching to %s", uri.c_str());
-
-    if (mSource->connect(uri.c_str()) != OK
-            || mSource->getSize(&mSourceSize) != OK) {
-        return false;
-    }
-
-    int32_t val;
-    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
-        mSignalDiscontinuity = true;
-    }
-
-    mPlaylistIndex++;
-
-    return true;
-}
-
-bool LiveSource::setupCipher() {
-    sp<AMessage> itemMeta;
-    bool found = false;
-    AString method;
-
-    for (ssize_t i = mPlaylistIndex; i >= 0; --i) {
-        AString uri;
-        CHECK(mPlaylist->itemAt(i, &uri, &itemMeta));
-
-        if (itemMeta->findString("cipher-method", &method)) {
-            found = true;
-            break;
-        }
-    }
-
-    if (!found) {
-        method = "NONE";
-    }
-
-    mStreamEncrypted = false;
-
-    if (method == "AES-128") {
-        AString keyURI;
-        if (!itemMeta->findString("cipher-uri", &keyURI)) {
-            LOGE("Missing key uri");
-            return false;
-        }
-
-        ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
-
-        sp<ABuffer> key;
-        if (index >= 0) {
-            key = mAESKeyForURI.valueAt(index);
-        } else {
-            key = new ABuffer(16);
-
-            sp<NuHTTPDataSource> keySource = new NuHTTPDataSource;
-            status_t err = keySource->connect(keyURI.c_str());
-
-            if (err == OK) {
-                size_t offset = 0;
-                while (offset < 16) {
-                    ssize_t n = keySource->readAt(
-                            offset, key->data() + offset, 16 - offset);
-                    if (n <= 0) {
-                        err = ERROR_IO;
-                        break;
-                    }
-
-                    offset += n;
-                }
-            }
-
-            if (err != OK) {
-                LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
-                return false;
-            }
-
-            mAESKeyForURI.add(keyURI, key);
-        }
-
-        if (AES_set_decrypt_key(key->data(), 128, (AES_KEY *)mAESKey) != 0) {
-            LOGE("failed to set AES decryption key.");
-            return false;
-        }
-
-        AString iv;
-        if (itemMeta->findString("cipher-iv", &iv)) {
-            if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
-                    || iv.size() != 16 * 2 + 2) {
-                LOGE("malformed cipher IV '%s'.", iv.c_str());
-                return false;
-            }
-
-            memset(mAESIVec, 0, sizeof(mAESIVec));
-            for (size_t i = 0; i < 16; ++i) {
-                char c1 = tolower(iv.c_str()[2 + 2 * i]);
-                char c2 = tolower(iv.c_str()[3 + 2 * i]);
-                if (!isxdigit(c1) || !isxdigit(c2)) {
-                    LOGE("malformed cipher IV '%s'.", iv.c_str());
-                    return false;
-                }
-                uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
-                uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
-
-                mAESIVec[i] = nibble1 << 4 | nibble2;
-            }
-        } else {
-            size_t seqNum = mPlaylistIndex + mFirstItemSequenceNumber;
-
-            memset(mAESIVec, 0, sizeof(mAESIVec));
-            mAESIVec[15] = seqNum & 0xff;
-            mAESIVec[14] = (seqNum >> 8) & 0xff;
-            mAESIVec[13] = (seqNum >> 16) & 0xff;
-            mAESIVec[12] = (seqNum >> 24) & 0xff;
-        }
-
-        mStreamEncrypted = true;
-    } else if (!(method == "NONE")) {
-        LOGE("Unsupported cipher method '%s'", method.c_str());
-        return false;
-    }
-
-    return true;
-}
-
-static const ssize_t kHeaderSize = 188;
-
-ssize_t LiveSource::readAt(off64_t offset, void *data, size_t size) {
-    CHECK(offset >= mOffsetBias);
-    offset -= mOffsetBias;
-
-    off64_t delta = mSignalDiscontinuity ? kHeaderSize : 0;
-
-    if (offset >= mSourceSize + delta) {
-        CHECK_EQ(offset, mSourceSize + delta);
-
-        offset -= mSourceSize + delta;
-        if (!switchToNext()) {
-            return ERROR_END_OF_STREAM;
-        }
-
-        if (mSignalDiscontinuity) {
-            LOGV("switchToNext changed streams");
-        } else {
-            LOGV("switchToNext stayed within the same stream");
-        }
-
-        mOffsetBias += delta;
-
-        delta = mSignalDiscontinuity ? kHeaderSize : 0;
-    }
-
-    if (offset < delta) {
-        size_t avail = delta - offset;
-        memset(data, 0, avail);
-        return avail;
-    }
-
-    bool done = false;
-    size_t numRead = 0;
-    while (numRead < size) {
-        ssize_t n = mSource->readAt(
-                offset + numRead - delta,
-                (uint8_t *)data + numRead, size - numRead);
-
-        if (n <= 0) {
-            break;
-        }
-
-        if (mStreamEncrypted) {
-            size_t nmod = n % 16;
-            CHECK(nmod == 0);
-
-            sp<ABuffer> tmp = new ABuffer(n);
-
-            AES_cbc_encrypt((const unsigned char *)data + numRead,
-                            tmp->data(),
-                            n,
-                            (const AES_KEY *)mAESKey,
-                            mAESIVec,
-                            AES_DECRYPT);
-
-            if (mSourceSize == (off64_t)(offset + numRead - delta + n)) {
-                // check for padding at the end of the file.
-
-                size_t pad = tmp->data()[n - 1];
-                CHECK_GT(pad, 0u);
-                CHECK_LE(pad, 16u);
-                CHECK_GE((size_t)n, pad);
-                for (size_t i = 0; i < pad; ++i) {
-                    CHECK_EQ((unsigned)tmp->data()[n - 1 - i], pad);
-                }
-
-                n -= pad;
-                mSourceSize -= pad;
-
-                done = true;
-            }
-
-            memcpy((uint8_t *)data + numRead, tmp->data(), n);
-        }
-
-        numRead += n;
-
-        if (done) {
-            break;
-        }
-    }
-
-    return numRead;
-}
-
-status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
-    *out = NULL;
-
-    sp<DataSource> source;
-
-    if (!strncasecmp(url, "file://", 7)) {
-        source = new FileSource(url + 7);
-    } else {
-        CHECK(!strncasecmp(url, "http://", 7));
-
-        status_t err = mSource->connect(url);
-
-        if (err != OK) {
-            return err;
-        }
-
-        source = mSource;
-    }
-
-    off64_t size;
-    status_t err = source->getSize(&size);
-
-    if (err != OK) {
-        size = 65536;
-    }
-
-    sp<ABuffer> buffer = new ABuffer(size);
-    buffer->setRange(0, 0);
-
-    for (;;) {
-        size_t bufferRemaining = buffer->capacity() - buffer->size();
-
-        if (bufferRemaining == 0) {
-            bufferRemaining = 32768;
-
-            LOGV("increasing download buffer to %d bytes",
-                 buffer->size() + bufferRemaining);
-
-            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
-            memcpy(copy->data(), buffer->data(), buffer->size());
-            copy->setRange(0, buffer->size());
-
-            buffer = copy;
-        }
-
-        ssize_t n = source->readAt(
-                buffer->size(), buffer->data() + buffer->size(),
-                bufferRemaining);
-
-        if (n < 0) {
-            return err;
-        }
-
-        if (n == 0) {
-            break;
-        }
-
-        buffer->setRange(0, buffer->size() + (size_t)n);
-    }
-
-    *out = buffer;
-
-    return OK;
-}
-
-bool LiveSource::seekTo(int64_t seekTimeUs) {
-    LOGV("seek to %lld us", seekTimeUs);
-
-    if (!mPlaylist->isComplete()) {
-        return false;
-    }
-
-    int32_t targetDuration;
-    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
-        return false;
-    }
-
-    int64_t seekTimeSecs = (seekTimeUs + 500000ll) / 1000000ll;
-
-    int64_t index = seekTimeSecs / targetDuration;
-
-    if (index < 0 || index >= mPlaylist->size()) {
-        return false;
-    }
-
-    if (index == mPlaylistIndex) {
-        return false;
-    }
-
-    mPlaylistIndex = index;
-
-    LOGV("seeking to index %lld", index);
-
-    switchToNext();
-    mOffsetBias = 0;
-
-    return true;
-}
-
-bool LiveSource::getDuration(int64_t *durationUs) const {
-    if (mDurationUs >= 0) {
-        *durationUs = mDurationUs;
-        return true;
-    }
-
-    *durationUs = 0;
-    return false;
-}
-
-bool LiveSource::isSeekable() const {
-    return mDurationUs >= 0;
-}
-
-void LiveSource::determineSeekability() {
-    mDurationUs = -1;
-
-    if (!mPlaylist->isComplete()) {
-        return;
-    }
-
-    int32_t targetDuration;
-    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
-        return;
-    }
-
-    mDurationUs = targetDuration * 1000000ll * mPlaylist->size();
-}
-
-}  // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 46f4a35..0c67432 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -45,6 +45,8 @@
 class DrmManagerClinet;
 class DecryptHandle;
 
+struct LiveSession;
+
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
 
@@ -193,6 +195,8 @@
     sp<ARTPSession> mRTPSession;
     sp<UDPPusher> mRTPPusher, mRTCPPusher;
 
+    sp<LiveSession> mLiveSession;
+
     DrmManagerClient *mDrmManagerClient;
     DecryptHandle *mDecryptHandle;
 
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
new file mode 100644
index 0000000..50c0a99
--- /dev/null
+++ b/media/libstagefright/include/LiveSession.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIVE_SESSION_H_
+
+#define LIVE_SESSION_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct DataSource;
+struct LiveDataSource;
+struct M3UParser;
+struct NuHTTPDataSource;
+
+struct LiveSession : public AHandler {
+    LiveSession();
+
+    sp<DataSource> getDataSource();
+
+    void connect(const char *url);
+    void disconnect();
+
+    // Blocks until seek is complete.
+    void seekTo(int64_t timeUs);
+
+    status_t getDuration(int64_t *durationUs);
+    bool isSeekable();
+
+protected:
+    virtual ~LiveSession();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kMaxNumQueuedFragments = 2,
+        kMaxNumRetries         = 3,
+    };
+
+    static const int64_t kMaxPlaylistAgeUs;
+
+    enum {
+        kWhatConnect        = 'conn',
+        kWhatDisconnect     = 'disc',
+        kWhatMonitorQueue   = 'moni',
+        kWhatSeek           = 'seek',
+    };
+
+    struct BandwidthItem {
+        AString mURI;
+        unsigned long mBandwidth;
+    };
+
+    sp<LiveDataSource> mDataSource;
+
+    sp<NuHTTPDataSource> mHTTPDataSource;
+
+    AString mMasterURL;
+    Vector<BandwidthItem> mBandwidthItems;
+
+    KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
+
+    ssize_t mPrevBandwidthIndex;
+    int64_t mLastPlaylistFetchTimeUs;
+    sp<M3UParser> mPlaylist;
+    int32_t mSeqNumber;
+    int64_t mSeekTimeUs;
+    int32_t mNumRetries;
+
+    Mutex mLock;
+    Condition mCondition;
+    int64_t mDurationUs;
+    bool mSeekDone;
+
+    int32_t mMonitorQueueGeneration;
+
+    void onConnect(const sp<AMessage> &msg);
+    void onDisconnect();
+    void onDownloadNext();
+    void onMonitorQueue();
+    void onSeek(const sp<AMessage> &msg);
+
+    status_t fetchFile(const char *url, sp<ABuffer> *out);
+    sp<M3UParser> fetchPlaylist(const char *url);
+    size_t getBandwidthIndex();
+
+    status_t decryptBuffer(
+            size_t playlistIndex, const sp<ABuffer> &buffer);
+
+    void postMonitorQueue(int64_t delayUs = 0);
+
+    static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
+
+    DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
+};
+
+}  // namespace android
+
+#endif  // LIVE_SESSION_H_
diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h
deleted file mode 100644
index 38fe328..0000000
--- a/media/libstagefright/include/LiveSource.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIVE_SOURCE_H_
-
-#define LIVE_SOURCE_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <utils/KeyedVector.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-struct ABuffer;
-struct NuHTTPDataSource;
-struct M3UParser;
-
-struct LiveSource : public DataSource {
-    LiveSource(const char *url);
-
-    virtual status_t initCheck() const;
-
-    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
-    virtual uint32_t flags() {
-        return kWantsPrefetching;
-    }
-
-    bool getDuration(int64_t *durationUs) const;
-
-    bool isSeekable() const;
-    bool seekTo(int64_t seekTimeUs);
-
-protected:
-    virtual ~LiveSource();
-
-private:
-    struct BandwidthItem {
-        AString mURI;
-        unsigned long mBandwidth;
-    };
-    Vector<BandwidthItem> mBandwidthItems;
-
-    AString mMasterURL;
-    AString mURL;
-    status_t mInitCheck;
-    int64_t mDurationUs;
-
-    sp<M3UParser> mPlaylist;
-    int32_t mFirstItemSequenceNumber;
-    size_t mPlaylistIndex;
-    int64_t mLastFetchTimeUs;
-
-    sp<NuHTTPDataSource> mSource;
-    off64_t mSourceSize;
-    off64_t mOffsetBias;
-
-    bool mSignalDiscontinuity;
-    ssize_t mPrevBandwidthIndex;
-
-    void *mAESKey;
-    unsigned char mAESIVec[16];
-    bool mStreamEncrypted;
-
-    KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
-
-    status_t fetchM3U(const char *url, sp<ABuffer> *buffer);
-
-    static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b);
-
-    bool switchToNext();
-    bool loadPlaylist(bool fetchMaster, size_t bandwidthIndex);
-    void determineSeekability();
-
-    size_t getBandwidthIndex();
-    bool setupCipher();
-
-    DISALLOW_EVIL_CONSTRUCTORS(LiveSource);
-};
-
-}  // namespace android
-
-#endif  // LIVE_SOURCE_H_
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 58401d4..efe7496 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -15,7 +15,7 @@
 struct DataSource;
 struct MPEG2TSSource;
 struct String8;
-struct LiveSource;
+struct LiveSession;
 
 struct MPEG2TSExtractor : public MediaExtractor {
     MPEG2TSExtractor(const sp<DataSource> &source);
@@ -28,7 +28,7 @@
 
     virtual uint32_t flags() const;
 
-    void setLiveSource(const sp<LiveSource> &liveSource);
+    void setLiveSession(const sp<LiveSession> &liveSession);
     void seekTo(int64_t seekTimeUs);
 
 private:
@@ -37,7 +37,7 @@
     mutable Mutex mLock;
 
     sp<DataSource> mDataSource;
-    sp<LiveSource> mLiveSource;
+    sp<LiveSession> mLiveSession;
 
     sp<ATSParser> mParser;
 
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index f0f7daf..78719c1 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -45,9 +45,6 @@
     size_t cachedSize();
     size_t approxDataRemaining(bool *eos);
 
-    void suspend();
-    void clearCacheAndResume();
-
     void resumeFetchingIfNecessary();
 
 protected:
@@ -69,7 +66,6 @@
     enum {
         kWhatFetchMore  = 'fetc',
         kWhatRead       = 'read',
-        kWhatSuspend    = 'susp',
     };
 
     sp<DataSource> mSource;
@@ -87,12 +83,10 @@
     sp<AMessage> mAsyncResult;
     bool mFetching;
     int64_t mLastFetchTimeUs;
-    bool mSuspended;
 
     void onMessageReceived(const sp<AMessage> &msg);
     void onFetch();
     void onRead(const sp<AMessage> &msg);
-    void onSuspend();
 
     void fetchInternal();
     ssize_t readInternal(off64_t offset, void *data, size_t size);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 37bcb23..1fb7c39 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -49,6 +49,33 @@
     mFormat.clear();
 }
 
+static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
+    if (size < 3) {
+        // Not enough data to verify header.
+        return false;
+    }
+
+    if (ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+        return false;
+    }
+
+    unsigned layer = (ptr[1] >> 1) & 3;
+
+    if (layer != 0) {
+        return false;
+    }
+
+    unsigned ID = (ptr[1] >> 3) & 1;
+    unsigned profile_ObjectType = ptr[2] >> 6;
+
+    if (ID == 1 && profile_ObjectType == 3) {
+        // MPEG-2 profile 3 is reserved.
+        return false;
+    }
+
+    return true;
+}
+
 status_t ElementaryStreamQueue::appendData(
         const void *data, size_t size, int64_t timeUs) {
     if (mBuffer == NULL || mBuffer->size() == 0) {
@@ -96,8 +123,8 @@
                 }
 #else
                 ssize_t startOffset = -1;
-                for (size_t i = 0; i + 1 < size; ++i) {
-                    if (ptr[i] == 0xff && (ptr[i + 1] >> 4) == 0x0f) {
+                for (size_t i = 0; i < size; ++i) {
+                    if (IsSeeminglyValidADTSHeader(&ptr[i], size - i)) {
                         startOffset = i;
                         break;
                     }
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 3176810..600116e 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -19,7 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/MPEG2TSExtractor.h"
-#include "include/LiveSource.h"
+#include "include/LiveSession.h"
 #include "include/NuCachedSource2.h"
 
 #include <media/stagefright/DataSource.h>
@@ -82,8 +82,8 @@
     sp<MetaData> meta = mImpl->getFormat();
 
     int64_t durationUs;
-    if (mExtractor->mLiveSource != NULL
-            && mExtractor->mLiveSource->getDuration(&durationUs)) {
+    if (mExtractor->mLiveSession != NULL
+            && mExtractor->mLiveSession->getDuration(&durationUs) == OK) {
         meta->setInt64(kKeyDuration, durationUs);
     }
 
@@ -226,32 +226,20 @@
     return OK;
 }
 
-void MPEG2TSExtractor::setLiveSource(const sp<LiveSource> &liveSource) {
+void MPEG2TSExtractor::setLiveSession(const sp<LiveSession> &liveSession) {
     Mutex::Autolock autoLock(mLock);
 
-    mLiveSource = liveSource;
+    mLiveSession = liveSession;
 }
 
 void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mLiveSource == NULL) {
+    if (mLiveSession == NULL) {
         return;
     }
 
-    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-        static_cast<NuCachedSource2 *>(mDataSource.get())->suspend();
-    }
-
-    if (mLiveSource->seekTo(seekTimeUs)) {
-        mParser->signalDiscontinuity(true  /* isSeek */);
-        mOffset = 0;
-    }
-
-    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-        static_cast<NuCachedSource2 *>(mDataSource.get())
-            ->clearCacheAndResume();
-    }
+    mLiveSession->seekTo(seekTimeUs);
 }
 
 uint32_t MPEG2TSExtractor::flags() const {
@@ -259,7 +247,7 @@
 
     uint32_t flags = CAN_PAUSE;
 
-    if (mLiveSource != NULL && mLiveSource->isSeekable()) {
+    if (mLiveSession != NULL && mLiveSession->isSeekable()) {
         flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK;
     }