Better support for HTTP streaming media content, fixes to the way HTTPDataSource streams the data, prefetcher implementation.

related-to-bug: 2295438
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
index b0fc4b2..30b7ad9 100644
--- a/include/media/stagefright/CachingDataSource.h
+++ b/include/media/stagefright/CachingDataSource.h
@@ -33,6 +33,8 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual uint32_t flags();
+
 protected:
     virtual ~CachingDataSource();
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index f88666a..0c0ace0 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -31,6 +31,10 @@
 
 class DataSource : public RefBase {
 public:
+    enum Flags {
+        kWantsPrefetching = 1,
+    };
+
     static sp<DataSource> CreateFromURI(const char *uri);
 
     DataSource() {}
@@ -45,6 +49,10 @@
     // May return ERROR_UNSUPPORTED.
     virtual status_t getSize(off_t *size);
 
+    virtual uint32_t flags() {
+        return 0;
+    }
+
     ////////////////////////////////////////////////////////////////////////////
 
     bool sniff(String8 *mimeType, float *confidence);
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index d5dc9e6..3075f1c 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -33,6 +33,10 @@
 
     virtual ssize_t readAt(off_t offset, void *data, size_t size);
 
+    virtual uint32_t flags() {
+        return kWantsPrefetching;
+    }
+
 protected:
     virtual ~HTTPDataSource();
 
@@ -52,6 +56,8 @@
 
     status_t mInitCheck;
 
+    ssize_t sendRangeRequest(size_t offset);
+
     HTTPDataSource(const HTTPDataSource &);
     HTTPDataSource &operator=(const HTTPDataSource &);
 };
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3813907..dbb52c6 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -31,6 +31,7 @@
         MPEG4Extractor.cpp        \
         MPEG4Writer.cpp           \
         MediaExtractor.cpp        \
+        Prefetcher.cpp            \
         SampleIterator.cpp        \
         SampleTable.cpp           \
         ShoutcastSource.cpp       \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f6cd46a..42b9acc 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/AwesomePlayer.h"
+#include "include/Prefetcher.h"
 #include "include/SoftwareRenderer.h"
 
 #include <binder/IPCThreadState.h>
@@ -118,6 +119,8 @@
     mVideoEventPending = false;
     mStreamDoneEvent = new AwesomeEvent(this, 1);
     mStreamDoneEventPending = false;
+    mBufferingEvent = new AwesomeEvent(this, 2);
+    mBufferingEventPending = false;
 
     mQueue.start();
 
@@ -132,11 +135,16 @@
     mClient.disconnect();
 }
 
-void AwesomePlayer::cancelPlayerEvents() {
+void AwesomePlayer::cancelPlayerEvents(bool keepBufferingGoing) {
     mQueue.cancelEvent(mVideoEvent->eventID());
     mVideoEventPending = false;
     mQueue.cancelEvent(mStreamDoneEvent->eventID());
     mStreamDoneEventPending = false;
+
+    if (!keepBufferingGoing) {
+        mQueue.cancelEvent(mBufferingEvent->eventID());
+        mBufferingEventPending = false;
+    }
 }
 
 void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) {
@@ -149,12 +157,22 @@
 
     reset_l();
 
-    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+    sp<DataSource> dataSource = DataSource::CreateFromURI(uri);
+
+    if (dataSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
 
     if (extractor == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (dataSource->flags() & DataSource::kWantsPrefetching) {
+        mPrefetcher = new Prefetcher;
+    }
+
     return setDataSource_l(extractor);
 }
 
@@ -182,8 +200,6 @@
 }
 
 status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
-    reset_l();
-
     bool haveAudio = false;
     bool haveVideo = false;
     for (size_t i = 0; i < extractor->countTracks(); ++i) {
@@ -253,6 +269,8 @@
 
     mSeeking = false;
     mSeekTimeUs = 0;
+
+    mPrefetcher.clear();
 }
 
 // static
@@ -278,16 +296,38 @@
     }
 }
 
-void AwesomePlayer::notifyListener_l(int msg) {
+void AwesomePlayer::notifyListener_l(int msg, int ext1) {
     if (mListener != NULL) {
         sp<MediaPlayerBase> listener = mListener.promote();
 
         if (listener != NULL) {
-            listener->sendEvent(msg);
+            listener->sendEvent(msg, ext1);
         }
     }
 }
 
+void AwesomePlayer::onBufferingUpdate() {
+    Mutex::Autolock autoLock(mLock);
+    mBufferingEventPending = false;
+
+    if (mDurationUs >= 0) {
+        int64_t cachedDurationUs = mPrefetcher->getCachedDurationUs();
+        int64_t positionUs = 0;
+        if (mVideoRenderer != NULL) {
+            positionUs = mVideoTimeUs;
+        } else if (mAudioPlayer != NULL) {
+            positionUs = mAudioPlayer->getMediaTimeUs();
+        }
+
+        cachedDurationUs += positionUs;
+
+        double percentage = (double)cachedDurationUs / mDurationUs;
+        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
+
+        postBufferingEvent_l();
+    }
+}
+
 void AwesomePlayer::onStreamDone() {
     // Posted whenever any stream finishes playing.
 
@@ -361,6 +401,8 @@
         seekAudioIfNecessary_l();
     }
 
+    postBufferingEvent_l();
+
     return OK;
 }
 
@@ -414,7 +456,7 @@
         return OK;
     }
 
-    cancelPlayerEvents();
+    cancelPlayerEvents(true /* keepBufferingGoing */);
 
     if (mAudioPlayer != NULL) {
         mAudioPlayer->pause();
@@ -518,11 +560,15 @@
     return OK;
 }
 
-status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {
+status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {
     if (source == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (mPrefetcher != NULL) {
+        source = mPrefetcher->addSource(source);
+    }
+
     sp<MetaData> meta = source->getFormat();
 
     const char *mime;
@@ -549,11 +595,15 @@
     return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
 }
 
-status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) {
+status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
     if (source == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    if (mPrefetcher != NULL) {
+        source = mPrefetcher->addSource(source);
+    }
+
     mVideoSource = OMXCodec::Create(
             mClient.interface(), source->getFormat(),
             false, // createEncoder
@@ -580,9 +630,13 @@
     if (code == 1) {
         onStreamDone();
         return;
+    } else if (code == 2) {
+        onBufferingUpdate();
+        return;
     }
 
     Mutex::Autolock autoLock(mLock);
+
     mVideoEventPending = false;
 
     if (mSeeking) {
@@ -718,5 +772,17 @@
     mQueue.postEvent(mStreamDoneEvent);
 }
 
+void AwesomePlayer::postBufferingEvent_l() {
+    if (mPrefetcher == NULL) {
+        return;
+    }
+
+    if (mBufferingEventPending) {
+        return;
+    }
+    mBufferingEventPending = true;
+    mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
index 23f4897..8d04ead 100644
--- a/media/libstagefright/CachingDataSource.cpp
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -65,6 +65,10 @@
     return mSource->initCheck();
 }
 
+uint32_t CachingDataSource::flags() {
+    return mSource->flags();
+}
+
 ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index 7e8bbc6..135a044 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -190,30 +190,12 @@
     mHttp = NULL;
 }
 
-ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
-    if (offset >= mBufferOffset
-            && offset < (off_t)(mBufferOffset + mBufferLength)) {
-        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
-
-        size_t copy = num_bytes_available;
-        if (copy > size) {
-            copy = size;
-        }
-
-        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
-
-        return copy;
-    }
-
-    mBufferOffset = offset;
-    mBufferLength = 0;
-
+ssize_t HTTPDataSource::sendRangeRequest(size_t offset) {
     char host[128];
     sprintf(host, "Host: %s\r\n", mHost);
 
     char range[128];
-    sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
-            mBufferOffset, mBufferOffset + kBufferSize - 1);
+    sprintf(range, "Range: bytes=%d-\r\n\r\n", offset);
 
     int http_status;
 
@@ -251,12 +233,44 @@
     char *end;
     unsigned long contentLength = strtoul(value.c_str(), &end, 10);
 
-    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
+    return contentLength;
+}
 
-    if (num_bytes_received <= 0) {
-        return num_bytes_received;
+ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
+    if (offset >= mBufferOffset
+            && offset < (off_t)(mBufferOffset + mBufferLength)) {
+        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
+
+        size_t copy = num_bytes_available;
+        if (copy > size) {
+            copy = size;
+        }
+
+        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
+
+        return copy;
     }
 
+    ssize_t contentLength = 0;
+    if (mBufferLength <= 0 || offset != mBufferOffset + mBufferLength) {
+        mHttp->disconnect();
+        contentLength = sendRangeRequest(offset);
+
+        if (contentLength > kBufferSize) {
+            contentLength = kBufferSize;
+        }
+    } else {
+        contentLength = kBufferSize;
+    }
+
+    mBufferOffset = offset;
+
+    if (contentLength <= 0) {
+        return contentLength;
+    }
+
+    ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
+
     mBufferLength = (size_t)num_bytes_received;
 
     size_t copy = mBufferLength;
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
new file mode 100644
index 0000000..93e3fdc
--- /dev/null
+++ b/media/libstagefright/Prefetcher.cpp
@@ -0,0 +1,381 @@
+/*
+ * 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_TAG "Prefetcher"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/Prefetcher.h"
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct PrefetchedSource : public MediaSource {
+    PrefetchedSource(
+            const sp<Prefetcher> &prefetcher,
+            size_t index,
+            const sp<MediaSource> &source);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+    virtual sp<MetaData> getFormat();
+
+protected:
+    virtual ~PrefetchedSource();
+
+private:
+    friend struct Prefetcher;
+
+    Mutex mLock;
+    Condition mCondition;
+
+    sp<Prefetcher> mPrefetcher;
+    sp<MediaSource> mSource;
+    size_t mIndex;
+    bool mStarted;
+    bool mReachedEOS;
+    int64_t mSeekTimeUs;
+    int64_t mCacheDurationUs;
+
+    List<MediaBuffer *> mCachedBuffers;
+
+    // Returns true iff source is currently caching.
+    bool getCacheDurationUs(int64_t *durationUs);
+
+    void updateCacheDuration_l();
+    void clearCache_l();
+
+    void cacheMore();
+
+    PrefetchedSource(const PrefetchedSource &);
+    PrefetchedSource &operator=(const PrefetchedSource &);
+};
+
+Prefetcher::Prefetcher()
+    : mDone(false),
+      mThreadExited(false) {
+    startThread();
+}
+
+Prefetcher::~Prefetcher() {
+    stopThread();
+}
+
+sp<MediaSource> Prefetcher::addSource(const sp<MediaSource> &source) {
+    Mutex::Autolock autoLock(mLock);
+
+    sp<PrefetchedSource> psource =
+        new PrefetchedSource(this, mSources.size(), source);
+
+    mSources.add(psource);
+
+    return psource;
+}
+
+void Prefetcher::startThread() {
+    mThreadExited = false;
+    mDone = false;
+
+    int res = androidCreateThreadEtc(
+            ThreadWrapper, this, "Prefetcher",
+            ANDROID_PRIORITY_DEFAULT, 0, &mThread);
+
+    CHECK_EQ(res, 1);
+}
+
+void Prefetcher::stopThread() {
+    Mutex::Autolock autoLock(mLock);
+
+    while (!mThreadExited) {
+        mDone = true;
+        mCondition.signal();
+        mCondition.wait(mLock);
+    }
+}
+
+// static
+int Prefetcher::ThreadWrapper(void *me) {
+    static_cast<Prefetcher *>(me)->threadFunc();
+
+    return 0;
+}
+
+// Cache about 10secs for each source.
+static int64_t kMaxCacheDurationUs = 10000000ll;
+
+void Prefetcher::threadFunc() {
+    for (;;) {
+        Mutex::Autolock autoLock(mLock);
+        if (mDone) {
+            mThreadExited = true;
+            mCondition.signal();
+            break;
+        }
+        mCondition.waitRelative(mLock, 10000000ll);
+
+        int64_t minCacheDurationUs = -1;
+        ssize_t minIndex = -1;
+        for (size_t i = 0; i < mSources.size(); ++i) {
+            sp<PrefetchedSource> source = mSources[i].promote();
+
+            if (source == NULL) {
+                continue;
+            }
+
+            int64_t cacheDurationUs;
+            if (!source->getCacheDurationUs(&cacheDurationUs)) {
+                continue;
+            }
+
+            if (cacheDurationUs >= kMaxCacheDurationUs) {
+                continue;
+            }
+
+            if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
+                minCacheDurationUs = cacheDurationUs;
+                minIndex = i;
+            }
+        }
+
+        if (minIndex < 0) {
+            continue;
+        }
+
+        sp<PrefetchedSource> source = mSources[minIndex].promote();
+        if (source != NULL) {
+            source->cacheMore();
+        }
+    }
+}
+
+int64_t Prefetcher::getCachedDurationUs() {
+    Mutex::Autolock autoLock(mLock);
+
+    int64_t minCacheDurationUs = -1;
+    ssize_t minIndex = -1;
+    for (size_t i = 0; i < mSources.size(); ++i) {
+        int64_t cacheDurationUs;
+        sp<PrefetchedSource> source = mSources[i].promote();
+        if (source == NULL) {
+            continue;
+        }
+
+        if (!source->getCacheDurationUs(&cacheDurationUs)) {
+            continue;
+        }
+
+        if (cacheDurationUs >= kMaxCacheDurationUs) {
+            continue;
+        }
+
+        if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
+            minCacheDurationUs = cacheDurationUs;
+            minIndex = i;
+        }
+    }
+
+    return minCacheDurationUs < 0 ? 0 : minCacheDurationUs;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+PrefetchedSource::PrefetchedSource(
+        const sp<Prefetcher> &prefetcher,
+        size_t index,
+        const sp<MediaSource> &source)
+    : mPrefetcher(prefetcher),
+      mSource(source),
+      mIndex(index),
+      mStarted(false),
+      mReachedEOS(false),
+      mSeekTimeUs(0),
+      mCacheDurationUs(0) {
+}
+
+PrefetchedSource::~PrefetchedSource() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t PrefetchedSource::start(MetaData *params) {
+    Mutex::Autolock autoLock(mLock);
+
+    status_t err = mSource->start(params);
+
+    if (err != OK) {
+        return err;
+    }
+
+    mStarted = true;
+
+    for (;;) {
+        // Buffer 2 secs on startup.
+        if (mReachedEOS || mCacheDurationUs > 2000000) {
+            break;
+        }
+
+        mCondition.wait(mLock);
+    }
+
+    return OK;
+}
+
+status_t PrefetchedSource::stop() {
+    Mutex::Autolock autoLock(mLock);
+
+    clearCache_l();
+
+    status_t err = mSource->stop();
+
+    mStarted = false;
+
+    return err;
+}
+
+status_t PrefetchedSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mStarted);
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        CHECK(seekTimeUs >= 0);
+
+        clearCache_l();
+
+        mReachedEOS = false;
+        mSeekTimeUs = seekTimeUs;
+    }
+
+    while (!mReachedEOS && mCachedBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (mCachedBuffers.empty()) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    *out = *mCachedBuffers.begin();
+    mCachedBuffers.erase(mCachedBuffers.begin());
+    updateCacheDuration_l();
+
+    return OK;
+}
+
+sp<MetaData> PrefetchedSource::getFormat() {
+    return mSource->getFormat();
+}
+
+bool PrefetchedSource::getCacheDurationUs(int64_t *durationUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mStarted || mReachedEOS) {
+        *durationUs = 0;
+
+        return false;
+    }
+
+    *durationUs = mCacheDurationUs;
+
+    return true;
+}
+
+void PrefetchedSource::cacheMore() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mStarted) {
+        return;
+    }
+
+    MediaBuffer *buffer;
+    MediaSource::ReadOptions options;
+    if (mSeekTimeUs >= 0) {
+        options.setSeekTo(mSeekTimeUs);
+        mSeekTimeUs = -1;
+    }
+
+    status_t err = mSource->read(&buffer, &options);
+
+    if (err != OK) {
+        mReachedEOS = true;
+        mCondition.signal();
+
+        return;
+    }
+
+    CHECK(buffer != NULL);
+
+    MediaBuffer *copy = new MediaBuffer(buffer->range_length());
+    memcpy(copy->data(),
+           (const uint8_t *)buffer->data() + buffer->range_offset(),
+           buffer->range_length());
+
+    sp<MetaData> from = buffer->meta_data();
+    sp<MetaData> to = copy->meta_data();
+
+    int64_t timeUs;
+    if (from->findInt64(kKeyTime, &timeUs)) {
+        to->setInt64(kKeyTime, timeUs);
+    }
+
+    buffer->release();
+    buffer = NULL;
+
+    mCachedBuffers.push_back(copy);
+    updateCacheDuration_l();
+    mCondition.signal();
+}
+
+void PrefetchedSource::updateCacheDuration_l() {
+    if (mCachedBuffers.size() < 2) {
+        mCacheDurationUs = 0;
+    } else {
+        int64_t firstTimeUs, lastTimeUs;
+        CHECK((*mCachedBuffers.begin())->meta_data()->findInt64(
+                    kKeyTime, &firstTimeUs));
+        CHECK((*--mCachedBuffers.end())->meta_data()->findInt64(
+                    kKeyTime, &lastTimeUs));
+
+        mCacheDurationUs = lastTimeUs - firstTimeUs;
+    }
+}
+
+void PrefetchedSource::clearCache_l() {
+    List<MediaBuffer *>::iterator it = mCachedBuffers.begin();
+    while (it != mCachedBuffers.end()) {
+        (*it)->release();
+
+        it = mCachedBuffers.erase(it);
+    }
+
+    updateCacheDuration_l();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index b28a12c..c2e46c0 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -26,10 +26,11 @@
 
 namespace android {
 
+struct AudioPlayer;
 struct MediaBuffer;
 struct MediaExtractor;
 struct MediaSource;
-struct AudioPlayer;
+struct Prefetcher;
 struct TimeSource;
 
 struct AwesomeRenderer : public RefBase {
@@ -109,13 +110,18 @@
     bool mVideoEventPending;
     sp<TimedEventQueue::Event> mStreamDoneEvent;
     bool mStreamDoneEventPending;
+    sp<TimedEventQueue::Event> mBufferingEvent;
+    bool mBufferingEventPending;
 
     void postVideoEvent_l(int64_t delayUs = -1);
+    void postBufferingEvent_l();
     void postStreamDoneEvent_l();
 
     MediaBuffer *mLastVideoBuffer;
     MediaBuffer *mVideoBuffer;
 
+    sp<Prefetcher> mPrefetcher;
+
     status_t setDataSource_l(const sp<MediaExtractor> &extractor);
     void reset_l();
     status_t seekTo_l(int64_t timeUs);
@@ -123,17 +129,19 @@
     void initRenderer_l();
     void seekAudioIfNecessary_l();
 
-    void cancelPlayerEvents();
+    void cancelPlayerEvents(bool keepBufferingGoing = false);
 
-    status_t setAudioSource(const sp<MediaSource> &source);
-    status_t setVideoSource(const sp<MediaSource> &source);
+    status_t setAudioSource(sp<MediaSource> source);
+    status_t setVideoSource(sp<MediaSource> source);
 
     void onEvent(int32_t code);
 
     static void AudioNotify(void *me, int what);
     void onStreamDone();
 
-    void notifyListener_l(int msg);
+    void notifyListener_l(int msg, int ext1 = 0);
+
+    void onBufferingUpdate();
 
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h
new file mode 100644
index 0000000..7a97785
--- /dev/null
+++ b/media/libstagefright/include/Prefetcher.h
@@ -0,0 +1,63 @@
+/*
+ * 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 PREFETCHER_H_
+
+#define PREFETCHER_H_
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct MediaSource;
+struct PrefetchedSource;
+
+struct Prefetcher : public RefBase {
+    Prefetcher();
+
+    // Given an existing MediaSource returns a new MediaSource
+    // that will benefit from prefetching/caching the original one.
+    sp<MediaSource> addSource(const sp<MediaSource> &source);
+
+    int64_t getCachedDurationUs();
+
+protected:
+    virtual ~Prefetcher();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    Vector<wp<PrefetchedSource> > mSources;
+    android_thread_id_t mThread;
+    bool mDone;
+    bool mThreadExited;
+
+    void startThread();
+    void stopThread();
+
+    static int ThreadWrapper(void *me);
+    void threadFunc();
+
+    Prefetcher(const Prefetcher &);
+    Prefetcher &operator=(const Prefetcher &);
+};
+
+}  // namespace android
+
+#endif  // PREFETCHER_H_