Merge "Squashed commit of the following:" into jb-mr2-dev
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index be1b2fc..0b1d1e4 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,6 +19,7 @@
 #define ANDROID_IOMX_H_
 
 #include <binder/IInterface.h>
+#include <gui/IGraphicBufferProducer.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
@@ -96,6 +97,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer) = 0;
+
+    virtual status_t signalEndOfInputStream(node_id node) = 0;
+
     // This API clearly only makes sense if the caller lives in the
     // same process as the callee, i.e. is the media_server, as the
     // returned "buffer_data" pointer is just that, a pointer into local
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 317b6f0..097ec5f 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -43,6 +43,8 @@
         kWhatError               = 'erro',
         kWhatComponentAllocated  = 'cAll',
         kWhatComponentConfigured = 'cCon',
+        kWhatInputSurfaceCreated = 'isfc',
+        kWhatSignaledInputEOS    = 'seos',
         kWhatBuffersAllocated    = 'allc',
     };
 
@@ -55,9 +57,11 @@
     void initiateShutdown(bool keepComponentAllocated = false);
 
     void signalSetParameters(const sp<AMessage> &msg);
+    void signalEndOfInputStream();
 
     void initiateAllocateComponent(const sp<AMessage> &msg);
     void initiateConfigureComponent(const sp<AMessage> &msg);
+    void initiateCreateInputSurface();
     void initiateStart();
 
     void signalRequestIDRFrame();
@@ -105,6 +109,8 @@
         kWhatDrainDeferredMessages   = 'drai',
         kWhatAllocateComponent       = 'allo',
         kWhatConfigureComponent      = 'conf',
+        kWhatCreateInputSurface      = 'cisf',
+        kWhatSignalEndOfInputStream  = 'eois',
         kWhatStart                   = 'star',
         kWhatRequestIDRFrame         = 'ridr',
         kWhatSetParameters           = 'setP',
diff --git a/include/media/stagefright/BufferProducerWrapper.h b/include/media/stagefright/BufferProducerWrapper.h
new file mode 100644
index 0000000..d8acf30
--- /dev/null
+++ b/include/media/stagefright/BufferProducerWrapper.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 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 BUFFER_PRODUCER_WRAPPER_H_
+
+#define BUFFER_PRODUCER_WRAPPER_H_
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+// Can't use static_cast to cast a RefBase back to an IGraphicBufferProducer,
+// because IGBP's parent (IInterface) uses virtual inheritance.  This class
+// wraps IGBP while we pass it through AMessage.
+
+struct BufferProducerWrapper : RefBase {
+    BufferProducerWrapper(
+            const sp<IGraphicBufferProducer>& bufferProducer) :
+        mBufferProducer(bufferProducer) { }
+
+    sp<IGraphicBufferProducer> getBufferProducer() const {
+        return mBufferProducer;
+    }
+
+private:
+    const sp<IGraphicBufferProducer> mBufferProducer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(BufferProducerWrapper);
+};
+
+}  // namespace android
+
+#endif  // BUFFER_PRODUCER_WRAPPER_H_
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 1002663..ef695a7 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -56,6 +56,8 @@
             const sp<ICrypto> &crypto,
             uint32_t flags);
 
+    status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+
     status_t start();
 
     // Returns to a state in which the component remains allocated but
@@ -101,6 +103,8 @@
     status_t renderOutputBufferAndRelease(size_t index);
     status_t releaseOutputBuffer(size_t index);
 
+    status_t signalEndOfInputStream();
+
     status_t getOutputFormat(sp<AMessage> *format) const;
 
     status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
@@ -143,6 +147,7 @@
     enum {
         kWhatInit                           = 'init',
         kWhatConfigure                      = 'conf',
+        kWhatCreateInputSurface             = 'cisf',
         kWhatStart                          = 'strt',
         kWhatStop                           = 'stop',
         kWhatRelease                        = 'rele',
@@ -150,6 +155,7 @@
         kWhatQueueInputBuffer               = 'queI',
         kWhatDequeueOutputBuffer            = 'deqO',
         kWhatReleaseOutputBuffer            = 'relO',
+        kWhatSignalEndOfInputStream         = 'eois',
         kWhatGetBuffers                     = 'getB',
         kWhatFlush                          = 'flus',
         kWhatGetOutputFormat                = 'getO',
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 48e427a..d6cd43a 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -40,6 +40,8 @@
     ENABLE_GRAPHIC_BUFFERS,
     USE_BUFFER,
     USE_GRAPHIC_BUFFER,
+    CREATE_INPUT_SURFACE,
+    SIGNAL_END_OF_INPUT_STREAM,
     STORE_META_DATA_IN_BUFFERS,
     ALLOC_BUFFER,
     ALLOC_BUFFER_WITH_BACKUP,
@@ -280,6 +282,45 @@
         return err;
     }
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer) {
+        Parcel data, reply;
+        status_t err;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply);
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+
+        err = reply.readInt32();
+        if (err != OK) {
+            return err;
+        }
+
+        *bufferProducer = IGraphicBufferProducer::asInterface(
+                reply.readStrongBinder());
+
+        return err;
+    }
+
+    virtual status_t signalEndOfInputStream(node_id node) {
+        Parcel data, reply;
+        status_t err;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        err = remote()->transact(SIGNAL_END_OF_INPUT_STREAM, data, &reply);
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+
+        return reply.readInt32();
+    }
+
     virtual status_t storeMetaDataInBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable) {
         Parcel data, reply;
@@ -404,7 +445,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#define CHECK_INTERFACE(interface, data, reply) \
+#define CHECK_OMX_INTERFACE(interface, data, reply) \
         do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
             ALOGW("Call incorrectly routed to " #interface); \
             return PERMISSION_DENIED; \
@@ -415,7 +456,7 @@
     switch (code) {
         case LIVES_LOCALLY:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
             node_id node = (void *)data.readIntPtr();
             pid_t pid = (pid_t)data.readInt32();
             reply->writeInt32(livesLocally(node, pid));
@@ -425,7 +466,7 @@
 
         case LIST_NODES:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             List<ComponentInfo> list;
             listNodes(&list);
@@ -448,7 +489,7 @@
 
         case ALLOCATE_NODE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             const char *name = data.readCString();
 
@@ -468,7 +509,7 @@
 
         case FREE_NODE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
 
@@ -479,7 +520,7 @@
 
         case SEND_COMMAND:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
 
@@ -497,7 +538,7 @@
         case GET_CONFIG:
         case SET_CONFIG:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
@@ -539,7 +580,7 @@
 
         case GET_STATE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_STATETYPE state = OMX_StateInvalid;
@@ -553,7 +594,7 @@
 
         case ENABLE_GRAPHIC_BUFFERS:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -567,7 +608,7 @@
 
         case GET_GRAPHIC_BUFFER_USAGE:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -582,7 +623,7 @@
 
         case USE_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -602,7 +643,7 @@
 
         case USE_GRAPHIC_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -621,9 +662,41 @@
             return NO_ERROR;
         }
 
+        case CREATE_INPUT_SURFACE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+
+            sp<IGraphicBufferProducer> bufferProducer;
+            status_t err = createInputSurface(node, port_index,
+                    &bufferProducer);
+
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->writeStrongBinder(bufferProducer->asBinder());
+            }
+
+            return NO_ERROR;
+        }
+
+        case SIGNAL_END_OF_INPUT_STREAM:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+
+            status_t err = signalEndOfInputStream(node);
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         case STORE_META_DATA_IN_BUFFERS:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -637,7 +710,7 @@
 
         case ALLOC_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -659,7 +732,7 @@
 
         case ALLOC_BUFFER_WITH_BACKUP:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -681,7 +754,7 @@
 
         case FREE_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             OMX_U32 port_index = data.readInt32();
@@ -693,7 +766,7 @@
 
         case FILL_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             buffer_id buffer = (void*)data.readIntPtr();
@@ -704,7 +777,7 @@
 
         case EMPTY_BUFFER:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             buffer_id buffer = (void*)data.readIntPtr();
@@ -723,7 +796,7 @@
 
         case GET_EXTENSION_INDEX:
         {
-            CHECK_INTERFACE(IOMX, data, reply);
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
 
             node_id node = (void*)data.readIntPtr();
             const char *parameter_name = data.readCString();
@@ -769,7 +842,7 @@
     switch (code) {
         case OBSERVER_ON_MSG:
         {
-            CHECK_INTERFACE(IOMXObserver, data, reply);
+            CHECK_OMX_INTERFACE(IOMXObserver, data, reply);
 
             omx_message msg;
             data.read(&msg, sizeof(msg));
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a6cc4eb..59fc45e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 
+#include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NativeWindowWrapper.h>
@@ -192,6 +193,7 @@
     friend struct ACodec::UninitializedState;
 
     bool onConfigureComponent(const sp<AMessage> &msg);
+    void onCreateInputSurface(const sp<AMessage> &msg);
     void onStart();
     void onShutdown(bool keepComponentAllocated);
 
@@ -239,6 +241,9 @@
     // to fill with data.
     void resume();
 
+    // Send EOS on input stream.
+    void onSignalEndOfInputStream();
+
     // Returns true iff input and output buffers are in play.
     bool active() const { return mActive; }
 
@@ -392,6 +397,14 @@
     msg->post();
 }
 
+void ACodec::initiateCreateInputSurface() {
+    (new AMessage(kWhatCreateInputSurface, id()))->post();
+}
+
+void ACodec::signalEndOfInputStream() {
+    (new AMessage(kWhatSignalEndOfInputStream, id()))->post();
+}
+
 void ACodec::initiateStart() {
     (new AMessage(kWhatStart, id()))->post();
 }
@@ -2469,6 +2482,14 @@
             return onOMXMessage(msg);
         }
 
+        case ACodec::kWhatCreateInputSurface:
+        case ACodec::kWhatSignalEndOfInputStream:
+        {
+            ALOGE("Message 0x%x was not handled", msg->what());
+            mCodec->signalError(OMX_ErrorUndefined, INVALID_OPERATION);
+            return true;
+        }
+
         default:
             return false;
     }
@@ -3232,6 +3253,13 @@
             break;
         }
 
+        case ACodec::kWhatCreateInputSurface:
+        {
+            onCreateInputSurface(msg);
+            handled = true;
+            break;
+        }
+
         case ACodec::kWhatStart:
         {
             onStart();
@@ -3310,6 +3338,32 @@
     return true;
 }
 
+void ACodec::LoadedState::onCreateInputSurface(
+        const sp<AMessage> &msg) {
+    ALOGV("onCreateInputSurface");
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatInputSurfaceCreated);
+
+    sp<IGraphicBufferProducer> bufferProducer;
+    status_t err;
+
+    err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
+            &bufferProducer);
+    if (err == OK) {
+        notify->setObject("input-surface",
+                new BufferProducerWrapper(bufferProducer));
+    } else {
+        // Can't use mCodec->signalError() here -- MediaCodec won't forward
+        // the error through because it's in the "configured" state.  We
+        // send a kWhatInputSurfaceCreated with an error value instead.
+        ALOGE("[%s] onCreateInputSurface returning error %d",
+                mCodec->mComponentName.c_str(), err);
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 void ACodec::LoadedState::onStart() {
     ALOGV("onStart");
 
@@ -3484,6 +3538,17 @@
     mActive = true;
 }
 
+void ACodec::ExecutingState::onSignalEndOfInputStream() {
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatSignaledInputEOS);
+
+    status_t err = mCodec->mOMX->signalEndOfInputStream(mCodec->mNode);
+    if (err != OK) {
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 void ACodec::ExecutingState::stateEntered() {
     ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
 
@@ -3573,6 +3638,13 @@
             break;
         }
 
+        case ACodec::kWhatSignalEndOfInputStream:
+        {
+            onSignalEndOfInputStream();
+            handled = true;
+            break;
+        }
+
         default:
             handled = BaseState::onMessageReceived(msg);
             break;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 83be0fd..79ea04c 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
+#include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/NativeWindowWrapper.h>
@@ -62,6 +63,7 @@
     : mState(UNINITIALIZED),
       mLooper(looper),
       mCodec(new ACodec),
+      mReplyID(0),
       mFlags(0),
       mSoftRenderer(NULL),
       mDequeueInputTimeoutGeneration(0),
@@ -154,6 +156,28 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::createInputSurface(
+        sp<IGraphicBufferProducer>* bufferProducer) {
+    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id());
+
+    // TODO(fadden): require MediaFormat colorFormat == AndroidOpaque
+
+    sp<AMessage> response;
+    status_t err = PostAndAwaitResponse(msg, &response);
+    if (err == NO_ERROR) {
+        // unwrap the sp<IGraphicBufferProducer>
+        sp<RefBase> obj;
+        bool found = response->findObject("input-surface", &obj);
+        CHECK(found);
+        sp<BufferProducerWrapper> wrapper(
+                static_cast<BufferProducerWrapper*>(obj.get()));
+        *bufferProducer = wrapper->getBufferProducer();
+    } else {
+        ALOGW("createInputSurface failed, err=%d", err);
+    }
+    return err;
+}
+
 status_t MediaCodec::start() {
     sp<AMessage> msg = new AMessage(kWhatStart, id());
 
@@ -232,6 +256,8 @@
 }
 
 status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+    // TODO(fadden): fail if an input Surface has been configured
+
     sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
     msg->setInt64("timeoutUs", timeoutUs);
 
@@ -288,6 +314,13 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::signalEndOfInputStream() {
+    sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, id());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
     sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
 
@@ -575,6 +608,36 @@
                     break;
                 }
 
+                case ACodec::kWhatInputSurfaceCreated:
+                {
+                    // response to ACodec::kWhatCreateInputSurface
+                    status_t err = NO_ERROR;
+                    sp<AMessage> response = new AMessage();
+                    if (!msg->findInt32("err", &err)) {
+                        sp<RefBase> obj;
+                        msg->findObject("input-surface", &obj);
+                        CHECK(obj != NULL);
+                        response->setObject("input-surface", obj);
+                    } else {
+                        response->setInt32("err", err);
+                    }
+                    response->postReply(mReplyID);
+                    break;
+                }
+
+                case ACodec::kWhatSignaledInputEOS:
+                {
+                    // response to ACodec::kWhatSignalEndOfInputStream
+                    sp<AMessage> response = new AMessage();
+                    status_t err;
+                    if (msg->findInt32("err", &err)) {
+                        response->setInt32("err", err);
+                    }
+                    response->postReply(mReplyID);
+                    break;
+                }
+
+
                 case ACodec::kWhatBuffersAllocated:
                 {
                     int32_t portIndex;
@@ -881,6 +944,25 @@
             break;
         }
 
+        case kWhatCreateInputSurface:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            // Must be configured, but can't have been started yet.
+            if (mState != CONFIGURED) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            mCodec->initiateCreateInputSurface();
+            break;
+        }
+
         case kWhatStart:
         {
             uint32_t replyID;
@@ -947,6 +1029,7 @@
 
         case kWhatDequeueInputBuffer:
         {
+            // TODO(fadden): make this fail if we're using an input Surface
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
@@ -1093,6 +1176,24 @@
             break;
         }
 
+        case kWhatSignalEndOfInputStream:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            if (mState != STARTED || (mFlags & kFlagStickyError)) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", INVALID_OPERATION);
+
+                response->postReply(replyID);
+                break;
+            }
+
+            mReplyID = replyID;
+            mCodec->signalEndOfInputStream();
+            break;
+        }
+
         case kWhatGetBuffers:
         {
             uint32_t replyID;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 7cdb793..ff72e0e 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -83,6 +83,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer);
+
+    virtual status_t signalEndOfInputStream(node_id node);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
@@ -274,6 +280,18 @@
             node, port_index, graphicBuffer, buffer);
 }
 
+status_t MuxOMX::createInputSurface(
+        node_id node, OMX_U32 port_index,
+        sp<IGraphicBufferProducer> *bufferProducer) {
+    status_t err = getOMX(node)->createInputSurface(
+            node, port_index, bufferProducer);
+    return err;
+}
+
+status_t MuxOMX::signalEndOfInputStream(node_id node) {
+    return getOMX(node)->signalEndOfInputStream(node);
+}
+
 status_t MuxOMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 2c87b34..24b8d98 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -79,6 +79,12 @@
             node_id node, OMX_U32 port_index,
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
 
+    virtual status_t createInputSurface(
+            node_id node, OMX_U32 port_index,
+            sp<IGraphicBufferProducer> *bufferProducer);
+
+    virtual status_t signalEndOfInputStream(node_id node);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 47ca579..67aba6b 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -27,6 +27,7 @@
 
 class IOMXObserver;
 struct OMXMaster;
+struct GraphicBufferSource;
 
 struct OMXNodeInstance {
     OMXNodeInstance(
@@ -65,6 +66,11 @@
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
             OMX::buffer_id *buffer);
 
+    status_t createInputSurface(
+            OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer);
+
+    status_t signalEndOfInputStream();
+
     status_t allocateBuffer(
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data);
@@ -82,12 +88,18 @@
             OMX_U32 rangeOffset, OMX_U32 rangeLength,
             OMX_U32 flags, OMX_TICKS timestamp);
 
+    status_t emptyDirectBuffer(
+            OMX_BUFFERHEADERTYPE *header,
+            OMX_U32 rangeOffset, OMX_U32 rangeLength,
+            OMX_U32 flags, OMX_TICKS timestamp);
+
     status_t getExtensionIndex(
             const char *parameterName, OMX_INDEXTYPE *index);
 
     void onMessage(const omx_message &msg);
     void onObserverDied(OMXMaster *master);
     void onGetHandleFailed();
+    void onEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2);
 
     static OMX_CALLBACKTYPE kCallbacks;
 
@@ -100,6 +112,13 @@
     sp<IOMXObserver> mObserver;
     bool mDying;
 
+    // Lock only covers mGraphicBufferSource.  We can't always use mLock
+    // because of rare instances where we'd end up locking it recursively.
+    Mutex mGraphicBufferSourceLock;
+    // Access this through getGraphicBufferSource().
+    sp<GraphicBufferSource> mGraphicBufferSource;
+
+
     struct ActiveBuffer {
         OMX_U32 mPortIndex;
         OMX::buffer_id mID;
@@ -132,6 +151,11 @@
             OMX_IN OMX_PTR pAppData,
             OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
 
+    status_t storeMetaDataInBuffers_l(OMX_U32 portIndex, OMX_BOOL enable);
+
+    sp<GraphicBufferSource> getGraphicBufferSource();
+    void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource);
+
     OMXNodeInstance(const OMXNodeInstance &);
     OMXNodeInstance &operator=(const OMXNodeInstance &);
 };
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index d7fbbbe..9129f08 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                     \
+        GraphicBufferSource.cpp       \
         OMX.cpp                       \
         OMXMaster.cpp                 \
         OMXNodeInstance.cpp           \
@@ -19,6 +20,7 @@
         libmedia                        \
         libutils                        \
         libui                           \
+        libgui                          \
         libcutils                       \
         libstagefright_foundation       \
         libdl
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
new file mode 100644
index 0000000..f207954
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicBufferSource"
+#include <utils/Log.h>
+
+#include <GraphicBufferSource.h>
+
+#include <OMX_Core.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <MetadataBufferType.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+static const bool EXTRA_CHECK = true;
+
+
+GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
+        uint32_t bufferWidth, uint32_t bufferHeight) :
+    mInitCheck(UNKNOWN_ERROR),
+    mNodeInstance(nodeInstance),
+    mExecuting(false),
+    mNumFramesAvailable(0),
+    mEndOfStream(false),
+    mEndOfStreamSent(false) {
+
+    ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight);
+
+    if (bufferWidth == 0 || bufferHeight == 0) {
+        ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
+        mInitCheck = BAD_VALUE;
+        return;
+    }
+
+    mBufferQueue = new BufferQueue(true);
+    mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
+    mBufferQueue->setSynchronousMode(true);
+    mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
+            GRALLOC_USAGE_HW_TEXTURE);
+
+    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+    // reference once the ctor ends, as that would cause the refcount of 'this'
+    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
+    // that's what we create.
+    wp<BufferQueue::ConsumerListener> listener;
+    listener = static_cast<BufferQueue::ConsumerListener*>(this);
+
+    sp<BufferQueue::ConsumerListener> proxy;
+    proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+    status_t err = mBufferQueue->consumerConnect(proxy);
+    if (err != NO_ERROR) {
+        ALOGE("Error connecting to BufferQueue: %s (%d)",
+                strerror(-err), err);
+        return;
+    }
+
+    mInitCheck = OK;
+}
+
+GraphicBufferSource::~GraphicBufferSource() {
+    ALOGV("~GraphicBufferSource");
+    status_t err = mBufferQueue->consumerDisconnect();
+    if (err != NO_ERROR) {
+        ALOGW("consumerDisconnect failed: %d", err);
+    }
+}
+
+void GraphicBufferSource::omxExecuting() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> executing; avail=%d, codec vec size=%zd",
+            mNumFramesAvailable, mCodecBuffers.size());
+    CHECK(!mExecuting);
+    mExecuting = true;
+
+    // Start by loading up as many buffers as possible.  We want to do this,
+    // rather than just submit the first buffer, to avoid a degenerate case:
+    // if all BQ buffers arrive before we start executing, and we only submit
+    // one here, the other BQ buffers will just sit until we get notified
+    // that the codec buffer has been released.  We'd then acquire and
+    // submit a single additional buffer, repeatedly, never using more than
+    // one codec buffer simultaneously.  (We could instead try to submit
+    // all BQ buffers whenever any codec buffer is freed, but if we get the
+    // initial conditions right that will never be useful.)
+    while (mNumFramesAvailable && isCodecBufferAvailable_l()) {
+        fillCodecBuffer_l();
+    }
+
+    ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
+
+    // If EOS has already been signaled, and there are no more frames to
+    // submit, try to send EOS now as well.
+    if (mEndOfStream && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+}
+
+void GraphicBufferSource::omxIdling(){
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("--> idling");
+    if (!mExecuting) {
+        // Transition from "loading" to "idling".  Nothing to do.
+        return;
+    }
+
+    ALOGV("Dropped down to idle, avail=%d eos=%d eosSent=%d",
+            mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
+
+    // Codec is no longer executing.  Discard all codec-related state.
+    mCodecBuffers.clear();
+    // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries
+    //       are null; complain if not
+
+    mExecuting = false;
+}
+
+void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mExecuting) {
+        // This should never happen -- buffers can only be allocated when
+        // transitioning from "loaded" to "idle".
+        ALOGE("addCodecBuffer: buffer added while executing");
+        return;
+    }
+
+    ALOGV("addCodecBuffer h=%p size=%lu p=%p",
+            header, header->nAllocLen, header->pBuffer);
+    CodecBuffer codecBuffer;
+    codecBuffer.mHeader = header;
+    mCodecBuffers.add(codecBuffer);
+}
+
+void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
+    Mutex::Autolock autoLock(mMutex);
+
+    CHECK(mExecuting);  // could this happen if app stop()s early?
+
+    int cbi = findMatchingCodecBuffer_l(header);
+    if (cbi < 0) {
+        // This should never happen.
+        ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
+        return;
+    }
+
+    ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p",
+            header, header->nAllocLen, header->nFilledLen,
+            header->pBuffer);
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+    // header->nFilledLen may not be the original value, so we can't compare
+    // that to zero to see of this was the EOS buffer.  Instead we just
+    // see if the GraphicBuffer reference was null, which should only ever
+    // happen for EOS.
+    if (codecBuffer.mGraphicBuffer == NULL) {
+        CHECK(mEndOfStream);
+        // No GraphicBuffer to deal with, no additional input or output is
+        // expected, so just return.
+        return;
+    }
+
+    if (EXTRA_CHECK) {
+        // Pull the graphic buffer handle back out of the buffer, and confirm
+        // that it matches expectations.
+        OMX_U8* data = header->pBuffer;
+        buffer_handle_t bufferHandle;
+        memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t));
+        if (bufferHandle != codecBuffer.mGraphicBuffer->handle) {
+            // should never happen
+            ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
+                    bufferHandle, codecBuffer.mGraphicBuffer->handle);
+            CHECK(!"codecBufferEmptied: mismatched buffer");
+        }
+    }
+
+    // Find matching entry in our cached copy of the BufferQueue slots.
+    // If we find a match, release that slot.  If we don't, the BufferQueue
+    // has dropped that GraphicBuffer, and there's nothing for us to release.
+    //
+    // (We could store "id" in CodecBuffer and avoid the slot search.)
+    int id;
+    for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
+        if (mBufferSlot[id] == NULL) {
+            continue;
+        }
+
+        if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
+            ALOGV("cbi %d matches bq slot %d, handle=%p",
+                    cbi, id, mBufferSlot[id]->handle);
+
+            mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+                    Fence::NO_FENCE);
+            break;
+        }
+    }
+    if (id == BufferQueue::NUM_BUFFER_SLOTS) {
+        ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
+                cbi);
+    }
+
+    // Mark the codec buffer as available by clearing the GraphicBuffer ref.
+    codecBuffer.mGraphicBuffer = NULL;
+
+    if (mNumFramesAvailable) {
+        // Fill this codec buffer.
+        CHECK(!mEndOfStream);
+        ALOGV("buffer freed, %d frames avail", mNumFramesAvailable);
+        fillCodecBuffer_l();
+    } else if (mEndOfStream) {
+        // No frames available, but EOS is pending, so use this buffer to
+        // send that.
+        ALOGV("buffer freed, EOS pending");
+        submitEndOfInputStream_l();
+    }
+    return;
+}
+
+status_t GraphicBufferSource::fillCodecBuffer_l() {
+    CHECK(mExecuting && mNumFramesAvailable > 0);
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        // No buffers available, bail.
+        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
+                mNumFramesAvailable);
+    } else {
+        ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
+                mNumFramesAvailable);
+        BufferQueue::BufferItem item;
+        status_t err = mBufferQueue->acquireBuffer(&item);
+        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+            // shouldn't happen
+            ALOGW("fillCodecBuffer_l: frame was not available");
+            return err;
+        } else if (err != OK) {
+            // now what? fake end-of-stream?
+            ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
+            return err;
+        }
+
+        mNumFramesAvailable--;
+
+        // Wait for it to become available.
+        err = item.mFence->waitForever(1000,
+                "GraphicBufferSource::fillCodecBuffer_l");
+        if (err != OK) {
+            ALOGW("failed to wait for buffer fence: %d", err);
+            // keep going
+        }
+
+        // If this is the first time we're seeing this buffer, add it to our
+        // slot table.
+        if (item.mGraphicBuffer != NULL) {
+            ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
+            mBufferSlot[item.mBuf] = item.mGraphicBuffer;
+        }
+
+        err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi);
+        if (err != OK) {
+            ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
+            mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+                EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        } else {
+            ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
+        }
+    }
+
+    return OK;
+}
+
+void GraphicBufferSource::signalEndOfInputStream() {
+    Mutex::Autolock autoLock(mMutex);
+    ALOGV("signalEndOfInputStream: exec=%d avail=%d",
+            mExecuting, mNumFramesAvailable);
+
+    // Set the end-of-stream flag.  If no frames are pending from the
+    // BufferQueue, and a codec buffer is available, and we're executing,
+    // we initiate the EOS from here.  Otherwise, we'll let
+    // codecBufferEmptied() (or omxExecuting) do it.
+    //
+    // Note: if there are no pending frames and all codec buffers are
+    // available, we *must* submit the EOS from here or we'll just
+    // stall since no future events are expected.
+    mEndOfStream = true;
+
+    if (mExecuting && mNumFramesAvailable == 0) {
+        submitEndOfInputStream_l();
+    }
+}
+
+status_t GraphicBufferSource::submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+        int64_t timestamp, int cbi) {
+    ALOGV("submitBuffer_l cbi=%d", cbi);
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+    codecBuffer.mGraphicBuffer = graphicBuffer;
+
+    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+    CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t));
+    OMX_U8* data = header->pBuffer;
+    const OMX_U32 type = kMetadataBufferTypeGrallocSource;
+    buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle;
+    memcpy(data, &type, 4);
+    memcpy(data + 4, &handle, sizeof(buffer_handle_t));
+
+    status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
+            4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
+            timestamp);
+    if (err != OK) {
+        ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
+        codecBuffer.mGraphicBuffer = NULL;
+        return err;
+    }
+
+    ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p",
+            header, header->pBuffer, handle);
+    return OK;
+}
+
+void GraphicBufferSource::submitEndOfInputStream_l() {
+    CHECK(mEndOfStream);
+    if (mEndOfStreamSent) {
+        ALOGV("EOS already sent");
+        return;
+    }
+
+    int cbi = findAvailableCodecBuffer_l();
+    if (cbi < 0) {
+        ALOGV("submitEndOfInputStream_l: no codec buffers available");
+        return;
+    }
+
+    // We reject any additional incoming graphic buffers, so there's no need
+    // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
+    // in-use.
+    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+    if (EXTRA_CHECK) {
+        // Guard against implementations that don't check nFilledLen.
+        size_t fillLen = 4 + sizeof(buffer_handle_t);
+        CHECK(header->nAllocLen >= fillLen);
+        OMX_U8* data = header->pBuffer;
+        memset(data, 0xcd, fillLen);
+    }
+
+    uint64_t timestamp = 0; // does this matter?
+
+    status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0,
+            /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
+            timestamp);
+    if (err != OK) {
+        ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
+    } else {
+        ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
+                header, cbi);
+    }
+}
+
+int GraphicBufferSource::findAvailableCodecBuffer_l() {
+    CHECK(mCodecBuffers.size() > 0);
+
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mGraphicBuffer == NULL) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int GraphicBufferSource::findMatchingCodecBuffer_l(
+        const OMX_BUFFERHEADERTYPE* header) {
+    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+        if (mCodecBuffers[i].mHeader == header) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onFrameAvailable() {
+    Mutex::Autolock autoLock(mMutex);
+
+    ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable);
+
+    if (mEndOfStream) {
+        // This should only be possible if a new buffer was queued after
+        // EOS was signaled, i.e. the app is misbehaving.
+        ALOGW("onFrameAvailable: EOS is set, ignoring frame");
+
+        BufferQueue::BufferItem item;
+        status_t err = mBufferQueue->acquireBuffer(&item);
+        if (err == OK) {
+            mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+                EGL_NO_SYNC_KHR, item.mFence);
+        }
+        return;
+    }
+
+    mNumFramesAvailable++;
+
+    if (mExecuting) {
+        fillCodecBuffer_l();
+    }
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onBuffersReleased() {
+    Mutex::Autolock lock(mMutex);
+
+    uint32_t slotMask;
+    if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) {
+        ALOGW("onBuffersReleased: unable to get released buffer set");
+        slotMask = 0xffffffff;
+    }
+
+    ALOGV("onBuffersReleased: 0x%08x", slotMask);
+
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        if ((slotMask & 0x01) != 0) {
+            mBufferSlot[i] = NULL;
+        }
+        slotMask >>= 1;
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
new file mode 100644
index 0000000..6d49f96
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013 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 GRAPHIC_BUFFER_SOURCE_H_
+
+#define GRAPHIC_BUFFER_SOURCE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferQueue.h>
+#include <utils/RefBase.h>
+
+#include <OMX_Core.h>
+#include "../include/OMXNodeInstance.h"
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+/*
+ * This class is used to feed OMX codecs from a Surface via BufferQueue.
+ *
+ * Instances of the class don't run on a dedicated thread.  Instead,
+ * various events trigger data movement:
+ *
+ *  - Availability of a new frame of data from the BufferQueue (notified
+ *    via the onFrameAvailable callback).
+ *  - The return of a codec buffer (via OnEmptyBufferDone).
+ *  - Application signaling end-of-stream.
+ *  - Transition to or from "executing" state.
+ *
+ * Frames of data (and, perhaps, the end-of-stream indication) can arrive
+ * before the codec is in the "executing" state, so we need to queue
+ * things up until we're ready to go.
+ */
+class GraphicBufferSource : public BufferQueue::ConsumerListener {
+public:
+    GraphicBufferSource(OMXNodeInstance* nodeInstance,
+            uint32_t bufferWidth, uint32_t bufferHeight);
+    virtual ~GraphicBufferSource();
+
+    // We can't throw an exception if the constructor fails, so we just set
+    // this and require that the caller test the value.
+    status_t initCheck() const {
+        return mInitCheck;
+    }
+
+    // Returns the handle to the producer side of the BufferQueue.  Buffers
+    // queued on this will be received by GraphicBufferSource.
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+        return mBufferQueue;
+    }
+
+    // This is called when OMX transitions to OMX_StateExecuting, which means
+    // we can start handing it buffers.  If we already have buffers of data
+    // sitting in the BufferQueue, this will send them to the codec.
+    void omxExecuting();
+
+    // This is called when OMX transitions to OMX_StateIdle.  If we were
+    // previously executing, this means we're about to be shut down.  (We
+    // also enter Idle on the way up.)
+    void omxIdling();
+
+    // A "codec buffer", i.e. a buffer that can be used to pass data into
+    // the encoder, has been allocated.  (This call does not call back into
+    // OMXNodeInstance.)
+    void addCodecBuffer(OMX_BUFFERHEADERTYPE* header);
+
+    // Called from OnEmptyBufferDone.  If we have a BQ buffer available,
+    // fill it with a new frame of data; otherwise, just mark it as available.
+    void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header);
+
+    // This is called after the last input frame has been submitted.  We
+    // need to submit an empty buffer with the EOS flag set.  If we don't
+    // have a codec buffer ready, we just set the mEndOfStream flag.
+    void signalEndOfInputStream();
+
+protected:
+    // BufferQueue::ConsumerListener interface, called when a new frame of
+    // data is available.  If we're executing and a codec buffer is
+    // available, we acquire the buffer, copy the GraphicBuffer reference
+    // into the codec buffer, and call Empty[This]Buffer.  If we're not yet
+    // executing or there's no codec buffer available, we just increment
+    // mNumFramesAvailable and return.
+    virtual void onFrameAvailable();
+
+    // BufferQueue::ConsumerListener interface, called when the client has
+    // released one or more GraphicBuffers.  We clear out the appropriate
+    // set of mBufferSlot entries.
+    virtual void onBuffersReleased();
+
+private:
+    // Keep track of codec input buffers.  They may either be available
+    // (mGraphicBuffer == NULL) or in use by the codec.
+    struct CodecBuffer {
+        OMX_BUFFERHEADERTYPE* mHeader;
+        sp<GraphicBuffer> mGraphicBuffer;
+    };
+
+    // Returns the index of an available codec buffer.  If none are
+    // available, returns -1.  Mutex must be held by caller.
+    int findAvailableCodecBuffer_l();
+
+    // Returns true if a codec buffer is available.
+    bool isCodecBufferAvailable_l() {
+        return findAvailableCodecBuffer_l() >= 0;
+    }
+
+    // Finds the mCodecBuffers entry that matches.  Returns -1 if not found.
+    int findMatchingCodecBuffer_l(const OMX_BUFFERHEADERTYPE* header);
+
+    // Fills a codec buffer with a frame from the BufferQueue.  This must
+    // only be called when we know that a frame of data is ready (i.e. we're
+    // in the onFrameAvailable callback, or if we're in codecBufferEmptied
+    // and mNumFramesAvailable is nonzero).  Returns without doing anything if
+    // we don't have a codec buffer available.
+    status_t fillCodecBuffer_l();
+
+    // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
+    // reference into the codec buffer, and submits the data to the codec.
+    status_t submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+            int64_t timestamp, int cbi);
+
+    // Submits an empty buffer, with the EOS flag set.   Returns without
+    // doing anything if we don't have a codec buffer available.
+    void submitEndOfInputStream_l();
+
+    // Lock, covers all member variables.
+    mutable Mutex mMutex;
+
+    // Used to report constructor failure.
+    status_t mInitCheck;
+
+    // Pointer back to the object that contains us.  We send buffers here.
+    OMXNodeInstance* mNodeInstance;
+
+    // Set by omxExecuting() / omxIdling().
+    bool mExecuting;
+
+    // We consume graphic buffers from this.
+    sp<BufferQueue> mBufferQueue;
+
+    // Number of frames pending in BufferQueue that haven't yet been
+    // forwarded to the codec.
+    size_t mNumFramesAvailable;
+
+    // Set to true if we want to send end-of-stream after we run out of
+    // frames in BufferQueue.
+    bool mEndOfStream;
+    bool mEndOfStreamSent;
+
+    // Cache of GraphicBuffers from the buffer queue.  When the codec
+    // is done processing a GraphicBuffer, we can use this to map back
+    // to a slot number.
+    sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+
+    // Tracks codec buffers.
+    Vector<CodecBuffer> mCodecBuffers;
+
+    DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
+};
+
+}  // namespace android
+
+#endif  // GRAPHIC_BUFFER_SOURCE_H_
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 29bc733..3987ead 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -345,6 +345,17 @@
             port_index, graphicBuffer, buffer);
 }
 
+status_t OMX::createInputSurface(
+        node_id node, OMX_U32 port_index,
+        sp<IGraphicBufferProducer> *bufferProducer) {
+    return findInstance(node)->createInputSurface(
+            port_index, bufferProducer);
+}
+
+status_t OMX::signalEndOfInputStream(node_id node) {
+    return findInstance(node)->signalEndOfInputStream();
+}
+
 status_t OMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
@@ -393,6 +404,9 @@
         OMX_IN OMX_PTR pEventData) {
     ALOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
 
+    // Forward to OMXNodeInstance.
+    findInstance(node)->onEvent(eEvent, nData1, nData2);
+
     omx_message msg;
     msg.type = omx_message::EVENT;
     msg.node = node;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index bff3def..6c2c33b 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -20,14 +20,18 @@
 
 #include "../include/OMXNodeInstance.h"
 #include "OMXMaster.h"
+#include "GraphicBufferSource.h"
 
 #include <OMX_Component.h>
 
 #include <binder/IMemory.h>
+#include <gui/BufferQueue.h>
 #include <HardwareAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaErrors.h>
 
+static const OMX_U32 kPortIndexInput = 0;
+
 namespace android {
 
 struct BufferMeta {
@@ -100,6 +104,17 @@
     mHandle = handle;
 }
 
+sp<GraphicBufferSource> OMXNodeInstance::getGraphicBufferSource() {
+    Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+    return mGraphicBufferSource;
+}
+
+void OMXNodeInstance::setGraphicBufferSource(
+        const sp<GraphicBufferSource>& bufferSource) {
+    Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+    mGraphicBufferSource = bufferSource;
+}
+
 OMX *OMXNodeInstance::owner() {
     return mOwner;
 }
@@ -354,7 +369,12 @@
         OMX_U32 portIndex,
         OMX_BOOL enable) {
     Mutex::Autolock autolock(mLock);
+    return storeMetaDataInBuffers_l(portIndex, enable);
+}
 
+status_t OMXNodeInstance::storeMetaDataInBuffers_l(
+        OMX_U32 portIndex,
+        OMX_BOOL enable) {
     OMX_INDEXTYPE index;
     OMX_STRING name = const_cast<OMX_STRING>(
             "OMX.google.android.index.storeMetaDataInBuffers");
@@ -411,6 +431,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -530,6 +555,60 @@
     return OK;
 }
 
+status_t OMXNodeInstance::createInputSurface(
+        OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
+    Mutex::Autolock autolock(mLock);
+    status_t err;
+
+    const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
+    if (surfaceCheck != NULL) {
+        return ALREADY_EXISTS;
+    }
+
+    // Input buffers will hold meta-data (gralloc references).
+    err = storeMetaDataInBuffers_l(portIndex, OMX_TRUE);
+    if (err != OK) {
+        return err;
+    }
+
+    // Retrieve the width and height of the graphic buffer, set when the
+    // codec was configured.
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 0;
+    def.nVersion.s.nRevision = 0;
+    def.nVersion.s.nStep = 0;
+    def.nPortIndex = portIndex;
+    OMX_ERRORTYPE oerr = OMX_GetParameter(
+            mHandle, OMX_IndexParamPortDefinition, &def);
+    CHECK(oerr == OMX_ErrorNone);
+
+    GraphicBufferSource* bufferSource = new GraphicBufferSource(
+            this, def.format.video.nFrameWidth, def.format.video.nFrameHeight);
+    if ((err = bufferSource->initCheck()) != OK) {
+        delete bufferSource;
+        return err;
+    }
+    setGraphicBufferSource(bufferSource);
+
+    *bufferProducer = bufferSource->getIGraphicBufferProducer();
+    return OK;
+}
+
+status_t OMXNodeInstance::signalEndOfInputStream() {
+    // For non-Surface input, the MediaCodec should convert the call to a
+    // pair of requests (dequeue input buffer, queue input buffer with EOS
+    // flag set).  Seems easier than doing the equivalent from here.
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource == NULL) {
+        ALOGW("signalEndOfInputStream should only be used with Surface input");
+        return INVALID_OPERATION;
+    };
+    bufferSource->signalEndOfInputStream();
+    return OK;
+}
+
 status_t OMXNodeInstance::allocateBuffer(
         OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
         void **buffer_data) {
@@ -560,6 +639,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -592,6 +676,11 @@
 
     addActiveBuffer(portIndex, *buffer);
 
+    sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+    if (bufferSource != NULL && portIndex == kPortIndexInput) {
+        bufferSource->addCodecBuffer(header);
+    }
+
     return OK;
 }
 
@@ -646,6 +735,26 @@
     return StatusFromOMXError(err);
 }
 
+// like emptyBuffer, but the data is already in header->pBuffer
+status_t OMXNodeInstance::emptyDirectBuffer(
+        OMX_BUFFERHEADERTYPE *header,
+        OMX_U32 rangeOffset, OMX_U32 rangeLength,
+        OMX_U32 flags, OMX_TICKS timestamp) {
+    Mutex::Autolock autoLock(mLock);
+
+    header->nFilledLen = rangeLength;
+    header->nOffset = rangeOffset;
+    header->nFlags = flags;
+    header->nTimeStamp = timestamp;
+
+    OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
+    if (err != OMX_ErrorNone) {
+        ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err);
+    }
+
+    return StatusFromOMXError(err);
+}
+
 status_t OMXNodeInstance::getExtensionIndex(
         const char *parameterName, OMX_INDEXTYPE *index) {
     Mutex::Autolock autoLock(mLock);
@@ -682,6 +791,22 @@
     delete this;
 }
 
+// OMXNodeInstance::OnEvent calls OMX::OnEvent, which then calls here.
+// Don't try to acquire mLock here -- in rare circumstances this will hang.
+void OMXNodeInstance::onEvent(
+        OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) {
+    const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
+
+    if (bufferSource != NULL && event == OMX_EventCmdComplete &&
+            arg1 == OMX_CommandStateSet) {
+        if (arg2 == OMX_StateExecuting) {
+            bufferSource->omxExecuting();
+        } else if (arg2 == OMX_StateIdle) {
+            bufferSource->omxIdling();
+        }
+    }
+}
+
 // static
 OMX_ERRORTYPE OMXNodeInstance::OnEvent(
         OMX_IN OMX_HANDLETYPE hComponent,
@@ -707,6 +832,17 @@
     if (instance->mDying) {
         return OMX_ErrorNone;
     }
+    const sp<GraphicBufferSource>& bufferSource(
+            instance->getGraphicBufferSource());
+    if (bufferSource != NULL) {
+        bufferSource->codecBufferEmptied(pBuffer);
+
+        // This is one of the buffers used exclusively by GraphicBufferSource.
+        // Don't dispatch a message back to ACodec, since it doesn't
+        // know that anyone asked to have the buffer emptied and will
+        // be very confused.
+        return OMX_ErrorNone;
+    }
     return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
 }