stagefright: remove OMXNodeInstance usage from GraphicBufferSource

This is a preliminary to separate GraphicBufferSource from IOMX.

- Use IOMX instead of internal OMXNodeInstance.

- Keep track of codec buffers by buffer_id, do not write to the
  OMX buffer headers directly

- Upon data space change, notify IOMX to handle it

bug: 31399200

Change-Id: I86534d7602294f70da582457b5af2eb9b6a58eda
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 15d691f..88371ed 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -39,6 +39,7 @@
 class IOMXRenderer;
 class NativeHandle;
 class Surface;
+struct omx_message;
 
 class IOMX : public IInterface {
 public:
@@ -179,6 +180,12 @@
             OMX_U32 range_offset, OMX_U32 range_length,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0;
 
+    virtual status_t emptyGraphicBuffer(
+            node_id node,
+            buffer_id buffer,
+            const sp<GraphicBuffer> &graphicBuffer,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) = 0;
+
     virtual status_t getExtensionIndex(
             node_id node,
             const char *parameter_name,
@@ -199,6 +206,8 @@
             InternalOptionType type,
             const void *data,
             size_t size) = 0;
+
+    virtual status_t dispatchMessage(const omx_message &msg) = 0;
 };
 
 struct omx_message {
@@ -218,6 +227,8 @@
             OMX_EVENTTYPE event;
             OMX_U32 data1;
             OMX_U32 data2;
+            OMX_U32 data3;
+            OMX_U32 data4;
         } event_data;
 
         // if type == EMPTY_BUFFER_DONE
diff --git a/include/media/stagefright/foundation/ColorUtils.h b/include/media/stagefright/foundation/ColorUtils.h
index 2368b82..b889a02 100644
--- a/include/media/stagefright/foundation/ColorUtils.h
+++ b/include/media/stagefright/foundation/ColorUtils.h
@@ -138,6 +138,12 @@
             int32_t primaries, int32_t transfer, int32_t coeffs, bool fullRange,
             ColorAspects &aspects);
 
+    // unpack a uint32_t to a full ColorAspects struct
+    static ColorAspects unpackToColorAspects(uint32_t packed);
+
+    // pack a full ColorAspects struct into a uint32_t
+    static uint32_t packToU32(const ColorAspects &aspects);
+
     // updates Unspecified color aspects to their defaults based on the video size
     static void setDefaultCodecColorAspectsIfNeeded(
             ColorAspects &aspects, int32_t width, int32_t height);
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 6d5e7f6..5a3717e 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -55,6 +55,7 @@
     FREE_BUFFER,
     FILL_BUFFER,
     EMPTY_BUFFER,
+    EMPTY_GRAPHIC_BUFFER,
     GET_EXTENSION_INDEX,
     OBSERVER_ON_MSG,
     GET_GRAPHIC_BUFFER_USAGE,
@@ -62,6 +63,7 @@
     UPDATE_GRAPHIC_BUFFER_IN_META,
     CONFIGURE_VIDEO_TUNNEL_MODE,
     UPDATE_NATIVE_HANDLE_IN_META,
+    DISPATCH_MESSAGE,
 };
 
 class BpOMX : public BpInterface<IOMX> {
@@ -585,6 +587,27 @@
         return reply.readInt32();
     }
 
+    virtual status_t emptyGraphicBuffer(
+            node_id node,
+            buffer_id buffer,
+            const sp<GraphicBuffer> &graphicBuffer,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeInt32((int32_t)node);
+        data.writeInt32((int32_t)buffer);
+        data.write(*graphicBuffer);
+        data.writeInt32(flags);
+        data.writeInt64(timestamp);
+        data.writeInt32(fenceFd >= 0);
+        if (fenceFd >= 0) {
+            data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
+        }
+        remote()->transact(EMPTY_GRAPHIC_BUFFER, data, &reply);
+
+        return reply.readInt32();
+    }
+
     virtual status_t getExtensionIndex(
             node_id node,
             const char *parameter_name,
@@ -623,6 +646,22 @@
 
         return reply.readInt32();
     }
+
+    virtual status_t dispatchMessage(const omx_message &msg) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeInt32((int32_t)msg.node);
+        data.writeInt32(msg.fenceFd >= 0);
+        if (msg.fenceFd >= 0) {
+            data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */);
+        }
+        data.writeInt32(msg.type);
+        data.write(&msg.u, sizeof(msg.u));
+
+        remote()->transact(DISPATCH_MESSAGE, data, &reply);
+
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
@@ -1193,6 +1232,24 @@
             return NO_ERROR;
         }
 
+        case EMPTY_GRAPHIC_BUFFER:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (node_id)data.readInt32();
+            buffer_id buffer = (buffer_id)data.readInt32();
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
+            data.read(*graphicBuffer);
+            OMX_U32 flags = data.readInt32();
+            OMX_TICKS timestamp = data.readInt64();
+            bool haveFence = data.readInt32();
+            int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
+            reply->writeInt32(emptyGraphicBuffer(
+                    node, buffer, graphicBuffer, flags, timestamp, fenceFd));
+
+            return NO_ERROR;
+        }
+
         case GET_EXTENSION_INDEX:
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
@@ -1218,6 +1275,24 @@
             return OK;
         }
 
+        case DISPATCH_MESSAGE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+            omx_message msg;
+            msg.node = data.readInt32();
+            int haveFence = data.readInt32();
+            msg.fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
+            msg.type = (typeof(msg.type))data.readInt32();
+            status_t err = data.read(&msg.u, sizeof(msg.u));
+
+            if (err == OK) {
+                err = dispatchMessage(msg);
+            }
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index aecb767..dc1f640 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5526,11 +5526,7 @@
 bool ACodec::BaseState::onOMXEvent(
         OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
     if (event == OMX_EventDataSpaceChanged) {
-        ColorAspects aspects;
-        aspects.mRange = (ColorAspects::Range)((data2 >> 24) & 0xFF);
-        aspects.mPrimaries = (ColorAspects::Primaries)((data2 >> 16) & 0xFF);
-        aspects.mMatrixCoeffs = (ColorAspects::MatrixCoeffs)((data2 >> 8) & 0xFF);
-        aspects.mTransfer = (ColorAspects::Transfer)(data2 & 0xFF);
+        ColorAspects aspects = ColorUtils::unpackToColorAspects(data2);
 
         mCodec->onDataSpaceChanged((android_dataspace)data1, aspects);
         return true;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index e994069..666fe82 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -148,6 +148,12 @@
             OMX_U32 range_offset, OMX_U32 range_length,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
+    virtual status_t emptyGraphicBuffer(
+            node_id node,
+            buffer_id buffer,
+            const sp<GraphicBuffer> &graphicBuffer,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
     virtual status_t getExtensionIndex(
             node_id node,
             const char *parameter_name,
@@ -160,6 +166,8 @@
             const void *data,
             size_t size);
 
+    virtual status_t dispatchMessage(const omx_message &msg);
+
 private:
     mutable Mutex mLock;
 
@@ -470,6 +478,15 @@
             node, buffer, range_offset, range_length, flags, timestamp, fenceFd);
 }
 
+status_t MuxOMX::emptyGraphicBuffer(
+        node_id node,
+        buffer_id buffer,
+        const sp<GraphicBuffer> &graphicBuffer,
+        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    return getOMX(node)->emptyGraphicBuffer(
+            node, buffer, graphicBuffer, flags, timestamp, fenceFd);
+}
+
 status_t MuxOMX::getExtensionIndex(
         node_id node,
         const char *parameter_name,
@@ -486,6 +503,10 @@
     return getOMX(node)->setInternalOption(node, port_index, type, data, size);
 }
 
+status_t MuxOMX::dispatchMessage(const omx_message &msg) {
+    return getOMX(msg.node)->dispatchMessage(msg);
+}
+
 OMXClient::OMXClient() {
     char value[PROPERTY_VALUE_MAX];
     if (property_get("media.stagefright.codecremote", value, NULL)
diff --git a/media/libstagefright/foundation/ColorUtils.cpp b/media/libstagefright/foundation/ColorUtils.cpp
index e329766..4652e7b 100644
--- a/media/libstagefright/foundation/ColorUtils.cpp
+++ b/media/libstagefright/foundation/ColorUtils.cpp
@@ -342,6 +342,23 @@
 }
 
 // static
+ColorAspects ColorUtils::unpackToColorAspects(uint32_t packed) {
+    ColorAspects aspects;
+    aspects.mRange        = (ColorAspects::Range)((packed >> 24) & 0xFF);
+    aspects.mPrimaries    = (ColorAspects::Primaries)((packed >> 16) & 0xFF);
+    aspects.mMatrixCoeffs = (ColorAspects::MatrixCoeffs)((packed >> 8) & 0xFF);
+    aspects.mTransfer     = (ColorAspects::Transfer)(packed & 0xFF);
+
+    return aspects;
+}
+
+// static
+uint32_t ColorUtils::packToU32(const ColorAspects &aspects) {
+    return (aspects.mRange << 24) | (aspects.mPrimaries << 16)
+            | (aspects.mMatrixCoeffs << 8) | aspects.mTransfer;
+}
+
+// static
 void ColorUtils::setDefaultCodecColorAspectsIfNeeded(
         ColorAspects &aspects, int32_t width, int32_t height) {
     ColorAspects::MatrixCoeffs coeffs;
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 6c073f0..f7b6ab6 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -132,6 +132,12 @@
             OMX_U32 range_offset, OMX_U32 range_length,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
+    virtual status_t emptyGraphicBuffer(
+            node_id node,
+            buffer_id buffer,
+            const sp<GraphicBuffer> &graphicBuffer,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
     virtual status_t getExtensionIndex(
             node_id node,
             const char *parameter_name,
@@ -144,6 +150,8 @@
             const void *data,
             size_t size);
 
+    virtual status_t dispatchMessage(const omx_message &msg);
+
     virtual void binderDied(const wp<IBinder> &the_late_who);
 
     virtual bool isSecure(IOMX::node_id node);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index bdd4811..b7ce140 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -98,8 +98,6 @@
 
     status_t signalEndOfInputStream();
 
-    void signalEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2);
-
     status_t allocateSecureBuffer(
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data, sp<NativeHandle> *native_handle);
@@ -118,7 +116,7 @@
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
     status_t emptyGraphicBuffer(
-            OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &buffer,
+            OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
     status_t getExtensionIndex(
@@ -136,7 +134,6 @@
 
     // handles messages and removes them from the list
     void onMessages(std::list<omx_message> &messages);
-    void onMessage(const omx_message &msg);
     void onObserverDied(OMXMaster *master);
     void onGetHandleFailed();
     void onEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2);
@@ -159,7 +156,6 @@
     // Access this through getGraphicBufferSource().
     sp<GraphicBufferSource> mGraphicBufferSource;
 
-
     struct ActiveBuffer {
         OMX_U32 mPortIndex;
         OMX::buffer_id mID;
@@ -260,6 +256,8 @@
     // |msg| does not need to be sent to the event listener.
     bool handleMessage(omx_message &msg);
 
+    bool handleDataSpaceChanged(omx_message &msg);
+
     OMXNodeInstance(const OMXNodeInstance &);
     OMXNodeInstance &operator=(const OMXNodeInstance &);
 };
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 0c8fd67..2cd894e 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -24,9 +24,6 @@
 
 #include "GraphicBufferSource.h"
 #include "OMXUtils.h"
-
-#include <OMX_Core.h>
-#include <OMX_IndexExt.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ColorUtils.h>
@@ -41,10 +38,6 @@
 
 namespace android {
 
-static const bool EXTRA_CHECK = true;
-
-static const OMX_U32 kPortIndexInput = 0;
-
 GraphicBufferSource::PersistentProxyListener::PersistentProxyListener(
         const wp<IGraphicBufferConsumer> &consumer,
         const wp<ConsumerListener>& consumerListener) :
@@ -113,14 +106,16 @@
 }
 
 GraphicBufferSource::GraphicBufferSource(
-        OMXNodeInstance* nodeInstance,
+        const sp<IOMX> &omx,
+        IOMX::node_id nodeID,
         uint32_t bufferWidth,
         uint32_t bufferHeight,
         uint32_t bufferCount,
         uint32_t consumerUsage,
         const sp<IGraphicBufferConsumer> &consumer) :
     mInitCheck(UNKNOWN_ERROR),
-    mNodeInstance(nodeInstance),
+    mOMX(omx),
+    mNodeID(nodeID),
     mExecuting(false),
     mSuspended(false),
     mLastDataSpace(HAL_DATASPACE_UNKNOWN),
@@ -307,7 +302,7 @@
     mExecuting = false;
 }
 
-void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
+void GraphicBufferSource::addCodecBuffer(IOMX::buffer_id bufferID) {
     Mutex::Autolock autoLock(mMutex);
 
     if (mExecuting) {
@@ -317,32 +312,33 @@
         return;
     }
 
-    ALOGV("addCodecBuffer h=%p size=%" PRIu32 " p=%p",
-            header, header->nAllocLen, header->pBuffer);
+    ALOGV("addCodecBuffer id=%u", bufferID);
+
     CodecBuffer codecBuffer;
-    codecBuffer.mHeader = header;
+    codecBuffer.mBufferID = bufferID;
     mCodecBuffers.add(codecBuffer);
 }
 
-void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd) {
+void GraphicBufferSource::codecBufferEmptied(const omx_message &msg) {
+    IOMX::buffer_id bufferID = msg.u.buffer_data.buffer;
+    int fenceFd = msg.fenceFd;
+
     Mutex::Autolock autoLock(mMutex);
     if (!mExecuting) {
         return;
     }
 
-    int cbi = findMatchingCodecBuffer_l(header);
+    int cbi = findMatchingCodecBuffer_l(bufferID);
     if (cbi < 0) {
         // This should never happen.
-        ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
+        ALOGE("codecBufferEmptied: buffer not recognized (id=%u)", bufferID);
         if (fenceFd >= 0) {
             ::close(fenceFd);
         }
         return;
     }
 
-    ALOGV("codecBufferEmptied h=%p size=%" PRIu32 " filled=%" PRIu32 " p=%p",
-            header, header->nAllocLen, header->nFilledLen,
-            header->pBuffer);
+    ALOGV("codecBufferEmptied id=%u", bufferID);
     CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
 
     // header->nFilledLen may not be the original value, so we can't compare
@@ -364,32 +360,6 @@
         return;
     }
 
-    if (EXTRA_CHECK && header->nAllocLen >= sizeof(MetadataBufferType)) {
-        // Pull the graphic buffer handle back out of the buffer, and confirm
-        // that it matches expectations.
-        OMX_U8* data = header->pBuffer;
-        MetadataBufferType type = *(MetadataBufferType *)data;
-        if (type == kMetadataBufferTypeGrallocSource
-                && header->nAllocLen >= sizeof(VideoGrallocMetadata)) {
-            VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)data;
-            if (grallocMeta.pHandle != codecBuffer.mGraphicBuffer->handle) {
-                // should never happen
-                ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
-                        grallocMeta.pHandle, codecBuffer.mGraphicBuffer->handle);
-                CHECK(!"codecBufferEmptied: mismatched buffer");
-            }
-        } else if (type == kMetadataBufferTypeANWBuffer
-                && header->nAllocLen >= sizeof(VideoNativeMetadata)) {
-            VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)data;
-            if (nativeMeta.pBuffer != codecBuffer.mGraphicBuffer->getNativeBuffer()) {
-                // should never happen
-                ALOGE("codecBufferEmptied: buffer is %p, expected %p",
-                        nativeMeta.pBuffer, codecBuffer.mGraphicBuffer->getNativeBuffer());
-                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.
@@ -438,22 +408,24 @@
     return;
 }
 
-void GraphicBufferSource::codecBufferFilled(OMX_BUFFERHEADERTYPE* header) {
+void GraphicBufferSource::codecBufferFilled(omx_message &msg) {
     Mutex::Autolock autoLock(mMutex);
 
+    OMX_U32 &flags = msg.u.extended_buffer_data.flags;
+    OMX_TICKS &timestamp = msg.u.extended_buffer_data.timestamp;
+
     if (mMaxTimestampGapUs > 0ll
-            && !(header->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
-        ssize_t index = mOriginalTimeUs.indexOfKey(header->nTimeStamp);
+            && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
+        ssize_t index = mOriginalTimeUs.indexOfKey(timestamp);
         if (index >= 0) {
             ALOGV("OUT timestamp: %lld -> %lld",
-                    static_cast<long long>(header->nTimeStamp),
+                    static_cast<long long>(timestamp),
                     static_cast<long long>(mOriginalTimeUs[index]));
-            header->nTimeStamp = mOriginalTimeUs[index];
+            timestamp = mOriginalTimeUs[index];
             mOriginalTimeUs.removeItemsAt(index);
         } else {
             // giving up the effort as encoder doesn't appear to preserve pts
-            ALOGW("giving up limiting timestamp gap (pts = %lld)",
-                    header->nTimeStamp);
+            ALOGW("giving up limiting timestamp gap (pts = %lld)", timestamp);
             mMaxTimestampGapUs = -1ll;
         }
         if (mOriginalTimeUs.size() > BufferQueue::NUM_BUFFER_SLOTS) {
@@ -512,67 +484,16 @@
     mLastDataSpace = dataSpace;
 
     if (ColorUtils::convertDataSpaceToV0(dataSpace)) {
-        ColorAspects aspects = mColorAspects; // initially requested aspects
+        omx_message msg;
+        msg.type = omx_message::EVENT;
+        msg.node = mNodeID;
+        msg.fenceFd = -1;
+        msg.u.event_data.event = OMX_EventDataSpaceChanged;
+        msg.u.event_data.data1 = mLastDataSpace;
+        msg.u.event_data.data2 = ColorUtils::packToU32(mColorAspects);
+        msg.u.event_data.data3 = pixelFormat;
 
-        // request color aspects to encode
-        OMX_INDEXTYPE index;
-        status_t err = mNodeInstance->getExtensionIndex(
-                "OMX.google.android.index.describeColorAspects", &index);
-        if (err == OK) {
-            // V0 dataspace
-            DescribeColorAspectsParams params;
-            InitOMXParams(&params);
-            params.nPortIndex = kPortIndexInput;
-            params.nDataSpace = mLastDataSpace;
-            params.nPixelFormat = pixelFormat;
-            params.bDataSpaceChanged = OMX_TRUE;
-            params.sAspects = mColorAspects;
-
-            err = mNodeInstance->getConfig(index, &params, sizeof(params));
-            if (err == OK) {
-                aspects = params.sAspects;
-                ALOGD("Codec resolved it to (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) err=%d(%s)",
-                        params.sAspects.mRange, asString(params.sAspects.mRange),
-                        params.sAspects.mPrimaries, asString(params.sAspects.mPrimaries),
-                        params.sAspects.mMatrixCoeffs, asString(params.sAspects.mMatrixCoeffs),
-                        params.sAspects.mTransfer, asString(params.sAspects.mTransfer),
-                        err, asString(err));
-            } else {
-                params.sAspects = aspects;
-                err = OK;
-            }
-            params.bDataSpaceChanged = OMX_FALSE;
-            for (int triesLeft = 2; --triesLeft >= 0; ) {
-                status_t err = mNodeInstance->setConfig(index, &params, sizeof(params));
-                if (err == OK) {
-                    err = mNodeInstance->getConfig(index, &params, sizeof(params));
-                }
-                if (err != OK || !ColorUtils::checkIfAspectsChangedAndUnspecifyThem(
-                        params.sAspects, aspects)) {
-                    // if we can't set or get color aspects, still communicate dataspace to client
-                    break;
-                }
-
-                ALOGW_IF(triesLeft == 0, "Codec repeatedly changed requested ColorAspects.");
-            }
-        }
-
-        ALOGV("Set color aspects to (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) err=%d(%s)",
-                aspects.mRange, asString(aspects.mRange),
-                aspects.mPrimaries, asString(aspects.mPrimaries),
-                aspects.mMatrixCoeffs, asString(aspects.mMatrixCoeffs),
-                aspects.mTransfer, asString(aspects.mTransfer),
-                err, asString(err));
-
-        // signal client that the dataspace has changed; this will update the output format
-        // TODO: we should tie this to an output buffer somehow, and signal the change
-        // just before the output buffer is returned to the client, but there are many
-        // ways this could fail (e.g. flushing), and we are not yet supporting this scenario.
-
-        mNodeInstance->signalEvent(
-                OMX_EventDataSpaceChanged, dataSpace,
-                (aspects.mRange << 24) | (aspects.mPrimaries << 16)
-                        | (aspects.mMatrixCoeffs << 8) | aspects.mTransfer);
+        mOMX->dispatchMessage(msg);
     }
 }
 
@@ -849,19 +770,21 @@
     codecBuffer.mSlot = item.mSlot;
     codecBuffer.mFrameNumber = item.mFrameNumber;
 
-    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
-    sp<GraphicBuffer> buffer = codecBuffer.mGraphicBuffer;
-    status_t err = mNodeInstance->emptyGraphicBuffer(
-            header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs,
-            item.mFence->isValid() ? item.mFence->dup() : -1);
+    IOMX::buffer_id bufferID = codecBuffer.mBufferID;
+    const sp<GraphicBuffer> &buffer = codecBuffer.mGraphicBuffer;
+    int fenceID = item.mFence->isValid() ? item.mFence->dup() : -1;
+
+    status_t err = mOMX->emptyGraphicBuffer(
+            mNodeID, bufferID, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs, fenceID);
+
     if (err != OK) {
-        ALOGW("WARNING: emptyNativeWindowBuffer failed: 0x%x", err);
+        ALOGW("WARNING: emptyGraphicBuffer failed: 0x%x", err);
         codecBuffer.mGraphicBuffer = NULL;
         return err;
     }
 
-    ALOGV("emptyNativeWindowBuffer succeeded, h=%p p=%p buf=%p bufhandle=%p",
-            header, header->pBuffer, buffer->getNativeBuffer(), buffer->handle);
+    ALOGV("emptyGraphicBuffer succeeded, id=%u buf=%p bufhandle=%p",
+            bufferID, buffer->getNativeBuffer(), buffer->handle);
     return OK;
 }
 
@@ -882,16 +805,17 @@
     // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
     // in-use.
     CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+    IOMX::buffer_id bufferID = codecBuffer.mBufferID;
 
-    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
-    status_t err = mNodeInstance->emptyGraphicBuffer(
-            header, NULL /* buffer */, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
+    status_t err = mOMX->emptyGraphicBuffer(
+            mNodeID, bufferID, NULL /* buffer */,
+            OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
             0 /* timestamp */, -1 /* fenceFd */);
     if (err != OK) {
         ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
     } else {
-        ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
-                header, cbi);
+        ALOGV("submitEndOfInputStream_l: buffer submitted, id=%u cbi=%d",
+                bufferID, cbi);
         mEndOfStreamSent = true;
     }
 }
@@ -907,10 +831,9 @@
     return -1;
 }
 
-int GraphicBufferSource::findMatchingCodecBuffer_l(
-        const OMX_BUFFERHEADERTYPE* header) {
+int GraphicBufferSource::findMatchingCodecBuffer_l(IOMX::buffer_id bufferID) {
     for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
-        if (mCodecBuffers[i].mHeader == header) {
+        if (mCodecBuffers[i].mBufferID == bufferID) {
             return i;
         }
     }
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index c8b0e62..b151000 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -52,7 +52,8 @@
 class GraphicBufferSource : public BufferQueue::ConsumerListener {
 public:
     GraphicBufferSource(
-            OMXNodeInstance* nodeInstance,
+            const sp<IOMX> &omx,
+            IOMX::node_id nodeID,
             uint32_t bufferWidth,
             uint32_t bufferHeight,
             uint32_t bufferCount,
@@ -94,15 +95,15 @@
     // 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);
+    void addCodecBuffer(IOMX::buffer_id bufferID);
 
     // 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, int fenceFd);
+    void codecBufferEmptied(const omx_message &msg);
 
     // Called when omx_message::FILL_BUFFER_DONE is received. (Currently the
     // buffer source will fix timestamp in the header if needed.)
-    void codecBufferFilled(OMX_BUFFERHEADERTYPE* header);
+    void codecBufferFilled(omx_message &msg);
 
     // 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
@@ -199,7 +200,7 @@
     // Keep track of codec input buffers.  They may either be available
     // (mGraphicBuffer == NULL) or in use by the codec.
     struct CodecBuffer {
-        OMX_BUFFERHEADERTYPE* mHeader;
+        IOMX::buffer_id mBufferID;
 
         // buffer producer's frame-number for buffer
         uint64_t mFrameNumber;
@@ -220,7 +221,7 @@
     }
 
     // Finds the mCodecBuffers entry that matches.  Returns -1 if not found.
-    int findMatchingCodecBuffer_l(const OMX_BUFFERHEADERTYPE* header);
+    int findMatchingCodecBuffer_l(IOMX::buffer_id bufferID);
 
     // 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
@@ -257,8 +258,9 @@
     // Used to report constructor failure.
     status_t mInitCheck;
 
-    // Pointer back to the object that contains us.  We send buffers here.
-    OMXNodeInstance* mNodeInstance;
+    // Pointer back to the IOMX that created us.  We send buffers here.
+    sp<IOMX> mOMX;
+    IOMX::node_id mNodeID;
 
     // Set by omxExecuting() / omxIdling().
     bool mExecuting;
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index f7058d7..36bae4a 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -606,6 +606,21 @@
             buffer, range_offset, range_length, flags, timestamp, fenceFd);
 }
 
+status_t OMX::emptyGraphicBuffer(
+        node_id node,
+        buffer_id buffer,
+        const sp<GraphicBuffer> &graphicBuffer,
+        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    OMXNodeInstance *instance = findInstance(node);
+
+    if (instance == NULL) {
+        return NAME_NOT_FOUND;
+    }
+
+    return instance->emptyGraphicBuffer(
+            buffer, graphicBuffer, flags, timestamp, fenceFd);
+}
+
 status_t OMX::getExtensionIndex(
         node_id node,
         const char *parameter_name,
@@ -635,6 +650,18 @@
     return instance->setInternalOption(port_index, type, data, size);
 }
 
+status_t OMX::dispatchMessage(const omx_message &msg) {
+    sp<OMX::CallbackDispatcher> dispatcher = findDispatcher(msg.node);
+
+    if (dispatcher == NULL) {
+        return OMX_ErrorComponentNotFound;
+    }
+
+    dispatcher->post(msg, true /*realTime*/);
+
+    return OMX_ErrorNone;
+}
+
 OMX_ERRORTYPE OMX::OnEvent(
         node_id node,
         OMX_IN OMX_EVENTTYPE eEvent,
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 0c1b2a2..2d536cc 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -35,6 +35,7 @@
 #include <HardwareAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ColorUtils.h>
 #include <media/stagefright/MediaErrors.h>
 #include <utils/misc.h>
 #include <utils/NativeHandle.h>
@@ -720,7 +721,7 @@
 
     sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
     if (bufferSource != NULL && portIndex == kPortIndexInput) {
-        bufferSource->addCodecBuffer(header);
+        bufferSource->addCodecBuffer(*buffer);
     }
 
     CLOG_BUFFER(useBuffer, NEW_BUFFER_FMT(
@@ -990,7 +991,9 @@
         usageBits = 0;
     }
 
-    sp<GraphicBufferSource> bufferSource = new GraphicBufferSource(this,
+    sp<GraphicBufferSource> bufferSource = new GraphicBufferSource(
+            mOwner,
+            mNodeID,
             def.format.video.nFrameWidth,
             def.format.video.nFrameHeight,
             def.nBufferCountActual,
@@ -1064,10 +1067,6 @@
     return createGraphicBufferSource(portIndex, bufferConsumer, type);
 }
 
-void OMXNodeInstance::signalEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) {
-    mOwner->OnEvent(mNodeID, event, arg1, arg2, NULL);
-}
-
 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
@@ -1123,7 +1122,7 @@
 
     sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
     if (bufferSource != NULL && portIndex == kPortIndexInput) {
-        bufferSource->addCodecBuffer(header);
+        bufferSource->addCodecBuffer(*buffer);
     }
     CLOG_BUFFER(allocateSecureBuffer, NEW_BUFFER_FMT(
             *buffer, portIndex, "%zu@%p:%p", size, *buffer_data,
@@ -1170,7 +1169,7 @@
 
     sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
     if (bufferSource != NULL && portIndex == kPortIndexInput) {
-        bufferSource->addCodecBuffer(header);
+        bufferSource->addCodecBuffer(*buffer);
     }
 
     CLOG_BUFFER(allocateBufferWithBackup, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p :> %u@%p",
@@ -1397,15 +1396,16 @@
 
 // like emptyBuffer, but the data is already in header->pBuffer
 status_t OMXNodeInstance::emptyGraphicBuffer(
-        OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &graphicBuffer,
+        OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
     if (header == NULL) {
         ALOGE("b/25884056");
         return BAD_VALUE;
     }
 
-    Mutex::Autolock autoLock(mLock);
-    OMX::buffer_id buffer = findBufferID(header);
     status_t err = updateGraphicBufferInMeta_l(
             kPortIndexInput, graphicBuffer, buffer, header,
             true /* updateCodecBuffer */);
@@ -1585,9 +1585,7 @@
 
         if (bufferSource != NULL) {
             // fix up the buffer info (especially timestamp) if needed
-            bufferSource->codecBufferFilled(buffer);
-
-            msg.u.extended_buffer_data.timestamp = buffer->nTimeStamp;
+            bufferSource->codecBufferFilled(msg);
         }
     } else if (msg.type == omx_message::EMPTY_BUFFER_DONE) {
         OMX_BUFFERHEADERTYPE *buffer =
@@ -1610,14 +1608,92 @@
             // 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.
-            bufferSource->codecBufferEmptied(buffer, msg.fenceFd);
+            bufferSource->codecBufferEmptied(msg);
             return true;
         }
+    } else if (msg.type == omx_message::EVENT &&
+            msg.u.event_data.event == OMX_EventDataSpaceChanged) {
+        handleDataSpaceChanged(msg);
     }
 
     return false;
 }
 
+bool OMXNodeInstance::handleDataSpaceChanged(omx_message &msg) {
+    android_dataspace dataSpace = (android_dataspace) msg.u.event_data.data1;
+    android_dataspace origDataSpace = dataSpace;
+
+    if (!ColorUtils::convertDataSpaceToV0(dataSpace)) {
+        // Do not process the data space change, don't notify client either
+        return true;
+    }
+
+    android_pixel_format pixelFormat = (android_pixel_format)msg.u.event_data.data3;
+
+    ColorAspects requestedAspects = ColorUtils::unpackToColorAspects(msg.u.event_data.data2);
+    ColorAspects aspects = requestedAspects; // initially requested aspects
+
+    // request color aspects to encode
+    OMX_INDEXTYPE index;
+    status_t err = getExtensionIndex(
+            "OMX.google.android.index.describeColorAspects", &index);
+    if (err == OK) {
+        // V0 dataspace
+        DescribeColorAspectsParams params;
+        InitOMXParams(&params);
+        params.nPortIndex = kPortIndexInput;
+        params.nDataSpace = origDataSpace;
+        params.nPixelFormat = pixelFormat;
+        params.bDataSpaceChanged = OMX_TRUE;
+        params.sAspects = requestedAspects;
+
+        err = getConfig(index, &params, sizeof(params));
+        if (err == OK) {
+            aspects = params.sAspects;
+            ALOGD("Codec resolved it to (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) err=%d(%s)",
+                    params.sAspects.mRange, asString(params.sAspects.mRange),
+                    params.sAspects.mPrimaries, asString(params.sAspects.mPrimaries),
+                    params.sAspects.mMatrixCoeffs, asString(params.sAspects.mMatrixCoeffs),
+                    params.sAspects.mTransfer, asString(params.sAspects.mTransfer),
+                    err, asString(err));
+        } else {
+            params.sAspects = aspects;
+            err = OK;
+        }
+        params.bDataSpaceChanged = OMX_FALSE;
+        for (int triesLeft = 2; --triesLeft >= 0; ) {
+            status_t err = setConfig(index, &params, sizeof(params));
+            if (err == OK) {
+                err = getConfig(index, &params, sizeof(params));
+            }
+            if (err != OK || !ColorUtils::checkIfAspectsChangedAndUnspecifyThem(
+                    params.sAspects, aspects)) {
+                // if we can't set or get color aspects, still communicate dataspace to client
+                break;
+            }
+
+            ALOGW_IF(triesLeft == 0, "Codec repeatedly changed requested ColorAspects.");
+        }
+    }
+
+    ALOGV("Set color aspects to (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) err=%d(%s)",
+            aspects.mRange, asString(aspects.mRange),
+            aspects.mPrimaries, asString(aspects.mPrimaries),
+            aspects.mMatrixCoeffs, asString(aspects.mMatrixCoeffs),
+            aspects.mTransfer, asString(aspects.mTransfer),
+            err, asString(err));
+
+    // signal client that the dataspace has changed; this will update the output format
+    // TODO: we should tie this to an output buffer somehow, and signal the change
+    // just before the output buffer is returned to the client, but there are many
+    // ways this could fail (e.g. flushing), and we are not yet supporting this scenario.
+
+    msg.u.event_data.data1 = (OMX_U32) dataSpace;
+    msg.u.event_data.data2 = (OMX_U32) ColorUtils::packToU32(aspects);
+
+    return false;
+}
+
 void OMXNodeInstance::onMessages(std::list<omx_message> &messages) {
     for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) {
         if (handleMessage(*it)) {