goldfish-codecs: add avcdec

This is used to pass h264 decoding to host h264 decoders.

BUG: 124388359

This CL is based on the following cl:
joshuaduong@
ag/9406402 and ag/9406404

Change-Id: I52a17a917e7bee24ab8e46317d1715ead11e469c
diff --git a/Android.mk b/Android.mk
index 467dbcd..2c0a5c2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -163,6 +163,7 @@
     # hardware codecs enabled after P
     include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/common/Android.mk
     include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/plugin/Android.mk
+    include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/avcdec/Android.mk
     include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/vpxdec/Android.mk
 endif
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7615d0..eba847a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@
 # instead run make from .../device/generic/goldfish-opengl
 # which will re-generate this file.
 set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/./Android.mk" "e4989c6c970b11f3d6baf80ac7115cc18f90031d0df26db2002c984a4b39229b")
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/./Android.mk" "2c3250b596a2b161e479dd3624a74ae6e485d6238d59d9a5dc55064c46482943")
 add_subdirectory(shared/OpenglCodecCommon)
 add_subdirectory(system/GLESv1_enc)
 add_subdirectory(system/GLESv2_enc)
@@ -14,4 +14,4 @@
 add_subdirectory(system/GLESv2)
 add_subdirectory(system/gralloc)
 add_subdirectory(system/egl)
-add_subdirectory(system/vulkan)
\ No newline at end of file
+add_subdirectory(system/vulkan)
diff --git a/system/codecs/omx/avcdec/Android.mk b/system/codecs/omx/avcdec/Android.mk
new file mode 100644
index 0000000..ea107db
--- /dev/null
+++ b/system/codecs/omx/avcdec/Android.mk
@@ -0,0 +1,48 @@
+#
+# Copyright 2019 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+commonSources := \
+        GoldfishAVCDec.cpp  \
+        MediaH264Decoder.cpp
+
+$(call emugl-begin-shared-library,libstagefright_goldfish_avcdec$(GOLDFISH_OPENGL_LIB_SUFFIX))
+
+LOCAL_SRC_FILES := $(commonSources)
+
+LOCAL_CFLAGS += -DLOG_TAG=\"goldfish_avcdec\"
+LOCAL_CFLAGS += -Wno-unused-private-field
+
+$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog)
+
+LOCAL_HEADER_LIBRARIES := media_plugin_headers \
+                          libmedia_headers \
+                          libbinder_headers \
+                          libhidlbase_impl_internal \
+                          libbase
+
+LOCAL_SHARED_LIBRARIES :=       \
+        libbinder               \
+        libutils                \
+        liblog                  \
+        libcutils               \
+        android.hardware.media.omx@1.0 \
+        libstagefright_foundation
+
+$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
+$(call emugl-import,libgoldfish_codecs_common)
+$(call emugl-import,libstagefrighthw)
+$(call emugl-end-module)
diff --git a/system/codecs/omx/avcdec/GoldfishAVCDec.cpp b/system/codecs/omx/avcdec/GoldfishAVCDec.cpp
new file mode 100644
index 0000000..cc57f4c
--- /dev/null
+++ b/system/codecs/omx/avcdec/GoldfishAVCDec.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "GoldfishAVCDec.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+#include <inttypes.h>
+
+namespace android {
+
+#define componentName                   "video_decoder.avc"
+#define codingType                      OMX_VIDEO_CodingAVC
+#define CODEC_MIME_TYPE                 MEDIA_MIMETYPE_VIDEO_AVC
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function              ih264d_api_function
+#define ivdext_create_ip_t              ih264d_create_ip_t
+#define ivdext_create_op_t              ih264d_create_op_t
+#define ivdext_delete_ip_t              ih264d_delete_ip_t
+#define ivdext_delete_op_t              ih264d_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t   ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   ih264d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+    { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileConstrainedHigh,     OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel52 },
+};
+
+GoldfishAVCDec::GoldfishAVCDec(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : GoldfishVideoDecoderOMXComponent(
+            name, componentName, codingType,
+            kProfileLevels, ARRAY_SIZE(kProfileLevels),
+            320 /* width */, 240 /* height */, callbacks,
+            appData, component),
+      mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mChangingResolution(false),
+      mSignalledError(false),
+      mInputOffset(0){
+    initPorts(
+            1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE,
+            1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE);
+
+    mTimeStart = mTimeEnd = systemTime();
+
+    // If input dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+}
+
+GoldfishAVCDec::~GoldfishAVCDec() {
+    CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+void GoldfishAVCDec::logVersion() {
+    // TODO: get emulation decoder implementation version from the host.
+    ALOGV("GoldfishAVC decoder version 1.0");
+}
+
+status_t GoldfishAVCDec::resetPlugin() {
+    mIsInFlush = false;
+    mReceivedEOS = false;
+
+    memset(mTimeStamps, 0, sizeof(mTimeStamps));
+    memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+    /* Initialize both start and end times */
+    mTimeStart = mTimeEnd = systemTime();
+
+    return OK;
+}
+
+status_t GoldfishAVCDec::resetDecoder() {
+    // The resolution may have changed, so our safest bet is to just destroy the
+    // current context and recreate another one, with the new width and height.
+    mContext->destroyH264Context();
+    mContext->initH264Context(mWidth,
+                              mHeight,
+                              outputBufferWidth(),
+                              outputBufferHeight(),
+                              MediaH264Decoder::PixelFormat::YUV420P);
+
+    return OK;
+}
+
+status_t GoldfishAVCDec::setFlushMode() {
+    /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+    mContext->flush();
+
+    mIsInFlush = true;
+    return OK;
+}
+
+status_t GoldfishAVCDec::initDecoder() {
+    /* Initialize the decoder */
+    mContext.reset(new MediaH264Decoder());
+    mContext->initH264Context(mWidth,
+                              mHeight,
+                              outputBufferWidth(),
+                              outputBufferHeight(),
+                              MediaH264Decoder::PixelFormat::YUV420P);
+
+    /* Reset the plugin state */
+    resetPlugin();
+
+    /* Get codec version */
+    logVersion();
+
+    return OK;
+}
+
+status_t GoldfishAVCDec::deInitDecoder() {
+    if (mContext) {
+        mContext->destroyH264Context();
+        mContext.reset();
+    }
+
+    mChangingResolution = false;
+
+    return OK;
+}
+
+void GoldfishAVCDec::onReset() {
+    GoldfishVideoDecoderOMXComponent::onReset();
+
+    mSignalledError = false;
+    mInputOffset = 0;
+    resetDecoder();
+    resetPlugin();
+}
+
+bool GoldfishAVCDec::getVUIParams() {
+    ALOGE("%s: NOT IMPLEMENTED", __func__);
+    /*
+    IV_API_CALL_STATUS_T status;
+    ih264d_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip;
+    ih264d_ctl_get_vui_params_op_t s_ctl_get_vui_params_op;
+
+    s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_get_vui_params_ip.e_sub_cmd =
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_GET_VUI_PARAMS;
+
+    s_ctl_get_vui_params_ip.u4_size =
+        sizeof(ih264d_ctl_get_vui_params_ip_t);
+
+    s_ctl_get_vui_params_op.u4_size = sizeof(ih264d_ctl_get_vui_params_op_t);
+
+    status = ivdec_api_function(
+            (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip,
+            (void *)&s_ctl_get_vui_params_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGW("Error in getting VUI params: 0x%x",
+                s_ctl_get_vui_params_op.u4_error_code);
+        return false;
+    }
+
+    int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries;
+    int32_t transfer = s_ctl_get_vui_params_op.u1_tfr_chars;
+    int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coeffs;
+    bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag;
+
+    ColorAspects colorAspects;
+    ColorUtils::convertIsoColorAspectsToCodecAspects(
+            primaries, transfer, coeffs, fullRange, colorAspects);
+
+    // Update color aspects if necessary.
+    if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+        mBitstreamColorAspects = colorAspects;
+        status_t err = handleColorAspectsChange();
+        CHECK(err == OK);
+    }
+    return true;
+    */
+    return false;
+}
+
+bool GoldfishAVCDec::setDecodeArgs(
+        OMX_BUFFERHEADERTYPE *inHeader,
+        OMX_BUFFERHEADERTYPE *outHeader,
+        size_t timeStampIx) {
+    size_t sizeY = outputBufferWidth() * outputBufferHeight();
+    size_t sizeUV = sizeY / 4;
+
+    /* When in flush and after EOS with zero byte input,
+     * inHeader is set to zero. Hence check for non-null */
+    if (inHeader) {
+        mCurrentTs = timeStampIx;
+        mConsumedBytes = inHeader->nFilledLen - mInputOffset;
+        mInPBuffer = inHeader->pBuffer + inHeader->nOffset + mInputOffset;
+    } else {
+        mCurrentTs = 0;
+        mConsumedBytes = 0;
+        mInPBuffer = nullptr;
+    }
+
+    if (outHeader) {
+        if (outHeader->nAllocLen < sizeY + (sizeUV * 2)) {
+            android_errorWriteLog(0x534e4554, "27833616");
+            return false;
+        }
+        mOutHeaderBuf = outHeader->pBuffer;
+    } else {
+        // We flush out on the host side
+        mOutHeaderBuf = nullptr;
+    }
+
+    return true;
+}
+
+void GoldfishAVCDec::onPortFlushCompleted(OMX_U32 portIndex) {
+    /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+    if (kOutputPortIndex == portIndex) {
+        setFlushMode();
+        resetPlugin();
+    } else {
+        mInputOffset = 0;
+    }
+}
+
+void GoldfishAVCDec::onQueueFilled(OMX_U32 portIndex) {
+    UNUSED(portIndex);
+    OMX_BUFFERHEADERTYPE *inHeader = NULL;
+    BufferInfo *inInfo = NULL;
+
+    if (mSignalledError) {
+        return;
+    }
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    if (mContext == nullptr) {
+        if (OK != initDecoder()) {
+            ALOGE("Failed to initialize decoder");
+            notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+    while (!outQueue.empty()) {
+        BufferInfo *outInfo;
+        OMX_BUFFERHEADERTYPE *outHeader;
+        size_t timeStampIx = 0;
+
+        if (!mIsInFlush && (NULL == inHeader)) {
+            if (!inQueue.empty()) {
+                inInfo = *inQueue.begin();
+                inHeader = inInfo->mHeader;
+                if (inHeader == NULL) {
+                    inQueue.erase(inQueue.begin());
+                    inInfo->mOwnedByUs = false;
+                    continue;
+                }
+            } else {
+                break;
+            }
+        }
+
+        outInfo = *outQueue.begin();
+        outHeader = outInfo->mHeader;
+        outHeader->nFlags = 0;
+        outHeader->nTimeStamp = 0;
+        outHeader->nOffset = 0;
+
+        if (inHeader != NULL) {
+            if (inHeader->nFilledLen == 0) {
+                // An empty buffer can be end of stream (EOS) buffer, so
+                // we'll set the decoder in flush mode if so. If it's not EOS,
+                // then just release the buffer.
+                inQueue.erase(inQueue.begin());
+                inInfo->mOwnedByUs = false;
+                notifyEmptyBufferDone(inHeader);
+
+                if (!(inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+                    return;
+                }
+
+                mReceivedEOS = true;
+                inHeader = NULL;
+                setFlushMode();
+            } else if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+                mReceivedEOS = true;
+            }
+        }
+
+        /* Get a free slot in timestamp array to hold input timestamp */
+        {
+            size_t i;
+            timeStampIx = 0;
+            for (i = 0; i < MAX_TIME_STAMPS; i++) {
+                if (!mTimeStampsValid[i]) {
+                    timeStampIx = i;
+                    break;
+                }
+            }
+            if (inHeader != NULL) {
+                mTimeStampsValid[timeStampIx] = true;
+                mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+            }
+        }
+
+        {
+            nsecs_t timeDelay, timeTaken;
+
+            if (!setDecodeArgs(inHeader, outHeader, timeStampIx)) {
+                ALOGE("Decoder arg setup failed");
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+                return;
+            }
+
+            mTimeStart = systemTime();
+            /* Compute time elapsed between end of previous decode()
+             * to start of current decode() */
+            timeDelay = mTimeStart - mTimeEnd;
+
+            // TODO: We also need to send the timestamp
+            h264_result_t h264Res = {(int)MediaH264Decoder::Err::NoErr, 0};
+            if (inHeader != nullptr) {
+                ALOGE("Decoding frame(sz=%lu)", (unsigned long)(inHeader->nFilledLen - mInputOffset));
+                h264Res = mContext->decodeFrame(mInPBuffer,
+                                                inHeader->nFilledLen - mInputOffset);
+                mConsumedBytes = h264Res.bytesProcessed;
+                if (h264Res.ret == (int)MediaH264Decoder::Err::DecoderRestarted) {
+                    // The host will always restart when given a new set of SPS
+                    // and PPS frames.
+                    mChangingResolution = true;
+                }
+            } else {
+                ALOGE("No more input data. Attempting to get a decoded frame, if any.");
+            }
+            h264_image_t img = mContext->getImage();
+
+
+            // TODO: support getVUIParams()
+//            getVUIParams();
+
+            mTimeEnd = systemTime();
+            /* Compute time taken for decode() */
+            timeTaken = mTimeEnd - mTimeStart;
+
+            ALOGV("timeTaken=%6lldus delay=%6lldus numBytes=Nan",
+                    (long long) (timeTaken / 1000LL), (long long) (timeDelay / 1000LL));
+
+            if ((inHeader != NULL) && (img.data == nullptr)) {
+                /* If the input did not contain picture data, then ignore
+                 * the associated timestamp */
+                mTimeStampsValid[timeStampIx] = false;
+            }
+
+            // If the decoder is in the changing resolution mode and there is no output present,
+            // that means the switching is done and it's ready to reset the decoder and the plugin.
+            if (mChangingResolution && img.data == nullptr) {
+                mChangingResolution = false;
+                resetPlugin();
+                // The decoder on the host has actually already restarted given
+                // the new information, so we don't have to refeed the same
+                // information again.
+                mInputOffset += mConsumedBytes;
+                continue;
+            }
+
+            // Combine the resolution change and coloraspects change in one
+            // PortSettingChange event if necessary.
+            if (img.data != nullptr) {
+                bool portWillReset = false;
+                handlePortSettingsChange(&portWillReset, img.width, img.height);
+                if (portWillReset) {
+                    ALOGE("port resetting (img.width=%u, img.height=%u, mWidth=%u, mHeight=%u)",
+                          img.width, img.height, mWidth, mHeight);
+                    resetDecoder();
+                    resetPlugin();
+                    return;
+                }
+            }
+
+            if (img.data != nullptr) {
+                outHeader->nFilledLen = img.ret;
+                memcpy(outHeader->pBuffer, img.data, img.ret);
+                outHeader->nTimeStamp = mTimeStamps[mCurrentTs];
+                mTimeStampsValid[mCurrentTs] = false;
+
+                outInfo->mOwnedByUs = false;
+                outQueue.erase(outQueue.begin());
+                outInfo = NULL;
+                notifyFillBufferDone(outHeader);
+                outHeader = NULL;
+            } else if (mIsInFlush) {
+                /* If in flush mode and no output is returned by the codec,
+                 * then come out of flush mode */
+                mIsInFlush = false;
+
+                /* If EOS was recieved on input port and there is no output
+                 * from the codec, then signal EOS on output port */
+                if (mReceivedEOS) {
+                    outHeader->nFilledLen = 0;
+                    outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                    resetPlugin();
+                }
+            }
+            mInputOffset += mConsumedBytes;
+        }
+
+        // If more than 4 bytes are remaining in input, then do not release it
+        if (inHeader != NULL && ((inHeader->nFilledLen - mInputOffset) <= 4)) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+            mInputOffset = 0;
+
+            /* If input EOS is seen and decoder is not in flush mode,
+             * set the decoder in flush mode.
+             * There can be a case where EOS is sent along with last picture data
+             * In that case, only after decoding that input data, decoder has to be
+             * put in flush. This case is handled here  */
+
+            if (mReceivedEOS && !mIsInFlush) {
+                setFlushMode();
+            }
+        }
+    }
+}
+
+int GoldfishAVCDec::getColorAspectPreference() {
+    return kPreferBitstream;
+}
+
+}  // namespace android
+
+android::GoldfishOMXComponent *createGoldfishOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+        OMX_COMPONENTTYPE **component) {
+    return new android::GoldfishAVCDec(name, callbacks, appData, component);
+}
+
diff --git a/system/codecs/omx/avcdec/GoldfishAVCDec.h b/system/codecs/omx/avcdec/GoldfishAVCDec.h
new file mode 100644
index 0000000..f0e338c
--- /dev/null
+++ b/system/codecs/omx/avcdec/GoldfishAVCDec.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2015 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 GOLDFISH_H264_DEC_H_
+
+#define GOLDFISH_H264_DEC_H_
+
+#include "GoldfishVideoDecoderOMXComponent.h"
+#include "MediaH264Decoder.h"
+#include <sys/time.h>
+
+namespace android {
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH     1920
+
+#define CODEC_MAX_HEIGHT    1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+struct GoldfishAVCDec : public GoldfishVideoDecoderOMXComponent {
+    GoldfishAVCDec(const char *name, const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~GoldfishAVCDec();
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onReset();
+    virtual int getColorAspectPreference();
+private:
+    // Number of input and output buffers
+    enum {
+        kNumBuffers = 8
+    };
+
+    size_t mNumCores;            // Number of cores to be uesd by the codec
+
+    nsecs_t mTimeStart;   // Time at the start of decode()
+    nsecs_t mTimeEnd;     // Time at the end of decode()
+
+    // Status of entries in the timestamp array
+    bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+    // Timestamp array - Since codec does not take 64 bit timestamps,
+    // they are maintained in the plugin
+    OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    OMX_COLOR_FORMATTYPE mOmxColorFormat;    // OMX Color format
+
+    bool mIsInFlush;        // codec is flush mode
+    bool mReceivedEOS;      // EOS is receieved on input port
+
+    // The input stream has changed to a different resolution, which is still supported by the
+    // codec. So the codec is switching to decode the new resolution.
+    bool mChangingResolution;
+    bool mSignalledError;
+    size_t mInputOffset;
+
+    status_t initDecoder();
+    status_t deInitDecoder();
+    status_t setFlushMode();
+    status_t setParams(size_t stride);
+    void logVersion();
+    status_t setNumCores();
+    status_t resetDecoder();
+    status_t resetPlugin();
+
+
+    bool setDecodeArgs(
+            OMX_BUFFERHEADERTYPE *inHeader,
+            OMX_BUFFERHEADERTYPE *outHeader,
+            size_t timeStampIx);
+
+    bool getVUIParams();
+
+    std::unique_ptr<MediaH264Decoder> mContext;
+    uint64_t mCurrentTs = 0;
+    uint64_t mConsumedBytes = 0;
+    uint8_t* mInPBuffer = nullptr;
+    uint8_t* mOutHeaderBuf = nullptr;
+    DISALLOW_EVIL_CONSTRUCTORS(GoldfishAVCDec);
+};
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/avcd_input"
+#define INPUT_DUMP_EXT      "h264"
+
+#define GENERATE_FILE_NAMES() {                         \
+    strcpy(mInFile, "");                                \
+    sprintf(mInFile, "%s_%lld.%s", INPUT_DUMP_PATH,     \
+            (long long) mTimeStart,                     \
+            INPUT_DUMP_EXT);                            \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not open file %s", m_filename);    \
+    }                                                   \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\
+{                                                       \
+    FILE *fp = fopen(m_filename, "ab");                 \
+    if (fp != NULL && m_buf != NULL && m_offset == 0) { \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int) m_size) {                        \
+            ALOGD("Error in fwrite, returned %d", i);   \
+            perror("Error in write to file");           \
+        }                                               \
+    } else if (fp == NULL) {                            \
+        ALOGD("Could not write to file %s", m_filename);\
+    }                                                   \
+    if (fp) {                                           \
+        fclose(fp);                                     \
+    }                                                   \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif  // GOLDFISH_H264_DEC_H_
diff --git a/system/codecs/omx/avcdec/MediaH264Decoder.cpp b/system/codecs/omx/avcdec/MediaH264Decoder.cpp
new file mode 100644
index 0000000..aa97c2d
--- /dev/null
+++ b/system/codecs/omx/avcdec/MediaH264Decoder.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+
+#include "MediaH264Decoder.h"
+#include "goldfish_media_utils.h"
+#include <string.h>
+
+void MediaH264Decoder::initH264Context(unsigned int width,
+                                       unsigned int height,
+                                       unsigned int outWidth,
+                                       unsigned int outHeight,
+                                       PixelFormat pixFmt) {
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->writeParam(width, 0);
+    transport->writeParam(height, 1);
+    transport->writeParam(outWidth, 2);
+    transport->writeParam(outHeight, 3);
+    transport->writeParam(static_cast<uint64_t>(pixFmt), 4);
+    transport->sendOperation(MediaCodecType::H264Codec,
+                             MediaOperation::InitContext);
+}
+
+void MediaH264Decoder::destroyH264Context() {
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->sendOperation(MediaCodecType::H264Codec,
+                             MediaOperation::DestroyContext);
+}
+
+h264_result_t MediaH264Decoder::decodeFrame(uint8_t* img, size_t szBytes) {
+    auto transport = GoldfishMediaTransport::getInstance();
+    uint8_t* hostSrc = transport->getInputAddr();
+    if (img != nullptr && szBytes > 0) {
+        memcpy(hostSrc, img, szBytes);
+    }
+    transport->writeParam(transport->offsetOf((uint64_t)(hostSrc)), 0);
+    transport->writeParam((uint64_t)szBytes, 1);
+    transport->sendOperation(MediaCodecType::H264Codec,
+                             MediaOperation::DecodeImage);
+
+    h264_result_t res = {0, 0};
+
+    auto* retptr = transport->getReturnAddr();
+    res.bytesProcessed = *(uint64_t*)(retptr);
+    res.ret = *(int*)(retptr + 8);
+
+    return res;
+}
+
+void MediaH264Decoder::flush() {
+    auto transport = GoldfishMediaTransport::getInstance();
+    transport->sendOperation(MediaCodecType::H264Codec,
+                             MediaOperation::Flush);
+}
+
+h264_image_t MediaH264Decoder::getImage() {
+    h264_image_t res = { nullptr, 0, 0, 0 };
+    auto transport = GoldfishMediaTransport::getInstance();
+    uint8_t* dst = transport->getOutputAddr();
+    transport->writeParam(transport->offsetOf((uint64_t)(dst)), 0);
+    transport->sendOperation(MediaCodecType::H264Codec,
+                             MediaOperation::GetImage);
+    auto* retptr = transport->getReturnAddr();
+    res.ret = *(int*)(retptr);
+    if (res.ret >= 0) {
+        res.data = dst;
+        res.width = *(uint32_t*)(retptr + 8);
+        res.height = *(uint32_t*)(retptr + 16);
+    }
+
+    return res;
+}
diff --git a/system/codecs/omx/avcdec/MediaH264Decoder.h b/system/codecs/omx/avcdec/MediaH264Decoder.h
new file mode 100644
index 0000000..510da28
--- /dev/null
+++ b/system/codecs/omx/avcdec/MediaH264Decoder.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 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 GOLDFISH_MEDIA_H264_DEC_H_
+#define GOLDFISH_MEDIA_H264_DEC_H_
+
+struct h264_result_t {
+    int ret;
+    uint64_t bytesProcessed;
+};
+
+struct h264_image_t {
+    const uint8_t* data;
+    uint32_t width;
+    uint32_t height;
+    // on success, |ret| will indicate the size of |data|.
+    // If failed, |ret| will contain some negative error code.
+    int ret;
+};
+
+class MediaH264Decoder {
+public:
+    MediaH264Decoder() = default;
+    virtual ~MediaH264Decoder() = default;
+
+    enum class PixelFormat : uint8_t {
+        YUV420P = 0,
+        UYVY422 = 1,
+        BGRA8888 = 2,
+    };
+
+    enum class Err : int {
+        NoErr = 0,
+        NoDecodedFrame = -1,
+        InitContextFailed = -2,
+        DecoderRestarted = -3,
+        NALUIgnored = -4,
+    };
+
+    void initH264Context(unsigned int width,
+                         unsigned int height,
+                         unsigned int outWidth,
+                         unsigned int outHeight,
+                         PixelFormat pixFmt);
+    void destroyH264Context();
+    h264_result_t decodeFrame(uint8_t* img, size_t szBytes);
+    void flush();
+    h264_image_t getImage();
+};
+#endif
diff --git a/system/codecs/omx/avcdec/exports.lds b/system/codecs/omx/avcdec/exports.lds
new file mode 100644
index 0000000..e6674f2
--- /dev/null
+++ b/system/codecs/omx/avcdec/exports.lds
@@ -0,0 +1,5 @@
+{
+    global:
+        _Z26createGoldfishOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE;
+    local: *;
+};