/*
* Copyright (c) 2009-2011 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_TAG "OMXVideoEncoderAVC"
#include "OMXVideoEncoderAVC.h"
#include "IntelMetadataBuffer.h"

static const char *AVC_MIME_TYPE = "video/h264";

struct ProfileMap {
    OMX_VIDEO_AVCPROFILETYPE key;
    VAProfile value;
    const char *name;
};

struct LevelMap {
    OMX_VIDEO_AVCLEVELTYPE key;
    uint32_t value;
    const char *name;
};

static ProfileMap ProfileTable[] = {
    { OMX_VIDEO_AVCProfileBaseline, VAProfileH264Baseline, "AVC Baseline" },
    { OMX_VIDEO_AVCProfileMain, VAProfileH264Main, "AVC Main" },
    { OMX_VIDEO_AVCProfileHigh, VAProfileH264High, "AVC High" },
    { (OMX_VIDEO_AVCPROFILETYPE) 0, (VAProfile) 0, "Not Supported" },
};

static LevelMap LevelTable[] = {
    { OMX_VIDEO_AVCLevel4, 40, "AVC Level4" },
    { OMX_VIDEO_AVCLevel41, 41, "AVC Level41" },
    { OMX_VIDEO_AVCLevel42, 42, "AVC Level42" },
    { OMX_VIDEO_AVCLevel5, 50, "AVC Level5" },
    { OMX_VIDEO_AVCLevel51, 51, "AVC Level51" },
    { (OMX_VIDEO_AVCLEVELTYPE) 0, 0, "Not Supported" },
};

#define FIND_BYKEY(table, x, y)  {\
        for(int ii = 0; ; ii++) { \
            if (table[ii].key == x || table[ii].key == 0) { \
                y = ii; \
                break; \
            } \
        } \
    }\

#define FIND_BYVALUE(table, x, y)  {\
        for(int ii = 0; ; ii++) { \
            if (table[ii].value == x || table[ii].value == 0) { \
                y = ii; \
                break; \
            } \
        } \
    } \

OMXVideoEncoderAVC::OMXVideoEncoderAVC() {
    BuildHandlerList();
    mVideoEncoder = createVideoEncoder(AVC_MIME_TYPE);
    if (!mVideoEncoder) {
        LOGE("OMX_ErrorInsufficientResources");
        return;
    }

    mAVCParams = new VideoParamsAVC();
    if (!mAVCParams) {
        LOGE("OMX_ErrorInsufficientResources");
        return;
    }

    //Query supported Profile/Level
    mPLTableCount = 0;

    VAProfile profiles[MAX_H264_PROFILE] = {VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline};

    VideoParamsProfileLevel pl;
    for (int i=0; i < MAX_H264_PROFILE; i++) {
        pl.profile = profiles[i];
        pl.level = 0;
        pl.isSupported = false;

        mVideoEncoder->getParameters(&pl);
        if (pl.isSupported) {
            uint32_t profile_index;
            uint32_t level_index;

            FIND_BYVALUE(ProfileTable, pl.profile,  profile_index);
            if (ProfileTable[profile_index].key == (OMX_VIDEO_AVCPROFILETYPE) 0)
                continue;

            FIND_BYVALUE(LevelTable, pl.level,  level_index);
            if (LevelTable[level_index].key == (OMX_VIDEO_AVCLEVELTYPE) 0)
                continue;

            mPLTable[mPLTableCount].profile = ProfileTable[profile_index].key;
            mPLTable[mPLTableCount].level = LevelTable[level_index].key;
            mPLTableCount ++;
            LOGV("Support Profile:%s, Level:%s\n", ProfileTable[profile_index].name, LevelTable[level_index].name);
        }
    }

    mEmptyEOSBuf = OMX_FALSE;
}

OMXVideoEncoderAVC::~OMXVideoEncoderAVC() {
    if(mAVCParams) {
        delete mAVCParams;
        mAVCParams = NULL;
    }
}

OMX_ERRORTYPE OMXVideoEncoderAVC::InitOutputPortFormatSpecific(OMX_PARAM_PORTDEFINITIONTYPE *paramPortDefinitionOutput) {
    // OMX_VIDEO_PARAM_AVCTYPE
    memset(&mParamAvc, 0, sizeof(mParamAvc));
    SetTypeHeader(&mParamAvc, sizeof(mParamAvc));
    mParamAvc.nPortIndex = OUTPORT_INDEX;

    if (mPLTableCount > 0) {
        mParamAvc.eProfile = (OMX_VIDEO_AVCPROFILETYPE) mPLTable[0].profile;
        mParamAvc.eLevel = (OMX_VIDEO_AVCLEVELTYPE)mPLTable[0].level;
    } else {
        LOGE("No supported profile/level\n");
        return OMX_ErrorUndefined;
    }
    mParamAvc.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
    mParamAvc.nPFrames = 29;
    mParamAvc.nBFrames = 0;

    // OMX_NALSTREAMFORMATTYPE
    memset(&mNalStreamFormat, 0, sizeof(mNalStreamFormat));
    SetTypeHeader(&mNalStreamFormat, sizeof(mNalStreamFormat));
    mNalStreamFormat.nPortIndex = OUTPORT_INDEX;
    // TODO: check if this is desired Nalu Format
    //mNalStreamFormat.eNaluFormat = OMX_NaluFormatLengthPrefixedSeparateFirstHeader;
    // OMX_VIDEO_CONFIG_AVCINTRAPERIOD
    memset(&mConfigAvcIntraPeriod, 0, sizeof(mConfigAvcIntraPeriod));
    SetTypeHeader(&mConfigAvcIntraPeriod, sizeof(mConfigAvcIntraPeriod));
    mConfigAvcIntraPeriod.nPortIndex = OUTPORT_INDEX;
    // TODO: need to be populated from Video Encoder
    mConfigAvcIntraPeriod.nIDRPeriod = 1;
    mConfigAvcIntraPeriod.nPFrames = 29;

    // OMX_VIDEO_CONFIG_NALSIZE
    memset(&mConfigNalSize, 0, sizeof(mConfigNalSize));
    SetTypeHeader(&mConfigNalSize, sizeof(mConfigNalSize));
    mConfigNalSize.nPortIndex = OUTPORT_INDEX;
    mConfigNalSize.nNaluBytes = 0;

    // OMX_VIDEO_PARAM_INTEL_AVCVUI
    memset(&mParamIntelAvcVui, 0, sizeof(mParamIntelAvcVui));
    SetTypeHeader(&mParamIntelAvcVui, sizeof(mParamIntelAvcVui));
    mParamIntelAvcVui.nPortIndex = OUTPORT_INDEX;
    mParamIntelAvcVui.bVuiGeneration = OMX_FALSE;

    // OMX_VIDEO_CONFIG_INTEL_SLICE_NUMBERS
    memset(&mConfigIntelSliceNumbers, 0, sizeof(mConfigIntelSliceNumbers));
    SetTypeHeader(&mConfigIntelSliceNumbers, sizeof(mConfigIntelSliceNumbers));
    mConfigIntelSliceNumbers.nPortIndex = OUTPORT_INDEX;
    mConfigIntelSliceNumbers.nISliceNumber = 1;
    mConfigIntelSliceNumbers.nPSliceNumber = 1;

    // Override OMX_PARAM_PORTDEFINITIONTYPE
    paramPortDefinitionOutput->nBufferCountActual = OUTPORT_ACTUAL_BUFFER_COUNT;
    paramPortDefinitionOutput->nBufferCountMin = OUTPORT_MIN_BUFFER_COUNT;
    paramPortDefinitionOutput->nBufferSize = OUTPORT_BUFFER_SIZE;
    paramPortDefinitionOutput->format.video.cMIMEType = (OMX_STRING)AVC_MIME_TYPE;
    paramPortDefinitionOutput->format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;

    // Override OMX_VIDEO_PARAM_PROFILELEVELTYPE
    // TODO: check if profile/level supported is correct
    mParamProfileLevel.eProfile = mParamAvc.eProfile;
    mParamProfileLevel.eLevel = mParamAvc.eLevel;

    // Override OMX_VIDEO_PARAM_BITRATETYPE
    mParamBitrate.nTargetBitrate = 192000;

    // Override OMX_VIDEO_CONFIG_INTEL_BITRATETYPE
    mConfigIntelBitrate.nInitialQP = 0;  // Initial QP for I frames

    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetVideoEncoderParam(void) {

    Encode_Status ret = ENCODE_SUCCESS;
    LOGV("OMXVideoEncoderAVC::SetVideoEncoderParam");

    if (!mEncoderParams) {
        LOGE("NULL pointer: mEncoderParams");
        return OMX_ErrorBadParameter;
    }

    mVideoEncoder->getParameters(mEncoderParams);
    uint32_t index;
    FIND_BYKEY(ProfileTable, mParamAvc.eProfile, index);
    if (ProfileTable[index].value != 0)
        mEncoderParams->profile = ProfileTable[index].value;

    if (mParamAvc.nAllowedPictureTypes & OMX_VIDEO_PictureTypeB)
        mEncoderParams->intraPeriod = mParamAvc.nPFrames + mParamAvc.nBFrames;
    else
        mEncoderParams->intraPeriod = mParamAvc.nPFrames + 1;

    // 0 - all luma and chroma block edges of the slice are filtered
    // 1 - deblocking is disabled for all block edges of the slice
    // 2 - all luma and chroma block edges of the slice are filtered
    // with exception of the block edges that coincide with slice boundaries
    mEncoderParams->disableDeblocking = 0;

    OMXVideoEncoderBase::SetVideoEncoderParam();

    mVideoEncoder->getParameters(mAVCParams);
    if(mParamIntelAvcVui.bVuiGeneration == OMX_TRUE) {
        mAVCParams->VUIFlag = 1;
    }
    // For resolution below VGA, single core can hit the performance target and provide VQ gain
    if (mEncoderParams->resolution.width <= 640 && mEncoderParams->resolution.height <= 480) {
        mConfigIntelSliceNumbers.nISliceNumber = 1;
        mConfigIntelSliceNumbers.nPSliceNumber = 1;
    }
    mAVCParams->sliceNum.iSliceNum = mConfigIntelSliceNumbers.nISliceNumber;
    mAVCParams->sliceNum.pSliceNum = mConfigIntelSliceNumbers.nPSliceNumber;
    mAVCParams->maxSliceSize = mConfigNalSize.nNaluBytes * 8;

    if (mEncoderParams->intraPeriod == 0) {
        mAVCParams->idrInterval = 0;
        mAVCParams->ipPeriod = 1;
    } else {
        mAVCParams->idrInterval = mConfigAvcIntraPeriod.nIDRPeriod; //idrinterval
        if (mParamAvc.nAllowedPictureTypes & OMX_VIDEO_PictureTypeB)
            mAVCParams->ipPeriod = mEncoderParams->intraPeriod / mParamAvc.nPFrames;
        else
            mAVCParams->ipPeriod = 1;
    }

    ret = mVideoEncoder ->setParameters(mAVCParams);
    CHECK_ENCODE_STATUS("setParameters");

    LOGV("VUIFlag = %d\n", mAVCParams->VUIFlag);
    LOGV("sliceNum.iSliceNum = %d\n", mAVCParams->sliceNum.iSliceNum);
    LOGV("sliceNum.pSliceNum = %d\n", mAVCParams->sliceNum.pSliceNum);
    LOGV("maxSliceSize = %d\n ", mAVCParams->maxSliceSize);
    LOGV("intraPeriod = %d\n ", mEncoderParams->intraPeriod);
    LOGV("idrInterval = %d\n ", mAVCParams->idrInterval);
    LOGV("ipPeriod = %d\n ", mAVCParams->ipPeriod);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessorInit(void) {
    mCSDOutputted = OMX_FALSE;
    mInputPictureCount = 0;
    mFrameEncodedCount = 0;
    return  OMXVideoEncoderBase::ProcessorInit();
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessorDeinit(void) {
    return OMXVideoEncoderBase::ProcessorDeinit();
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessorStop(void) {
    OMX_BUFFERHEADERTYPE *omxbuf = NULL;

    while(!mBFrameList.empty()) {
        omxbuf = * mBFrameList.begin();
        this->ports[INPORT_INDEX]->ReturnThisBuffer(omxbuf);
        mBFrameList.erase(mBFrameList.begin());
    }

    mEmptyEOSBuf = OMX_FALSE;
    return OMXVideoEncoderBase::ProcessorStop();
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessorPreEmptyBuffer(OMX_BUFFERHEADERTYPE* buffer) {
    OMX_U32 EncodeInfo = 0;
    OMX_U32 EncodeFrameType = 0;

    uint32_t poc = 0;
    uint32_t idrPeriod = mAVCParams->idrInterval;
    uint32_t IntraPeriod = mEncoderParams->intraPeriod;
    uint32_t IpPeriod = mAVCParams->ipPeriod;
    bool BFrameEnabled = IpPeriod > 1;
    uint32_t GOP = 0;

    if (idrPeriod == 0 || IntraPeriod == 0) {
        GOP = 0xFFFFFFFF;
        if (IntraPeriod == 0)
            IntraPeriod = 0xFFFFFFFF;
    } else if (BFrameEnabled)
        GOP = IntraPeriod*idrPeriod + 1;
    else
        GOP = IntraPeriod*idrPeriod;

    LOGV("ProcessorPreEmptyBuffer idrPeriod=%d, IntraPeriod=%d, IpPeriod=%d, BFrameEnabled=%d\n", idrPeriod, IntraPeriod, IpPeriod, BFrameEnabled);

    //decide frame type, refer Merrifield Video Encoder Driver HLD Chapter 3.17
    poc = mInputPictureCount % GOP;

    if (poc == 0 /*IDR*/) {
            EncodeFrameType = F_IDR;
    } else if (IntraPeriod == 1) {
            EncodeFrameType = F_I;
    }else if ((poc > IpPeriod) && ((poc - IpPeriod) % IntraPeriod == 0))/*I*/{
            EncodeFrameType = F_I;
            if (BFrameEnabled)
                SET_CO(EncodeInfo, CACHE_POP);
    } else if ((poc % IpPeriod == 0) /*P*/ || (buffer->nFlags & OMX_BUFFERFLAG_EOS)/*EOS,always P*/) {
            EncodeFrameType = F_P;
            if (BFrameEnabled)
                SET_CO(EncodeInfo, CACHE_POP);
    } else { /*B*/
            EncodeFrameType = F_B;
            SET_CO(EncodeInfo, CACHE_PUSH);
    }

    SET_FT(EncodeInfo, EncodeFrameType);
    SET_FC(EncodeInfo, mInputPictureCount);

    buffer->pPlatformPrivate = (OMX_PTR) EncodeInfo;

    LOGV("ProcessorPreEmptyBuffer Frame %d, Type %s, EncodeInfo %x\n", mInputPictureCount, FrameTypeStr[EncodeFrameType], EncodeInfo);

    mInputPictureCount ++;
    return OMX_ErrorNone;
}

OMX_BOOL OMXVideoEncoderAVC::ProcessCacheOperation(OMX_BUFFERHEADERTYPE **buffers) {

    OMX_BOOL Cached = OMX_FALSE;

    //get frame encode info
    Encode_Info eInfo;
    uint32_t encodeInfo 	= (uint32_t) buffers[INPORT_INDEX]->pPlatformPrivate;
    eInfo.FrameType 		   = GET_FT(encodeInfo);

    eInfo.CacheOperation	= GET_CO(encodeInfo);
    eInfo.NotStopFrame		= encodeInfo & ENC_NSTOP;
    eInfo.FrameCount		 = GET_FC(encodeInfo);

    LOGV("ProcessCacheOperation Frame %d, type:%s, CacheOps:%s, NoSTOP=%d, EOS=%d\n",
            eInfo.FrameCount, FrameTypeStr[eInfo.FrameType], CacheOperationStr[eInfo.CacheOperation],
            eInfo.NotStopFrame, buffers[INPORT_INDEX]->nFlags & OMX_BUFFERFLAG_EOS);

    OMX_BOOL emptyEOSBuf = OMX_FALSE;
    if (buffers[INPORT_INDEX]->nFilledLen == 0 && buffers[INPORT_INDEX]->nFlags & OMX_BUFFERFLAG_EOS) {
        //meet an empty EOS buffer
        emptyEOSBuf = OMX_TRUE;
        LOGV("ProcessCacheOperation: This frame is Empty EOS buffer\n");
    }

    if (eInfo.CacheOperation == CACHE_NONE) {
        //nothing to do
    } else if (eInfo.CacheOperation == CACHE_PUSH) {
        mBFrameList.push_front(buffers[INPORT_INDEX]);
        Cached = OMX_TRUE;
        LOGV("ProcessCacheOperation: This B frame is cached\n");

    } else if (eInfo.CacheOperation == CACHE_POP) {
        eInfo.NotStopFrame = true;  //it is also a nstop frame

        OMX_BUFFERHEADERTYPE *omxbuf = NULL;
        uint32_t i = 0;
        uint32_t bframecount = mBFrameList.size();

        LOGV("BFrameList size = %d\n", bframecount);

        while(!mBFrameList.empty()) {
            /*TODO: need to handle null data buffer with EOS
                     !NULL EOS case:   B1 B2 P(EOS)     ->    P B1 B2(EOS)
                     NULL EOS case: B1 B2 NULL(EOS)    ->    B2 B1 NULL(EOS)
            */

            if (emptyEOSBuf) {
                omxbuf = *mBFrameList.begin();
                ports[INPORT_INDEX]->PushThisBuffer(omxbuf);
                mBFrameList.erase(mBFrameList.begin()); //clear it from internal queue

            } else {
                omxbuf = *mBFrameList.begin();

                if (buffers[INPORT_INDEX]->nFlags & OMX_BUFFERFLAG_EOS && i == 0 )  {
                    //this is final encode frame, mark it is new EOS and remove original EOS
                    omxbuf->nFlags |= OMX_BUFFERFLAG_EOS;
				    buffers[INPORT_INDEX]->nFlags &= ~OMX_BUFFERFLAG_EOS;
                } else {
                    //all these frames except final B frame in miniGOP can't be stopped at any time
                    //to avoid not breaking miniGOP integrity
                    if (i > 0) {
                        uint32_t tmp = (uint32_t) omxbuf->pPlatformPrivate;
                        tmp |= ENC_NSTOP;
                        omxbuf->pPlatformPrivate = (OMX_PTR) tmp;
                    }
                }
                ports[INPORT_INDEX]->RetainThisBuffer(omxbuf, false); //push bufferq head

                mBFrameList.erase(mBFrameList.begin()); //clear it from internal queue
            }

            i++;
        }

        if (emptyEOSBuf)
            ports[INPORT_INDEX]->PushThisBuffer(buffers[INPORT_INDEX]); //put it at the tail

    } else if (eInfo.CacheOperation == CACHE_RESET) {
//        mBFrameList.clear();
    }

    eInfo.CacheOperation = CACHE_NONE;

    /* restore all states into input OMX buffer
    */
    if (eInfo.NotStopFrame)
        encodeInfo |= ENC_NSTOP;
    else
        encodeInfo &= ~ENC_NSTOP;

    SET_CO(encodeInfo, eInfo.CacheOperation);
    buffers[INPORT_INDEX]->pPlatformPrivate = (OMX_PTR) encodeInfo;

    LOGV("ProcessCacheOperation Completed return %d\n", Cached);
    return Cached;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessDataRetrieve(
    OMX_BUFFERHEADERTYPE **buffers, OMX_BOOL *outBufReturned) {

    OMX_NALUFORMATSTYPE NaluFormat = mNalStreamFormat.eNaluFormat;

    // NaluFormat not set, setting default
    if (NaluFormat == 0) {
        NaluFormat = (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader;
        mNalStreamFormat.eNaluFormat = NaluFormat;
    }

    VideoEncOutputBuffer outBuf;
    outBuf.data = buffers[OUTPORT_INDEX]->pBuffer;
    outBuf.bufferSize = buffers[OUTPORT_INDEX]->nAllocLen;
    outBuf.dataSize = 0;
    outBuf.remainingSize = 0;
    outBuf.flag = 0;
    outBuf.timeStamp = 0;
    outBuf.offset = 0;

    switch (NaluFormat) {
        case OMX_NaluFormatStartCodes:
            outBuf.format = OUTPUT_EVERYTHING;
            break;

        case OMX_NaluFormatOneNaluPerBuffer:
            outBuf.format = OUTPUT_ONE_NAL;
            break;

        default:
            if (NaluFormat == (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader||
                NaluFormat == (OMX_NALUFORMATSTYPE)OMX_NaluFormatLengthPrefixedSeparateFirstHeader){
                if(!mCSDOutputted) {
                    LOGV("Output codec data for first frame\n");
                    outBuf.format = OUTPUT_CODEC_DATA;
                } else {
                    if (NaluFormat == (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader)
                        outBuf.format = OUTPUT_EVERYTHING;
                    else
                        outBuf.format = OUTPUT_NALULENGTHS_PREFIXED;
                }
                break;
            } else {
                return OMX_ErrorUndefined;
            }
    }

    //start getOutput
    Encode_Status ret = mVideoEncoder->getOutput(&outBuf, FUNC_NONBLOCK);

    if (ret < ENCODE_SUCCESS) {
        LOGE("libMIX getOutput Failed. ret = 0x%08x\n", ret);
        outBuf.dataSize = 0;
        outBuf.flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
        if (ret == ENCODE_NO_REQUEST_DATA) {
            if (mEmptyEOSBuf) {
                //make sure no data encoding in HW, then emit one empty out buffer with EOS
                outBuf.flag |= ENCODE_BUFFERFLAG_ENDOFSTREAM;
                LOGV("no more data encoding, will signal empty EOS output buf\n");
            } else {
                //if not meet Empty EOS buffer, shouldn't get this error
                LOGE("sever error, should not happend here\n");
                //return OMX_ErrorUndefined; //not return error here to avoid omxcodec crash
            }
        }

    } else if (ret == ENCODE_BUFFER_TOO_SMALL) {
        LOGE("output buffer too small\n");
        // Return code could not be ENCODE_BUFFER_TOO_SMALL, or we will have dead lock issue
        return OMX_ErrorUndefined;
    } else if (ret == ENCODE_DATA_NOT_READY) {
        LOGV("Call libMIX getOutput againe due to 'data not ready'\n");
        ret = mVideoEncoder->getOutput(&outBuf);
    }

    LOGV("libMIX getOutput data size= %d, flag=0x%08x", outBuf.dataSize, outBuf.flag);
    OMX_U32 outfilledlen = outBuf.dataSize;
    OMX_U32 outoffset = outBuf.offset;
    OMX_S64 outtimestamp = outBuf.timeStamp;
    OMX_U32 outflags = 0;

    //if codecconfig
    if (outBuf.flag & ENCODE_BUFFERFLAG_CODECCONFIG)
        outflags |= OMX_BUFFERFLAG_CODECCONFIG;

    //if syncframe
    if (outBuf.flag & ENCODE_BUFFERFLAG_SYNCFRAME)
        outflags |= OMX_BUFFERFLAG_SYNCFRAME;

    //if eos
    if (outBuf.flag & ENCODE_BUFFERFLAG_ENDOFSTREAM)
        outflags |= OMX_BUFFERFLAG_EOS;

    //if full encoded data retrieved
    if(outBuf.flag & ENCODE_BUFFERFLAG_ENDOFFRAME) {
        LOGV("got a complete libmix Frame\n");
        outflags |= OMX_BUFFERFLAG_ENDOFFRAME;

        if ((NaluFormat == (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader
             || NaluFormat == (OMX_NALUFORMATSTYPE)OMX_NaluFormatLengthPrefixedSeparateFirstHeader )
             && !mCSDOutputted && outfilledlen > 0) {
            mCSDOutputted = OMX_TRUE;

        } else {
            ports[INPORT_INDEX]->ReturnOneRetainedBuffer();  //return one retained frame from head
            mFrameOutputCount  ++;
        }
    }

    if (outfilledlen == 0) {
        if (mEmptyEOSBuf) {
            //emit empty EOS out buf since meet empty EOS input buf
            buffers[OUTPORT_INDEX]->nFilledLen = 0;
            buffers[OUTPORT_INDEX]->nTimeStamp = 0;
            buffers[OUTPORT_INDEX]->nFlags = outflags;
            *outBufReturned = OMX_TRUE;
            LOGV("emit one empty EOS OMX output buf = %p:%d, flag = 0x%08x, ts=%lld", buffers[OUTPORT_INDEX]->pBuffer, outfilledlen, outflags, outtimestamp);
        } else
            //not emit out buf since something wrong
            *outBufReturned = OMX_FALSE;

    } else {
        buffers[OUTPORT_INDEX]->nOffset = outoffset;
        buffers[OUTPORT_INDEX]->nFilledLen = outfilledlen;
        buffers[OUTPORT_INDEX]->nTimeStamp = outtimestamp;
        buffers[OUTPORT_INDEX]->nFlags = outflags;
        if (outBuf.flag & ENCODE_BUFFERFLAG_NSTOPFRAME)
            buffers[OUTPORT_INDEX]->pPlatformPrivate = (OMX_PTR) 0x00000001;  //indicate it is nstop frame
        *outBufReturned = OMX_TRUE;
        LOGV("emit one OMX output buf = %p:%d, flag = 0x%08x, ts=%lld", buffers[OUTPORT_INDEX]->pBuffer, outfilledlen, outflags, outtimestamp);

    }

    LOGV("ProcessDataRetrieve OK, mFrameEncodedCount=%d , mFrameOutputCount=%d\n", mFrameEncodedCount, mFrameOutputCount);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::ProcessorProcess(
    OMX_BUFFERHEADERTYPE **buffers,
    buffer_retain_t *retains,
    OMX_U32) {

    OMX_ERRORTYPE oret = OMX_ErrorNone;
    Encode_Status ret = ENCODE_SUCCESS;

    bool FrameEncoded = false;

    if (buffers[INPORT_INDEX]) {
        LOGV("input buffer has new frame\n");

        //get frame encode info
        Encode_Info eInfo;
        uint32_t encodeInfo 	= (uint32_t) buffers[INPORT_INDEX]->pPlatformPrivate;
        eInfo.FrameType 		   = GET_FT(encodeInfo);
        eInfo.CacheOperation	= GET_CO(encodeInfo);
        eInfo.NotStopFrame		= encodeInfo & ENC_NSTOP;
        eInfo.FrameCount		 = GET_FC(encodeInfo);

        //handle frame cache operation
        if (ProcessCacheOperation(buffers)) {
            //frame is cached, nothing should be done in this case, just store status and return
            retains[INPORT_INDEX] = BUFFER_RETAIN_CACHE;
            retains[OUTPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
            return OMX_ErrorNone;
        }

        //try encode if frame is not cached
        VideoEncRawBuffer inBuf;

        inBuf.data = buffers[INPORT_INDEX]->pBuffer + buffers[INPORT_INDEX]->nOffset;
        inBuf.size = buffers[INPORT_INDEX]->nFilledLen;
        inBuf.flag = 0;
        inBuf.timeStamp = buffers[INPORT_INDEX]->nTimeStamp;

        if (inBuf.size == 0 && buffers[INPORT_INDEX]->nFlags & OMX_BUFFERFLAG_EOS) {
            //meet an empty EOS buffer, retain it directly and return from here
            retains[INPORT_INDEX] = BUFFER_RETAIN_ACCUMULATE;
            retains[OUTPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
            mEmptyEOSBuf = OMX_TRUE;
            return OMX_ErrorNone;
        }

        if (buffers[INPORT_INDEX]->nFlags & OMX_BUFFERFLAG_EOS)
            inBuf.flag |= ENCODE_BUFFERFLAG_ENDOFSTREAM;
        if (eInfo.NotStopFrame)
            inBuf.flag |= ENCODE_BUFFERFLAG_NSTOPFRAME;
        inBuf.type = (FrameType) eInfo.FrameType;

        LOGV("start libmix encoding\n");
        // encode and setConfig need to be thread safe
        pthread_mutex_lock(&mSerializationLock);
        ret = mVideoEncoder->encode(&inBuf, FUNC_NONBLOCK);
        pthread_mutex_unlock(&mSerializationLock);
        LOGV("end libmix encoding\n");

		retains[INPORT_INDEX] = BUFFER_RETAIN_NOT_RETAIN;
        if (ret == ENCODE_DEVICE_BUSY) {
			//encoder is busy, put buf back and come again
            LOGV("encoder is busy, push buffer back to get again\n");
            retains[INPORT_INDEX] = BUFFER_RETAIN_GETAGAIN;
        } else {
            //if error, this buf will be returned
            CHECK_ENCODE_STATUS("encode");

            LOGV("put buffer to encoder and retain this buffer\n");
            mFrameEncodedCount ++;
            FrameEncoded = true;
            retains[INPORT_INDEX] = BUFFER_RETAIN_ACCUMULATE;
        }

    } else {
        //no new coming frames, but maybe still have frames not outputted
        LOGV("input buffer is null\n");
    }

    retains[OUTPORT_INDEX] = BUFFER_RETAIN_GETAGAIN; //set to default value
    //just call getoutput if no frame encoded in this cycle to avoid retained buffer queue wrong state
    if (!FrameEncoded) {
        OMX_BOOL OutBufReturned = OMX_FALSE;
        oret = ProcessDataRetrieve(buffers, &OutBufReturned);
        if (OutBufReturned)
            retains[OUTPORT_INDEX] = BUFFER_RETAIN_NOT_RETAIN;
    }

    LOGV("ProcessorProcess ret=%x", oret);
    return oret;

}

OMX_ERRORTYPE OMXVideoEncoderAVC::BuildHandlerList(void) {
    OMXVideoEncoderBase::BuildHandlerList();
    AddHandler(OMX_IndexParamVideoAvc, GetParamVideoAvc, SetParamVideoAvc);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamNalStreamFormat, GetParamNalStreamFormat, SetParamNalStreamFormat);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamNalStreamFormatSupported, GetParamNalStreamFormatSupported, SetParamNalStreamFormatSupported);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamNalStreamFormatSelect, GetParamNalStreamFormatSelect, SetParamNalStreamFormatSelect);
    AddHandler(OMX_IndexConfigVideoAVCIntraPeriod, GetConfigVideoAVCIntraPeriod, SetConfigVideoAVCIntraPeriod);
    AddHandler(OMX_IndexConfigVideoNalSize, GetConfigVideoNalSize, SetConfigVideoNalSize);
    AddHandler((OMX_INDEXTYPE)OMX_IndexConfigIntelSliceNumbers, GetConfigIntelSliceNumbers, SetConfigIntelSliceNumbers);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamIntelAVCVUI, GetParamIntelAVCVUI, SetParamIntelAVCVUI);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamVideoBytestream, GetParamVideoBytestream, SetParamVideoBytestream);
    AddHandler((OMX_INDEXTYPE)OMX_IndexParamVideoProfileLevelQuerySupported, GetParamVideoProfileLevelQuerySupported, SetParamVideoProfileLevelQuerySupported);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamVideoProfileLevelQuerySupported(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_PROFILELEVELTYPE *p = (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    CHECK_ENUMERATION_RANGE(p->nProfileIndex,mPLTableCount);

    p->eProfile = mPLTable[p->nProfileIndex].profile;
    p->eLevel = mPLTable[p->nProfileIndex].level;

    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamVideoProfileLevelQuerySupported(OMX_PTR) {
    LOGW("SetParamVideoAVCProfileLevel is not supported.");
    return OMX_ErrorUnsupportedSetting;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamVideoAvc(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_AVCTYPE *p = (OMX_VIDEO_PARAM_AVCTYPE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    mVideoEncoder->getParameters(mAVCParams);
    if(mParamAvc.eProfile == OMX_VIDEO_AVCProfileHigh)
    {
        mAVCParams->bEntropyCodingCABAC = 1;
        mAVCParams->bDirect8x8Inference = 1;
    }
    mParamAvc.bEntropyCodingCABAC = (OMX_BOOL)mAVCParams->bEntropyCodingCABAC;
    mParamAvc.bWeightedPPrediction = (OMX_BOOL)mAVCParams->bWeightedPPrediction;
    mParamAvc.nRefIdx10ActiveMinus1 = mAVCParams->refIdx10ActiveMinus1;
    mParamAvc.nRefIdx11ActiveMinus1 = mAVCParams->refIdx11ActiveMinus1;
    mParamAvc.nWeightedBipredicitonMode = mAVCParams->weightedBipredicitonMode;
    mParamAvc.bDirect8x8Inference = (OMX_BOOL)mAVCParams->bDirect8x8Inference;
    mParamAvc.bDirectSpatialTemporal = (OMX_BOOL)mAVCParams->bDirectSpatialTemporal;
    mParamAvc.nCabacInitIdc = mAVCParams->cabacInitIdc;
    mParamAvc.bFrameMBsOnly = (OMX_BOOL)mAVCParams->bFrameMBsOnly;
    mParamAvc.bconstIpred = (OMX_BOOL)mAVCParams->bConstIpred;
    memcpy(p, &mParamAvc, sizeof(*p));
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamVideoAvc(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_AVCTYPE *p = (OMX_VIDEO_PARAM_AVCTYPE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    CHECK_SET_PARAM_STATE();

    //Check if parameters are valid

    if(p->bEnableASO == OMX_TRUE)
        return OMX_ErrorUnsupportedSetting;

    if(p->bEnableFMO == OMX_TRUE)
        return OMX_ErrorUnsupportedSetting;

    if(p->bEnableUEP == OMX_TRUE)
        return OMX_ErrorUnsupportedSetting;

    if(p->bEnableRS == OMX_TRUE)
        return OMX_ErrorUnsupportedSetting;

    if (p->eProfile == OMX_VIDEO_AVCProfileBaseline &&
            (p->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) )
        return OMX_ErrorBadParameter;

    if (p->nAllowedPictureTypes & OMX_VIDEO_PictureTypeP && (p->nPFrames == 0))
        return OMX_ErrorBadParameter;

    if (p->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB ) {
        if (p->nBFrames == 0)
            return OMX_ErrorBadParameter;

        //IpPeriod must be integer
        uint32_t IntraPeriod = mParamAvc.nPFrames + mParamAvc.nBFrames ;
        if (IntraPeriod % mParamAvc.nPFrames != 0)
            return OMX_ErrorBadParameter;

        //IntraPeriod must be multipe of IpPeriod.
        uint32_t IpPeriod = IntraPeriod /mParamAvc.nPFrames;
        if (IntraPeriod % IpPeriod != 0)
            return OMX_ErrorBadParameter;
    }

    // TODO: do we need to check if port is enabled?
    // TODO: see SetPortAvcParam implementation - Can we make simple copy????
    memcpy(&mParamAvc, p, sizeof(mParamAvc));
    mVideoEncoder->getParameters(mAVCParams);
    mAVCParams->bEntropyCodingCABAC = mParamAvc.bEntropyCodingCABAC;
    mAVCParams->bDirect8x8Inference = mParamAvc.bDirect8x8Inference;
    if(mParamAvc.eProfile == OMX_VIDEO_AVCProfileBaseline){
        mAVCParams->bEntropyCodingCABAC = 0;
        mAVCParams->bDirect8x8Inference = 0;
    }
    mVideoEncoder->setParameters(mAVCParams);


    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamNalStreamFormat(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_NALSTREAMFORMATTYPE *p = (OMX_NALSTREAMFORMATTYPE *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    // TODO: check if this is desired format
    p->eNaluFormat = mNalStreamFormat.eNaluFormat; //OMX_NaluFormatStartCodes;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamNalStreamFormat(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_NALSTREAMFORMATTYPE *p = (OMX_NALSTREAMFORMATTYPE *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    LOGV("p->eNaluFormat =%d\n",p->eNaluFormat);
    if(p->eNaluFormat != OMX_NaluFormatStartCodes &&
            p->eNaluFormat != (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader &&
            p->eNaluFormat != OMX_NaluFormatOneNaluPerBuffer &&
            p->eNaluFormat != (OMX_NALUFORMATSTYPE)OMX_NaluFormatLengthPrefixedSeparateFirstHeader) {
        LOGE("Format not support\n");
        return OMX_ErrorUnsupportedSetting;
    }
    mNalStreamFormat.eNaluFormat = p->eNaluFormat;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamNalStreamFormatSupported(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_NALSTREAMFORMATTYPE *p = (OMX_NALSTREAMFORMATTYPE *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    p->eNaluFormat = (OMX_NALUFORMATSTYPE)
                     (OMX_NaluFormatStartCodes |
                      OMX_NaluFormatStartCodesSeparateFirstHeader |
                      OMX_NaluFormatOneNaluPerBuffer|
                      OMX_NaluFormatLengthPrefixedSeparateFirstHeader);

    // TODO: check if this is desired format
    // OMX_NaluFormatFourByteInterleaveLength |
    // OMX_NaluFormatZeroByteInterleaveLength);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamNalStreamFormatSupported(OMX_PTR) {
    LOGW("SetParamNalStreamFormatSupported is not supported.");
    return OMX_ErrorUnsupportedSetting;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamNalStreamFormatSelect(OMX_PTR) {
    LOGW("GetParamNalStreamFormatSelect is not supported.");
    return OMX_ErrorUnsupportedSetting;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamNalStreamFormatSelect(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_NALSTREAMFORMATTYPE *p = (OMX_NALSTREAMFORMATTYPE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // return OMX_ErrorIncorrectStateOperation if not in Loaded state
    CHECK_SET_PARAM_STATE();

    if (p->eNaluFormat != OMX_NaluFormatStartCodes &&
            p->eNaluFormat != (OMX_NALUFORMATSTYPE)OMX_NaluFormatStartCodesSeparateFirstHeader &&
            p->eNaluFormat != OMX_NaluFormatOneNaluPerBuffer&&
            p->eNaluFormat != (OMX_NALUFORMATSTYPE)OMX_NaluFormatLengthPrefixedSeparateFirstHeader) {
        //p->eNaluFormat != OMX_NaluFormatFourByteInterleaveLength &&
        //p->eNaluFormat != OMX_NaluFormatZeroByteInterleaveLength) {
        // TODO: check if this is desried
        return OMX_ErrorBadParameter;
    }

    mNalStreamFormat = *p;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetConfigVideoAVCIntraPeriod(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_CONFIG_AVCINTRAPERIOD *p = (OMX_VIDEO_CONFIG_AVCINTRAPERIOD *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    // TODO: populate mConfigAvcIntraPeriod from VideoEncoder
    // return OMX_ErrorNotReady if VideoEncoder is not created.
    memcpy(p, &mConfigAvcIntraPeriod, sizeof(*p));
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetConfigVideoAVCIntraPeriod(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    Encode_Status retStatus = ENCODE_SUCCESS;
    OMX_VIDEO_CONFIG_AVCINTRAPERIOD *p = (OMX_VIDEO_CONFIG_AVCINTRAPERIOD *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // return OMX_ErrorNone if not in Executing state
    // TODO:  return OMX_ErrorIncorrectStateOperation?
    CHECK_SET_CONFIG_STATE();

    //check if parameters are valid
    if ( ( (mParamAvc.nAllowedPictureTypes & OMX_VIDEO_PictureTypeP) || 
           (mParamAvc.nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) ) && 
         p->nPFrames == 0 )
        return OMX_ErrorBadParameter;

    // TODO: apply AVC Intra Period configuration in Executing state
    VideoConfigAVCIntraPeriod avcIntraPreriod;

    if (mParamAvc.nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) {
        avcIntraPreriod.intraPeriod = p->nPFrames;
        if (p->nPFrames % mParamAvc.nBFrames != 0)
            return OMX_ErrorBadParameter;
        avcIntraPreriod.ipPeriod = p->nPFrames / mParamAvc.nBFrames;

        if (avcIntraPreriod.intraPeriod % avcIntraPreriod.ipPeriod != 0)
            return OMX_ErrorBadParameter;

        avcIntraPreriod.idrInterval = p->nIDRPeriod;
    } else {
        avcIntraPreriod.intraPeriod = p->nPFrames + 1;
        avcIntraPreriod.ipPeriod = 1;
        if (avcIntraPreriod.intraPeriod == 0)
            avcIntraPreriod.idrInterval = 0;
        else
            avcIntraPreriod.idrInterval = p->nIDRPeriod;
    }

    retStatus = mVideoEncoder->setConfig(&avcIntraPreriod);
    if(retStatus !=  ENCODE_SUCCESS) {
        LOGW("set avc intra period config failed");
    }

    mEncoderParams->intraPeriod = avcIntraPreriod.intraPeriod;
    mAVCParams->idrInterval = avcIntraPreriod.idrInterval;
    mAVCParams->ipPeriod = avcIntraPreriod.ipPeriod;

    mConfigAvcIntraPeriod = *p;
    mConfigAvcIntraPeriod.nIDRPeriod = avcIntraPreriod.idrInterval;

    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetConfigVideoNalSize(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_CONFIG_NALSIZE *p = (OMX_VIDEO_CONFIG_NALSIZE *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    memcpy(p, &mConfigNalSize, sizeof(*p));
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetConfigVideoNalSize(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    Encode_Status retStatus = ENCODE_SUCCESS;
    if (mParamBitrate.eControlRate == OMX_Video_ControlRateMax) {
        LOGE("SetConfigVideoNalSize failed. Feature is disabled.");
        return OMX_ErrorUnsupportedIndex;
    }
    OMX_VIDEO_CONFIG_NALSIZE *p = (OMX_VIDEO_CONFIG_NALSIZE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // set in either Loaded  state (ComponentSetParam) or Executing state (ComponentSetConfig)
    mConfigNalSize = *p;

    // return OMX_ErrorNone if not in Executing state
    // TODO: return OMX_ErrorIncorrectStateOperation?
    CHECK_SET_CONFIG_STATE();

    if (mParamBitrate.eControlRate != (OMX_VIDEO_CONTROLRATETYPE)OMX_Video_Intel_ControlRateVideoConferencingMode) {
        LOGE("SetConfigVideoNalSize failed. Feature is supported only in VCM.");
        return OMX_ErrorUnsupportedSetting;
    }
    VideoConfigNALSize configNalSize;
    configNalSize.maxSliceSize = mConfigNalSize.nNaluBytes * 8;
    retStatus = mVideoEncoder->setConfig(&configNalSize);
    if(retStatus != ENCODE_SUCCESS) {
        LOGW("set NAL size config failed");
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetConfigIntelSliceNumbers(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_CONFIG_INTEL_SLICE_NUMBERS *p = (OMX_VIDEO_CONFIG_INTEL_SLICE_NUMBERS *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    memcpy(p, &mConfigIntelSliceNumbers, sizeof(*p));
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetConfigIntelSliceNumbers(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    Encode_Status retStatus = ENCODE_SUCCESS;
    if (mParamBitrate.eControlRate == OMX_Video_ControlRateMax) {
        LOGE("SetConfigIntelSliceNumbers failed. Feature is disabled.");
        return OMX_ErrorUnsupportedIndex;
    }
    OMX_VIDEO_CONFIG_INTEL_SLICE_NUMBERS *p = (OMX_VIDEO_CONFIG_INTEL_SLICE_NUMBERS *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // set in either Loaded  state (ComponentSetParam) or Executing state (ComponentSetConfig)
    mConfigIntelSliceNumbers = *p;

    // return OMX_ErrorNone if not in Executing state
    // TODO: return OMX_ErrorIncorrectStateOperation?
    CHECK_SET_CONFIG_STATE();

    if (mParamBitrate.eControlRate != (OMX_VIDEO_CONTROLRATETYPE)OMX_Video_Intel_ControlRateVideoConferencingMode) {
        LOGE("SetConfigIntelSliceNumbers failed. Feature is supported only in VCM.");
        return OMX_ErrorUnsupportedSetting;
    }
    VideoConfigSliceNum sliceNum;
    sliceNum.sliceNum.iSliceNum = mConfigIntelSliceNumbers.nISliceNumber;
    sliceNum.sliceNum.pSliceNum = mConfigIntelSliceNumbers.nPSliceNumber;
    retStatus = mVideoEncoder->setConfig(&sliceNum);
    if(retStatus != ENCODE_SUCCESS) {
        LOGW("set silce num config failed!\n");
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamIntelAVCVUI(OMX_PTR pStructure) {

    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_INTEL_AVCVUI *p = (OMX_VIDEO_PARAM_INTEL_AVCVUI *)pStructure;

    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);
    memcpy(p, &mParamIntelAvcVui, sizeof(*p));

    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamIntelAVCVUI(OMX_PTR pStructure) {

    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_INTEL_AVCVUI *p = (OMX_VIDEO_PARAM_INTEL_AVCVUI *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // set only in Loaded state (ComponentSetParam)
    CHECK_SET_PARAM_STATE();

    mParamIntelAvcVui = *p;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::GetParamVideoBytestream(OMX_PTR) {
    return OMX_ErrorUnsupportedSetting;
}

OMX_ERRORTYPE OMXVideoEncoderAVC::SetParamVideoBytestream(OMX_PTR pStructure) {
    OMX_ERRORTYPE ret;
    OMX_VIDEO_PARAM_BYTESTREAMTYPE *p = (OMX_VIDEO_PARAM_BYTESTREAMTYPE *)pStructure;
    CHECK_TYPE_HEADER(p);
    CHECK_PORT_INDEX(p, OUTPORT_INDEX);

    // set only in Loaded state (ComponentSetParam)
    CHECK_SET_PARAM_STATE();

    if (p->bBytestream == OMX_TRUE) {
        mNalStreamFormat.eNaluFormat = OMX_NaluFormatStartCodes;
    } else {
        // TODO: do we need to override the Nalu format?
        mNalStreamFormat.eNaluFormat = (OMX_NALUFORMATSTYPE)OMX_NaluFormatZeroByteInterleaveLength;
    }

    return OMX_ErrorNone;
}


DECLARE_OMX_COMPONENT("OMX.Intel.VideoEncoder.AVC", "video_encoder.avc", OMXVideoEncoderAVC);