/*
* Copyright (c) 2012 Intel Corporation.  All rights reserved.
*
* 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_TIME 0
//#define LOG_NDEBUG 0
#define LOG_TAG "OMXVideoDecoderVP9Hybrid"
#include <wrs_omxil_core/log.h>
#include "OMXVideoDecoderVP9Hybrid.h"

#include <hardware/hardware.h>
#include <hardware/gralloc.h>
#include <system/graphics.h>

#include <hal_public.h>

#define VP9_YV12_ALIGN (128-1)
static const char* VP9_MIME_TYPE = "video/x-vnd.on2.vp9";

OMXVideoDecoderVP9Hybrid::OMXVideoDecoderVP9Hybrid() {
    LOGV("OMXVideoDecoderVP9Hybrid is constructed.");
    mNativeBufferCount = OUTPORT_NATIVE_BUFFER_COUNT;
    BuildHandlerList();
    mLibHandle = NULL;
    mOpenDecoder = NULL;
    mInitDecoder = NULL;
    mCloseDecoder = NULL;
    mSingalRenderDone = NULL;
    mDecoderDecode = NULL;
    mCheckBufferAvailable = NULL;
    mGetOutput = NULL;
    mGetRawDataOutput = NULL;
    mGetFrameResolution = NULL;
    mDeinitDecoder = NULL;
    mLastTimeStamp = 0;
    mWorkingMode = RAWDATA_MODE;
    mDecodedImageWidth = 0;
    mDecodedImageHeight = 0;
    mDecodedImageNewWidth = 0;
    mDecodedImageNewHeight = 0;
}

OMXVideoDecoderVP9Hybrid::~OMXVideoDecoderVP9Hybrid() {
    LOGV("OMXVideoDecoderVP9Hybrid is destructed.");
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::InitInputPortFormatSpecific(
    OMX_PARAM_PORTDEFINITIONTYPE *paramPortDefinitionInput) {
    // OMX_PARAM_PORTDEFINITIONTYPE
    paramPortDefinitionInput->nBufferCountActual = INPORT_ACTUAL_BUFFER_COUNT;
    paramPortDefinitionInput->nBufferCountMin = INPORT_MIN_BUFFER_COUNT;
    paramPortDefinitionInput->nBufferSize = INPORT_BUFFER_SIZE;
    paramPortDefinitionInput->format.video.cMIMEType = (OMX_STRING)VP9_MIME_TYPE;
    paramPortDefinitionInput->format.video.eCompressionFormat = OMX_VIDEO_CodingVP9;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorInit(void) {
    uint32_t buff[MAX_GRAPHIC_BUFFER_NUM];
    uint32_t i, bufferCount;
    bool gralloc_mode = (mWorkingMode == GRAPHICBUFFER_MODE);
    uint32_t bufferSize, bufferHStride, bufferHeight, bufferVStride, bufferWidth;
    if (!gralloc_mode) {
        bufferHStride = 1920;
        bufferVStride = 1088;
        bufferWidth = 1920;
        bufferHeight = 1080;
        bufferCount = 12;
    } else {
        if (mAPMode == METADATA_MODE) {
            const OMX_PARAM_PORTDEFINITIONTYPE *def_output = this->ports[OUTPORT_INDEX]->GetPortDefinition();
            if (def_output == NULL) {
                return OMX_ErrorBadParameter;
            }
            bufferCount = mMetaDataBuffersNum = def_output->nBufferCountActual;
            mOMXBufferHeaderTypePtrNum = 0;

            mGraphicBufferParam.graphicBufferColorFormat = def_output->format.video.eColorFormat;
            mGraphicBufferParam.graphicBufferHStride = (def_output->format.video.nFrameWidth + VP9_YV12_ALIGN) & ~VP9_YV12_ALIGN;
            mGraphicBufferParam.graphicBufferVStride = (def_output->format.video.nFrameHeight + 0x1f) & ~0x1f;
            mGraphicBufferParam.graphicBufferWidth = def_output->format.video.nFrameWidth;
            mGraphicBufferParam.graphicBufferHeight = def_output->format.video.nFrameHeight;
            mDecodedImageWidth = def_output->format.video.nFrameWidth;
            mDecodedImageHeight = def_output->format.video.nFrameHeight;
        } else{
            bufferCount = mOMXBufferHeaderTypePtrNum;

            for (i = 0; i < bufferCount; i++ ) {
                OMX_BUFFERHEADERTYPE *buf_hdr = mOMXBufferHeaderTypePtrArray[i];
                buff[i] = (uint32_t)(buf_hdr->pBuffer);
            }
        }

        bufferHStride = mGraphicBufferParam.graphicBufferHStride;
        bufferVStride = mGraphicBufferParam.graphicBufferVStride;
        bufferWidth = mGraphicBufferParam.graphicBufferWidth;
        bufferHeight = mGraphicBufferParam.graphicBufferHeight;
    }

    bufferSize = bufferHStride * bufferVStride * 1.5;

    mLibHandle = dlopen("libDecoderVP9Hybrid.so", RTLD_NOW);
    if (mLibHandle == NULL) {
        LOGE("dlopen libDecoderVP9Hybrid.so fail\n");
        return OMX_ErrorBadParameter;
    } else {
        LOGI("dlopen libDecoderVP9Hybrid.so successfully\n");
    }
    mOpenDecoder = (OpenFunc)dlsym(mLibHandle, "Decoder_Open");
    mCloseDecoder = (CloseFunc)dlsym(mLibHandle, "Decoder_Close");
    mInitDecoder = (InitFunc)dlsym(mLibHandle, "Decoder_Init");
    mSingalRenderDone = (SingalRenderDoneFunc)dlsym(mLibHandle, "Decoder_SingalRenderDone");
    mDecoderDecode = (DecodeFunc)dlsym(mLibHandle, "Decoder_Decode");
    mCheckBufferAvailable = (IsBufferAvailableFunc)dlsym(mLibHandle, "Decoder_IsBufferAvailable");
    mGetOutput = (GetOutputFunc)dlsym(mLibHandle, "Decoder_GetOutput");
    mGetRawDataOutput = (GetRawDataOutputFunc)dlsym(mLibHandle, "Decoder_GetRawDataOutput");
    mGetFrameResolution = (GetFrameResolutionFunc)dlsym(mLibHandle, "Decoder_GetFrameResolution");
    mDeinitDecoder = (DeinitFunc)dlsym(mLibHandle, "Decoder_Deinit");
    if (mOpenDecoder == NULL || mCloseDecoder == NULL
        || mInitDecoder == NULL || mSingalRenderDone == NULL
        || mDecoderDecode == NULL || mCheckBufferAvailable == NULL
        || mGetOutput == NULL || mGetRawDataOutput == NULL
        || mGetFrameResolution == NULL || mDeinitDecoder == NULL) {
        return OMX_ErrorBadParameter;
    }

    if (mOpenDecoder(&mCtx,&mHybridCtx) == false) {
        LOGE("open hybrid Decoder fail\n");
        return OMX_ErrorBadParameter;
    }

    // FIXME: The proprietary part of the vp9hybrid decoder should be updated
    //        to take VStride as well as Height. For now it's convenient to
    //        use VStride as that was effectively what was done before..
    mInitDecoder(mHybridCtx, bufferSize, bufferHStride, bufferWidth,
        bufferHeight, bufferCount, gralloc_mode, buff, (uint32_t)mAPMode);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorReset(void)
{
    uint32_t buff[MAX_GRAPHIC_BUFFER_NUM];
    uint32_t i, bufferCount;
    bool gralloc_mode = (mWorkingMode == GRAPHICBUFFER_MODE);
    uint32_t bufferSize, bufferHStride, bufferHeight, bufferVStride, bufferWidth;
    if (!gralloc_mode) {
        bufferHStride = mDecodedImageWidth;
        bufferVStride = mDecodedImageHeight;
        bufferWidth = mDecodedImageWidth;
        bufferHeight = mDecodedImageHeight;
        bufferSize = bufferHStride * bufferVStride * 1.5;
        bufferCount = 12;
    } else {
        if (mAPMode == METADATA_MODE) {
            const OMX_PARAM_PORTDEFINITIONTYPE *def_output = this->ports[OUTPORT_INDEX]->GetPortDefinition();
            if (def_output == NULL) {
                return OMX_ErrorBadParameter;
            }
            bufferCount = mMetaDataBuffersNum = def_output->nBufferCountActual;
            mOMXBufferHeaderTypePtrNum = 0;

            mGraphicBufferParam.graphicBufferColorFormat = def_output->format.video.eColorFormat;
            mGraphicBufferParam.graphicBufferHStride = (def_output->format.video.nFrameWidth + VP9_YV12_ALIGN) & ~VP9_YV12_ALIGN;
            mGraphicBufferParam.graphicBufferVStride = (def_output->format.video.nFrameHeight + 0x1f) & ~0x1f;
            mGraphicBufferParam.graphicBufferWidth = def_output->format.video.nFrameWidth;
            mGraphicBufferParam.graphicBufferHeight = def_output->format.video.nFrameHeight;
        } else{
            bufferCount = mOMXBufferHeaderTypePtrNum;

            for (i = 0; i < bufferCount; i++ ) {
                OMX_BUFFERHEADERTYPE *buf_hdr = mOMXBufferHeaderTypePtrArray[i];
                buff[i] = (uint32_t)(buf_hdr->pBuffer);
            }
        }
        bufferHStride = mGraphicBufferParam.graphicBufferHStride;
        bufferVStride = mGraphicBufferParam.graphicBufferVStride;
        bufferWidth = mGraphicBufferParam.graphicBufferWidth;
        bufferHeight = mGraphicBufferParam.graphicBufferHeight;
    }

    bufferSize = bufferHStride * bufferVStride * 1.5;

    // FIXME: The proprietary part of the vp9hybrid decoder should be updated
    //        to take VStride as well as Height. For now it's convenient to
    //        use VStride as that was effectively what was done before..
    mInitDecoder(mHybridCtx, bufferSize, bufferHStride, bufferWidth,
        bufferHeight, bufferCount, gralloc_mode, buff, (uint32_t)mAPMode);
    mFormatChanged = false;
    return OMX_ErrorNone;
}

bool OMXVideoDecoderVP9Hybrid::isReallocateNeeded(const uint8_t * data,uint32_t data_sz)
{
    bool gralloc_mode = (mWorkingMode == GRAPHICBUFFER_MODE);
    uint32_t width, height;
    bool ret = true;
    if (gralloc_mode) {
        ret = mGetFrameResolution(data,data_sz, &width, &height);
        if (width == 0 || height == 0)
            return false;

        if (ret) {
            if (mAPMode == METADATA_MODE) {
                ret = (width != mDecodedImageWidth)
                    || (height != mDecodedImageHeight);
            } else {
                ret = width > mGraphicBufferParam.graphicBufferWidth
                    || height > mGraphicBufferParam.graphicBufferHeight;
            }
            if (ret) {
                mDecodedImageNewWidth = width;
                mDecodedImageNewHeight = height;
                return true;
            }
        }
    }

    return ret;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorDeinit(void) {
    mCloseDecoder(mCtx,mHybridCtx);
    mOMXBufferHeaderTypePtrNum = 0;
    if (mLibHandle != NULL) {
        dlclose(mLibHandle);
        mLibHandle = NULL;
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorStop(void) {
    return OMXComponentCodecBase::ProcessorStop();
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorFlush(OMX_U32 portIndex) {
    if (portIndex == INPORT_INDEX || portIndex == OMX_ALL) {
        // end the last frame
        unsigned int width, height;
        mDecoderDecode(mCtx,mHybridCtx,NULL,0,true);
        mGetOutput(mCtx,mHybridCtx, &width, &height);
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorPreFillBuffer(OMX_BUFFERHEADERTYPE* buffer) {
    if (buffer->nOutputPortIndex == OUTPORT_INDEX){
        unsigned int handle;
        if (mAPMode == METADATA_MODE) {
            bool found = false;
            if (mOMXBufferHeaderTypePtrNum < mMetaDataBuffersNum) {
                for (uint32_t i = 0; i < mOMXBufferHeaderTypePtrNum; i++) {
                    if (mOMXBufferHeaderTypePtrArray[i] == buffer) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    mOMXBufferHeaderTypePtrArray[mOMXBufferHeaderTypePtrNum] = buffer;
                    mOMXBufferHeaderTypePtrNum++;
                }
            } else {
                found = true;
            }

            android::VideoGrallocMetadata *metadata = (android::VideoGrallocMetadata *)(buffer->pBuffer);
            handle = (unsigned int)metadata->pHandle;
            mSingalRenderDone(mHybridCtx, handle, !found);
        } else {
            handle = (unsigned int)buffer->pBuffer;
            mSingalRenderDone(mHybridCtx, handle, false);
        }
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::ProcessorProcess(
        OMX_BUFFERHEADERTYPE ***pBuffers,
        buffer_retain_t *retains,
        OMX_U32)
{
    OMX_ERRORTYPE ret;
    OMX_BUFFERHEADERTYPE *inBuffer = *pBuffers[INPORT_INDEX];
    OMX_BUFFERHEADERTYPE *outBuffer = *pBuffers[OUTPORT_INDEX];
    OMX_BOOL isResolutionChange = OMX_FALSE;
    bool eos = (inBuffer->nFlags & OMX_BUFFERFLAG_EOS)? true : false;
    eos = eos && (inBuffer->nFilledLen == 0);
    static unsigned char *firstFrame = NULL;
    static uint32_t firstFrameSize = 0;

    if (inBuffer->pBuffer == NULL) {
        LOGE("Buffer to decode is empty.");
        return OMX_ErrorBadParameter;
    }

    if (inBuffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
        LOGI("Buffer has OMX_BUFFERFLAG_CODECCONFIG flag.");
    }

    if (inBuffer->nFlags & OMX_BUFFERFLAG_DECODEONLY) {
        LOGW("Buffer has OMX_BUFFERFLAG_DECODEONLY flag.");
    }

    if (firstFrameSize == 0 && inBuffer->nFilledLen != 0 && inBuffer->nTimeStamp != 0) {
        if (firstFrame != NULL) {
            free(firstFrame);
            firstFrame = NULL;
        }

        firstFrame = (unsigned char *)malloc(inBuffer->nFilledLen);
        memcpy(firstFrame, inBuffer->pBuffer + inBuffer->nOffset, inBuffer->nFilledLen);
        firstFrameSize = inBuffer->nFilledLen;
    }

    if ((mWorkingMode == GRAPHICBUFFER_MODE) && (mAPMode == METADATA_MODE) && (!mFormatChanged)) {
        bool mRet = mGetFrameResolution(inBuffer->pBuffer + inBuffer->nOffset, inBuffer->nFilledLen,
            &mDecodedImageNewWidth,&mDecodedImageNewHeight);

        if (mRet && ((mDecodedImageNewWidth != 0) && (mDecodedImageNewHeight != 0)) &&
            ((mDecodedImageWidth != 0) && (mDecodedImageHeight != 0)) &&
            ((mDecodedImageNewWidth != mDecodedImageWidth || mDecodedImageNewHeight != mDecodedImageHeight))) {
            if (mLastTimeStamp == 0) {
                retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
                HandleFormatChange();
                return OMX_ErrorNone;
            } else {
                // Detected format change in time.
                // drain the last frame, keep the current input buffer
                mDecoderDecode(mCtx, mHybridCtx, firstFrame, firstFrameSize, false);
                retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;

                mFormatChanged = true;

                ret = FillRenderBuffer(pBuffers[OUTPORT_INDEX], &retains[OUTPORT_INDEX],
                    eos ? OMX_BUFFERFLAG_EOS : 0, &isResolutionChange);

                if (ret == OMX_ErrorNone)
                    (*pBuffers[OUTPORT_INDEX])->nTimeStamp = mLastTimeStamp;

                mLastTimeStamp = inBuffer->nTimeStamp;

                free(firstFrame);
                firstFrame = NULL;
                firstFrameSize = 0;
                return ret;
            }
        } else if (!mRet && (mDecodedImageNewWidth == 0 || mDecodedImageNewHeight == 0)) {
            retains[INPORT_INDEX] = BUFFER_RETAIN_NOT_RETAIN;
            return OMX_ErrorBadParameter;
        }
    }

#if LOG_TIME == 1
    struct timeval tv_start, tv_end;
    int32_t time_ms;
    gettimeofday(&tv_start,NULL);
#endif
    int res = mDecoderDecode(mCtx,mHybridCtx,inBuffer->pBuffer + inBuffer->nOffset,inBuffer->nFilledLen, eos);
    if (res != 0) {
        if (res == -2) {
            if (isReallocateNeeded(inBuffer->pBuffer + inBuffer->nOffset,inBuffer->nFilledLen)) {
                if (mAPMode == METADATA_MODE) {
                    mFormatChanged = true;
                } else {
                    retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
                    HandleFormatChange();
                    return OMX_ErrorNone;
                }
            }
            // drain the last frame, keep the current input buffer
            res = mDecoderDecode(mCtx,mHybridCtx,NULL,0,true);
            retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
        } else if (res == -3) {
            LOGW("on2 decoder skipped to decode the frame.");
            (*pBuffers[OUTPORT_INDEX])->nOffset = 0;
            (*pBuffers[OUTPORT_INDEX])->nFilledLen = 0;
            return OMX_ErrorNone;
        } else {
            LOGE("on2 decoder failed to decode frame.");
            return OMX_ErrorBadParameter;
        }
    }

#if LOG_TIME == 1
    gettimeofday(&tv_end,NULL);
    time_ms = (int32_t)(tv_end.tv_sec - tv_start.tv_sec) * 1000 + (int32_t)(tv_end.tv_usec - tv_start.tv_usec)/1000;
    LOGI("vpx_codec_decode: %d ms", time_ms);
#endif

    ret = FillRenderBuffer(pBuffers[OUTPORT_INDEX],
                           &retains[OUTPORT_INDEX],
                           eos? OMX_BUFFERFLAG_EOS:0,
                           &isResolutionChange);

    if (ret == OMX_ErrorNone) {
        (*pBuffers[OUTPORT_INDEX])->nTimeStamp = mLastTimeStamp;
    }
    mLastTimeStamp = inBuffer->nTimeStamp;

    if (isResolutionChange == OMX_TRUE) {
        HandleFormatChange();
    }
    bool inputEoS = ((*pBuffers[INPORT_INDEX])->nFlags & OMX_BUFFERFLAG_EOS);
    bool outputEoS = ((*pBuffers[OUTPORT_INDEX])->nFlags & OMX_BUFFERFLAG_EOS);
    // if output port is not eos, retain the input buffer
    // until all the output buffers are drained.
    if (inputEoS && !outputEoS && retains[INPORT_INDEX] != BUFFER_RETAIN_GETAGAIN) {
        retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
        // the input buffer is retained for draining purpose.
        // Set nFilledLen to 0 so buffer will not be decoded again.
        (*pBuffers[INPORT_INDEX])->nFilledLen = 0;
    }

    if (ret == OMX_ErrorNotReady) {
        retains[OUTPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
        ret = OMX_ErrorNone;
    }

    return ret;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::FillRenderBuffer(OMX_BUFFERHEADERTYPE **pBuffer,
                                                      buffer_retain_t *retain,
                                                      OMX_U32 inportBufferFlags,
                                                      OMX_BOOL *isResolutionChange)
{
    OMX_BUFFERHEADERTYPE *buffer = *pBuffer;
    OMX_BUFFERHEADERTYPE *buffer_orign = buffer;

    OMX_ERRORTYPE ret = OMX_ErrorNone;

    int fb_index;
    if (mWorkingMode == RAWDATA_MODE) {
        const OMX_PARAM_PORTDEFINITIONTYPE *paramPortDefinitionOutput
                       = this->ports[OUTPORT_INDEX]->GetPortDefinition();
        int32_t stride = paramPortDefinitionOutput->format.video.nStride;
        int32_t height =  paramPortDefinitionOutput->format.video.nFrameHeight;
        int32_t width = paramPortDefinitionOutput->format.video.nFrameWidth;
        unsigned char *dst = buffer->pBuffer;
        fb_index = mGetRawDataOutput(mCtx,mHybridCtx,dst,height,stride);
        if (fb_index == -1) {
            if (inportBufferFlags & OMX_BUFFERFLAG_EOS) {
                // eos frame is non-shown frame
                buffer->nFlags = OMX_BUFFERFLAG_EOS;
                buffer->nOffset = 0;
                buffer->nFilledLen = 0;
                return OMX_ErrorNone;
            }
            LOGV("vpx_codec_get_frame return NULL.");
            return OMX_ErrorNotReady;
        }
        buffer->nOffset = 0;
        buffer->nFilledLen = stride*height*3/2;
        if (inportBufferFlags & OMX_BUFFERFLAG_EOS) {
            buffer->nFlags = OMX_BUFFERFLAG_EOS;
        }
        return OMX_ErrorNone;
    }

    if (mFormatChanged && mAPMode == METADATA_MODE) {
        fb_index = mGetOutput(mCtx,mHybridCtx, &mDecodedImageWidth, &mDecodedImageHeight);
    } else {
        fb_index = mGetOutput(mCtx,mHybridCtx, &mDecodedImageNewWidth, &mDecodedImageNewHeight);
    }
    if (fb_index == -1) {
        if (mFormatChanged && mAPMode == METADATA_MODE) {
            *isResolutionChange = OMX_TRUE;
            return OMX_ErrorNone;
    }

        if (inportBufferFlags & OMX_BUFFERFLAG_EOS) {
            // eos frame is no-shown frame
            buffer->nFlags = OMX_BUFFERFLAG_EOS;
            buffer->nOffset = 0;
            buffer->nFilledLen = 0;
            return OMX_ErrorNone;
        }
        LOGV("vpx_codec_get_frame return NULL.");
        return OMX_ErrorNotReady;
    }
    if (mDecodedImageHeight == 0 && mDecodedImageWidth == 0) {
        mDecodedImageWidth = mDecodedImageNewWidth;
        mDecodedImageHeight = mDecodedImageNewHeight;
        if (mAPMode == LEGACY_MODE)
            *isResolutionChange = OMX_TRUE;
    }

    if (mAPMode == LEGACY_MODE) {
    if ((mDecodedImageNewWidth != mDecodedImageWidth)
        || (mDecodedImageNewHeight!= mDecodedImageHeight))
        *isResolutionChange = OMX_TRUE;
    } else {
        if (mFormatChanged && ((mDecodedImageNewWidth != mDecodedImageWidth)
            || (mDecodedImageNewHeight!= mDecodedImageHeight)))
            *isResolutionChange = OMX_TRUE;
    }
    buffer = *pBuffer = mOMXBufferHeaderTypePtrArray[fb_index];
    buffer->nOffset = 0;
    buffer->nFilledLen = sizeof(OMX_U8*);
    if (inportBufferFlags & OMX_BUFFERFLAG_EOS) {
        buffer->nFlags = OMX_BUFFERFLAG_EOS;
    }

    if (buffer_orign != buffer) {
        *retain = BUFFER_RETAIN_OVERRIDDEN;
    }

    ret = OMX_ErrorNone;

    return ret;

}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::PrepareConfigBuffer(VideoConfigBuffer *) {
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::PrepareDecodeBuffer(OMX_BUFFERHEADERTYPE *,
                                                         buffer_retain_t *,
                                                         VideoDecodeBuffer *) {
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::BuildHandlerList(void) {
    OMXVideoDecoderBase::BuildHandlerList();
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::GetParamVideoVp9(OMX_PTR) {
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::SetParamVideoVp9(OMX_PTR) {
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::HandleFormatChange(void)
{
    ALOGE("handle format change from %dx%d to %dx%d",
        mDecodedImageWidth,mDecodedImageHeight,mDecodedImageNewWidth,mDecodedImageNewHeight);
    mDecodedImageWidth = mDecodedImageNewWidth;
    mDecodedImageHeight = mDecodedImageNewHeight;
    // Sync port definition as it may change.
    OMX_PARAM_PORTDEFINITIONTYPE paramPortDefinitionInput, paramPortDefinitionOutput;

    memcpy(&paramPortDefinitionInput,
        this->ports[INPORT_INDEX]->GetPortDefinition(),
        sizeof(paramPortDefinitionInput));

    memcpy(&paramPortDefinitionOutput,
        this->ports[OUTPORT_INDEX]->GetPortDefinition(),
        sizeof(paramPortDefinitionOutput));

    unsigned int width = mDecodedImageWidth;
    unsigned int height = mDecodedImageHeight;
    unsigned int stride = mDecodedImageWidth;
    unsigned int sliceHeight = mDecodedImageHeight;

    unsigned int widthCropped = mDecodedImageWidth;
    unsigned int heightCropped = mDecodedImageHeight;
    unsigned int strideCropped = widthCropped;
    unsigned int sliceHeightCropped = heightCropped;

    if (widthCropped == paramPortDefinitionOutput.format.video.nFrameWidth &&
        heightCropped == paramPortDefinitionOutput.format.video.nFrameHeight) {
        if (mWorkingMode == RAWDATA_MODE) {
            LOGW("Change of portsetting is not reported as size is not changed.");
            return OMX_ErrorNone;
        }
    }

    if (mAPMode == METADATA_MODE) {
       paramPortDefinitionOutput.nBufferCountActual = mNativeBufferCount;
       paramPortDefinitionOutput.nBufferCountMin = mNativeBufferCount - 4;
    }
    paramPortDefinitionInput.format.video.nFrameWidth = width;
    paramPortDefinitionInput.format.video.nFrameHeight = height;
    paramPortDefinitionInput.format.video.nStride = stride;
    paramPortDefinitionInput.format.video.nSliceHeight = sliceHeight;

    if (mWorkingMode == RAWDATA_MODE) {
        paramPortDefinitionOutput.format.video.nFrameWidth = widthCropped;
        paramPortDefinitionOutput.format.video.nFrameHeight = heightCropped;
        paramPortDefinitionOutput.format.video.nStride = strideCropped;
        paramPortDefinitionOutput.format.video.nSliceHeight = sliceHeightCropped;
    } else if (mWorkingMode == GRAPHICBUFFER_MODE) {
        // when the width and height ES parse are not larger than allocated graphic buffer in outport,
        // there is no need to reallocate graphic buffer,just report the crop info to omx client
        if (mAPMode == LEGACY_MODE &&
            width <= mGraphicBufferParam.graphicBufferWidth &&
            height <= mGraphicBufferParam.graphicBufferHeight) {
            this->ports[INPORT_INDEX]->SetPortDefinition(&paramPortDefinitionInput, true);
            this->ports[OUTPORT_INDEX]->ReportOutputCrop();
            return OMX_ErrorNone;
        }

        if (mAPMode == METADATA_MODE ||
            width > mGraphicBufferParam.graphicBufferWidth ||
            height > mGraphicBufferParam.graphicBufferHeight) {
            // update the real decoded resolution to outport instead of display resolution
            // for graphic buffer reallocation
            // when the width and height parsed from ES are larger than allocated graphic buffer in outport,
            paramPortDefinitionOutput.format.video.nFrameWidth = width;
            paramPortDefinitionOutput.format.video.nFrameHeight = height;
            paramPortDefinitionOutput.format.video.eColorFormat = GetOutputColorFormat(
                    paramPortDefinitionOutput.format.video.nFrameWidth);
            paramPortDefinitionOutput.format.video.nStride = stride;
            paramPortDefinitionOutput.format.video.nSliceHeight = sliceHeight;
       }
    }

    paramPortDefinitionOutput.bEnabled = (OMX_BOOL)false;
    mOMXBufferHeaderTypePtrNum = 0;
    mMetaDataBuffersNum = 0;
    memset(&mGraphicBufferParam, 0, sizeof(mGraphicBufferParam));
    mDeinitDecoder(mHybridCtx);

    this->ports[INPORT_INDEX]->SetPortDefinition(&paramPortDefinitionInput, true);
    this->ports[OUTPORT_INDEX]->SetPortDefinition(&paramPortDefinitionOutput, true);

    this->ports[OUTPORT_INDEX]->ReportPortSettingsChanged();
    return OMX_ErrorNone;
}


OMX_COLOR_FORMATTYPE OMXVideoDecoderVP9Hybrid::GetOutputColorFormat(int) {
    LOGV("Output color format is HAL_PIXEL_FORMAT_INTEL_YV12.");
    return (OMX_COLOR_FORMATTYPE)HAL_PIXEL_FORMAT_INTEL_YV12;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::GetDecoderOutputCropSpecific(OMX_PTR pStructure) {

    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)pStructure;

    CHECK_TYPE_HEADER(rectParams);

    if (rectParams->nPortIndex != OUTPORT_INDEX) {
        return OMX_ErrorUndefined;
    }

    const OMX_PARAM_PORTDEFINITIONTYPE *paramPortDefinitionInput
                                      = this->ports[INPORT_INDEX]->GetPortDefinition();

    rectParams->nLeft = VPX_DECODE_BORDER;
    rectParams->nTop = VPX_DECODE_BORDER;
    rectParams->nWidth = paramPortDefinitionInput->format.video.nFrameWidth;
    rectParams->nHeight = paramPortDefinitionInput->format.video.nFrameHeight;

    return ret;
}

OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::GetNativeBufferUsageSpecific(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    android::GetAndroidNativeBufferUsageParams *param =
        (android::GetAndroidNativeBufferUsageParams*)pStructure;
    CHECK_TYPE_HEADER(param);

    param->nUsage |= (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN
                     | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_EXTERNAL_DISP);
    return OMX_ErrorNone;

}
OMX_ERRORTYPE OMXVideoDecoderVP9Hybrid::SetNativeBufferModeSpecific(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    android::EnableAndroidNativeBuffersParams *param =
        (android::EnableAndroidNativeBuffersParams*)pStructure;

    CHECK_TYPE_HEADER(param);
    CHECK_PORT_INDEX_RANGE(param);
    CHECK_SET_PARAM_STATE();

    PortVideo *port = NULL;
    port = static_cast<PortVideo *>(this->ports[OUTPORT_INDEX]);
    OMX_PARAM_PORTDEFINITIONTYPE port_def;
    memcpy(&port_def,port->GetPortDefinition(),sizeof(port_def));

    if (!param->enable) {
        mWorkingMode = RAWDATA_MODE;
        LOGI("Raw data mode is used");
        // If it is fallback from native mode the color format has been
        // already set to INTEL format.
        // We need to set back the default color format and Native stuff.
        port_def.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
        port_def.format.video.pNativeRender = NULL;
        port_def.format.video.pNativeWindow = NULL;
        port->SetPortDefinition(&port_def,true);
        return OMX_ErrorNone;
    }

    mWorkingMode = GRAPHICBUFFER_MODE;
    port_def.nBufferCountMin = mNativeBufferCount - 4;
    port_def.nBufferCountActual = mNativeBufferCount;
    port_def.format.video.cMIMEType = (OMX_STRING)VA_VED_RAW_MIME_TYPE;
    // add borders for libvpx decode need.
    port_def.format.video.nFrameWidth += VPX_DECODE_BORDER * 2;
    port_def.format.video.nFrameHeight += VPX_DECODE_BORDER * 2;
    mDecodedImageWidth = port_def.format.video.nFrameWidth;
    mDecodedImageHeight = port_def.format.video.nFrameHeight;
    port_def.format.video.eColorFormat = GetOutputColorFormat(port_def.format.video.nFrameWidth);
    port->SetPortDefinition(&port_def,true);

     return OMX_ErrorNone;
}


bool OMXVideoDecoderVP9Hybrid::IsAllBufferAvailable(void) {
    bool b = ComponentBase::IsAllBufferAvailable();
    if (b == false) {
        return false;
    }

    PortVideo *port = NULL;
    port = static_cast<PortVideo *>(this->ports[OUTPORT_INDEX]);
    const OMX_PARAM_PORTDEFINITIONTYPE* port_def = port->GetPortDefinition();
     // if output port is disabled, retain the input buffer
    if (!port_def->bEnabled) {
        return false;
    }
    return mCheckBufferAvailable(mHybridCtx);
}

DECLARE_OMX_COMPONENT("OMX.Intel.VideoDecoder.VP9.hybrid", "video_decoder.vp9", OMXVideoDecoderVP9Hybrid);