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

Change-Id: I153eec439d260a5524b21270e16d36940ec3161a
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index f8650eb..178032d 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -122,3 +122,29 @@
 LOCAL_MODULE:= stream
 
 include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=         \
+        sf2.cpp    \
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder libstagefright_foundation \
+        libmedia libsurfaceflinger_client libcutils libui
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= sf2
+
+include $(BUILD_EXECUTABLE)
+
+
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
new file mode 100644
index 0000000..1dc08ea
--- /dev/null
+++ b/cmds/stagefright/sf2.cpp
@@ -0,0 +1,564 @@
+#include <binder/ProcessState.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include "include/ESDS.h"
+
+using namespace android;
+
+struct Controller : public AHandler {
+    Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface)
+        : mURI(uri),
+          mDecodeAudio(decodeAudio),
+          mSurface(surface),
+          mCodec(new ACodec) {
+        CHECK(!mDecodeAudio || mSurface == NULL);
+    }
+
+    void startAsync() {
+        (new AMessage(kWhatStart, id()))->post();
+    }
+
+protected:
+    virtual ~Controller() {
+    }
+
+    virtual void onMessageReceived(const sp<AMessage> &msg) {
+        switch (msg->what()) {
+            case kWhatStart:
+            {
+#if 1
+                mDecodeLooper = looper();
+#else
+                mDecodeLooper = new ALooper;
+                mDecodeLooper->setName("sf2 decode looper");
+                mDecodeLooper->start();
+#endif
+
+                sp<DataSource> dataSource =
+                    DataSource::CreateFromURI(mURI.c_str());
+
+                sp<MediaExtractor> extractor =
+                    MediaExtractor::Create(dataSource);
+
+                for (size_t i = 0; i < extractor->countTracks(); ++i) {
+                    sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+                    const char *mime;
+                    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+                    if (!strncasecmp(mDecodeAudio ? "audio/" : "video/",
+                                     mime, 6)) {
+                        mSource = extractor->getTrack(i);
+                        break;
+                    }
+                }
+                CHECK(mSource != NULL);
+
+                CHECK_EQ(mSource->start(), (status_t)OK);
+
+                mDecodeLooper->registerHandler(mCodec);
+
+                mCodec->setNotificationMessage(
+                        new AMessage(kWhatCodecNotify, id()));
+
+                sp<AMessage> format = makeFormat(mSource->getFormat());
+
+                if (mSurface != NULL) {
+                    format->setObject("surface", mSurface);
+                }
+
+                mCodec->initiateSetup(format);
+
+                mCSDIndex = 0;
+                mStartTimeUs = ALooper::GetNowUs();
+                mNumOutputBuffersReceived = 0;
+                mTotalBytesReceived = 0;
+                mLeftOverBuffer = NULL;
+                mFinalResult = OK;
+                mSeekState = SEEK_NONE;
+
+                // (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                break;
+            }
+
+            case kWhatSeek:
+            {
+                printf("+");
+                fflush(stdout);
+
+                CHECK(mSeekState == SEEK_NONE
+                        || mSeekState == SEEK_FLUSH_COMPLETED);
+
+                if (mLeftOverBuffer != NULL) {
+                    mLeftOverBuffer->release();
+                    mLeftOverBuffer = NULL;
+                }
+
+                mSeekState = SEEK_FLUSHING;
+                mSeekTimeUs = 30000000ll;
+
+                mCodec->signalFlush();
+                break;
+            }
+
+            case kWhatStop:
+            {
+                if (mLeftOverBuffer != NULL) {
+                    mLeftOverBuffer->release();
+                    mLeftOverBuffer = NULL;
+                }
+
+                CHECK_EQ(mSource->stop(), (status_t)OK);
+                mSource.clear();
+
+                mCodec->initiateShutdown();
+                break;
+            }
+
+            case kWhatCodecNotify:
+            {
+                int32_t what;
+                CHECK(msg->findInt32("what", &what));
+
+                if (what == ACodec::kWhatFillThisBuffer) {
+                    onFillThisBuffer(msg);
+                } else if (what == ACodec::kWhatDrainThisBuffer) {
+                    if ((mNumOutputBuffersReceived++ % 16) == 0) {
+                        printf(".");
+                        fflush(stdout);
+                    }
+
+                    onDrainThisBuffer(msg);
+                } else if (what == ACodec::kWhatEOS) {
+                    printf("$\n");
+
+                    int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+
+                    if (mDecodeAudio) {
+                        printf("%lld bytes received. %.2f KB/sec\n",
+                               mTotalBytesReceived,
+                               mTotalBytesReceived * 1E6 / 1024 / delayUs);
+                    } else {
+                        printf("%d frames decoded, %.2f fps. %lld bytes "
+                               "received. %.2f KB/sec\n",
+                               mNumOutputBuffersReceived,
+                               mNumOutputBuffersReceived * 1E6 / delayUs,
+                               mTotalBytesReceived,
+                               mTotalBytesReceived * 1E6 / 1024 / delayUs);
+                    }
+
+                    (new AMessage(kWhatStop, id()))->post();
+                } else if (what == ACodec::kWhatFlushCompleted) {
+                    mSeekState = SEEK_FLUSH_COMPLETED;
+                    mCodec->signalResume();
+
+                    (new AMessage(kWhatSeek, id()))->post(5000000ll);
+                } else {
+                    CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
+
+                    mDecodeLooper->unregisterHandler(mCodec->id());
+
+                    if (mDecodeLooper != looper()) {
+                        mDecodeLooper->stop();
+                    }
+
+                    looper()->stop();
+                }
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+private:
+    enum {
+        kWhatStart             = 'strt',
+        kWhatStop              = 'stop',
+        kWhatCodecNotify       = 'noti',
+        kWhatSeek              = 'seek',
+    };
+
+    sp<ALooper> mDecodeLooper;
+
+    AString mURI;
+    bool mDecodeAudio;
+    sp<Surface> mSurface;
+    sp<ACodec> mCodec;
+    sp<MediaSource> mSource;
+
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+
+    MediaBuffer *mLeftOverBuffer;
+    status_t mFinalResult;
+
+    int64_t mStartTimeUs;
+    int32_t mNumOutputBuffersReceived;
+    int64_t mTotalBytesReceived;
+
+    enum SeekState {
+        SEEK_NONE,
+        SEEK_FLUSHING,
+        SEEK_FLUSH_COMPLETED,
+    };
+    SeekState mSeekState;
+    int64_t mSeekTimeUs;
+
+    sp<AMessage> 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)) {
+            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);
+        }
+
+        int32_t maxInputSize;
+        if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+            msg->setInt32("max-input-size", maxInputSize);
+        }
+
+        return msg;
+    }
+
+    void onFillThisBuffer(const sp<AMessage> &msg) {
+        sp<AMessage> reply;
+        CHECK(msg->findMessage("reply", &reply));
+
+        if (mSeekState == SEEK_FLUSHING) {
+            reply->post();
+            return;
+        }
+
+        sp<RefBase> obj;
+        CHECK(msg->findObject("buffer", &obj));
+        sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+
+        if (mCSDIndex < mCSD.size()) {
+            outBuffer = mCSD.editItemAt(mCSDIndex++);
+            outBuffer->meta()->setInt64("timeUs", 0);
+        } else {
+            size_t sizeLeft = outBuffer->capacity();
+            outBuffer->setRange(0, 0);
+
+            int32_t n = 0;
+
+            for (;;) {
+                MediaBuffer *inBuffer;
+
+                if (mLeftOverBuffer != NULL) {
+                    inBuffer = mLeftOverBuffer;
+                    mLeftOverBuffer = NULL;
+                } else if (mFinalResult != OK) {
+                    break;
+                } else {
+                    MediaSource::ReadOptions options;
+                    if (mSeekState == SEEK_FLUSH_COMPLETED) {
+                        options.setSeekTo(mSeekTimeUs);
+                        mSeekState = SEEK_NONE;
+                    }
+                    status_t err = mSource->read(&inBuffer, &options);
+
+                    if (err != OK) {
+                        mFinalResult = err;
+                        break;
+                    }
+                }
+
+                if (inBuffer->range_length() > sizeLeft) {
+                    if (outBuffer->size() == 0) {
+                        LOGE("Unable to fit even a single input buffer of size %d.",
+                             inBuffer->range_length());
+                    }
+                    CHECK_GT(outBuffer->size(), 0u);
+
+                    mLeftOverBuffer = inBuffer;
+                    break;
+                }
+
+                ++n;
+
+                if (outBuffer->size() == 0) {
+                    int64_t timeUs;
+                    CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+                    outBuffer->meta()->setInt64("timeUs", timeUs);
+                }
+
+                memcpy(outBuffer->data() + outBuffer->size(),
+                       (const uint8_t *)inBuffer->data()
+                        + inBuffer->range_offset(),
+                       inBuffer->range_length());
+
+                outBuffer->setRange(
+                        0, outBuffer->size() + inBuffer->range_length());
+
+                sizeLeft -= inBuffer->range_length();
+
+                inBuffer->release();
+                inBuffer = NULL;
+
+                // break;  // Don't coalesce
+            }
+
+            LOGV("coalesced %d input buffers", n);
+
+            if (outBuffer->size() == 0) {
+                CHECK_NE(mFinalResult, (status_t)OK);
+
+                reply->setInt32("err", mFinalResult);
+                reply->post();
+                return;
+            }
+        }
+
+        reply->setObject("buffer", outBuffer);
+        reply->post();
+    }
+
+    void onDrainThisBuffer(const sp<AMessage> &msg) {
+        sp<RefBase> obj;
+        CHECK(msg->findObject("buffer", &obj));
+
+        sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+        mTotalBytesReceived += buffer->size();
+
+        sp<AMessage> reply;
+        CHECK(msg->findMessage("reply", &reply));
+
+        reply->post();
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(Controller);
+};
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s\n", me);
+    fprintf(stderr, "       -h(elp)\n");
+    fprintf(stderr, "       -a(udio)\n");
+
+    fprintf(stderr,
+            "       -s(surface) Allocate output buffers on a surface.\n");
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    bool decodeAudio = false;
+    bool useSurface = false;
+
+    int res;
+    while ((res = getopt(argc, argv, "has")) >= 0) {
+        switch (res) {
+            case 'a':
+                decodeAudio = true;
+                break;
+
+            case 's':
+                useSurface = true;
+                break;
+
+            case '?':
+            case 'h':
+            default:
+            {
+                usage(argv[0]);
+                return 1;
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        usage(argv[-optind]);
+        return 1;
+    }
+
+    DataSource::RegisterDefaultSniffers();
+
+    sp<ALooper> looper = new ALooper;
+    looper->setName("sf2");
+
+    sp<SurfaceComposerClient> composerClient;
+    sp<SurfaceControl> control;
+    sp<Surface> surface;
+
+    if (!decodeAudio && useSurface) {
+        composerClient = new SurfaceComposerClient;
+        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+        control = composerClient->createSurface(
+                getpid(),
+                String8("A Surface"),
+                0,
+                1280,
+                800,
+                PIXEL_FORMAT_RGB_565,
+                0);
+
+        CHECK(control != NULL);
+        CHECK(control->isValid());
+
+        CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+        CHECK_EQ(control->setLayer(30000), (status_t)OK);
+        CHECK_EQ(control->show(), (status_t)OK);
+        CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+
+        surface = control->getSurface();
+        CHECK(surface != NULL);
+    }
+
+    sp<Controller> controller = new Controller(argv[0], decodeAudio, surface);
+    looper->registerHandler(controller);
+
+    controller->startAsync();
+
+    CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
+
+    looper->unregisterHandler(controller->id());
+
+    if (!decodeAudio && useSurface) {
+        composerClient->dispose();
+    }
+
+    return 0;
+}
+
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index a3d7a6e..ccae92e 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -28,6 +28,8 @@
 
 private:
     int mFd;
+    off64_t mFileSize;
+    int64_t mNextSeekTimeUs;
 
     sp<IStreamListener> mListener;
     Vector<sp<IMemory> > mBuffers;
@@ -36,8 +38,13 @@
 };
 
 MyStreamSource::MyStreamSource(int fd)
-    : mFd(fd) {
+    : mFd(fd),
+      mFileSize(0),
+      mNextSeekTimeUs(ALooper::GetNowUs() + 5000000ll) {
     CHECK_GE(fd, 0);
+
+    mFileSize = lseek64(fd, 0, SEEK_END);
+    lseek64(fd, 0, SEEK_SET);
 }
 
 MyStreamSource::~MyStreamSource() {
@@ -53,6 +60,20 @@
 
 void MyStreamSource::onBufferAvailable(size_t index) {
     CHECK_LT(index, mBuffers.size());
+
+    if (mNextSeekTimeUs >= 0 && mNextSeekTimeUs <= ALooper::GetNowUs()) {
+        off64_t offset = (off64_t)(((float)rand() / RAND_MAX) * mFileSize * 0.8);
+        offset = (offset / 188) * 188;
+
+        lseek(mFd, offset, SEEK_SET);
+
+        mListener->issueCommand(
+                IStreamListener::DISCONTINUITY, false /* synchronous */);
+
+        mNextSeekTimeUs = -1;
+        mNextSeekTimeUs = ALooper::GetNowUs() + 5000000ll;
+    }
+
     sp<IMemory> mem = mBuffers.itemAt(index);
 
     ssize_t n = read(mFd, mem->pointer(), mem->size());
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index e7f1d6d..c0963a6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -40,7 +40,8 @@
 enum player_type {
     PV_PLAYER = 1,
     SONIVOX_PLAYER = 2,
-    STAGEFRIGHT_PLAYER = 4,
+    STAGEFRIGHT_PLAYER = 3,
+    NU_PLAYER = 4,
     // Test players are available only in the 'test' and 'eng' builds.
     // The shared library with the test player is passed passed as an
     // argument to the 'test:' url in the setDataSource call.
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
new file mode 100644
index 0000000..940470d
--- /dev/null
+++ b/include/media/stagefright/ACodec.h
@@ -0,0 +1,152 @@
+#ifndef A_CODEC_H_
+
+#define A_CODEC_H_
+
+#include <stdint.h>
+#include <android/native_window.h>
+#include <media/IOMX.h>
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+namespace android {
+
+struct ABuffer;
+struct MemoryDealer;
+
+struct ACodec : public AHierarchicalStateMachine {
+    enum {
+        kWhatFillThisBuffer    = 'fill',
+        kWhatDrainThisBuffer   = 'drai',
+        kWhatEOS               = 'eos ',
+        kWhatShutdownCompleted = 'scom',
+        kWhatFlushCompleted    = 'fcom',
+    };
+
+    ACodec();
+
+    void setNotificationMessage(const sp<AMessage> &msg);
+    void initiateSetup(const sp<AMessage> &msg);
+    void signalFlush();
+    void signalResume();
+    void initiateShutdown();
+
+protected:
+    virtual ~ACodec();
+
+private:
+    struct BaseState;
+    struct UninitializedState;
+    struct LoadedToIdleState;
+    struct IdleToExecutingState;
+    struct ExecutingState;
+    struct OutputPortSettingsChangedState;
+    struct ExecutingToIdleState;
+    struct IdleToLoadedState;
+    struct ErrorState;
+    struct FlushingState;
+
+    enum {
+        kWhatSetup                   = 'setu',
+        kWhatOMXMessage              = 'omx ',
+        kWhatInputBufferFilled       = 'inpF',
+        kWhatOutputBufferDrained     = 'outD',
+        kWhatShutdown                = 'shut',
+        kWhatFlush                   = 'flus',
+        kWhatResume                  = 'resm',
+        kWhatDrainDeferredMessages   = 'drai',
+    };
+
+    enum {
+        kPortIndexInput  = 0,
+        kPortIndexOutput = 1
+    };
+
+    struct BufferInfo {
+        enum Status {
+            OWNED_BY_US,
+            OWNED_BY_COMPONENT,
+            OWNED_BY_UPSTREAM,
+            OWNED_BY_DOWNSTREAM,
+            OWNED_BY_NATIVE_WINDOW,
+        };
+
+        IOMX::buffer_id mBufferID;
+        Status mStatus;
+
+        sp<ABuffer> mData;
+        sp<GraphicBuffer> mGraphicBuffer;
+    };
+
+    sp<AMessage> mNotify;
+
+    sp<UninitializedState> mUninitializedState;
+    sp<LoadedToIdleState> mLoadedToIdleState;
+    sp<IdleToExecutingState> mIdleToExecutingState;
+    sp<ExecutingState> mExecutingState;
+    sp<OutputPortSettingsChangedState> mOutputPortSettingsChangedState;
+    sp<ExecutingToIdleState> mExecutingToIdleState;
+    sp<IdleToLoadedState> mIdleToLoadedState;
+    sp<ErrorState> mErrorState;
+    sp<FlushingState> mFlushingState;
+
+    AString mComponentName;
+    sp<IOMX> mOMX;
+    IOMX::node_id mNode;
+    sp<MemoryDealer> mDealer[2];
+
+    sp<ANativeWindow> mNativeWindow;
+
+    Vector<BufferInfo> mBuffers[2];
+    bool mPortEOS[2];
+
+    List<sp<AMessage> > mDeferredQueue;
+
+    status_t allocateBuffersOnPort(OMX_U32 portIndex);
+    status_t freeBuffersOnPort(OMX_U32 portIndex);
+    status_t freeBuffer(OMX_U32 portIndex, size_t i);
+
+    status_t allocateOutputBuffersFromNativeWindow();
+    status_t cancelBufferToNativeWindow(BufferInfo *info);
+    status_t freeOutputBuffersOwnedByNativeWindow();
+    BufferInfo *dequeueBufferFromNativeWindow();
+
+    BufferInfo *findBufferByID(
+            uint32_t portIndex, IOMX::buffer_id bufferID,
+            ssize_t *index = NULL);
+
+    void setComponentRole(bool isEncoder, const char *mime);
+    void configureCodec(const char *mime, const sp<AMessage> &msg);
+
+    status_t setVideoPortFormatType(
+            OMX_U32 portIndex,
+            OMX_VIDEO_CODINGTYPE compressionFormat,
+            OMX_COLOR_FORMATTYPE colorFormat);
+
+    status_t setSupportedOutputFormat();
+
+    status_t setupVideoDecoder(
+            const char *mime, int32_t width, int32_t height);
+
+    status_t setVideoFormatOnPort(
+            OMX_U32 portIndex,
+            int32_t width, int32_t height,
+            OMX_VIDEO_CODINGTYPE compressionFormat);
+
+    status_t setupAACDecoder(int32_t numChannels, int32_t sampleRate);
+    status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
+
+    status_t initNativeWindow();
+
+    // Returns true iff all buffers on the given port have status OWNED_BY_US.
+    bool allYourBuffersAreBelongToUs(OMX_U32 portIndex);
+
+    bool allYourBuffersAreBelongToUs();
+
+    void deferMessage(const sp<AMessage> &msg);
+    void processDeferredMessages();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ACodec);
+};
+
+}  // namespace android
+
+#endif  // A_CODEC_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 69021d8..eb5e494 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -32,6 +32,7 @@
 #define CHECK(condition)                                \
     LOG_ALWAYS_FATAL_IF(                                \
             !(condition),                               \
+            "%s",                                       \
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
             " CHECK(" #condition ") failed.")
 
@@ -58,10 +59,12 @@
     do {                                                                \
         AString ___res = Compare_##suffix(x, y);                        \
         if (!___res.empty()) {                                          \
-            LOG_ALWAYS_FATAL(                                           \
-                    __FILE__ ":" LITERAL_TO_STRING(__LINE__)            \
-                    " CHECK_" #suffix "( " #x "," #y ") failed: %s",    \
-                    ___res.c_str());                                    \
+            AString ___full =                                           \
+                __FILE__ ":" LITERAL_TO_STRING(__LINE__)                \
+                    " CHECK_" #suffix "( " #x "," #y ") failed: ";      \
+            ___full.append(___res);                                     \
+                                                                        \
+            LOG_ALWAYS_FATAL("%s", ___full.c_str());                    \
         }                                                               \
     } while (false)
 
diff --git a/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
new file mode 100644
index 0000000..b5786fb
--- /dev/null
+++ b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
@@ -0,0 +1,49 @@
+#ifndef A_HIERARCHICAL_STATE_MACHINE_H_
+
+#define A_HIERARCHICAL_STATE_MACHINE_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct AState : public RefBase {
+    AState(const sp<AState> &parentState = NULL);
+
+    sp<AState> parentState();
+
+protected:
+    virtual ~AState();
+
+    virtual void stateEntered();
+    virtual void stateExited();
+
+    virtual bool onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+    friend struct AHierarchicalStateMachine;
+
+    sp<AState> mParentState;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AState);
+};
+
+struct AHierarchicalStateMachine : public AHandler {
+    AHierarchicalStateMachine();
+
+protected:
+    virtual ~AHierarchicalStateMachine();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    // Only to be called in response to a message.
+    void changeState(const sp<AState> &state);
+
+private:
+    sp<AState> mState;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AHierarchicalStateMachine);
+};
+
+}  // namespace android
+
+#endif  // A_HIERARCHICAL_STATE_MACHINE_H_
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 2fbdddc..941f6b9 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -40,6 +40,8 @@
     void setTarget(ALooper::handler_id target);
     ALooper::handler_id target() const;
 
+    void clear();
+
     void setInt32(const char *name, int32_t value);
     void setInt64(const char *name, int64_t value);
     void setSize(const char *name, size_t value);
@@ -106,7 +108,6 @@
     Item mItems[kMaxNumItems];
     size_t mNumItems;
 
-    void clear();
     Item *allocateItem(const char *name);
     void freeItem(Item *item);
     const Item *findItem(const char *name, Type type) const;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index a9d537f..f7f0d95 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -35,7 +35,8 @@
 	libsurfaceflinger_client
 
 LOCAL_STATIC_LIBRARIES := \
-        libstagefright_rtsp
+        libstagefright_rtsp                     \
+        libstagefright_nuplayer                 \
 
 ifneq ($(TARGET_SIMULATOR),true)
 LOCAL_SHARED_LIBRARIES += libdl
@@ -47,9 +48,11 @@
 	$(TOP)/frameworks/base/include/media/stagefright/openmax \
 	$(TOP)/frameworks/base/media/libstagefright/include             \
 	$(TOP)/frameworks/base/media/libstagefright/rtsp                \
-        $(TOP)/external/tremolo/Tremolo
+        $(TOP)/external/tremolo/Tremolo \
 
 LOCAL_MODULE:= libmediaplayerservice
 
 include $(BUILD_SHARED_LIBRARY)
 
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 63d09d6..6f011ce 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,6 +58,7 @@
 #include "MidiFile.h"
 #include "TestPlayerStub.h"
 #include "StagefrightPlayer.h"
+#include "nuplayer/NuPlayerDriver.h"
 
 #include <OMX.h>
 
@@ -759,6 +760,10 @@
             LOGV(" create StagefrightPlayer");
             p = new StagefrightPlayer;
             break;
+        case NU_PLAYER:
+            LOGV(" create NuPlayer");
+            p = new NuPlayerDriver;
+            break;
         case TEST_PLAYER:
             LOGV("Create Test Player stub");
             p = new TestPlayerStub();
@@ -887,7 +892,7 @@
 status_t MediaPlayerService::Client::setDataSource(
         const sp<IStreamSource> &source) {
     // create the right type of player
-    sp<MediaPlayerBase> p = createPlayer(STAGEFRIGHT_PLAYER);
+    sp<MediaPlayerBase> p = createPlayer(NU_PLAYER);
 
     if (p == NULL) {
         return NO_INIT;
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_
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
new file mode 100644
index 0000000..77276ab
--- /dev/null
+++ b/media/libstagefright/ACodec.cpp
@@ -0,0 +1,2097 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACodec"
+
+#include <media/stagefright/ACodec.h>
+
+#include <binder/MemoryDealer.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/MediaDefs.h>
+#include <media/stagefright/OMXClient.h>
+
+#include <surfaceflinger/Surface.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+struct CodecObserver : public BnOMXObserver {
+    CodecObserver() {}
+
+    void setNotificationMessage(const sp<AMessage> &msg) {
+        mNotify = msg;
+    }
+
+    // from IOMXObserver
+    virtual void onMessage(const omx_message &omx_msg) {
+        sp<AMessage> msg = mNotify->dup();
+
+        msg->setInt32("type", omx_msg.type);
+        msg->setPointer("node", omx_msg.node);
+
+        switch (omx_msg.type) {
+            case omx_message::EVENT:
+            {
+                msg->setInt32("event", omx_msg.u.event_data.event);
+                msg->setInt32("data1", omx_msg.u.event_data.data1);
+                msg->setInt32("data2", omx_msg.u.event_data.data2);
+                break;
+            }
+
+            case omx_message::EMPTY_BUFFER_DONE:
+            {
+                msg->setPointer("buffer", omx_msg.u.buffer_data.buffer);
+                break;
+            }
+
+            case omx_message::FILL_BUFFER_DONE:
+            {
+                msg->setPointer(
+                        "buffer", omx_msg.u.extended_buffer_data.buffer);
+                msg->setInt32(
+                        "range_offset",
+                        omx_msg.u.extended_buffer_data.range_offset);
+                msg->setInt32(
+                        "range_length",
+                        omx_msg.u.extended_buffer_data.range_length);
+                msg->setInt32(
+                        "flags",
+                        omx_msg.u.extended_buffer_data.flags);
+                msg->setInt64(
+                        "timestamp",
+                        omx_msg.u.extended_buffer_data.timestamp);
+                msg->setPointer(
+                        "platform_private",
+                        omx_msg.u.extended_buffer_data.platform_private);
+                msg->setPointer(
+                        "data_ptr",
+                        omx_msg.u.extended_buffer_data.data_ptr);
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+
+        msg->post();
+    }
+
+protected:
+    virtual ~CodecObserver() {}
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CodecObserver);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::BaseState : public AState {
+    BaseState(ACodec *codec, const sp<AState> &parentState = NULL);
+
+protected:
+    enum PortMode {
+        KEEP_BUFFERS,
+        RESUBMIT_BUFFERS,
+        FREE_BUFFERS,
+    };
+
+    ACodec *mCodec;
+
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+    void postFillThisBuffer(BufferInfo *info);
+
+private:
+    bool onOMXMessage(const sp<AMessage> &msg);
+
+    bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID);
+
+    bool onOMXFillBufferDone(
+            IOMX::buffer_id bufferID,
+            size_t rangeOffset, size_t rangeLength,
+            OMX_U32 flags,
+            int64_t timeUs,
+            void *platformPrivate,
+            void *dataPtr);
+
+    void getMoreInputDataIfPossible();
+
+    DISALLOW_EVIL_CONSTRUCTORS(BaseState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::UninitializedState : public ACodec::BaseState {
+    UninitializedState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    void onSetup(const sp<AMessage> &msg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::LoadedToIdleState : public ACodec::BaseState {
+    LoadedToIdleState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+    virtual void stateEntered();
+
+private:
+    status_t allocateBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(LoadedToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToExecutingState : public ACodec::BaseState {
+    IdleToExecutingState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+    virtual void stateEntered();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IdleToExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingState : public ACodec::BaseState {
+    ExecutingState(ACodec *codec);
+
+    void submitOutputBuffers();
+
+    // Submit output buffers to the decoder, submit input buffers to client
+    // to fill with data.
+    void resume();
+
+protected:
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::OutputPortSettingsChangedState : public ACodec::BaseState {
+    OutputPortSettingsChangedState(ACodec *codec);
+
+protected:
+    virtual PortMode getPortMode(OMX_U32 portIndex);
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(OutputPortSettingsChangedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingToIdleState : public ACodec::BaseState {
+    ExecutingToIdleState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+    void changeStateIfWeOwnAllBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToLoadedState : public ACodec::BaseState {
+    IdleToLoadedState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IdleToLoadedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ErrorState : public ACodec::BaseState {
+    ErrorState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::FlushingState : public ACodec::BaseState {
+    FlushingState(ACodec *codec);
+
+protected:
+    virtual bool onMessageReceived(const sp<AMessage> &msg);
+    virtual void stateEntered();
+
+    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+    virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+    virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+    bool mFlushComplete[2];
+
+    void changeStateIfWeOwnAllBuffers();
+
+    DISALLOW_EVIL_CONSTRUCTORS(FlushingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ACodec()
+    : mNode(NULL) {
+    mUninitializedState = new UninitializedState(this);
+    mLoadedToIdleState = new LoadedToIdleState(this);
+    mIdleToExecutingState = new IdleToExecutingState(this);
+    mExecutingState = new ExecutingState(this);
+
+    mOutputPortSettingsChangedState =
+        new OutputPortSettingsChangedState(this);
+
+    mExecutingToIdleState = new ExecutingToIdleState(this);
+    mIdleToLoadedState = new IdleToLoadedState(this);
+    mErrorState = new ErrorState(this);
+    mFlushingState = new FlushingState(this);
+
+    mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
+
+    changeState(mUninitializedState);
+}
+
+ACodec::~ACodec() {
+}
+
+void ACodec::setNotificationMessage(const sp<AMessage> &msg) {
+    mNotify = msg;
+}
+
+void ACodec::initiateSetup(const sp<AMessage> &msg) {
+    msg->setWhat(kWhatSetup);
+    msg->setTarget(id());
+    msg->post();
+}
+
+void ACodec::signalFlush() {
+    (new AMessage(kWhatFlush, id()))->post();
+}
+
+void ACodec::signalResume() {
+    (new AMessage(kWhatResume, id()))->post();
+}
+
+void ACodec::initiateShutdown() {
+    (new AMessage(kWhatShutdown, id()))->post();
+}
+
+status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+    CHECK(mDealer[portIndex] == NULL);
+    CHECK(mBuffers[portIndex].isEmpty());
+
+    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
+        return allocateOutputBuffersFromNativeWindow();
+    }
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    LOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+            mComponentName.c_str(),
+            def.nBufferCountActual, def.nBufferSize,
+            portIndex == kPortIndexInput ? "input" : "output");
+
+    size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+    mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+
+    for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+        sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+        CHECK(mem.get() != NULL);
+
+        IOMX::buffer_id buffer;
+#if 0
+        err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);
+#else
+        err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+#endif
+
+        if (err != OK) {
+            return err;
+        }
+
+        BufferInfo info;
+        info.mBufferID = buffer;
+        info.mStatus = BufferInfo::OWNED_BY_US;
+        info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+        mBuffers[portIndex].push(info);
+    }
+
+    return OK;
+}
+
+status_t ACodec::allocateOutputBuffersFromNativeWindow() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = native_window_set_buffers_geometry(
+            mNativeWindow.get(),
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.format.video.eColorFormat);
+
+    if (err != 0) {
+        LOGE("native_window_set_buffers_geometry failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+
+    // Increase the buffer count by one to allow for the ANativeWindow to hold
+    // on to one of the buffers.
+    def.nBufferCountActual++;
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    // Set up the native window.
+    // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+    err = native_window_set_usage(
+            mNativeWindow.get(),
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+
+    if (err != 0) {
+        LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_set_buffer_count(
+            mNativeWindow.get(), def.nBufferCountActual);
+
+    if (err != 0) {
+        LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+                -err);
+        return err;
+    }
+
+    // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
+    // the same set of buffers.
+
+    LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
+         "output port",
+         mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
+
+    // Dequeue buffers and send them to OMX
+    OMX_U32 i;
+    for (i = 0; i < def.nBufferCountActual; i++) {
+        android_native_buffer_t *buf;
+        err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
+        if (err != 0) {
+            LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+        IOMX::buffer_id bufferId;
+        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+                &bufferId);
+        if (err != 0) {
+            break;
+        }
+
+        LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+             mComponentName.c_str(),
+             bufferId, graphicBuffer.get());
+
+        BufferInfo info;
+        info.mBufferID = bufferId;
+        info.mStatus = BufferInfo::OWNED_BY_US;
+        info.mData = new ABuffer(0);
+        info.mGraphicBuffer = graphicBuffer;
+        mBuffers[kPortIndexOutput].push(info);
+    }
+
+    OMX_U32 cancelStart;
+    OMX_U32 cancelEnd;
+
+    if (err != 0) {
+        // If an error occurred while dequeuing we need to cancel any buffers
+        // that were dequeued.
+        cancelStart = 0;
+        cancelEnd = i;
+    } else {
+        // Return the last two buffers to the native window.
+        // XXX TODO: The number of buffers the native window owns should
+        // probably be queried from it when we put the native window in
+        // fixed buffer pool mode (which needs to be implemented).
+        // Currently it's hard-coded to 2.
+        cancelStart = def.nBufferCountActual - 2;
+        cancelEnd = def.nBufferCountActual;
+    }
+
+    for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
+        BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+        cancelBufferToNativeWindow(info);
+    }
+
+    return err;
+}
+
+status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+    LOGV("[%s] Calling cancelBuffer on buffer %p",
+         mComponentName.c_str(), info->mBufferID);
+
+    int err = mNativeWindow->cancelBuffer(
+        mNativeWindow.get(), info->mGraphicBuffer.get());
+
+    CHECK_EQ(err, 0);
+
+    info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+
+    return OK;
+}
+
+ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
+    android_native_buffer_t *buf;
+    CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
+
+    for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+        BufferInfo *info =
+            &mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (info->mGraphicBuffer->handle == buf->handle) {
+            CHECK_EQ((int)info->mStatus,
+                     (int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+            info->mStatus = BufferInfo::OWNED_BY_US;
+
+            return info;
+        }
+    }
+
+    TRESPASS();
+
+    return NULL;
+}
+
+status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+    for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
+        CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
+    }
+
+    mDealer[portIndex].clear();
+
+    return OK;
+}
+
+status_t ACodec::freeOutputBuffersOwnedByNativeWindow() {
+    for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+        BufferInfo *info =
+            &mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (info->mStatus ==
+                BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+            CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
+        }
+    }
+
+    return OK;
+}
+
+status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
+    BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+    CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+            || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+    if (portIndex == kPortIndexOutput && mNativeWindow != NULL
+            && info->mStatus == BufferInfo::OWNED_BY_US) {
+        CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
+    }
+
+    CHECK_EQ(mOMX->freeBuffer(
+                mNode, portIndex, info->mBufferID),
+             (status_t)OK);
+
+    mBuffers[portIndex].removeAt(i);
+
+    return OK;
+}
+
+ACodec::BufferInfo *ACodec::findBufferByID(
+        uint32_t portIndex, IOMX::buffer_id bufferID,
+        ssize_t *index) {
+    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+        BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+        if (info->mBufferID == bufferID) {
+            if (index != NULL) {
+                *index = i;
+            }
+            return info;
+        }
+    }
+
+    TRESPASS();
+
+    return NULL;
+}
+
+void ACodec::setComponentRole(
+        bool isEncoder, const char *mime) {
+    struct MimeToRole {
+        const char *mime;
+        const char *decoderRole;
+        const char *encoderRole;
+    };
+
+    static const MimeToRole kMimeToRole[] = {
+        { MEDIA_MIMETYPE_AUDIO_MPEG,
+            "audio_decoder.mp3", "audio_encoder.mp3" },
+        { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+            "audio_decoder.amrnb", "audio_encoder.amrnb" },
+        { MEDIA_MIMETYPE_AUDIO_AMR_WB,
+            "audio_decoder.amrwb", "audio_encoder.amrwb" },
+        { MEDIA_MIMETYPE_AUDIO_AAC,
+            "audio_decoder.aac", "audio_encoder.aac" },
+        { MEDIA_MIMETYPE_VIDEO_AVC,
+            "video_decoder.avc", "video_encoder.avc" },
+        { MEDIA_MIMETYPE_VIDEO_MPEG4,
+            "video_decoder.mpeg4", "video_encoder.mpeg4" },
+        { MEDIA_MIMETYPE_VIDEO_H263,
+            "video_decoder.h263", "video_encoder.h263" },
+    };
+
+    static const size_t kNumMimeToRole =
+        sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
+
+    size_t i;
+    for (i = 0; i < kNumMimeToRole; ++i) {
+        if (!strcasecmp(mime, kMimeToRole[i].mime)) {
+            break;
+        }
+    }
+
+    if (i == kNumMimeToRole) {
+        return;
+    }
+
+    const char *role =
+        isEncoder ? kMimeToRole[i].encoderRole
+                  : kMimeToRole[i].decoderRole;
+
+    if (role != NULL) {
+        OMX_PARAM_COMPONENTROLETYPE roleParams;
+        InitOMXParams(&roleParams);
+
+        strncpy((char *)roleParams.cRole,
+                role, OMX_MAX_STRINGNAME_SIZE - 1);
+
+        roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->setParameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &roleParams, sizeof(roleParams));
+
+        if (err != OK) {
+            LOGW("[%s] Failed to set standard component role '%s'.",
+                 mComponentName.c_str(), role);
+        }
+    }
+}
+
+void ACodec::configureCodec(
+        const char *mime, const sp<AMessage> &msg) {
+    setComponentRole(false /* isEncoder */, mime);
+
+    if (!strncasecmp(mime, "video/", 6)) {
+        int32_t width, height;
+        CHECK(msg->findInt32("width", &width));
+        CHECK(msg->findInt32("height", &height));
+
+        CHECK_EQ(setupVideoDecoder(mime, width, height),
+                 (status_t)OK);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        int32_t numChannels, sampleRate;
+        CHECK(msg->findInt32("channel-count", &numChannels));
+        CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+        CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+    } else {
+        TRESPASS();
+    }
+
+    int32_t maxInputSize;
+    if (msg->findInt32("max-input-size", &maxInputSize)) {
+        CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
+                 (status_t)OK);
+    } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
+        CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192),  // XXX
+                 (status_t)OK);
+    }
+}
+
+status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (def.nBufferSize >= size) {
+        return OK;
+    }
+
+    def.nBufferSize = size;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err != OK) {
+        return err;
+    }
+
+    CHECK(def.nBufferSize >= size);
+
+    return OK;
+}
+
+status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+    OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+    InitOMXParams(&profile);
+    profile.nPortIndex = kPortIndexInput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+    if (err != OK) {
+        return err;
+    }
+
+    profile.nChannels = numChannels;
+    profile.nSampleRate = sampleRate;
+    profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+    return err;
+}
+
+status_t ACodec::setVideoPortFormatType(
+        OMX_U32 portIndex,
+        OMX_VIDEO_CODINGTYPE compressionFormat,
+        OMX_COLOR_FORMATTYPE colorFormat) {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    InitOMXParams(&format);
+    format.nPortIndex = portIndex;
+    format.nIndex = 0;
+    bool found = false;
+
+    OMX_U32 index = 0;
+    for (;;) {
+        format.nIndex = index;
+        status_t err = mOMX->getParameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+
+        if (err != OK) {
+            return err;
+        }
+
+        // The following assertion is violated by TI's video decoder.
+        // CHECK_EQ(format.nIndex, index);
+
+        if (!strcmp("OMX.TI.Video.encoder", mComponentName.c_str())) {
+            if (portIndex == kPortIndexInput
+                    && colorFormat == format.eColorFormat) {
+                // eCompressionFormat does not seem right.
+                found = true;
+                break;
+            }
+            if (portIndex == kPortIndexOutput
+                    && compressionFormat == format.eCompressionFormat) {
+                // eColorFormat does not seem right.
+                found = true;
+                break;
+            }
+        }
+
+        if (format.eCompressionFormat == compressionFormat
+            && format.eColorFormat == colorFormat) {
+            found = true;
+            break;
+        }
+
+        ++index;
+    }
+
+    if (!found) {
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = mOMX->setParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+
+    return err;
+}
+
+status_t ACodec::setSupportedOutputFormat() {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    InitOMXParams(&format);
+    format.nPortIndex = kPortIndexOutput;
+    format.nIndex = 0;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+    CHECK_EQ(err, (status_t)OK);
+    CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
+
+    static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+    CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+           || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+           || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+           || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+    return mOMX->setParameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+}
+
+status_t ACodec::setupVideoDecoder(
+        const char *mime, int32_t width, int32_t height) {
+    OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        compressionFormat = OMX_VIDEO_CodingAVC;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+        compressionFormat = OMX_VIDEO_CodingMPEG4;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+        compressionFormat = OMX_VIDEO_CodingH263;
+    } else {
+        TRESPASS();
+    }
+
+    status_t err = setVideoPortFormatType(
+            kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setSupportedOutputFormat();
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoFormatOnPort(
+            kPortIndexInput, width, height, compressionFormat);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = setVideoFormatOnPort(
+            kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return OK;
+}
+
+status_t ACodec::setVideoFormatOnPort(
+        OMX_U32 portIndex,
+        int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = portIndex;
+
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    CHECK_EQ(err, (status_t)OK);
+
+    if (portIndex == kPortIndexInput) {
+        // XXX Need a (much) better heuristic to compute input buffer sizes.
+        const size_t X = 64 * 1024;
+        if (def.nBufferSize < X) {
+            def.nBufferSize = X;
+        }
+    }
+
+    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    if (portIndex == kPortIndexInput) {
+        video_def->eCompressionFormat = compressionFormat;
+        video_def->eColorFormat = OMX_COLOR_FormatUnused;
+    }
+
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    return err;
+}
+
+status_t ACodec::initNativeWindow() {
+    if (mNativeWindow != NULL) {
+        return mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
+    }
+
+    mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE);
+    return OK;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs(
+        OMX_U32 portIndex) {
+    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+        BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+        if (info->mStatus != BufferInfo::OWNED_BY_US
+                && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+            LOGV("[%s] Buffer %p on port %ld still has status %d",
+                    mComponentName.c_str(),
+                    info->mBufferID, portIndex, info->mStatus);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs() {
+    return allYourBuffersAreBelongToUs(kPortIndexInput)
+        && allYourBuffersAreBelongToUs(kPortIndexOutput);
+}
+
+void ACodec::deferMessage(const sp<AMessage> &msg) {
+    bool wasEmptyBefore = mDeferredQueue.empty();
+    mDeferredQueue.push_back(msg);
+}
+
+void ACodec::processDeferredMessages() {
+    List<sp<AMessage> > queue = mDeferredQueue;
+    mDeferredQueue.clear();
+
+    List<sp<AMessage> >::iterator it = queue.begin();
+    while (it != queue.end()) {
+        onMessageReceived(*it++);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
+    : AState(parentState),
+      mCodec(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) {
+    return KEEP_BUFFERS;
+}
+
+bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatInputBufferFilled:
+        {
+            onInputBufferFilled(msg);
+            break;
+        }
+
+        case kWhatOutputBufferDrained:
+        {
+            onOutputBufferDrained(msg);
+            break;
+        }
+
+        case ACodec::kWhatOMXMessage:
+        {
+            return onOMXMessage(msg);
+        }
+
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
+    int32_t type;
+    CHECK(msg->findInt32("type", &type));
+
+    IOMX::node_id nodeID;
+    CHECK(msg->findPointer("node", &nodeID));
+    CHECK_EQ(nodeID, mCodec->mNode);
+
+    switch (type) {
+        case omx_message::EVENT:
+        {
+            int32_t event, data1, data2;
+            CHECK(msg->findInt32("event", &event));
+            CHECK(msg->findInt32("data1", &data1));
+            CHECK(msg->findInt32("data2", &data2));
+
+            return onOMXEvent(
+                    static_cast<OMX_EVENTTYPE>(event),
+                    static_cast<OMX_U32>(data1),
+                    static_cast<OMX_U32>(data2));
+        }
+
+        case omx_message::EMPTY_BUFFER_DONE:
+        {
+            IOMX::buffer_id bufferID;
+            CHECK(msg->findPointer("buffer", &bufferID));
+
+            return onOMXEmptyBufferDone(bufferID);
+        }
+
+        case omx_message::FILL_BUFFER_DONE:
+        {
+            IOMX::buffer_id bufferID;
+            CHECK(msg->findPointer("buffer", &bufferID));
+
+            int32_t rangeOffset, rangeLength, flags;
+            int64_t timeUs;
+            void *platformPrivate;
+            void *dataPtr;
+
+            CHECK(msg->findInt32("range_offset", &rangeOffset));
+            CHECK(msg->findInt32("range_length", &rangeLength));
+            CHECK(msg->findInt32("flags", &flags));
+            CHECK(msg->findInt64("timestamp", &timeUs));
+            CHECK(msg->findPointer("platform_private", &platformPrivate));
+            CHECK(msg->findPointer("data_ptr", &dataPtr));
+
+            return onOMXFillBufferDone(
+                    bufferID,
+                    (size_t)rangeOffset, (size_t)rangeLength,
+                    (OMX_U32)flags,
+                    timeUs,
+                    platformPrivate,
+                    dataPtr);
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+bool ACodec::BaseState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    if (event != OMX_EventError) {
+        LOGI("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
+             mCodec->mComponentName.c_str(), event, data1, data2);
+
+        return false;
+    }
+
+    LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
+         mCodec->mComponentName.c_str(), data1, data2);
+
+    mCodec->changeState(mCodec->mErrorState);
+
+    return true;
+}
+
+bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexInput, bufferID);
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexInput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+            break;
+
+        case RESUBMIT_BUFFERS:
+            postFillThisBuffer(info);
+            break;
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+            TRESPASS();  // Not currently used
+            break;
+        }
+    }
+
+    return true;
+}
+
+void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
+    if (mCodec->mPortEOS[kPortIndexInput]) {
+        return;
+    }
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+    notify->setPointer("buffer-id", info->mBufferID);
+
+    info->mData->meta()->clear();
+    notify->setObject("buffer", info->mData);
+
+    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
+    reply->setPointer("buffer-id", info->mBufferID);
+
+    notify->setMessage("reply", reply);
+
+    notify->post();
+
+    info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
+}
+
+void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
+    IOMX::buffer_id bufferID;
+    CHECK(msg->findPointer("buffer-id", &bufferID));
+
+    sp<RefBase> obj;
+    int32_t err = OK;
+    if (!msg->findObject("buffer", &obj)) {
+        CHECK(msg->findInt32("err", &err));
+
+        obj.clear();
+    }
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
+
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexInput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+        {
+            if (buffer == NULL) {
+                mCodec->mPortEOS[kPortIndexInput] = true;
+            }
+            break;
+        }
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (buffer != NULL) {
+                CHECK(!mCodec->mPortEOS[kPortIndexInput]);
+
+                int64_t timeUs;
+                CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+                OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+
+                int32_t isCSD;
+                if (buffer->meta()->findInt32("csd", &isCSD) && isCSD != 0) {
+                    flags |= OMX_BUFFERFLAG_CODECCONFIG;
+                }
+
+                if (buffer != info->mData) {
+                    if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
+                        LOGV("[%s] Needs to copy input data.",
+                             mCodec->mComponentName.c_str());
+                    }
+
+                    CHECK_LE(buffer->size(), info->mData->capacity());
+                    memcpy(info->mData->data(), buffer->data(), buffer->size());
+                }
+
+                CHECK_EQ(mCodec->mOMX->emptyBuffer(
+                            mCodec->mNode,
+                            bufferID,
+                            0,
+                            buffer->size(),
+                            flags,
+                            timeUs),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+                getMoreInputDataIfPossible();
+            } else if (!mCodec->mPortEOS[kPortIndexInput]) {
+                LOGV("[%s] Signalling EOS on the input port",
+                     mCodec->mComponentName.c_str());
+
+                CHECK_EQ(mCodec->mOMX->emptyBuffer(
+                            mCodec->mNode,
+                            bufferID,
+                            0,
+                            0,
+                            OMX_BUFFERFLAG_EOS,
+                            0),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+                mCodec->mPortEOS[kPortIndexInput] = true;
+            }
+            break;
+
+            default:
+                CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+                break;
+        }
+    }
+}
+
+void ACodec::BaseState::getMoreInputDataIfPossible() {
+    if (mCodec->mPortEOS[kPortIndexInput]) {
+        return;
+    }
+
+    BufferInfo *eligible = NULL;
+
+    for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) {
+        BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
+
+#if 0
+        if (info->mStatus == BufferInfo::OWNED_BY_UPSTREAM) {
+            // There's already a "read" pending.
+            return;
+        }
+#endif
+
+        if (info->mStatus == BufferInfo::OWNED_BY_US) {
+            eligible = info;
+        }
+    }
+
+    if (eligible == NULL) {
+        return;
+    }
+
+    postFillThisBuffer(eligible);
+}
+
+bool ACodec::BaseState::onOMXFillBufferDone(
+        IOMX::buffer_id bufferID,
+        size_t rangeOffset, size_t rangeLength,
+        OMX_U32 flags,
+        int64_t timeUs,
+        void *platformPrivate,
+        void *dataPtr) {
+    ssize_t index;
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+
+    info->mStatus = BufferInfo::OWNED_BY_US;
+
+    PortMode mode = getPortMode(kPortIndexOutput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+            break;
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (rangeLength == 0) {
+                if (!(flags & OMX_BUFFERFLAG_EOS)) {
+                    CHECK_EQ(mCodec->mOMX->fillBuffer(
+                                mCodec->mNode, info->mBufferID),
+                             (status_t)OK);
+
+                    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                }
+            } else {
+                if (mCodec->mNativeWindow == NULL) {
+                    info->mData->setRange(rangeOffset, rangeLength);
+                }
+
+                info->mData->meta()->setInt64("timeUs", timeUs);
+
+                sp<AMessage> notify = mCodec->mNotify->dup();
+                notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+                notify->setPointer("buffer-id", info->mBufferID);
+                notify->setObject("buffer", info->mData);
+
+                sp<AMessage> reply =
+                    new AMessage(kWhatOutputBufferDrained, mCodec->id());
+
+                reply->setPointer("buffer-id", info->mBufferID);
+
+                notify->setMessage("reply", reply);
+
+                notify->post();
+
+                info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
+            }
+
+            if (flags & OMX_BUFFERFLAG_EOS) {
+                sp<AMessage> notify = mCodec->mNotify->dup();
+                notify->setInt32("what", ACodec::kWhatEOS);
+                notify->post();
+
+                mCodec->mPortEOS[kPortIndexOutput] = true;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+            CHECK_EQ((status_t)OK,
+                     mCodec->freeBuffer(kPortIndexOutput, index));
+            break;
+        }
+    }
+
+    return true;
+}
+
+void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
+    IOMX::buffer_id bufferID;
+    CHECK(msg->findPointer("buffer-id", &bufferID));
+
+    ssize_t index;
+    BufferInfo *info =
+        mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
+
+    int32_t render;
+    if (mCodec->mNativeWindow != NULL
+            && msg->findInt32("render", &render) && render != 0) {
+        // The client wants this buffer to be rendered.
+
+        CHECK_EQ(mCodec->mNativeWindow->queueBuffer(
+                    mCodec->mNativeWindow.get(),
+                    info->mGraphicBuffer.get()),
+                 0);
+
+        info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+    } else {
+        info->mStatus = BufferInfo::OWNED_BY_US;
+    }
+
+    PortMode mode = getPortMode(kPortIndexOutput);
+
+    switch (mode) {
+        case KEEP_BUFFERS:
+        {
+            // XXX fishy, revisit!!! What about the FREE_BUFFERS case below?
+
+            if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                // We cannot resubmit the buffer we just rendered, dequeue
+                // the spare instead.
+
+                info = mCodec->dequeueBufferFromNativeWindow();
+            }
+            break;
+        }
+
+        case RESUBMIT_BUFFERS:
+        {
+            if (!mCodec->mPortEOS[kPortIndexOutput]) {
+                if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                    // We cannot resubmit the buffer we just rendered, dequeue
+                    // the spare instead.
+
+                    info = mCodec->dequeueBufferFromNativeWindow();
+                }
+
+                CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                         (status_t)OK);
+
+                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+            CHECK_EQ((status_t)OK,
+                     mCodec->freeBuffer(kPortIndexOutput, index));
+            break;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::UninitializedState::UninitializedState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case ACodec::kWhatSetup:
+        {
+            onSetup(msg);
+
+            handled = true;
+            break;
+        }
+
+        case ACodec::kWhatShutdown:
+        {
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+            notify->post();
+
+            handled = true;
+        }
+
+        case ACodec::kWhatFlush:
+        {
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->post();
+
+            handled = true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+
+    return handled;
+}
+
+void ACodec::UninitializedState::onSetup(
+        const sp<AMessage> &msg) {
+    OMXClient client;
+    CHECK_EQ(client.connect(), (status_t)OK);
+
+    sp<IOMX> omx = client.interface();
+
+    AString mime;
+    CHECK(msg->findString("mime", &mime));
+
+    AString componentName;
+
+    if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+        componentName = "OMX.Nvidia.h264.decode";
+    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+        componentName = "OMX.Nvidia.aac.decoder";
+    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
+        componentName = "OMX.Nvidia.mp3.decoder";
+    } else {
+        TRESPASS();
+    }
+
+    sp<CodecObserver> observer = new CodecObserver;
+
+    IOMX::node_id node;
+    CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
+             (status_t)OK);
+
+    sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
+    observer->setNotificationMessage(notify);
+
+    mCodec->mComponentName = componentName;
+    mCodec->mOMX = omx;
+    mCodec->mNode = node;
+
+    mCodec->configureCodec(mime.c_str(), msg);
+
+    sp<RefBase> obj;
+    if (msg->findObject("surface", &obj)) {
+        mCodec->mNativeWindow = static_cast<Surface *>(obj.get());
+    }
+
+    CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
+
+    CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+             (status_t)OK);
+
+    mCodec->changeState(mCodec->mLoadedToIdleState);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::LoadedToIdleState::LoadedToIdleState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::LoadedToIdleState::stateEntered() {
+    LOGI("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
+
+    CHECK_EQ(allocateBuffers(), (status_t)OK);
+}
+
+status_t ACodec::LoadedToIdleState::allocateBuffers() {
+    status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return mCodec->allocateBuffersOnPort(kPortIndexOutput);
+}
+
+bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            return true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+}
+
+bool ACodec::LoadedToIdleState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandStateSet, OMX_StateExecuting),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mIdleToExecutingState);
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToExecutingState::IdleToExecutingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::IdleToExecutingState::stateEntered() {
+    LOGI("[%s] Now Idle->Executing", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            return true;
+        }
+
+        default:
+            return BaseState::onMessageReceived(msg);
+    }
+}
+
+bool ACodec::IdleToExecutingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateExecuting);
+
+            mCodec->mExecutingState->resume();
+            mCodec->changeState(mCodec->mExecutingState);
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingState::ExecutingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
+        OMX_U32 portIndex) {
+    return RESUBMIT_BUFFERS;
+}
+
+void ACodec::ExecutingState::submitOutputBuffers() {
+    for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) {
+        BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i);
+
+        if (mCodec->mNativeWindow != NULL) {
+            CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+                    || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+            if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                continue;
+            }
+
+            status_t err = mCodec->mNativeWindow->lockBuffer(
+                    mCodec->mNativeWindow.get(),
+                    info->mGraphicBuffer.get());
+            CHECK_EQ(err, (status_t)OK);
+        } else {
+            CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+        }
+
+        CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                 (status_t)OK);
+
+        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+    }
+}
+
+void ACodec::ExecutingState::resume() {
+    submitOutputBuffers();
+
+    // Post the first input buffer.
+    CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
+    BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
+
+    postFillThisBuffer(info);
+}
+
+void ACodec::ExecutingState::stateEntered() {
+    LOGI("[%s] Now Executing", mCodec->mComponentName.c_str());
+
+    mCodec->processDeferredMessages();
+}
+
+bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mExecutingToIdleState);
+
+            handled = true;
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            CHECK_EQ(mCodec->mOMX->sendCommand(
+                        mCodec->mNode, OMX_CommandFlush, OMX_ALL),
+                     (status_t)OK);
+
+            mCodec->changeState(mCodec->mFlushingState);
+
+            handled = true;
+            break;
+        }
+
+        case kWhatResume:
+        {
+            resume();
+
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+bool ACodec::ExecutingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventPortSettingsChanged:
+        {
+            CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
+
+            if (data2 == OMX_IndexParamPortDefinition) {
+                CHECK_EQ(mCodec->mOMX->sendCommand(
+                            mCodec->mNode,
+                            OMX_CommandPortDisable, kPortIndexOutput),
+                         (status_t)OK);
+
+                if (mCodec->mNativeWindow != NULL) {
+                    CHECK_EQ((status_t)OK,
+                             mCodec->freeOutputBuffersOwnedByNativeWindow());
+                }
+
+                mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
+            } else {
+                LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
+                     mCodec->mComponentName.c_str(), data2);
+            }
+
+            return true;
+        }
+
+        case OMX_EventBufferFlag:
+        {
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::OutputPortSettingsChangedState::OutputPortSettingsChangedState(
+        ACodec *codec)
+    : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::OutputPortSettingsChangedState::getPortMode(
+        OMX_U32 portIndex) {
+    if (portIndex == kPortIndexOutput) {
+        return FREE_BUFFERS;
+    }
+
+    CHECK_EQ(portIndex, (OMX_U32)kPortIndexInput);
+
+    return RESUBMIT_BUFFERS;
+}
+
+bool ACodec::OutputPortSettingsChangedState::onMessageReceived(
+        const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatFlush:
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::OutputPortSettingsChangedState::stateEntered() {
+    LOGI("[%s] Now handling output port settings change",
+         mCodec->mComponentName.c_str());
+}
+
+bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            if (data1 == (OMX_U32)OMX_CommandPortDisable) {
+                CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+                LOGV("[%s] Output port now disabled.",
+                        mCodec->mComponentName.c_str());
+
+                CHECK(mCodec->mBuffers[kPortIndexOutput].isEmpty());
+                mCodec->mDealer[kPortIndexOutput].clear();
+
+                CHECK_EQ(mCodec->mOMX->sendCommand(
+                            mCodec->mNode, OMX_CommandPortEnable, kPortIndexOutput),
+                         (status_t)OK);
+
+                CHECK_EQ(mCodec->allocateBuffersOnPort(kPortIndexOutput),
+                         (status_t)OK);
+
+                return true;
+            } else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
+                CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+                LOGV("[%s] Output port now reenabled.",
+                        mCodec->mComponentName.c_str());
+
+                mCodec->mExecutingState->submitOutputBuffers();
+                mCodec->changeState(mCodec->mExecutingState);
+
+                return true;
+            }
+
+            return false;
+        }
+
+        default:
+            return false;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatFlush:
+        {
+            // Don't send me a flush request if you previously wanted me
+            // to shutdown.
+            TRESPASS();
+            break;
+        }
+
+        case kWhatShutdown:
+        {
+            // We're already doing that...
+
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::ExecutingToIdleState::stateEntered() {
+    LOGI("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ExecutingToIdleState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+            changeStateIfWeOwnAllBuffers();
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
+    if (mCodec->allYourBuffersAreBelongToUs()) {
+        CHECK_EQ(mCodec->mOMX->sendCommand(
+                    mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
+                 (status_t)OK);
+
+        CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK);
+        CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK);
+
+        mCodec->changeState(mCodec->mIdleToLoadedState);
+    }
+}
+
+void ACodec::ExecutingToIdleState::onInputBufferFilled(
+        const sp<AMessage> &msg) {
+    BaseState::onInputBufferFilled(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::ExecutingToIdleState::onOutputBufferDrained(
+        const sp<AMessage> &msg) {
+    BaseState::onOutputBufferDrained(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToLoadedState::IdleToLoadedState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::IdleToLoadedState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            // We're already doing that...
+
+            handled = true;
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            // Don't send me a flush request if you previously wanted me
+            // to shutdown.
+            TRESPASS();
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+void ACodec::IdleToLoadedState::stateEntered() {
+    LOGI("[%s] Now Idle->Loaded", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToLoadedState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+            CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
+
+            LOGI("[%s] Now Loaded", mCodec->mComponentName.c_str());
+
+            CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+            mCodec->mNativeWindow.clear();
+            mCodec->mNode = NULL;
+            mCodec->mOMX.clear();
+            mCodec->mComponentName.clear();
+
+            mCodec->changeState(mCodec->mUninitializedState);
+
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+            notify->post();
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ErrorState::ErrorState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
+    return BaseState::onMessageReceived(msg);
+}
+
+void ACodec::ErrorState::stateEntered() {
+    LOGI("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ErrorState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    LOGI("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::FlushingState::FlushingState(ACodec *codec)
+    : BaseState(codec) {
+}
+
+void ACodec::FlushingState::stateEntered() {
+    LOGI("[%s] Now Flushing", mCodec->mComponentName.c_str());
+
+    mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
+}
+
+bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
+    bool handled = false;
+
+    switch (msg->what()) {
+        case kWhatShutdown:
+        {
+            mCodec->deferMessage(msg);
+            break;
+        }
+
+        case kWhatFlush:
+        {
+            // We're already doing this right now.
+            handled = true;
+            break;
+        }
+
+        default:
+            handled = BaseState::onMessageReceived(msg);
+            break;
+    }
+
+    return handled;
+}
+
+bool ACodec::FlushingState::onOMXEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    switch (event) {
+        case OMX_EventCmdComplete:
+        {
+            CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
+
+            if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
+                CHECK(!mFlushComplete[data2]);
+                mFlushComplete[data2] = true;
+            } else {
+                CHECK_EQ(data2, OMX_ALL);
+                CHECK(mFlushComplete[kPortIndexInput]);
+                CHECK(mFlushComplete[kPortIndexOutput]);
+
+                changeStateIfWeOwnAllBuffers();
+            }
+
+            return true;
+        }
+
+        default:
+            return BaseState::onOMXEvent(event, data1, data2);
+    }
+
+    return true;
+}
+
+void ACodec::FlushingState::onOutputBufferDrained(const sp<AMessage> &msg) {
+    BaseState::onOutputBufferDrained(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::onInputBufferFilled(const sp<AMessage> &msg) {
+    BaseState::onInputBufferFilled(msg);
+
+    changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() {
+    if (mFlushComplete[kPortIndexInput]
+            && mFlushComplete[kPortIndexOutput]
+            && mCodec->allYourBuffersAreBelongToUs()) {
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatFlushCompleted);
+        notify->post();
+
+        mCodec->mPortEOS[kPortIndexInput] =
+            mCodec->mPortEOS[kPortIndexOutput] = false;
+
+        mCodec->changeState(mCodec->mExecutingState);
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index db23836..2d486e3 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -4,6 +4,7 @@
 include frameworks/base/media/libstagefright/codecs/common/Config.mk
 
 LOCAL_SRC_FILES:=                         \
+        ACodec.cpp                        \
         AMRExtractor.cpp                  \
         AMRWriter.cpp                     \
         AudioPlayer.cpp                   \
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
new file mode 100644
index 0000000..30286d8
--- /dev/null
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -0,0 +1,97 @@
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+AState::AState(const sp<AState> &parentState)
+    : mParentState(parentState) {
+}
+
+AState::~AState() {
+}
+
+sp<AState> AState::parentState() {
+    return mParentState;
+}
+
+void AState::stateEntered() {
+}
+
+void AState::stateExited() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AHierarchicalStateMachine::AHierarchicalStateMachine() {
+}
+
+AHierarchicalStateMachine::~AHierarchicalStateMachine() {
+}
+
+void AHierarchicalStateMachine::onMessageReceived(const sp<AMessage> &msg) {
+    sp<AState> save = mState;
+
+    sp<AState> cur = mState;
+    while (cur != NULL && !cur->onMessageReceived(msg)) {
+        // If you claim not to have handled the message you shouldn't
+        // have called setState...
+        CHECK(save == mState);
+
+        cur = cur->parentState();
+    }
+
+    if (cur != NULL) {
+        return;
+    }
+
+    LOGW("Warning message %s unhandled in root state.",
+         msg->debugString().c_str());
+}
+
+void AHierarchicalStateMachine::changeState(const sp<AState> &state) {
+    if (state == mState) {
+        // Quick exit for the easy case.
+        return;
+    }
+
+    Vector<sp<AState> > A;
+    sp<AState> cur = mState;
+    for (;;) {
+        A.push(cur);
+        if (cur == NULL) {
+            break;
+        }
+        cur = cur->parentState();
+    }
+
+    Vector<sp<AState> > B;
+    cur = state;
+    for (;;) {
+        B.push(cur);
+        if (cur == NULL) {
+            break;
+        }
+        cur = cur->parentState();
+    }
+
+    // Remove the common tail.
+    while (A.size() > 0 && B.size() > 0 && A.top() == B.top()) {
+        A.pop();
+        B.pop();
+    }
+
+    mState = state;
+
+    for (size_t i = 0; i < A.size(); ++i) {
+        A.editItemAt(i)->stateExited();
+    }
+
+    for (size_t i = B.size(); i-- > 0;) {
+        B.editItemAt(i)->stateEntered();
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index a4d4809..4e07f6f 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -1,16 +1,17 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=               \
-    AAtomizer.cpp               \
-    ABitReader.cpp              \
-    ABuffer.cpp                 \
-    AHandler.cpp                \
-    ALooper.cpp                 \
-    ALooperRoster.cpp           \
-    AMessage.cpp                \
-    AString.cpp                 \
-    base64.cpp                  \
+LOCAL_SRC_FILES:=                 \
+    AAtomizer.cpp                 \
+    ABitReader.cpp                \
+    ABuffer.cpp                   \
+    AHandler.cpp                  \
+    AHierarchicalStateMachine.cpp \
+    ALooper.cpp                   \
+    ALooperRoster.cpp             \
+    AMessage.cpp                  \
+    AString.cpp                   \
+    base64.cpp                    \
     hexdump.cpp
 
 LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index a559b21..de6346b 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -43,19 +43,21 @@
 static const size_t kTSPacketSize = 188;
 
 struct ATSParser::Program : public RefBase {
-    Program(unsigned programMapPID);
+    Program(ATSParser *parser, unsigned programMapPID);
 
     bool parsePID(
             unsigned pid, unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(bool isASeek);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
 
     int64_t convertPTSToTimestamp(uint64_t PTS);
 
 private:
+    ATSParser *mParser;
     unsigned mProgramMapPID;
     KeyedVector<unsigned, sp<Stream> > mStreams;
     bool mFirstPTSValid;
@@ -73,7 +75,8 @@
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(bool isASeek);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
 
@@ -105,8 +108,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::Program::Program(unsigned programMapPID)
-    : mProgramMapPID(programMapPID),
+ATSParser::Program::Program(ATSParser *parser, unsigned programMapPID)
+    : mParser(parser),
+      mProgramMapPID(programMapPID),
       mFirstPTSValid(false),
       mFirstPTS(0) {
 }
@@ -135,9 +139,15 @@
     return true;
 }
 
-void ATSParser::Program::signalDiscontinuity(bool isASeek) {
+void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        mStreams.editValueAt(i)->signalDiscontinuity(isASeek);
+        mStreams.editValueAt(i)->signalDiscontinuity(type);
+    }
+}
+
+void ATSParser::Program::signalEOS(status_t finalResult) {
+    for (size_t i = 0; i < mStreams.size(); ++i) {
+        mStreams.editValueAt(i)->signalEOS(finalResult);
     }
 }
 
@@ -155,7 +165,7 @@
 
     unsigned section_length = br->getBits(12);
     LOGV("  section_length = %u", section_length);
-    CHECK((section_length & 0xc00) == 0);
+    CHECK_EQ(section_length & 0xc00, 0u);
     CHECK_LE(section_length, 1021u);
 
     MY_LOGV("  program_number = %u", br->getBits(16));
@@ -170,7 +180,7 @@
 
     unsigned program_info_length = br->getBits(12);
     LOGV("  program_info_length = %u", program_info_length);
-    CHECK((program_info_length & 0xc00) == 0);
+    CHECK_EQ(program_info_length & 0xc00, 0u);
 
     br->skipBits(program_info_length * 8);  // skip descriptors
 
@@ -194,7 +204,7 @@
 
         unsigned ES_info_length = br->getBits(12);
         LOGV("    ES_info_length = %u", ES_info_length);
-        CHECK((ES_info_length & 0xc00) == 0);
+        CHECK_EQ(ES_info_length & 0xc00, 0u);
 
         CHECK_GE(infoBytesRemaining - 5, ES_info_length);
 
@@ -305,7 +315,7 @@
     }
 
     size_t payloadSizeBits = br->numBitsLeft();
-    CHECK((payloadSizeBits % 8) == 0);
+    CHECK_EQ(payloadSizeBits % 8, 0u);
 
     CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
 
@@ -313,27 +323,45 @@
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
 }
 
-void ATSParser::Stream::signalDiscontinuity(bool isASeek) {
-    isASeek = false;  // Always signal a "real" discontinuity
-
+void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
-    mQueue.clear();
+    switch (type) {
+        case DISCONTINUITY_HTTPLIVE:
+        {
+            mQueue.clear(true);
 
-    if (isASeek) {
-        // This is only a "minor" discontinuity, we stay within the same
-        // bitstream.
-
-        if (mSource != NULL) {
-            mSource->clear();
+            if (mStreamType == 0x1b && mSource != NULL) {
+                // Don't signal discontinuities on audio streams.
+                mSource->queueDiscontinuity();
+            }
+            break;
         }
-        return;
-    }
 
-    if (mStreamType == 0x1b && mSource != NULL) {
-        // Don't signal discontinuities on audio streams.
-        mSource->queueDiscontinuity();
+        case DISCONTINUITY_SEEK:
+        case DISCONTINUITY_FORMATCHANGE:
+        {
+            bool isASeek = (type == DISCONTINUITY_SEEK);
+
+            mQueue.clear(!isASeek);
+
+            if (mSource != NULL) {
+                mSource->clear();
+                mSource->queueDiscontinuity();
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void ATSParser::Stream::signalEOS(status_t finalResult) {
+    if (mSource != NULL) {
+        mSource->signalEOS(finalResult);
     }
 }
 
@@ -478,7 +506,7 @@
                     br->data(), br->numBitsLeft() / 8);
 
             size_t payloadSizeBits = br->numBitsLeft();
-            CHECK((payloadSizeBits % 8) == 0);
+            CHECK_EQ(payloadSizeBits % 8, 0u);
 
             LOGV("There's %d bytes of payload.", payloadSizeBits / 8);
         }
@@ -526,6 +554,7 @@
             if (meta != NULL) {
                 LOGV("created source!");
                 mSource = new AnotherPacketSource(meta);
+
                 mSource->queueAccessUnit(accessUnit);
             }
         } else if (mQueue.getFormat() != NULL) {
@@ -561,9 +590,17 @@
     parseTS(&br);
 }
 
-void ATSParser::signalDiscontinuity(bool isASeek) {
+void ATSParser::signalDiscontinuity(DiscontinuityType type) {
     for (size_t i = 0; i < mPrograms.size(); ++i) {
-        mPrograms.editItemAt(i)->signalDiscontinuity(isASeek);
+        mPrograms.editItemAt(i)->signalDiscontinuity(type);
+    }
+}
+
+void ATSParser::signalEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    for (size_t i = 0; i < mPrograms.size(); ++i) {
+        mPrograms.editItemAt(i)->signalEOS(finalResult);
     }
 }
 
@@ -581,7 +618,7 @@
 
     unsigned section_length = br->getBits(12);
     LOGV("  section_length = %u", section_length);
-    CHECK((section_length & 0xc00) == 0);
+    CHECK_EQ(section_length & 0xc00, 0u);
 
     MY_LOGV("  transport_stream_id = %u", br->getBits(16));
     MY_LOGV("  reserved = %u", br->getBits(2));
@@ -606,7 +643,7 @@
 
             LOGV("    program_map_PID = 0x%04x", programMapPID);
 
-            mPrograms.push(new Program(programMapPID));
+            mPrograms.push(new Program(this, programMapPID));
         }
     }
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 11b1de4..ef78c77 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -21,19 +21,28 @@
 #include <sys/types.h>
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
 #include <utils/Vector.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
 struct ABitReader;
+struct ABuffer;
 struct MediaSource;
 
 struct ATSParser : public RefBase {
+    enum DiscontinuityType {
+        DISCONTINUITY_HTTPLIVE,
+        DISCONTINUITY_SEEK,
+        DISCONTINUITY_FORMATCHANGE
+    };
+
     ATSParser();
 
     void feedTSPacket(const void *data, size_t size);
-    void signalDiscontinuity(bool isASeek = false);
+    void signalDiscontinuity(DiscontinuityType type);
+    void signalEOS(status_t finalResult);
 
     enum SourceType {
         AVC_VIDEO,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index ea747c8..7a1d5b0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,6 +48,32 @@
     return mFormat;
 }
 
+status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
+    buffer->clear();
+
+    Mutex::Autolock autoLock(mLock);
+    while (mEOSResult == OK && mBuffers.empty()) {
+        mCondition.wait(mLock);
+    }
+
+    if (!mBuffers.empty()) {
+        *buffer = *mBuffers.begin();
+        mBuffers.erase(mBuffers.begin());
+
+        int32_t discontinuity;
+        if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)
+                && discontinuity) {
+            buffer->clear();
+
+            return INFO_DISCONTINUITY;
+        }
+
+        return OK;
+    }
+
+    return mEOSResult;
+}
+
 status_t AnotherPacketSource::read(
         MediaBuffer **out, const ReadOptions *) {
     *out = NULL;
@@ -66,9 +92,8 @@
                 && discontinuity) {
             return INFO_DISCONTINUITY;
         } else {
-            uint64_t timeUs;
-            CHECK(buffer->meta()->findInt64(
-                        "time", (int64_t *)&timeUs));
+            int64_t timeUs;
+            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
             MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
             mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -92,7 +117,7 @@
     }
 
     int64_t timeUs;
-    CHECK(buffer->meta()->findInt64("time", &timeUs));
+    CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     LOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6);
 
     Mutex::Autolock autoLock(mLock);
@@ -134,4 +159,19 @@
     return false;
 }
 
+status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
+    *timeUs = 0;
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mBuffers.empty()) {
+        return mEOSResult != OK ? mEOSResult : -EWOULDBLOCK;
+    }
+
+    sp<ABuffer> buffer = *mBuffers.begin();
+    CHECK(buffer->meta()->findInt64("timeUs", timeUs));
+
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6999175..2bc7404 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -39,12 +39,16 @@
 
     bool hasBufferAvailable(status_t *finalResult);
 
+    status_t nextBufferTime(int64_t *timeUs);
+
     void queueAccessUnit(const sp<ABuffer> &buffer);
     void queueDiscontinuity();
     void signalEOS(status_t result);
 
     void clear();
 
+    status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+
 protected:
     virtual ~AnotherPacketSource();
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 4b4490d..4e7759d 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -40,13 +40,16 @@
     return mFormat;
 }
 
-void ElementaryStreamQueue::clear() {
+void ElementaryStreamQueue::clear(bool clearFormat) {
     if (mBuffer != NULL) {
         mBuffer->setRange(0, 0);
     }
 
     mRangeInfos.clear();
-    mFormat.clear();
+
+    if (clearFormat) {
+        mFormat.clear();
+    }
 }
 
 static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
@@ -289,7 +292,7 @@
     mBuffer->setRange(0, mBuffer->size() - offset);
 
     if (timeUs >= 0) {
-        accessUnit->meta()->setInt64("time", timeUs);
+        accessUnit->meta()->setInt64("timeUs", timeUs);
     } else {
         LOGW("no time for AAC access unit");
     }
@@ -470,7 +473,7 @@
             int64_t timeUs = fetchTimestamp(nextScan);
             CHECK_GE(timeUs, 0ll);
 
-            accessUnit->meta()->setInt64("time", timeUs);
+            accessUnit->meta()->setInt64("timeUs", timeUs);
 
             if (mFormat == NULL) {
                 mFormat = MakeAVCCodecSpecificData(accessUnit);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index adce153..5b7957e 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -35,7 +35,7 @@
     ElementaryStreamQueue(Mode mode);
 
     status_t appendData(const void *data, size_t size, int64_t timeUs);
-    void clear();
+    void clear(bool clearFormat);
 
     sp<ABuffer> dequeueAccessUnit();
 
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 600116e..a1f0796 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -214,7 +214,7 @@
 
     if (isDiscontinuity(packet, n)) {
         LOGI("XXX discontinuity detected");
-        mParser->signalDiscontinuity();
+        mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE);
     } else if (n < (ssize_t)kTSPacketSize) {
         return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
     } else {