Implementation of a java media codec interface and associated tools.

Change-Id: I13e54062d4de584355c5d82bb027a68aeaf2923b
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 11e94e8..f26747b 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -147,4 +147,28 @@
 
 include $(BUILD_EXECUTABLE)
 
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=               \
+        codec.cpp               \
+        SimplePlayer.cpp        \
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder libstagefright_foundation \
+        libmedia libgui 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:= codec
+
+include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
new file mode 100644
index 0000000..d5b9223
--- /dev/null
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2012 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 "SimplePlayer"
+#include <utils/Log.h>
+
+#include "SimplePlayer.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+SimplePlayer::SimplePlayer()
+    : mState(UNINITIALIZED),
+      mDoMoreStuffGeneration(0),
+      mStartTimeRealUs(-1ll) {
+}
+
+SimplePlayer::~SimplePlayer() {
+}
+
+// static
+status_t PostAndAwaitResponse(
+        const sp<AMessage> &msg, sp<AMessage> *response) {
+    status_t err = msg->postAndAwaitResponse(response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (!(*response)->findInt32("err", &err)) {
+        err = OK;
+    }
+
+    return err;
+}
+status_t SimplePlayer::setDataSource(const char *path) {
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    msg->setString("path", path);
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::setSurface(const sp<ISurfaceTexture> &surfaceTexture) {
+    sp<AMessage> msg = new AMessage(kWhatSetSurface, id());
+
+    sp<SurfaceTextureClient> surfaceTextureClient;
+    if (surfaceTexture != NULL) {
+        surfaceTextureClient = new SurfaceTextureClient(surfaceTexture);
+    }
+
+    msg->setObject(
+            "native-window", new NativeWindowWrapper(surfaceTextureClient));
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::prepare() {
+    sp<AMessage> msg = new AMessage(kWhatPrepare, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::start() {
+    sp<AMessage> msg = new AMessage(kWhatStart, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::stop() {
+    sp<AMessage> msg = new AMessage(kWhatStop, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t SimplePlayer::reset() {
+    sp<AMessage> msg = new AMessage(kWhatReset, id());
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSetDataSource:
+        {
+            status_t err;
+            if (mState != UNINITIALIZED) {
+                err = INVALID_OPERATION;
+            } else {
+                CHECK(msg->findString("path", &mPath));
+                mState = UNPREPARED;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatSetSurface:
+        {
+            status_t err;
+            if (mState != UNPREPARED) {
+                err = INVALID_OPERATION;
+            } else {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("native-window", &obj));
+
+                mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
+
+                err = OK;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatPrepare:
+        {
+            status_t err;
+            if (mState != UNPREPARED) {
+                err = INVALID_OPERATION;
+            } else {
+                err = onPrepare();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatStart:
+        {
+            status_t err = OK;
+
+            if (mState == UNPREPARED) {
+                err = onPrepare();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            if (err == OK) {
+                if (mState != STOPPED) {
+                    err = INVALID_OPERATION;
+                } else {
+                    err = onStart();
+
+                    if (err == OK) {
+                        mState = STARTED;
+                    }
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatStop:
+        {
+            status_t err;
+
+            if (mState != STARTED) {
+                err = INVALID_OPERATION;
+            } else {
+                err = onStop();
+
+                if (err == OK) {
+                    mState = STOPPED;
+                }
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatReset:
+        {
+            status_t err = OK;
+
+            if (mState == STARTED) {
+                CHECK_EQ(onStop(), (status_t)OK);
+                mState = STOPPED;
+            }
+
+            if (mState == STOPPED) {
+                err = onReset();
+                mState = UNINITIALIZED;
+            }
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatDoMoreStuff:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mDoMoreStuffGeneration) {
+                break;
+            }
+
+            status_t err = onDoMoreStuff();
+
+            if (err == OK) {
+                msg->post(5000ll);
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+status_t SimplePlayer::onPrepare() {
+    CHECK_EQ(mState, UNPREPARED);
+
+    mExtractor = new NuMediaExtractor;
+
+    status_t err = mExtractor->setDataSource(mPath.c_str());
+
+    if (err != OK) {
+        mExtractor.clear();
+        return err;
+    }
+
+    if (mCodecLooper == NULL) {
+        mCodecLooper = new ALooper;
+        mCodecLooper->start();
+    }
+
+    bool haveAudio = false;
+    bool haveVideo = false;
+    for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+        sp<AMessage> format;
+        status_t err = mExtractor->getTrackFormat(i, &format);
+        CHECK_EQ(err, (status_t)OK);
+
+        AString mime;
+        CHECK(format->findString("mime", &mime));
+
+        if (!haveAudio && !strncasecmp(mime.c_str(), "audio/", 6)) {
+            haveAudio = true;
+        } else if (!haveVideo && !strncasecmp(mime.c_str(), "video/", 6)) {
+            haveVideo = true;
+        } else {
+            continue;
+        }
+
+        err = mExtractor->selectTrack(i);
+        CHECK_EQ(err, (status_t)OK);
+
+        CodecState *state =
+            &mStateByTrackIndex.editValueAt(
+                    mStateByTrackIndex.add(i, CodecState()));
+
+        state->mNumFramesWritten = 0;
+        state->mCodec = MediaCodec::CreateByType(
+                mCodecLooper, mime.c_str(), false /* encoder */);
+
+        CHECK(state->mCodec != NULL);
+
+        err = state->mCodec->configure(
+                format, mNativeWindow->getSurfaceTextureClient(),
+                0 /* flags */);
+
+        CHECK_EQ(err, (status_t)OK);
+
+        size_t j = 0;
+        sp<RefBase> obj;
+        while (format->findObject(StringPrintf("csd-%d", j).c_str(), &obj)) {
+            sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+            state->mCSD.push_back(buffer);
+
+            ++j;
+        }
+    }
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        status_t err = state->mCodec->start();
+        CHECK_EQ(err, (status_t)OK);
+
+        err = state->mCodec->getInputBuffers(&state->mBuffers[0]);
+        CHECK_EQ(err, (status_t)OK);
+
+        err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
+        CHECK_EQ(err, (status_t)OK);
+
+        for (size_t j = 0; j < state->mCSD.size(); ++j) {
+            const sp<ABuffer> &srcBuffer = state->mCSD.itemAt(j);
+
+            size_t index;
+            err = state->mCodec->dequeueInputBuffer(&index, -1ll);
+            CHECK_EQ(err, (status_t)OK);
+
+            const sp<ABuffer> &dstBuffer = state->mBuffers[0].itemAt(index);
+
+            CHECK_LE(srcBuffer->size(), dstBuffer->capacity());
+            dstBuffer->setRange(0, srcBuffer->size());
+            memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size());
+
+            err = state->mCodec->queueInputBuffer(
+                    index,
+                    0,
+                    dstBuffer->size(),
+                    0ll,
+                    MediaCodec::BUFFER_FLAG_CODECCONFIG);
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    return OK;
+}
+
+status_t SimplePlayer::onStart() {
+    CHECK_EQ(mState, STOPPED);
+
+    mStartTimeRealUs = -1ll;
+
+    sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, id());
+    msg->setInt32("generation", ++mDoMoreStuffGeneration);
+    msg->post();
+
+    return OK;
+}
+
+status_t SimplePlayer::onStop() {
+    CHECK_EQ(mState, STARTED);
+
+    ++mDoMoreStuffGeneration;
+
+    return OK;
+}
+
+status_t SimplePlayer::onReset() {
+    CHECK_EQ(mState, STOPPED);
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        CHECK_EQ(state->mCodec->stop(), (status_t)OK);
+    }
+
+    mStartTimeRealUs = -1ll;
+
+    mStateByTrackIndex.clear();
+    mCodecLooper.clear();
+    mExtractor.clear();
+    mNativeWindow.clear();
+    mPath.clear();
+
+    return OK;
+}
+
+status_t SimplePlayer::onDoMoreStuff() {
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        size_t index;
+        status_t err = state->mCodec->dequeueInputBuffer(&index);
+
+        if (err == OK) {
+            state->mAvailInputBufferIndices.push_back(index);
+        }
+
+        BufferInfo info;
+        err = state->mCodec->dequeueOutputBuffer(
+                &info.mIndex,
+                &info.mOffset,
+                &info.mSize,
+                &info.mPresentationTimeUs,
+                &info.mFlags);
+
+        if (err == OK) {
+            state->mAvailOutputBufferInfos.push_back(info);
+        } else if (err == INFO_FORMAT_CHANGED) {
+            err = onOutputFormatChanged(mStateByTrackIndex.keyAt(i), state);
+            CHECK_EQ(err, (status_t)OK);
+        } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+            err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    for (;;) {
+        size_t trackIndex;
+        status_t err = mExtractor->getSampleTrackIndex(&trackIndex);
+
+        if (err != OK) {
+            ALOGI("encountered input EOS.");
+            break;
+        } else {
+            CodecState *state = &mStateByTrackIndex.editValueFor(trackIndex);
+
+            if (state->mAvailInputBufferIndices.empty()) {
+                break;
+            }
+
+            size_t index = *state->mAvailInputBufferIndices.begin();
+            state->mAvailInputBufferIndices.erase(
+                    state->mAvailInputBufferIndices.begin());
+
+            const sp<ABuffer> &dstBuffer =
+                state->mBuffers[0].itemAt(index);
+
+            err = mExtractor->readSampleData(dstBuffer);
+            CHECK_EQ(err, (status_t)OK);
+
+            int64_t timeUs;
+            CHECK_EQ(mExtractor->getSampleTime(&timeUs), (status_t)OK);
+
+            err = state->mCodec->queueInputBuffer(
+                    index,
+                    dstBuffer->offset(),
+                    dstBuffer->size(),
+                    timeUs,
+                    0);
+            CHECK_EQ(err, (status_t)OK);
+
+            err = mExtractor->advance();
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mStartTimeRealUs < 0ll) {
+        mStartTimeRealUs = nowUs + 1000000ll;
+    }
+
+    for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) {
+        CodecState *state = &mStateByTrackIndex.editValueAt(i);
+
+        while (!state->mAvailOutputBufferInfos.empty()) {
+            BufferInfo *info = &*state->mAvailOutputBufferInfos.begin();
+
+            int64_t whenRealUs = info->mPresentationTimeUs + mStartTimeRealUs;
+            int64_t lateByUs = nowUs - whenRealUs;
+
+            if (lateByUs > -10000ll) {
+                bool release = true;
+
+                if (lateByUs > 30000ll) {
+                    ALOGI("track %d buffer late by %lld us, dropping.",
+                          i, lateByUs);
+                    state->mCodec->releaseOutputBuffer(info->mIndex);
+                } else {
+                    if (state->mAudioTrack != NULL) {
+                        const sp<ABuffer> &srcBuffer =
+                            state->mBuffers[1].itemAt(info->mIndex);
+
+                        renderAudio(state, info, srcBuffer);
+
+                        if (info->mSize > 0) {
+                            release = false;
+                        }
+                    }
+
+                    if (release) {
+                        state->mCodec->renderOutputBufferAndRelease(
+                                info->mIndex);
+                    }
+                }
+
+                if (release) {
+                    state->mAvailOutputBufferInfos.erase(
+                            state->mAvailOutputBufferInfos.begin());
+
+                    info = NULL;
+                } else {
+                    break;
+                }
+            } else {
+                ALOGV("track %d buffer early by %lld us.", i, -lateByUs);
+                break;
+            }
+        }
+    }
+
+    return OK;
+}
+
+status_t SimplePlayer::onOutputFormatChanged(
+        size_t trackIndex, CodecState *state) {
+    sp<AMessage> format;
+    status_t err = state->mCodec->getOutputFormat(&format);
+
+    if (err != OK) {
+        return err;
+    }
+
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+
+    if (!strncasecmp(mime.c_str(), "audio/", 6)) {
+        int32_t channelCount;
+        int32_t sampleRate;
+        CHECK(format->findInt32("channel-count", &channelCount));
+        CHECK(format->findInt32("sample-rate", &sampleRate));
+
+        state->mAudioTrack = new AudioTrack(
+                AUDIO_STREAM_MUSIC,
+                sampleRate,
+                AUDIO_FORMAT_PCM_16_BIT,
+                (channelCount == 1)
+                    ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO,
+                0);
+
+        state->mNumFramesWritten = 0;
+    }
+
+    return OK;
+}
+
+void SimplePlayer::renderAudio(
+        CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer) {
+    CHECK(state->mAudioTrack != NULL);
+
+    if (state->mAudioTrack->stopped()) {
+        state->mAudioTrack->start();
+    }
+
+    uint32_t numFramesPlayed;
+    CHECK_EQ(state->mAudioTrack->getPosition(&numFramesPlayed), (status_t)OK);
+
+    uint32_t numFramesAvailableToWrite =
+        state->mAudioTrack->frameCount()
+            - (state->mNumFramesWritten - numFramesPlayed);
+
+    size_t numBytesAvailableToWrite =
+        numFramesAvailableToWrite * state->mAudioTrack->frameSize();
+
+    size_t copy = info->mSize;
+    if (copy > numBytesAvailableToWrite) {
+        copy = numBytesAvailableToWrite;
+    }
+
+    if (copy == 0) {
+        return;
+    }
+
+    int64_t startTimeUs = ALooper::GetNowUs();
+
+    ssize_t nbytes = state->mAudioTrack->write(
+            buffer->base() + info->mOffset, copy);
+
+    CHECK_EQ(nbytes, (ssize_t)copy);
+
+    int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+
+    uint32_t numFramesWritten = nbytes / state->mAudioTrack->frameSize();
+
+    if (delayUs > 2000ll) {
+        ALOGW("AudioTrack::write took %lld us, numFramesAvailableToWrite=%u, "
+              "numFramesWritten=%u",
+              delayUs, numFramesAvailableToWrite, numFramesWritten);
+    }
+
+    info->mOffset += nbytes;
+    info->mSize -= nbytes;
+
+    state->mNumFramesWritten += numFramesWritten;
+}
+
+}  // namespace android
diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h
new file mode 100644
index 0000000..2548252
--- /dev/null
+++ b/cmds/stagefright/SimplePlayer.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 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/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ALooper;
+struct AudioTrack;
+struct ISurfaceTexture;
+struct MediaCodec;
+struct NativeWindowWrapper;
+struct NuMediaExtractor;
+
+struct SimplePlayer : public AHandler {
+    SimplePlayer();
+
+    status_t setDataSource(const char *path);
+    status_t setSurface(const sp<ISurfaceTexture> &surfaceTexture);
+    status_t prepare();
+    status_t start();
+    status_t stop();
+    status_t reset();
+
+protected:
+    virtual ~SimplePlayer();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum State {
+        UNINITIALIZED,
+        UNPREPARED,
+        STOPPED,
+        STARTED
+    };
+
+    enum {
+        kWhatSetDataSource,
+        kWhatSetSurface,
+        kWhatPrepare,
+        kWhatStart,
+        kWhatStop,
+        kWhatReset,
+        kWhatDoMoreStuff,
+    };
+
+    struct BufferInfo {
+        size_t mIndex;
+        size_t mOffset;
+        size_t mSize;
+        int64_t mPresentationTimeUs;
+        uint32_t mFlags;
+    };
+
+    struct CodecState
+    {
+        sp<MediaCodec> mCodec;
+        Vector<sp<ABuffer> > mCSD;
+        Vector<sp<ABuffer> > mBuffers[2];
+
+        List<size_t> mAvailInputBufferIndices;
+        List<BufferInfo> mAvailOutputBufferInfos;
+
+        sp<AudioTrack> mAudioTrack;
+        uint32_t mNumFramesWritten;
+    };
+
+    State mState;
+    AString mPath;
+    sp<NativeWindowWrapper> mNativeWindow;
+
+    sp<NuMediaExtractor> mExtractor;
+    sp<ALooper> mCodecLooper;
+    KeyedVector<size_t, CodecState> mStateByTrackIndex;
+    int32_t mDoMoreStuffGeneration;
+
+    int64_t mStartTimeRealUs;
+
+    status_t onPrepare();
+    status_t onStart();
+    status_t onStop();
+    status_t onReset();
+    status_t onDoMoreStuff();
+    status_t onOutputFormatChanged(size_t trackIndex, CodecState *state);
+
+    void renderAudio(
+            CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SimplePlayer);
+};
+
+}  // namespace android
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
new file mode 100644
index 0000000..02dc8dd
--- /dev/null
+++ b/cmds/stagefright/codec.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2012 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 "codec"
+#include <utils/Log.h>
+
+#include "SimplePlayer.h"
+
+#include <binder/ProcessState.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/DataSource.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s [-a] use audio\n"
+                    "\t\t[-v] use video\n"
+                    "\t\t[-p] playback\n", me);
+
+    exit(1);
+}
+
+namespace android {
+
+struct CodecState {
+    sp<MediaCodec> mCodec;
+    Vector<sp<ABuffer> > mCSD;
+    size_t mCSDIndex;
+    Vector<sp<ABuffer> > mInBuffers;
+    Vector<sp<ABuffer> > mOutBuffers;
+    bool mSawOutputEOS;
+};
+
+}  // namespace android
+
+static int decode(
+        const android::sp<android::ALooper> &looper,
+        const char *path,
+        bool useAudio,
+        bool useVideo) {
+    using namespace android;
+
+    sp<NuMediaExtractor> extractor = new NuMediaExtractor;
+    if (extractor->setDataSource(path) != OK) {
+        fprintf(stderr, "unable to instantiate extractor.\n");
+        return 1;
+    }
+
+    KeyedVector<size_t, CodecState> stateByTrack;
+
+    bool haveAudio = false;
+    bool haveVideo = false;
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<AMessage> format;
+        status_t err = extractor->getTrackFormat(i, &format);
+        CHECK_EQ(err, (status_t)OK);
+
+        AString mime;
+        CHECK(format->findString("mime", &mime));
+
+        if (useAudio && !haveAudio
+                && !strncasecmp(mime.c_str(), "audio/", 6)) {
+            haveAudio = true;
+        } else if (useVideo && !haveVideo
+                && !strncasecmp(mime.c_str(), "video/", 6)) {
+            haveVideo = true;
+        } else {
+            continue;
+        }
+
+        ALOGV("selecting track %d", i);
+
+        err = extractor->selectTrack(i);
+        CHECK_EQ(err, (status_t)OK);
+
+        CodecState *state =
+            &stateByTrack.editValueAt(stateByTrack.add(i, CodecState()));
+
+        state->mCodec = MediaCodec::CreateByType(
+                looper, mime.c_str(), false /* encoder */);
+
+        CHECK(state->mCodec != NULL);
+
+        err = state->mCodec->configure(
+                format, NULL /* surfaceTexture */, 0 /* flags */);
+
+        CHECK_EQ(err, (status_t)OK);
+
+        size_t j = 0;
+        sp<RefBase> obj;
+        while (format->findObject(StringPrintf("csd-%d", j).c_str(), &obj)) {
+            sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+            state->mCSD.push_back(buffer);
+
+            ++j;
+        }
+
+        state->mCSDIndex = 0;
+        state->mSawOutputEOS = false;
+
+        ALOGV("got %d pieces of codec specific data.", state->mCSD.size());
+    }
+
+    CHECK(!stateByTrack.isEmpty());
+
+    for (size_t i = 0; i < stateByTrack.size(); ++i) {
+        CodecState *state = &stateByTrack.editValueAt(i);
+
+        sp<MediaCodec> codec = state->mCodec;
+
+        CHECK_EQ((status_t)OK, codec->start());
+
+        CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
+        CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
+
+        ALOGV("got %d input and %d output buffers",
+              state->mInBuffers.size(), state->mOutBuffers.size());
+
+        while (state->mCSDIndex < state->mCSD.size()) {
+            size_t index;
+            status_t err = codec->dequeueInputBuffer(&index);
+
+            if (err == -EAGAIN) {
+                usleep(10000);
+                continue;
+            }
+
+            CHECK_EQ(err, (status_t)OK);
+
+            const sp<ABuffer> &srcBuffer =
+                state->mCSD.itemAt(state->mCSDIndex++);
+
+            const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
+
+            memcpy(buffer->data(), srcBuffer->data(), srcBuffer->size());
+
+            err = codec->queueInputBuffer(
+                    index,
+                    0 /* offset */,
+                    srcBuffer->size(),
+                    0ll /* timeUs */,
+                    MediaCodec::BUFFER_FLAG_CODECCONFIG);
+
+            CHECK_EQ(err, (status_t)OK);
+        }
+    }
+
+    bool sawInputEOS = false;
+
+    for (;;) {
+        if (!sawInputEOS) {
+            size_t trackIndex;
+            status_t err = extractor->getSampleTrackIndex(&trackIndex);
+
+            if (err != OK) {
+                ALOGV("signalling EOS.");
+
+                for (size_t i = 0; i < stateByTrack.size(); ++i) {
+                    CodecState *state = &stateByTrack.editValueAt(i);
+
+                    for (;;) {
+                        size_t index;
+                        err = state->mCodec->dequeueInputBuffer(&index);
+
+                        if (err == -EAGAIN) {
+                            continue;
+                        }
+
+                        CHECK_EQ(err, (status_t)OK);
+
+                        err = state->mCodec->queueInputBuffer(
+                                index,
+                                0 /* offset */,
+                                0 /* size */,
+                                0ll /* timeUs */,
+                                MediaCodec::BUFFER_FLAG_EOS);
+
+                        CHECK_EQ(err, (status_t)OK);
+                        break;
+                    }
+                }
+
+                sawInputEOS = true;
+            } else {
+                CodecState *state = &stateByTrack.editValueFor(trackIndex);
+
+                size_t index;
+                err = state->mCodec->dequeueInputBuffer(&index);
+
+                if (err == OK) {
+                    ALOGV("filling input buffer %d", index);
+
+                    const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
+
+                    err = extractor->readSampleData(buffer);
+                    CHECK_EQ(err, (status_t)OK);
+
+                    int64_t timeUs;
+                    err = extractor->getSampleTime(&timeUs);
+                    CHECK_EQ(err, (status_t)OK);
+
+                    err = state->mCodec->queueInputBuffer(
+                            index,
+                            0 /* offset */,
+                            buffer->size(),
+                            timeUs,
+                            0 /* flags */);
+
+                    CHECK_EQ(err, (status_t)OK);
+
+                    extractor->advance();
+                } else {
+                    CHECK_EQ(err, -EAGAIN);
+                }
+            }
+        }
+
+        bool sawOutputEOSOnAllTracks = true;
+        for (size_t i = 0; i < stateByTrack.size(); ++i) {
+            CodecState *state = &stateByTrack.editValueAt(i);
+            if (!state->mSawOutputEOS) {
+                sawOutputEOSOnAllTracks = false;
+                break;
+            }
+        }
+
+        if (sawOutputEOSOnAllTracks) {
+            break;
+        }
+
+        for (size_t i = 0; i < stateByTrack.size(); ++i) {
+            CodecState *state = &stateByTrack.editValueAt(i);
+
+            if (state->mSawOutputEOS) {
+                continue;
+            }
+
+            size_t index;
+            size_t offset;
+            size_t size;
+            int64_t presentationTimeUs;
+            uint32_t flags;
+            status_t err = state->mCodec->dequeueOutputBuffer(
+                    &index, &offset, &size, &presentationTimeUs, &flags,
+                    10000ll);
+
+            if (err == OK) {
+                ALOGV("draining output buffer %d, time = %lld us",
+                      index, presentationTimeUs);
+
+                err = state->mCodec->releaseOutputBuffer(index);
+                CHECK_EQ(err, (status_t)OK);
+
+                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+                    ALOGV("reached EOS on output.");
+
+                    state->mSawOutputEOS = true;
+                }
+            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+                ALOGV("INFO_OUTPUT_BUFFERS_CHANGED");
+                CHECK_EQ((status_t)OK,
+                         state->mCodec->getOutputBuffers(&state->mOutBuffers));
+
+                ALOGV("got %d output buffers", state->mOutBuffers.size());
+            } else if (err == INFO_FORMAT_CHANGED) {
+                sp<AMessage> format;
+                CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
+
+                ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str());
+            } else {
+                CHECK_EQ(err, -EAGAIN);
+            }
+        }
+    }
+
+    for (size_t i = 0; i < stateByTrack.size(); ++i) {
+        CodecState *state = &stateByTrack.editValueAt(i);
+
+        CHECK_EQ((status_t)OK, state->mCodec->stop());
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    const char *me = argv[0];
+
+    bool useAudio = false;
+    bool useVideo = false;
+    bool playback = false;
+
+    int res;
+    while ((res = getopt(argc, argv, "havp")) >= 0) {
+        switch (res) {
+            case 'a':
+            {
+                useAudio = true;
+                break;
+            }
+
+            case 'v':
+            {
+                useVideo = true;
+                break;
+            }
+
+            case 'p':
+            {
+                playback = true;
+                break;
+            }
+
+            case '?':
+            case 'h':
+            default:
+            {
+                usage(me);
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        usage(me);
+    }
+
+    if (!useAudio && !useVideo) {
+        useAudio = useVideo = true;
+    }
+
+    ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    sp<ALooper> looper = new ALooper;
+    looper->start();
+
+    if (playback) {
+        sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
+        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+        ssize_t displayWidth = composerClient->getDisplayWidth(0);
+        ssize_t displayHeight = composerClient->getDisplayHeight(0);
+
+        ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
+
+        sp<SurfaceControl> control =
+            composerClient->createSurface(
+                    String8("A Surface"),
+                    0,
+                    displayWidth,
+                    displayHeight,
+                    PIXEL_FORMAT_RGB_565,
+                    0);
+
+        CHECK(control != NULL);
+        CHECK(control->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
+        CHECK_EQ(control->show(), (status_t)OK);
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<Surface> surface = control->getSurface();
+        CHECK(surface != NULL);
+
+        sp<SimplePlayer> player = new SimplePlayer;
+        looper->registerHandler(player);
+
+        player->setDataSource(argv[0]);
+        player->setSurface(surface->getSurfaceTexture());
+        player->start();
+        sleep(10);
+        player->stop();
+        player->reset();
+
+        composerClient->dispose();
+    } else {
+        decode(looper, argv[0], useAudio, useVideo);
+    }
+
+    looper->stop();
+
+    return 0;
+}
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index ae80f88..18e2532 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -198,9 +198,7 @@
 
                     (new AMessage(kWhatSeek, id()))->post(5000000ll);
                 } else if (what == ACodec::kWhatOutputFormatChanged) {
-                } else {
-                    CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
-
+                } else if (what == ACodec::kWhatShutdownCompleted) {
                     mDecodeLooper->unregisterHandler(mCodec->id());
 
                     if (mDecodeLooper != looper()) {