Initial support for a true streaming player for mpeg2 transport streams.

Change-Id: I153eec439d260a5524b21270e16d36940ec3161a
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
new file mode 100644
index 0000000..c4f3764
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                       \
+        NuPlayer.cpp                    \
+        NuPlayerDecoder.cpp             \
+        NuPlayerDriver.cpp              \
+        NuPlayerRenderer.cpp            \
+        NuPlayerStreamListener.cpp      \
+        DecoderWrapper.cpp              \
+
+LOCAL_C_INCLUDES := \
+        $(TOP)/frameworks/base/include/media/stagefright/openmax        \
+	$(TOP)/frameworks/base/media/libstagefright/include             \
+        $(TOP)/frameworks/base/media/libstagefright/mpeg2ts             \
+
+LOCAL_MODULE:= libstagefright_nuplayer
+
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
new file mode 100644
index 0000000..89a5e69
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 "DecoderWrapper"
+#include <utils/Log.h>
+
+#include "DecoderWrapper.h"
+
+#include "AACDecoder.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/ACodec.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct DecoderWrapper::WrapperSource : public MediaSource {
+    WrapperSource(
+            const sp<MetaData> &meta,
+            const sp<AMessage> &notify);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+    void queueBuffer(const sp<ABuffer> &buffer);
+    void queueEOS(status_t finalResult);
+    void clear();
+
+protected:
+    virtual ~WrapperSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    sp<MetaData> mMeta;
+    sp<AMessage> mNotify;
+
+    List<sp<ABuffer> > mQueue;
+    status_t mFinalResult;
+
+    DISALLOW_EVIL_CONSTRUCTORS(WrapperSource);
+};
+
+DecoderWrapper::WrapperSource::WrapperSource(
+        const sp<MetaData> &meta, const sp<AMessage> &notify)
+    : mMeta(meta),
+      mNotify(notify),
+      mFinalResult(OK) {
+}
+
+DecoderWrapper::WrapperSource::~WrapperSource() {
+}
+
+status_t DecoderWrapper::WrapperSource::start(MetaData *params) {
+    return OK;
+}
+
+status_t DecoderWrapper::WrapperSource::stop() {
+    return OK;
+}
+
+sp<MetaData> DecoderWrapper::WrapperSource::getFormat() {
+    return mMeta;
+}
+
+status_t DecoderWrapper::WrapperSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    Mutex::Autolock autoLock(mLock);
+
+    bool requestedBuffer = false;
+
+    while (mQueue.empty() && mFinalResult == OK) {
+        if (!requestedBuffer) {
+            mNotify->dup()->post();
+            requestedBuffer = true;
+        }
+
+        mCondition.wait(mLock);
+    }
+
+    if (mQueue.empty()) {
+        return mFinalResult;
+    }
+
+    sp<ABuffer> src = *mQueue.begin();
+    mQueue.erase(mQueue.begin());
+
+    MediaBuffer *dst = new MediaBuffer(src->size());
+    memcpy(dst->data(), src->data(), src->size());
+
+    int64_t timeUs;
+    CHECK(src->meta()->findInt64("timeUs", &timeUs));
+
+    dst->meta_data()->setInt64(kKeyTime, timeUs);
+
+    *out = dst;
+
+    return OK;
+}
+
+void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) {
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(buffer);
+    mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    Mutex::Autolock autoLock(mLock);
+    mFinalResult = finalResult;
+    mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::clear() {
+    Mutex::Autolock autoLock(mLock);
+    mQueue.clear();
+    mFinalResult = OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct DecoderWrapper::WrapperReader : public AHandler {
+    WrapperReader(
+            const sp<MediaSource> &decoder,
+            const sp<AMessage> &notify);
+
+    void start();
+    void readMore(bool flush = false);
+
+protected:
+    virtual ~WrapperReader();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatRead
+    };
+
+    sp<MediaSource> mDecoder;
+    sp<AMessage> mNotify;
+    bool mEOS;
+
+    DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
+};
+
+DecoderWrapper::WrapperReader::WrapperReader(
+        const sp<MediaSource> &decoder, const sp<AMessage> &notify)
+    : mDecoder(decoder),
+      mNotify(notify),
+      mEOS(false) {
+}
+
+DecoderWrapper::WrapperReader::~WrapperReader() {
+}
+
+void DecoderWrapper::WrapperReader::start() {
+    CHECK_EQ(mDecoder->start(), (status_t)OK);
+    readMore();
+}
+
+void DecoderWrapper::WrapperReader::readMore(bool flush) {
+    if (!flush && mEOS) {
+        return;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatRead, id());
+    msg->setInt32("flush", static_cast<int32_t>(flush));
+    msg->post();
+}
+
+void DecoderWrapper::WrapperReader::onMessageReceived(
+        const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRead:
+        {
+            int32_t flush;
+            CHECK(msg->findInt32("flush", &flush));
+
+            MediaSource::ReadOptions options;
+            if (flush) {
+                // Dummy seek
+                options.setSeekTo(0);
+                mEOS = false;
+            }
+
+            CHECK(!mEOS);
+
+            MediaBuffer *src;
+            status_t err = mDecoder->read(&src, &options);
+
+            sp<AMessage> notify = mNotify->dup();
+
+            sp<AMessage> realNotify;
+            CHECK(notify->findMessage("real-notify", &realNotify));
+
+            if (err == OK) {
+                realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+
+                sp<ABuffer> dst = new ABuffer(src->range_length());
+                memcpy(dst->data(),
+                       (const uint8_t *)src->data() + src->range_offset(),
+                       src->range_length());
+
+                int64_t timeUs;
+                CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs));
+                src->release();
+                src = NULL;
+
+                dst->meta()->setInt64("timeUs", timeUs);
+
+                realNotify->setObject("buffer", dst);
+            } else {
+                realNotify->setInt32("what", ACodec::kWhatEOS);
+                mEOS = true;
+            }
+
+            notify->post();
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DecoderWrapper::DecoderWrapper()
+    : mNumOutstandingInputBuffers(0),
+      mNumOutstandingOutputBuffers(0),
+      mNumPendingDecodes(0),
+      mFlushing(false) {
+}
+
+DecoderWrapper::~DecoderWrapper() {
+}
+
+void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) {
+    mNotify = msg;
+}
+
+void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatSetup);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void DecoderWrapper::initiateShutdown() {
+    (new AMessage(kWhatShutdown, id()))->post();
+}
+
+void DecoderWrapper::signalFlush() {
+    (new AMessage(kWhatFlush, id()))->post();
+}
+
+void DecoderWrapper::signalResume() {
+    (new AMessage(kWhatResume, id()))->post();
+}
+
+void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetup:
+            onSetup(msg);
+            break;
+
+        case kWhatInputDataRequested:
+        {
+            postFillBuffer();
+            ++mNumOutstandingInputBuffers;
+            break;
+        }
+
+        case kWhatInputBufferFilled:
+        {
+            CHECK_GT(mNumOutstandingInputBuffers, 0);
+            --mNumOutstandingInputBuffers;
+
+            if (mFlushing) {
+                mSource->queueEOS(INFO_DISCONTINUITY);
+
+                completeFlushIfPossible();
+                break;
+            }
+
+            sp<RefBase> obj;
+            if (!msg->findObject("buffer", &obj)) {
+                int32_t err = OK;
+                CHECK(msg->findInt32("err", &err));
+
+                mSource->queueEOS(err);
+                break;
+            }
+
+            sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+            mSource->queueBuffer(buffer);
+            break;
+        }
+
+        case kWhatFillBufferDone:
+        {
+            CHECK_GT(mNumPendingDecodes, 0);
+            --mNumPendingDecodes;
+
+            if (mFlushing) {
+                completeFlushIfPossible();
+                break;
+            }
+
+            sp<AMessage> notify;
+            CHECK(msg->findMessage("real-notify", &notify));
+
+            sp<AMessage> reply =
+                new AMessage(kWhatOutputBufferDrained, id());
+
+            notify->setMessage("reply", reply);
+            notify->post();
+
+            ++mNumOutstandingOutputBuffers;
+            break;
+        }
+
+        case kWhatOutputBufferDrained:
+        {
+            CHECK_GT(mNumOutstandingOutputBuffers, 0);
+            --mNumOutstandingOutputBuffers;
+
+            if (mFlushing) {
+                completeFlushIfPossible();
+                break;
+            }
+
+            ++mNumPendingDecodes;
+            mReader->readMore();
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            onFlush();
+            break;
+        }
+
+        case kWhatResume:
+        {
+            onResume();
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void DecoderWrapper::onSetup(const sp<AMessage> &msg) {
+    AString mime;
+    CHECK(msg->findString("mime", &mime));
+
+    CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC));
+
+    int32_t numChannels, sampleRate;
+    CHECK(msg->findInt32("channel-count", &numChannels));
+    CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("esds", &obj));
+    sp<ABuffer> esds = static_cast<ABuffer *>(obj.get());
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, mime.c_str());
+    meta->setInt32(kKeySampleRate, sampleRate);
+    meta->setInt32(kKeyChannelCount, numChannels);
+    meta->setData(kKeyESDS, 0, esds->data(), esds->size());
+
+    mSource = new WrapperSource(
+            meta, new AMessage(kWhatInputDataRequested, id()));
+
+    sp<MediaSource> decoder = new AACDecoder(mSource);
+
+    mReaderLooper = new ALooper;
+    mReaderLooper->setName("DecoderWrapper looper");
+
+    mReaderLooper->start(
+            false, /* runOnCallingThread */
+            false, /* canCallJava */
+            PRIORITY_AUDIO);
+
+    sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id());
+    notify->setMessage("real-notify", mNotify);
+
+    mReader = new WrapperReader(decoder, notify);
+    mReaderLooper->registerHandler(mReader);
+
+    mReader->start();
+    ++mNumPendingDecodes;
+}
+
+void DecoderWrapper::postFillBuffer() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
+    notify->setMessage("reply", reply);
+    notify->post();
+}
+
+void DecoderWrapper::onFlush() {
+    CHECK(!mFlushing);
+    mFlushing = true;
+
+    completeFlushIfPossible();
+}
+
+void DecoderWrapper::completeFlushIfPossible() {
+    CHECK(mFlushing);
+
+    if (mNumOutstandingInputBuffers > 0
+            || mNumOutstandingOutputBuffers > 0
+            || mNumPendingDecodes > 0) {
+        return;
+    }
+
+    mFlushing = false;
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFlushCompleted);
+    notify->post();
+}
+
+void DecoderWrapper::onResume() {
+    CHECK(!mFlushing);
+
+    ++mNumPendingDecodes;
+
+    mSource->clear();
+    mReader->readMore(true /* flush */);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
new file mode 100644
index 0000000..883b356
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * 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 DECODER_WRAPPER_H_
+
+#define DECODER_WRAPPER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct MediaSource;
+
+struct DecoderWrapper : public AHandler {
+    DecoderWrapper();
+
+    void setNotificationMessage(const sp<AMessage> &msg);
+    void initiateSetup(const sp<AMessage> &msg);
+    void initiateShutdown();
+    void signalFlush();
+    void signalResume();
+
+protected:
+    virtual ~DecoderWrapper();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    struct WrapperSource;
+    struct WrapperReader;
+
+    enum {
+        kWhatSetup,
+        kWhatInputBufferFilled,
+        kWhatOutputBufferDrained,
+        kWhatShutdown,
+        kWhatFillBufferDone,
+        kWhatInputDataRequested,
+        kWhatFlush,
+        kWhatResume,
+    };
+
+    sp<AMessage> mNotify;
+
+    sp<WrapperSource> mSource;
+
+    sp<ALooper> mReaderLooper;
+    sp<WrapperReader> mReader;
+
+    int32_t mNumOutstandingInputBuffers;
+    int32_t mNumOutstandingOutputBuffers;
+    int32_t mNumPendingDecodes;
+    bool mFlushing;
+
+    void onSetup(const sp<AMessage> &msg);
+    void onFlush();
+    void onResume();
+
+    void postFillBuffer();
+    void completeFlushIfPossible();
+
+    DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper);
+};
+
+}  // namespace android
+
+#endif  // DECODER_WRAPPER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
new file mode 100644
index 0000000..403029a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 "NuPlayer"
+#include <utils/Log.h>
+
+#include "NuPlayer.h"
+#include "NuPlayerDecoder.h"
+#include "NuPlayerRenderer.h"
+#include "NuPlayerStreamListener.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuPlayer::NuPlayer()
+    : mEOS(false),
+      mAudioEOS(false),
+      mVideoEOS(false),
+      mFlushingAudio(NONE),
+      mFlushingVideo(NONE) {
+}
+
+NuPlayer::~NuPlayer() {
+}
+
+void NuPlayer::setListener(const wp<MediaPlayerBase> &listener) {
+    mListener = listener;
+}
+
+void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+
+    source->incStrong(this);
+    msg->setPointer("source", source.get());  // XXX unsafe.
+
+    msg->post();
+}
+
+void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, id());
+    msg->setObject("surface", surface);
+    msg->post();
+}
+
+void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
+    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+    msg->setObject("sink", sink);
+    msg->post();
+}
+
+void NuPlayer::start() {
+    (new AMessage(kWhatStart, id()))->post();
+}
+
+void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetDataSource:
+        {
+            LOGI("kWhatSetDataSource");
+
+            CHECK(mSource == NULL);
+
+            void *ptr;
+            CHECK(msg->findPointer("source", &ptr));
+
+            mSource = static_cast<IStreamSource *>(ptr);
+            mSource->decStrong(this);
+
+            mStreamListener = new NuPlayerStreamListener(mSource, id());
+            mTSParser = new ATSParser;
+            break;
+        }
+
+        case kWhatSetVideoSurface:
+        {
+            LOGI("kWhatSetVideoSurface");
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("surface", &obj));
+
+            mSurface = static_cast<Surface *>(obj.get());
+            break;
+        }
+
+        case kWhatSetAudioSink:
+        {
+            LOGI("kWhatSetAudioSink");
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("sink", &obj));
+
+            mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
+            break;
+        }
+
+        case kWhatStart:
+        {
+            mStreamListener->start();
+
+            mRenderer = new Renderer(
+                    mAudioSink,
+                    new AMessage(kWhatRendererNotify, id()));
+
+            looper()->registerHandler(mRenderer);
+
+            (new AMessage(kWhatScanSources, id()))->post();
+            break;
+        }
+
+        case kWhatScanSources:
+        {
+            instantiateDecoder(false, &mVideoDecoder);
+
+            if (mAudioSink != NULL) {
+                instantiateDecoder(true, &mAudioDecoder);
+            }
+
+            if (mEOS) {
+                break;
+            }
+
+            feedMoreTSData();
+
+            if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
+                msg->post(100000ll);
+            }
+            break;
+        }
+
+        case kWhatVideoNotify:
+        case kWhatAudioNotify:
+        {
+            bool audio = msg->what() == kWhatAudioNotify;
+
+            sp<AMessage> codecRequest;
+            CHECK(msg->findMessage("codec-request", &codecRequest));
+
+            int32_t what;
+            CHECK(codecRequest->findInt32("what", &what));
+
+            if (what == ACodec::kWhatFillThisBuffer) {
+                status_t err = feedDecoderInputData(
+                        audio, codecRequest);
+
+                if (err == -EWOULDBLOCK && !mEOS) {
+                    feedMoreTSData();
+                    msg->post();
+                }
+            } else if (what == ACodec::kWhatEOS) {
+                mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+            } else if (what == ACodec::kWhatFlushCompleted) {
+                if (audio) {
+                    CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+                    mFlushingAudio = FLUSHED;
+                } else {
+                    CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+                    mFlushingVideo = FLUSHED;
+                }
+
+                LOGI("decoder %s flush completed", audio ? "audio" : "video");
+
+                if (mFlushingAudio == FLUSHED && mFlushingVideo == FLUSHED) {
+                    LOGI("both audio and video are flushed now.");
+
+                    mRenderer->signalTimeDiscontinuity();
+
+                    if (mAudioDecoder != NULL) {
+                        mAudioDecoder->signalResume();
+                    }
+
+                    if (mVideoDecoder != NULL) {
+                        mVideoDecoder->signalResume();
+                    }
+
+                    mFlushingAudio = NONE;
+                    mFlushingVideo = NONE;
+                }
+            } else {
+                CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
+
+                renderBuffer(audio, codecRequest);
+            }
+
+            break;
+        }
+
+        case kWhatRendererNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == Renderer::kWhatEOS) {
+                int32_t audio;
+                CHECK(msg->findInt32("audio", &audio));
+
+                if (audio) {
+                    mAudioEOS = true;
+                } else {
+                    mVideoEOS = true;
+                }
+
+                LOGI("reached %s EOS", audio ? "audio" : "video");
+
+                if ((mAudioEOS || mAudioDecoder == NULL)
+                        && (mVideoEOS || mVideoDecoder == NULL)) {
+                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
+                }
+            } else {
+                CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
+
+                int32_t audio;
+                CHECK(msg->findInt32("audio", &audio));
+
+                LOGI("renderer %s flush completed.", audio ? "audio" : "video");
+            }
+            break;
+        }
+
+        case kWhatMoreDataQueued:
+        {
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void NuPlayer::feedMoreTSData() {
+    CHECK(!mEOS);
+
+    for (int32_t i = 0; i < 10; ++i) {
+        char buffer[188];
+        ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+
+        if (n == 0) {
+            LOGI("input data EOS reached.");
+            mTSParser->signalEOS(ERROR_END_OF_STREAM);
+            mEOS = true;
+            break;
+        } else if (n == INFO_DISCONTINUITY) {
+            mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+        } else if (n < 0) {
+            CHECK_EQ(n, -EWOULDBLOCK);
+            break;
+        } else {
+            if (buffer[0] == 0x00) {
+                // XXX legacy
+                mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+            } else {
+                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+            }
+        }
+    }
+}
+
+status_t NuPlayer::dequeueNextAccessUnit(
+        ATSParser::SourceType *type, sp<ABuffer> *accessUnit) {
+    accessUnit->clear();
+
+    status_t audioErr = -EWOULDBLOCK;
+    int64_t audioTimeUs;
+
+    sp<AnotherPacketSource> audioSource =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(ATSParser::MPEG2ADTS_AUDIO).get());
+
+    if (audioSource != NULL) {
+        audioErr = audioSource->nextBufferTime(&audioTimeUs);
+    }
+
+    status_t videoErr = -EWOULDBLOCK;
+    int64_t videoTimeUs;
+
+    sp<AnotherPacketSource> videoSource =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(ATSParser::AVC_VIDEO).get());
+
+    if (videoSource != NULL) {
+        videoErr = videoSource->nextBufferTime(&videoTimeUs);
+    }
+
+    if (audioErr == -EWOULDBLOCK || videoErr == -EWOULDBLOCK) {
+        return -EWOULDBLOCK;
+    }
+
+    if (audioErr != OK && videoErr != OK) {
+        return audioErr;
+    }
+
+    if (videoErr != OK || (audioErr == OK && audioTimeUs < videoTimeUs)) {
+        *type = ATSParser::MPEG2ADTS_AUDIO;
+        return audioSource->dequeueAccessUnit(accessUnit);
+    } else {
+        *type = ATSParser::AVC_VIDEO;
+        return videoSource->dequeueAccessUnit(accessUnit);
+    }
+}
+
+status_t NuPlayer::dequeueAccessUnit(
+        ATSParser::SourceType type, sp<ABuffer> *accessUnit) {
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return -EWOULDBLOCK;
+    }
+
+    status_t finalResult;
+    if (!source->hasBufferAvailable(&finalResult)) {
+        return finalResult == OK ? -EWOULDBLOCK : finalResult;
+    }
+
+    return source->dequeueAccessUnit(accessUnit);
+}
+
+status_t NuPlayer::instantiateDecoder(
+        bool audio, sp<Decoder> *decoder) {
+    if (*decoder != NULL) {
+        return OK;
+    }
+
+    ATSParser::SourceType type =
+        audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO;
+
+    sp<AnotherPacketSource> source =
+        static_cast<AnotherPacketSource *>(
+                mTSParser->getSource(type).get());
+
+    if (source == NULL) {
+        return -EWOULDBLOCK;
+    }
+
+    sp<AMessage> notify =
+        new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
+                     id());
+
+    *decoder = new Decoder(notify, audio ? NULL : mSurface);
+    looper()->registerHandler(*decoder);
+
+    const sp<MetaData> &meta = source->getFormat();
+    (*decoder)->configure(meta);
+
+    if (audio) {
+        int32_t sampleRate;
+        int32_t channelCount;
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+        CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+
+        channelCount = 2;  // XXX
+
+        CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
+        mAudioSink->start();
+    }
+
+    return OK;
+}
+
+status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    if ((audio && mFlushingAudio == FLUSHING_DECODER)
+            || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+        reply->setInt32("err", INFO_DISCONTINUITY);
+        reply->post();
+        return OK;
+    }
+
+    sp<ABuffer> accessUnit;
+    status_t err = dequeueAccessUnit(
+            audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO,
+            &accessUnit);
+
+    if (err == -EWOULDBLOCK) {
+        return err;
+    } else if (err != OK) {
+        if (err == INFO_DISCONTINUITY) {
+            LOGI("%s discontinuity", audio ? "audio" : "video");
+            (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+            mRenderer->flush(audio);
+
+            if (audio) {
+                CHECK(mFlushingAudio == NONE
+                        || mFlushingAudio == AWAITING_DISCONTINUITY);
+                mFlushingAudio = FLUSHING_DECODER;
+                if (mFlushingVideo == NONE) {
+                    mFlushingVideo = (mVideoDecoder != NULL)
+                        ? AWAITING_DISCONTINUITY
+                        : FLUSHED;
+                }
+            } else {
+                CHECK(mFlushingVideo == NONE
+                        || mFlushingVideo == AWAITING_DISCONTINUITY);
+                mFlushingVideo = FLUSHING_DECODER;
+                if (mFlushingAudio == NONE) {
+                    mFlushingAudio = (mAudioDecoder != NULL)
+                        ? AWAITING_DISCONTINUITY
+                        : FLUSHED;
+                }
+            }
+        }
+
+        reply->setInt32("err", err);
+        reply->post();
+        return OK;
+    }
+
+    LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
+
+#if 0
+    int64_t mediaTimeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
+    LOGI("feeding %s input buffer at media time %.2f secs",
+         audio ? "audio" : "video",
+         mediaTimeUs / 1E6);
+#endif
+
+    reply->setObject("buffer", accessUnit);
+    reply->post();
+
+    return OK;
+}
+
+void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
+    LOGV("renderBuffer %s", audio ? "audio" : "video");
+
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    mRenderer->queueBuffer(audio, buffer, reply);
+}
+
+void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    sp<MediaPlayerBase> listener = mListener.promote();
+
+    if (listener == NULL) {
+        return;
+    }
+
+    listener->sendEvent(msg, ext1, ext2);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
new file mode 100644
index 0000000..9a5a6c4
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -0,0 +1,108 @@
+/*
+ * 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 NU_PLAYER_H_
+
+#define NU_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "AnotherPacketSource.h"
+
+namespace android {
+
+struct ACodec;
+struct MetaData;
+
+struct NuPlayer : public AHandler {
+    NuPlayer();
+
+    void setListener(const wp<MediaPlayerBase> &listener);
+
+    void setDataSource(const sp<IStreamSource> &source);
+    void setVideoSurface(const sp<Surface> &surface);
+    void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
+    void start();
+
+protected:
+    virtual ~NuPlayer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    struct Renderer;
+    struct Decoder;
+    struct NuPlayerStreamListener;
+
+    enum {
+        kWhatSetDataSource,
+        kWhatSetVideoSurface,
+        kWhatSetAudioSink,
+        kWhatMoreDataQueued,
+        kWhatStart,
+        kWhatScanSources,
+        kWhatVideoNotify,
+        kWhatAudioNotify,
+        kWhatRendererNotify,
+    };
+
+    wp<MediaPlayerBase> mListener;
+    sp<IStreamSource> mSource;
+    sp<Surface> mSurface;
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<NuPlayerStreamListener> mStreamListener;
+    sp<ATSParser> mTSParser;
+    sp<Decoder> mVideoDecoder;
+    sp<Decoder> mAudioDecoder;
+    sp<Renderer> mRenderer;
+
+    bool mEOS;
+    bool mAudioEOS;
+    bool mVideoEOS;
+
+    enum FlushStatus {
+        NONE,
+        AWAITING_DISCONTINUITY,
+        FLUSHING_DECODER,
+        FLUSHED
+    };
+
+    FlushStatus mFlushingAudio;
+    FlushStatus mFlushingVideo;
+
+    status_t instantiateDecoder(
+            bool audio, sp<Decoder> *decoder);
+
+    status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
+    void renderBuffer(bool audio, const sp<AMessage> &msg);
+
+    status_t dequeueNextAccessUnit(
+            ATSParser::SourceType *type, sp<ABuffer> *accessUnit);
+
+    status_t dequeueAccessUnit(
+            ATSParser::SourceType type, sp<ABuffer> *accessUnit);
+
+    void feedMoreTSData();
+    void notifyListener(int msg, int ext1, int ext2);
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
+};
+
+}  // namespace android
+
+#endif  // NU_PLAYER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
new file mode 100644
index 0000000..d1ed222
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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 "NuPlayerDecoder"
+#include <utils/Log.h>
+
+#include "NuPlayerDecoder.h"
+
+#include "DecoderWrapper.h"
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+NuPlayer::Decoder::Decoder(
+        const sp<AMessage> &notify, const sp<Surface> &surface)
+    : mNotify(notify),
+      mSurface(surface) {
+}
+
+NuPlayer::Decoder::~Decoder() {
+}
+
+void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+    CHECK(mCodec == NULL);
+    CHECK(mWrapper == NULL);
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> notifyMsg =
+        new AMessage(kWhatCodecNotify, id());
+
+    sp<AMessage> format = makeFormat(meta);
+
+    if (mSurface != NULL) {
+        format->setObject("surface", mSurface);
+    }
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        mWrapper = new DecoderWrapper;
+        looper()->registerHandler(mWrapper);
+
+        mWrapper->setNotificationMessage(notifyMsg);
+        mWrapper->initiateSetup(format);
+    } else {
+        mCodec = new ACodec;
+        looper()->registerHandler(mCodec);
+
+        mCodec->setNotificationMessage(notifyMsg);
+        mCodec->initiateSetup(format);
+    }
+}
+
+void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatCodecNotify:
+        {
+            int32_t what;
+            CHECK(msg->findInt32("what", &what));
+
+            if (what == ACodec::kWhatFillThisBuffer) {
+                onFillThisBuffer(msg);
+            } else {
+                sp<AMessage> notify = mNotify->dup();
+                notify->setMessage("codec-request", msg);
+                notify->post();
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
+    CHECK(mCSD.isEmpty());
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    sp<AMessage> msg = new AMessage;
+    msg->setString("mime", mime);
+
+    if (!strncasecmp("video/", mime, 6)) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        msg->setInt32("width", width);
+        msg->setInt32("height", height);
+    } else {
+        CHECK(!strncasecmp("audio/", mime, 6));
+
+        int32_t numChannels, sampleRate;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        msg->setInt32("channel-count", numChannels);
+        msg->setInt32("sample-rate", sampleRate);
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        // Parse the AVCDecoderConfigurationRecord
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        uint8_t profile = ptr[1];
+        uint8_t level = ptr[3];
+
+        // There is decodable content out there that fails the following
+        // assertion, let's be lenient for now...
+        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+        size_t lengthSize = 1 + (ptr[4] & 3);
+
+        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+        // violates it...
+        // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+        size_t numSeqParameterSets = ptr[5] & 31;
+
+        ptr += 6;
+        size -= 6;
+
+        sp<ABuffer> buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        for (size_t i = 0; i < numSeqParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+
+        buffer = new ABuffer(1024);
+        buffer->setRange(0, 0);
+
+        CHECK(size >= 1);
+        size_t numPictureParameterSets = *ptr;
+        ++ptr;
+        --size;
+
+        for (size_t i = 0; i < numPictureParameterSets; ++i) {
+            CHECK(size >= 2);
+            size_t length = U16_AT(ptr);
+
+            ptr += 2;
+            size -= 2;
+
+            CHECK(size >= length);
+
+            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+            buffer->setRange(0, buffer->size() + 4 + length);
+
+            ptr += length;
+            size -= length;
+        }
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+
+        msg->setObject("csd", buffer);
+    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+#if 0
+        ESDS esds((const char *)data, size);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+        const void *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                &codec_specific_data, &codec_specific_data_size);
+
+        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+        memcpy(buffer->data(), codec_specific_data,
+               codec_specific_data_size);
+
+        buffer->meta()->setInt32("csd", true);
+        mCSD.push(buffer);
+#else
+        sp<ABuffer> buffer = new ABuffer(size);
+        memcpy(buffer->data(), data, size);
+
+        msg->setObject("esds", buffer);
+#endif
+    }
+
+    int32_t maxInputSize;
+    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+        msg->setInt32("max-input-size", maxInputSize);
+    }
+
+    mCSDIndex = 0;
+
+    return msg;
+}
+
+void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+#if 0
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+    sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+#else
+    sp<ABuffer> outBuffer;
+#endif
+
+    if (mCSDIndex < mCSD.size()) {
+        outBuffer = mCSD.editItemAt(mCSDIndex++);
+        outBuffer->meta()->setInt64("timeUs", 0);
+
+        reply->setObject("buffer", outBuffer);
+        reply->post();
+        return;
+    }
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setMessage("codec-request", msg);
+    notify->post();
+}
+
+void NuPlayer::Decoder::signalFlush() {
+    if (mCodec != NULL) {
+        mCodec->signalFlush();
+    } else {
+        CHECK(mWrapper != NULL);
+        mWrapper->signalFlush();
+    }
+}
+
+void NuPlayer::Decoder::signalResume() {
+    if (mCodec != NULL) {
+        mCodec->signalResume();
+    } else {
+        CHECK(mWrapper != NULL);
+        mWrapper->signalResume();
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
new file mode 100644
index 0000000..77800be
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -0,0 +1,64 @@
+/*
+ * 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 NUPLAYER_DECODER_H_
+
+#define NUPLAYER_DECODER_H_
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct DecoderWrapper;
+
+struct NuPlayer::Decoder : public AHandler {
+    Decoder(const sp<AMessage> &notify, const sp<Surface> &surface = NULL);
+
+    void configure(const sp<MetaData> &meta);
+    void signalFlush();
+    void signalResume();
+
+protected:
+    virtual ~Decoder();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatCodecNotify,
+    };
+
+    sp<AMessage> mNotify;
+    sp<Surface> mSurface;
+
+    sp<ACodec> mCodec;
+    sp<DecoderWrapper> mWrapper;
+
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+
+    sp<AMessage> makeFormat(const sp<MetaData> &meta);
+
+    void onFillThisBuffer(const sp<AMessage> &msg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(Decoder);
+};
+
+}  // namespace android
+
+#endif  // NUPLAYER_DECODER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
new file mode 100644
index 0000000..b79251a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "NuPlayerDriver"
+#include <utils/Log.h>
+
+#include "NuPlayerDriver.h"
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+
+namespace android {
+
+NuPlayerDriver::NuPlayerDriver()
+    : mLooper(new ALooper) {
+    mLooper->setName("NuPlayerDriver Looper");
+
+    mLooper->start(
+            false, /* runOnCallingThread */
+            true,  /* canCallJava */
+            PRIORITY_AUDIO);
+
+    mPlayer = new NuPlayer;
+    mLooper->registerHandler(mPlayer);
+
+    mPlayer->setListener(this);
+}
+
+NuPlayerDriver::~NuPlayerDriver() {
+    mLooper->stop();
+}
+
+status_t NuPlayerDriver::initCheck() {
+    return OK;
+}
+
+status_t NuPlayerDriver::setDataSource(
+        const char *url, const KeyedVector<String8, String8> *headers) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+    mPlayer->setDataSource(source);
+
+    return OK;
+}
+
+status_t NuPlayerDriver::setVideoSurface(const sp<Surface> &surface) {
+    mPlayer->setVideoSurface(surface);
+
+    return OK;
+}
+
+status_t NuPlayerDriver::prepare() {
+    return OK;
+}
+
+status_t NuPlayerDriver::prepareAsync() {
+    return OK;
+}
+
+status_t NuPlayerDriver::start() {
+    mPlayer->start();
+
+    return OK;
+}
+
+status_t NuPlayerDriver::stop() {
+    return OK;
+}
+
+status_t NuPlayerDriver::pause() {
+    return OK;
+}
+
+bool NuPlayerDriver::isPlaying() {
+    return false;
+}
+
+status_t NuPlayerDriver::seekTo(int msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getCurrentPosition(int *msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getDuration(int *msec) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::reset() {
+    return OK;
+}
+
+status_t NuPlayerDriver::setLooping(int loop) {
+    return INVALID_OPERATION;
+}
+
+player_type NuPlayerDriver::playerType() {
+    return NU_PLAYER;
+}
+
+status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
+    return INVALID_OPERATION;
+}
+
+void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
+    mPlayer->setAudioSink(audioSink);
+}
+
+status_t NuPlayerDriver::getMetadata(
+        const media::Metadata::Filter& ids, Parcel *records) {
+    return INVALID_OPERATION;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
new file mode 100644
index 0000000..245f1dd
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include <media/MediaPlayerInterface.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ALooper;
+struct NuPlayer;
+
+struct NuPlayerDriver : public MediaPlayerInterface {
+    NuPlayerDriver();
+
+    virtual status_t initCheck();
+
+    virtual status_t setDataSource(
+            const char *url, const KeyedVector<String8, String8> *headers);
+
+    virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+    virtual status_t setDataSource(const sp<IStreamSource> &source);
+
+    virtual status_t setVideoSurface(const sp<Surface> &surface);
+    virtual status_t prepare();
+    virtual status_t prepareAsync();
+    virtual status_t start();
+    virtual status_t stop();
+    virtual status_t pause();
+    virtual bool isPlaying();
+    virtual status_t seekTo(int msec);
+    virtual status_t getCurrentPosition(int *msec);
+    virtual status_t getDuration(int *msec);
+    virtual status_t reset();
+    virtual status_t setLooping(int loop);
+    virtual player_type playerType();
+    virtual status_t invoke(const Parcel &request, Parcel *reply);
+    virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+    virtual status_t getMetadata(
+            const media::Metadata::Filter& ids, Parcel *records);
+
+protected:
+    virtual ~NuPlayerDriver();
+
+private:
+    sp<ALooper> mLooper;
+    sp<NuPlayer> mPlayer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
+};
+
+}  // namespace android
+
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
new file mode 100644
index 0000000..855bc0a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -0,0 +1,496 @@
+/*
+ * 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 "NuPlayerRenderer"
+#include <utils/Log.h>
+
+#include "NuPlayerRenderer.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::Renderer::Renderer(
+        const sp<MediaPlayerBase::AudioSink> &sink,
+        const sp<AMessage> &notify)
+    : mAudioSink(sink),
+      mNotify(notify),
+      mNumFramesWritten(0),
+      mDrainAudioQueuePending(false),
+      mDrainVideoQueuePending(false),
+      mAudioQueueGeneration(0),
+      mVideoQueueGeneration(0),
+      mAnchorTimeMediaUs(-1),
+      mAnchorTimeRealUs(-1),
+      mFlushingAudio(false),
+      mFlushingVideo(false),
+      mSyncQueues(true) {
+}
+
+NuPlayer::Renderer::~Renderer() {
+}
+
+void NuPlayer::Renderer::queueBuffer(
+        bool audio,
+        const sp<ABuffer> &buffer,
+        const sp<AMessage> &notifyConsumed) {
+    sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->setObject("buffer", buffer);
+    msg->setMessage("notifyConsumed", notifyConsumed);
+    msg->post();
+}
+
+void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->setInt32("finalResult", finalResult);
+    msg->post();
+}
+
+void NuPlayer::Renderer::flush(bool audio) {
+    {
+        Mutex::Autolock autoLock(mFlushLock);
+        if (audio) {
+            CHECK(!mFlushingAudio);
+            mFlushingAudio = true;
+        } else {
+            CHECK(!mFlushingVideo);
+            mFlushingVideo = true;
+        }
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+    msg->setInt32("audio", static_cast<int32_t>(audio));
+    msg->post();
+}
+
+void NuPlayer::Renderer::signalTimeDiscontinuity() {
+    CHECK(mAudioQueue.empty());
+    CHECK(mVideoQueue.empty());
+    mAnchorTimeMediaUs = -1;
+    mAnchorTimeRealUs = -1;
+    mSyncQueues = true;
+}
+
+void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatDrainAudioQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mAudioQueueGeneration) {
+                break;
+            }
+
+            mDrainAudioQueuePending = false;
+
+            onDrainAudioQueue();
+
+            postDrainAudioQueue();
+            break;
+        }
+
+        case kWhatDrainVideoQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+            if (generation != mVideoQueueGeneration) {
+                break;
+            }
+
+            mDrainVideoQueuePending = false;
+
+            onDrainVideoQueue();
+
+            postDrainVideoQueue();
+            break;
+        }
+
+        case kWhatQueueBuffer:
+        {
+            onQueueBuffer(msg);
+            break;
+        }
+
+        case kWhatQueueEOS:
+        {
+            onQueueEOS(msg);
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            onFlush(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void NuPlayer::Renderer::postDrainAudioQueue() {
+    if (mDrainAudioQueuePending || mSyncQueues) {
+        return;
+    }
+
+    if (mAudioQueue.empty()) {
+        return;
+    }
+
+    mDrainAudioQueuePending = true;
+    sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
+    msg->setInt32("generation", mAudioQueueGeneration);
+    msg->post(10000);
+}
+
+void NuPlayer::Renderer::onDrainAudioQueue() {
+    uint32_t numFramesPlayed;
+    CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+    ssize_t numFramesAvailableToWrite =
+        mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+    CHECK_GE(numFramesAvailableToWrite, 0);
+
+    size_t numBytesAvailableToWrite =
+        numFramesAvailableToWrite * mAudioSink->frameSize();
+
+    while (numBytesAvailableToWrite > 0) {
+        if (mAudioQueue.empty()) {
+            break;
+        }
+
+        QueueEntry *entry = &*mAudioQueue.begin();
+
+        if (entry->mBuffer == NULL) {
+            // EOS
+
+            notifyEOS(true /* audio */);
+
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+            return;
+        }
+
+        if (entry->mOffset == 0) {
+            int64_t mediaTimeUs;
+            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+            LOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+
+            mAnchorTimeMediaUs = mediaTimeUs;
+
+            uint32_t numFramesPlayed;
+            CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+            uint32_t numFramesPendingPlayout =
+                mNumFramesWritten - numFramesPlayed;
+
+            int64_t realTimeOffsetUs =
+                (mAudioSink->latency() / 2  /* XXX */
+                    + numFramesPendingPlayout
+                        * mAudioSink->msecsPerFrame()) * 1000ll;
+
+            // LOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
+
+            mAnchorTimeRealUs =
+                ALooper::GetNowUs() + realTimeOffsetUs;
+        }
+
+        size_t copy = entry->mBuffer->size() - entry->mOffset;
+        if (copy > numBytesAvailableToWrite) {
+            copy = numBytesAvailableToWrite;
+        }
+
+        CHECK_EQ(mAudioSink->write(
+                    entry->mBuffer->data() + entry->mOffset, copy),
+                 (ssize_t)copy);
+
+        entry->mOffset += copy;
+        if (entry->mOffset == entry->mBuffer->size()) {
+            entry->mNotifyConsumed->post();
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+        }
+
+        numBytesAvailableToWrite -= copy;
+        mNumFramesWritten += copy / mAudioSink->frameSize();
+    }
+}
+
+void NuPlayer::Renderer::postDrainVideoQueue() {
+    if (mDrainVideoQueuePending || mSyncQueues) {
+        return;
+    }
+
+    if (mVideoQueue.empty()) {
+        return;
+    }
+
+    QueueEntry &entry = *mVideoQueue.begin();
+
+    sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
+    msg->setInt32("generation", mVideoQueueGeneration);
+
+    int64_t delayUs;
+
+    if (entry.mBuffer == NULL) {
+        // EOS doesn't carry a timestamp.
+        delayUs = 0;
+    } else {
+        int64_t mediaTimeUs;
+        CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+        if (mAnchorTimeMediaUs < 0) {
+            delayUs = 0;
+
+            if (mAudioSink == NULL) {
+                mAnchorTimeMediaUs = mediaTimeUs;
+                mAnchorTimeRealUs = ALooper::GetNowUs();
+            }
+        } else {
+            int64_t realTimeUs =
+                (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
+
+            delayUs = realTimeUs - ALooper::GetNowUs();
+        }
+    }
+
+    msg->post(delayUs);
+
+    mDrainVideoQueuePending = true;
+}
+
+void NuPlayer::Renderer::onDrainVideoQueue() {
+    if (mVideoQueue.empty()) {
+        return;
+    }
+
+    QueueEntry *entry = &*mVideoQueue.begin();
+
+    if (entry->mBuffer == NULL) {
+        // EOS
+
+        notifyEOS(false /* audio */);
+
+        mVideoQueue.erase(mVideoQueue.begin());
+        entry = NULL;
+        return;
+    }
+
+#if 0
+    int64_t mediaTimeUs;
+    CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+    LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+#endif
+
+    entry->mNotifyConsumed->setInt32("render", true);
+    entry->mNotifyConsumed->post();
+    mVideoQueue.erase(mVideoQueue.begin());
+    entry = NULL;
+}
+
+void NuPlayer::Renderer::notifyEOS(bool audio) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatEOS);
+    notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->post();
+}
+
+void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    if (dropBufferWhileFlushing(audio, msg)) {
+        return;
+    }
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    sp<AMessage> notifyConsumed;
+    CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
+
+    QueueEntry entry;
+    entry.mBuffer = buffer;
+    entry.mNotifyConsumed = notifyConsumed;
+    entry.mOffset = 0;
+    entry.mFinalResult = OK;
+
+    if (audio) {
+        mAudioQueue.push_back(entry);
+        postDrainAudioQueue();
+    } else {
+        mVideoQueue.push_back(entry);
+        postDrainVideoQueue();
+    }
+
+    if (mSyncQueues && !mAudioQueue.empty() && !mVideoQueue.empty()) {
+        int64_t firstAudioTimeUs;
+        int64_t firstVideoTimeUs;
+        CHECK((*mAudioQueue.begin()).mBuffer->meta()
+                ->findInt64("timeUs", &firstAudioTimeUs));
+        CHECK((*mVideoQueue.begin()).mBuffer->meta()
+                ->findInt64("timeUs", &firstVideoTimeUs));
+
+        int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
+
+        LOGV("queueDiff = %.2f secs", diff / 1E6);
+
+        if (diff > 100000ll) {
+            // Audio data starts More than 0.1 secs before video.
+            // Drop some audio.
+
+            (*mAudioQueue.begin()).mNotifyConsumed->post();
+            mAudioQueue.erase(mAudioQueue.begin());
+            return;
+        }
+
+        syncQueuesDone();
+    }
+}
+
+void NuPlayer::Renderer::syncQueuesDone() {
+    if (!mSyncQueues) {
+        return;
+    }
+
+    mSyncQueues = false;
+
+    if (!mAudioQueue.empty()) {
+        postDrainAudioQueue();
+    }
+
+    if (!mVideoQueue.empty()) {
+        postDrainVideoQueue();
+    }
+}
+
+void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    if (dropBufferWhileFlushing(audio, msg)) {
+        return;
+    }
+
+    int32_t finalResult;
+    CHECK(msg->findInt32("finalResult", &finalResult));
+
+    QueueEntry entry;
+    entry.mOffset = 0;
+    entry.mFinalResult = finalResult;
+
+    if (audio) {
+        mAudioQueue.push_back(entry);
+        postDrainAudioQueue();
+    } else {
+        mVideoQueue.push_back(entry);
+        postDrainVideoQueue();
+    }
+}
+
+void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    // If we're currently syncing the queues, i.e. dropping audio while
+    // aligning the first audio/video buffer times and only one of the
+    // two queues has data, we may starve that queue by not requesting
+    // more buffers from the decoder. If the other source then encounters
+    // a discontinuity that leads to flushing, we'll never find the
+    // corresponding discontinuity on the other queue.
+    // Therefore we'll stop syncing the queues if at least one of them
+    // is flushed.
+    syncQueuesDone();
+
+    if (audio) {
+        flushQueue(&mAudioQueue);
+
+        Mutex::Autolock autoLock(mFlushLock);
+        mFlushingAudio = false;
+
+        mDrainAudioQueuePending = false;
+        ++mAudioQueueGeneration;
+    } else {
+        flushQueue(&mVideoQueue);
+
+        Mutex::Autolock autoLock(mFlushLock);
+        mFlushingVideo = false;
+
+        mDrainVideoQueuePending = false;
+        ++mVideoQueueGeneration;
+    }
+
+    notifyFlushComplete(audio);
+}
+
+void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
+    while (!queue->empty()) {
+        QueueEntry *entry = &*queue->begin();
+
+        if (entry->mBuffer != NULL) {
+            entry->mNotifyConsumed->post();
+        }
+
+        queue->erase(queue->begin());
+        entry = NULL;
+    }
+}
+
+void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatFlushComplete);
+    notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->post();
+}
+
+bool NuPlayer::Renderer::dropBufferWhileFlushing(
+        bool audio, const sp<AMessage> &msg) {
+    bool flushing = false;
+
+    {
+        Mutex::Autolock autoLock(mFlushLock);
+        if (audio) {
+            flushing = mFlushingAudio;
+        } else {
+            flushing = mFlushingVideo;
+        }
+    }
+
+    if (!flushing) {
+        return false;
+    }
+
+    sp<AMessage> notifyConsumed;
+    if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
+        notifyConsumed->post();
+    }
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
new file mode 100644
index 0000000..834ddc5
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -0,0 +1,108 @@
+/*
+ * 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 NUPLAYER_RENDERER_H_
+
+#define NUPLAYER_RENDERER_H_
+
+#include "NuPlayer.h"
+
+namespace android {
+
+struct NuPlayer::Renderer : public AHandler {
+    Renderer(const sp<MediaPlayerBase::AudioSink> &sink,
+             const sp<AMessage> &notify);
+
+    void queueBuffer(
+            bool audio,
+            const sp<ABuffer> &buffer,
+            const sp<AMessage> &notifyConsumed);
+
+    void queueEOS(bool audio, status_t finalResult);
+
+    void flush(bool audio);
+
+    void signalTimeDiscontinuity();
+
+    enum {
+        kWhatEOS,
+        kWhatFlushComplete,
+    };
+
+protected:
+    virtual ~Renderer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatDrainAudioQueue,
+        kWhatDrainVideoQueue,
+        kWhatQueueBuffer,
+        kWhatQueueEOS,
+        kWhatFlush,
+    };
+
+    struct QueueEntry {
+        sp<ABuffer> mBuffer;
+        sp<AMessage> mNotifyConsumed;
+        size_t mOffset;
+        status_t mFinalResult;
+    };
+
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+    sp<AMessage> mNotify;
+    List<QueueEntry> mAudioQueue;
+    List<QueueEntry> mVideoQueue;
+    uint32_t mNumFramesWritten;
+
+    bool mDrainAudioQueuePending;
+    bool mDrainVideoQueuePending;
+    int32_t mAudioQueueGeneration;
+    int32_t mVideoQueueGeneration;
+
+    int64_t mAnchorTimeMediaUs;
+    int64_t mAnchorTimeRealUs;
+
+    Mutex mFlushLock;  // protects the following 2 member vars.
+    bool mFlushingAudio;
+    bool mFlushingVideo;
+
+    bool mSyncQueues;
+
+    void onDrainAudioQueue();
+    void postDrainAudioQueue();
+
+    void onDrainVideoQueue();
+    void postDrainVideoQueue();
+
+    void onQueueBuffer(const sp<AMessage> &msg);
+    void onQueueEOS(const sp<AMessage> &msg);
+    void onFlush(const sp<AMessage> &msg);
+
+    void notifyEOS(bool audio);
+    void notifyFlushComplete(bool audio);
+
+    void flushQueue(List<QueueEntry> *queue);
+    bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
+    void syncQueuesDone();
+
+    DISALLOW_EVIL_CONSTRUCTORS(Renderer);
+};
+
+}  // namespace android
+
+#endif  // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
new file mode 100644
index 0000000..92642a8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "NuPlayerStreamListener"
+#include <utils/Log.h>
+
+#include "NuPlayerStreamListener.h"
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
+        const sp<IStreamSource> &source,
+        ALooper::handler_id id)
+    : mSource(source),
+      mTargetID(id),
+      mEOS(false),
+      mSendDataNotification(true) {
+    mSource->setListener(this);
+
+    mMemoryDealer = new MemoryDealer(kNumBuffers * kBufferSize);
+    for (size_t i = 0; i < kNumBuffers; ++i) {
+        sp<IMemory> mem = mMemoryDealer->allocate(kBufferSize);
+        CHECK(mem != NULL);
+
+        mBuffers.push(mem);
+    }
+    mSource->setBuffers(mBuffers);
+}
+
+void NuPlayer::NuPlayerStreamListener::start() {
+    for (size_t i = 0; i < kNumBuffers; ++i) {
+        mSource->onBufferAvailable(i);
+    }
+}
+
+void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) {
+    QueueEntry entry;
+    entry.mIsCommand = false;
+    entry.mIndex = index;
+    entry.mSize = size;
+    entry.mOffset = 0;
+
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(entry);
+
+    if (mSendDataNotification) {
+        mSendDataNotification = false;
+        (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+    }
+}
+
+void NuPlayer::NuPlayerStreamListener::issueCommand(
+        Command cmd, bool synchronous, const sp<AMessage> &extra) {
+    CHECK(!synchronous);
+
+    QueueEntry entry;
+    entry.mIsCommand = true;
+    entry.mCommand = cmd;
+    entry.mExtra = extra;
+
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(entry);
+
+    if (mSendDataNotification) {
+        mSendDataNotification = false;
+        (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+    }
+}
+
+ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+    CHECK_GT(size, 0u);
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mEOS) {
+        return 0;
+    }
+
+    if (mQueue.empty()) {
+        mSendDataNotification = true;
+
+        return -EWOULDBLOCK;
+    }
+
+    QueueEntry *entry = &*mQueue.begin();
+
+    if (entry->mIsCommand) {
+        switch (entry->mCommand) {
+            case EOS:
+            {
+                mQueue.erase(mQueue.begin());
+                entry = NULL;
+
+                mEOS = true;
+                return 0;
+            }
+
+            case DISCONTINUITY:
+            {
+                mQueue.erase(mQueue.begin());
+                entry = NULL;
+
+                return INFO_DISCONTINUITY;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+    size_t copy = entry->mSize;
+    if (copy > size) {
+        copy = size;
+    }
+
+    memcpy(data,
+           (const uint8_t *)mBuffers.editItemAt(entry->mIndex)->pointer()
+            + entry->mOffset,
+           copy);
+
+    entry->mOffset += copy;
+    entry->mSize -= copy;
+
+    if (entry->mSize == 0) {
+        mSource->onBufferAvailable(entry->mIndex);
+        mQueue.erase(mQueue.begin());
+        entry = NULL;
+    }
+
+    return copy;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
new file mode 100644
index 0000000..f88e945
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -0,0 +1,74 @@
+/*
+ * 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 NUPLAYER_STREAM_LISTENER_H_
+
+#define NUPLAYER_STREAM_LISTENER_H_
+
+#include "NuPlayer.h"
+
+#include <media/IStreamSource.h>
+
+namespace android {
+
+struct MemoryDealer;
+
+struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
+    NuPlayerStreamListener(
+            const sp<IStreamSource> &source,
+            ALooper::handler_id targetID);
+
+    virtual void queueBuffer(size_t index, size_t size);
+
+    virtual void issueCommand(
+            Command cmd, bool synchronous, const sp<AMessage> &extra);
+
+    void start();
+    ssize_t read(void *data, size_t size);
+
+private:
+    enum {
+        kNumBuffers = 16,
+        kBufferSize = 188 * 20
+    };
+
+    struct QueueEntry {
+        bool mIsCommand;
+
+        size_t mIndex;
+        size_t mSize;
+        size_t mOffset;
+
+        Command mCommand;
+        sp<AMessage> mExtra;
+    };
+
+    Mutex mLock;
+
+    sp<IStreamSource> mSource;
+    ALooper::handler_id mTargetID;
+    sp<MemoryDealer> mMemoryDealer;
+    Vector<sp<IMemory> > mBuffers;
+    List<QueueEntry> mQueue;
+    bool mEOS;
+    bool mSendDataNotification;
+
+    DISALLOW_EVIL_CONSTRUCTORS(NuPlayerStreamListener);
+};
+
+}  // namespace android
+
+#endif // NUPLAYER_STREAM_LISTENER_H_