C++程序  |  1708行  |  55.69 KB

/*
* 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.
*/

#include "VideoDecoderBase.h"
#include "VideoDecoderTrace.h"
#include <string.h>
#include <va/va_android.h>
#include <va/va_tpi.h>
#ifdef  __SSE4_1__
#include "use_util_sse4.h"
#endif

#define INVALID_PTS ((uint64_t)-1)
#define MAXIMUM_POC  0x7FFFFFFF
#define MINIMUM_POC  0x80000000
#define ANDROID_DISPLAY_HANDLE 0x18C34078

VideoDecoderBase::VideoDecoderBase(const char *mimeType, _vbp_parser_type type)
    : mInitialized(false),
      mLowDelay(false),
      mStoreMetaData(false),
      mDisplay(NULL),
      mVADisplay(NULL),
      mVAContext(VA_INVALID_ID),
      mVAConfig(VA_INVALID_ID),
      mVAStarted(false),
      mCurrentPTS(INVALID_PTS),
      mAcquiredBuffer(NULL),
      mLastReference(NULL),
      mForwardReference(NULL),
      mDecodingFrame(false),
      mSizeChanged(false),
      mShowFrame(true),
      mOutputWindowSize(OUTPUT_WINDOW_SIZE),
      mRotationDegrees(0),
      mErrReportEnabled(false),
      mWiDiOn(false),
      mRawOutput(false),
      mManageReference(true),
      mOutputMethod(OUTPUT_BY_PCT),
      mNumSurfaces(0),
      mSurfaceBuffers(NULL),
      mOutputHead(NULL),
      mOutputTail(NULL),
      mSurfaces(NULL),
      mVASurfaceAttrib(NULL),
      mSurfaceUserPtr(NULL),
      mSurfaceAcquirePos(0),
      mNextOutputPOC(MINIMUM_POC),
      mParserType(type),
      mParserHandle(NULL),
      mSignalBufferSize(0) {

    memset(&mVideoFormatInfo, 0, sizeof(VideoFormatInfo));
    memset(&mConfigBuffer, 0, sizeof(mConfigBuffer));
    for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) {
         mSignalBufferPre[i] = NULL;
    }
    pthread_mutex_init(&mLock, NULL);
    pthread_mutex_init(&mFormatLock, NULL);
    mVideoFormatInfo.mimeType = strdup(mimeType);
    mUseGEN = false;
    mMetaDataBuffersNum = 0;
    mLibHandle = NULL;
    mParserOpen = NULL;
    mParserClose = NULL;
    mParserParse = NULL;
    mParserQuery = NULL;
    mParserFlush = NULL;
    mParserUpdate = NULL;
}

VideoDecoderBase::~VideoDecoderBase() {
    pthread_mutex_destroy(&mLock);
    pthread_mutex_destroy(&mFormatLock);
    stop();
    free(mVideoFormatInfo.mimeType);
}

Decode_Status VideoDecoderBase::start(VideoConfigBuffer *buffer) {
    if (buffer == NULL) {
        return DECODE_INVALID_DATA;
    }

    if (mParserHandle != NULL) {
        WTRACE("Decoder has already started.");
        return DECODE_SUCCESS;
    }
    mLibHandle = dlopen("libmixvbp.so", RTLD_NOW);
    if (mLibHandle == NULL) {
       return DECODE_NO_PARSER;
    }
    mParserOpen = (OpenFunc)dlsym(mLibHandle, "vbp_open");
    mParserClose = (CloseFunc)dlsym(mLibHandle, "vbp_close");
    mParserParse = (ParseFunc)dlsym(mLibHandle, "vbp_parse");
    mParserQuery = (QueryFunc)dlsym(mLibHandle, "vbp_query");
    mParserFlush = (FlushFunc)dlsym(mLibHandle, "vbp_flush");
    if (mParserOpen == NULL || mParserClose == NULL || mParserParse == NULL
        || mParserQuery == NULL || mParserFlush == NULL) {
        return DECODE_NO_PARSER;
    }
#if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING)
    mParserUpdate = (UpdateFunc)dlsym(mLibHandle, "vbp_update");
    if (mParserUpdate == NULL) {
        return DECODE_NO_PARSER;
    }
#endif
    if ((int32_t)mParserType != VBP_INVALID) {
        ITRACE("mParserType = %d", mParserType);
        if (mParserOpen(mParserType, &mParserHandle) != VBP_OK) {
            ETRACE("Failed to open VBP parser.");
            return DECODE_NO_PARSER;
        }
    }
    // keep a copy of configure buffer, meta data only. It can be used to override VA setup parameter.
    mConfigBuffer = *buffer;
    mConfigBuffer.data = NULL;
    mConfigBuffer.size = 0;

    mVideoFormatInfo.width = buffer->width;
    mVideoFormatInfo.height = buffer->height;
    if (buffer->flag & USE_NATIVE_GRAPHIC_BUFFER) {
        mVideoFormatInfo.surfaceWidth = buffer->graphicBufferWidth;
        mVideoFormatInfo.surfaceHeight = buffer->graphicBufferHeight;
    }
    mLowDelay = buffer->flag & WANT_LOW_DELAY;
    mStoreMetaData = buffer->flag & WANT_STORE_META_DATA;
    mRawOutput = buffer->flag & WANT_RAW_OUTPUT;
    if (mRawOutput) {
        WTRACE("Output is raw data.");
    }

    return DECODE_SUCCESS;
}


Decode_Status VideoDecoderBase::reset(VideoConfigBuffer *buffer) {
    if (buffer == NULL) {
        return DECODE_INVALID_DATA;
    }

    // if VA is already started, terminate VA as graphic buffers are reallocated by omxcodec
    terminateVA();

    // reset the mconfigBuffer to pass it for startVA.
    mConfigBuffer = *buffer;
    mConfigBuffer.data = NULL;
    mConfigBuffer.size = 0;

    mVideoFormatInfo.width = buffer->width;
    mVideoFormatInfo.height = buffer->height;
    if (buffer->flag & USE_NATIVE_GRAPHIC_BUFFER) {
        mVideoFormatInfo.surfaceWidth = buffer->graphicBufferWidth;
        mVideoFormatInfo.surfaceHeight = buffer->graphicBufferHeight;
    }
    mVideoFormatInfo.actualBufferNeeded = mConfigBuffer.surfaceNumber;
    mLowDelay = buffer->flag & WANT_LOW_DELAY;
    mStoreMetaData = buffer->flag & WANT_STORE_META_DATA;
    mMetaDataBuffersNum = 0;
    mRawOutput = buffer->flag & WANT_RAW_OUTPUT;
    if (mRawOutput) {
        WTRACE("Output is raw data.");
    }
    return DECODE_SUCCESS;
}



void VideoDecoderBase::stop(void) {
    terminateVA();

    mCurrentPTS = INVALID_PTS;
    mAcquiredBuffer = NULL;
    mLastReference = NULL;
    mForwardReference = NULL;
    mDecodingFrame = false;
    mSizeChanged = false;

    // private variables
    mLowDelay = false;
    mStoreMetaData = false;
    mRawOutput = false;
    mNumSurfaces = 0;
    mSurfaceAcquirePos = 0;
    mNextOutputPOC = MINIMUM_POC;
    mVideoFormatInfo.valid = false;
    if (mParserHandle){
        mParserClose(mParserHandle);
        mParserHandle = NULL;
    }
    if (mLibHandle) {
        dlclose(mLibHandle);
        mLibHandle = NULL;
    }
}

void VideoDecoderBase::flush(void) {
    if (mVAStarted == false) {
        // nothing to flush at this stage
        return;
    }

    endDecodingFrame(true);

    VideoSurfaceBuffer *p = mOutputHead;
    // check if there's buffer with DRC flag in the output queue
    while (p) {
        if (p->renderBuffer.flag & IS_RESOLUTION_CHANGE) {
            mSizeChanged = true;
            break;
        }
        p = p->next;
    }
    // avoid setting mSurfaceAcquirePos  to 0 as it may cause tearing
    // (surface is still being rendered)
    mSurfaceAcquirePos = (mSurfaceAcquirePos  + 1) % mNumSurfaces;
    mNextOutputPOC = MINIMUM_POC;
    mCurrentPTS = INVALID_PTS;
    mAcquiredBuffer = NULL;
    mLastReference = NULL;
    mForwardReference = NULL;
    mOutputHead = NULL;
    mOutputTail = NULL;
    mDecodingFrame = false;

    // flush vbp parser
    if (mParserHandle && (mParserFlush(mParserHandle) != VBP_OK)) {
        WTRACE("Failed to flush parser. Continue");
    }

    // initialize surface buffer without resetting mapped/raw data
    initSurfaceBuffer(false);

}

void VideoDecoderBase::freeSurfaceBuffers(void) {
    if (mVAStarted == false) {
        // nothing to free surface buffers at this stage
        return;
    }

    pthread_mutex_lock(&mLock);

    endDecodingFrame(true);

    // if VA is already started, terminate VA as graphic buffers are reallocated by omxcodec
    terminateVA();

    pthread_mutex_unlock(&mLock);
}

const VideoFormatInfo* VideoDecoderBase::getFormatInfo(void) {
    if ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) && mStoreMetaData) {
        // Do nothing here, just to avoid thread
        // contention in updateFormatInfo()
        pthread_mutex_lock(&mFormatLock);
        pthread_mutex_unlock(&mFormatLock);
    }

    return &mVideoFormatInfo;
}

int VideoDecoderBase::getOutputQueueLength(void) {
    VideoSurfaceBuffer *p = mOutputHead;

    int i = 0;
    while (p) {
        p = p->next;
        i++;
    }

    return i;
}

const VideoRenderBuffer* VideoDecoderBase::getOutput(bool draining, VideoErrorBuffer *outErrBuf) {
    if (mVAStarted == false) {
        return NULL;
    }
    bool useGraphicBuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;

    if (draining) {
        // complete decoding the last frame and ignore return
        endDecodingFrame(false);
    }

    if (mOutputHead == NULL) {
        return NULL;
    }

    // output by position (the first buffer)
    VideoSurfaceBuffer *outputByPos = mOutputHead;

    if (mLowDelay) {
        mOutputHead = mOutputHead->next;
        if (mOutputHead == NULL) {
            mOutputTail = NULL;
        }
        vaSetTimestampForSurface(mVADisplay, outputByPos->renderBuffer.surface, outputByPos->renderBuffer.timeStamp);
        if (useGraphicBuffer && !mUseGEN) {
            vaSyncSurface(mVADisplay, outputByPos->renderBuffer.surface);
            fillDecodingErrors(&(outputByPos->renderBuffer));
        }
        if (draining && mOutputTail == NULL) {
            outputByPos->renderBuffer.flag |= IS_EOS;
        }
        drainDecodingErrors(outErrBuf, &(outputByPos->renderBuffer));

        return &(outputByPos->renderBuffer);
    }

    VideoSurfaceBuffer *output = NULL;
    if (mOutputMethod == OUTPUT_BY_POC) {
        output = findOutputByPoc(draining);
    } else if (mOutputMethod == OUTPUT_BY_PCT) {
        output = findOutputByPct(draining);
    } else {
        ETRACE("Invalid output method.");
        return NULL;
    }

    if (output == NULL) {
        return NULL;
    }

    if (output != outputByPos) {
        // remove this output from middle or end of the list
        VideoSurfaceBuffer *p = outputByPos;
        while (p->next != output) {
            p = p->next;
        }
        p->next = output->next;
        if (mOutputTail == output) {
            mOutputTail = p;
        }
    } else {
        // remove this output from head of the list
        mOutputHead = mOutputHead->next;
        if (mOutputHead == NULL) {
            mOutputTail = NULL;
        }
    }
    //VTRACE("Output POC %d for display (pts = %.2f)", output->pictureOrder, output->renderBuffer.timeStamp/1E6);
    vaSetTimestampForSurface(mVADisplay, output->renderBuffer.surface, output->renderBuffer.timeStamp);

    if (useGraphicBuffer && !mUseGEN) {
        vaSyncSurface(mVADisplay, output->renderBuffer.surface);
        fillDecodingErrors(&(output->renderBuffer));
    }

    if (draining && mOutputTail == NULL) {
        output->renderBuffer.flag |= IS_EOS;
    }

    drainDecodingErrors(outErrBuf, &(output->renderBuffer));

    return &(output->renderBuffer);
}

VideoSurfaceBuffer* VideoDecoderBase::findOutputByPts() {
    // output by presentation time stamp - buffer with the smallest time stamp is output
    VideoSurfaceBuffer *p = mOutputHead;
    VideoSurfaceBuffer *outputByPts = NULL;
    uint64_t pts = INVALID_PTS;
    do {
        if ((uint64_t)(p->renderBuffer.timeStamp) <= pts) {
            // find buffer with the smallest PTS
            pts = p->renderBuffer.timeStamp;
            outputByPts = p;
        }
        p = p->next;
    } while (p != NULL);

    return outputByPts;
}

VideoSurfaceBuffer* VideoDecoderBase::findOutputByPct(bool draining) {
    // output by picture coding type (PCT)
    // if there is more than one reference frame, the first reference frame is ouput, otherwise,
    // output non-reference frame if there is any.

    VideoSurfaceBuffer *p = mOutputHead;
    VideoSurfaceBuffer *outputByPct = NULL;
    int32_t reference = 0;
    do {
        if (p->referenceFrame) {
            reference++;
            if (reference > 1) {
                // mOutputHead must be a reference frame
                outputByPct = mOutputHead;
                break;
            }
        } else {
            // first non-reference frame
            outputByPct = p;
            break;
        }
        p = p->next;
    } while (p != NULL);

    if (outputByPct == NULL && draining) {
        outputByPct = mOutputHead;
    }
    return  outputByPct;
}

#if 0
VideoSurfaceBuffer* VideoDecoderBase::findOutputByPoc(bool draining) {
    // output by picture order count (POC)
    // Output criteria:
    // if there is IDR frame (POC == 0), all the frames before IDR must be output;
    // Otherwise, if draining flag is set or list is full, frame with the least POC is output;
    // Otherwise, NOTHING is output

    int32_t dpbFullness = 0;
    for (int32_t i = 0; i < mNumSurfaces; i++) {
        // count num of reference frames
        if (mSurfaceBuffers[i].asReferernce) {
            dpbFullness++;
        }
    }

    if (mAcquiredBuffer && mAcquiredBuffer->asReferernce) {
        // frame is being decoded and is not ready for output yet
        dpbFullness--;
    }

    VideoSurfaceBuffer *p = mOutputHead;
    while (p != NULL) {
        // count dpbFullness with non-reference frame in the output queue
        if (p->asReferernce == false) {
            dpbFullness++;
        }
        p = p->next;
    }

Retry:
    p = mOutputHead;
    VideoSurfaceBuffer *outputByPoc = NULL;
    int32_t count = 0;
    int32_t poc = MAXIMUM_POC;

    do {
        if (p->pictureOrder == 0) {
            // output picture with the least POC before IDR
            if (outputByPoc != NULL) {
                mNextOutputPOC = outputByPoc->pictureOrder + 1;
                return outputByPoc;
            } else {
                mNextOutputPOC = MINIMUM_POC;
            }
        }

        // POC of  the output candidate must not be less than mNextOutputPOC
        if (p->pictureOrder < mNextOutputPOC) {
            break;
        }

        if (p->pictureOrder < poc) {
            // update the least POC.
            poc = p->pictureOrder;
            outputByPoc = p;
        }
        count++;
        p = p->next;
    } while (p != NULL && count < mOutputWindowSize);

    if (draining == false && dpbFullness < mOutputWindowSize) {
        // list is not  full and we are not  in draining state
        // if DPB is already full, one frame must be output
        return NULL;
    }

    if (outputByPoc == NULL) {
        mNextOutputPOC = MINIMUM_POC;
        goto Retry;
    }

    // for debugging purpose
    if (outputByPoc->pictureOrder != 0 && outputByPoc->pictureOrder < mNextOutputPOC) {
        ETRACE("Output POC is not incremental, expected %d, actual %d", mNextOutputPOC, outputByPoc->pictureOrder);
        //gaps_in_frame_num_value_allowed_flag is not currently supported
    }

    mNextOutputPOC = outputByPoc->pictureOrder + 1;

    return outputByPoc;
}
#else
VideoSurfaceBuffer* VideoDecoderBase::findOutputByPoc(bool draining) {
    VideoSurfaceBuffer *output = NULL;
    VideoSurfaceBuffer *p = mOutputHead;
    int32_t count = 0;
    int32_t poc = MAXIMUM_POC;
    VideoSurfaceBuffer *outputleastpoc = mOutputHead;
    do {
        count++;
        if (p->pictureOrder == 0) {
            // any picture before this POC (new IDR) must be output
            if (output == NULL) {
                mNextOutputPOC = MINIMUM_POC;
                // looking for any POC with negative value
            } else {
                mNextOutputPOC = output->pictureOrder + 1;
                break;
            }
        }
        if (p->pictureOrder < poc && p->pictureOrder >= mNextOutputPOC) {
            // this POC meets ouput criteria.
            poc = p->pictureOrder;
            output = p;
            outputleastpoc = p;
        }
        if (poc == mNextOutputPOC || count == mOutputWindowSize) {
            if (output != NULL) {
                // this indicates two cases:
                // 1) the next output POC is found.
                // 2) output queue is full and there is at least one buffer meeting the output criteria.
                mNextOutputPOC = output->pictureOrder + 1;
                break;
            } else {
                // this indicates output queue is full and no buffer in the queue meets the output criteria
                // restart processing as queue is FULL and output criteria is changed. (next output POC is 0)
                mNextOutputPOC = MINIMUM_POC;
                count = 0;
                poc = MAXIMUM_POC;
                p = mOutputHead;
                continue;
            }
        }
        if (p->next == NULL) {
            output = NULL;
        }

        p = p->next;
    } while (p != NULL);

    if (draining == true && output == NULL) {
        output = outputleastpoc;
    }

    return output;
}
#endif

bool VideoDecoderBase::checkBufferAvail(void) {
    if (!mInitialized) {
        if ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) == 0) {
            return true;
        }
        for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) {
            if (mSignalBufferPre[i] != NULL) {
                return true;
            }
        }
        return false;
    }
    // check whether there is buffer available for decoding
    // TODO: check frame being referenced for frame skipping
    VideoSurfaceBuffer *buffer = NULL;
    for (int32_t i = 0; i < mNumSurfaces; i++) {
        buffer = mSurfaceBuffers + i;

        if (buffer->asReferernce == false &&
            buffer->renderBuffer.renderDone == true) {
            querySurfaceRenderStatus(buffer);
            if (buffer->renderBuffer.driverRenderDone == true)
                return true;
        }
     }
    return false;
}

Decode_Status VideoDecoderBase::acquireSurfaceBuffer(void) {
    if (mVAStarted == false) {
        return DECODE_FAIL;
    }

    if (mAcquiredBuffer != NULL) {
        ETRACE("mAcquiredBuffer is not NULL. Implementation bug.");
        return DECODE_FAIL;
    }

    int nextAcquire = mSurfaceAcquirePos;
    VideoSurfaceBuffer *acquiredBuffer = NULL;
    bool acquired = false;

    while (acquired == false) {
        acquiredBuffer = mSurfaceBuffers + nextAcquire;

        querySurfaceRenderStatus(acquiredBuffer);

        if (acquiredBuffer->asReferernce == false && acquiredBuffer->renderBuffer.renderDone == true && acquiredBuffer->renderBuffer.driverRenderDone == true) {
            // this is potential buffer for acquisition. Check if it is referenced by other surface for frame skipping
            VideoSurfaceBuffer *temp;
            acquired = true;
            for (int i = 0; i < mNumSurfaces; i++) {
                if (i == nextAcquire) {
                    continue;
                }
                temp = mSurfaceBuffers + i;
                // use mSurfaces[nextAcquire] instead of acquiredBuffer->renderBuffer.surface as its the actual surface to use.
                if (temp->renderBuffer.surface == mSurfaces[nextAcquire] &&
                    temp->renderBuffer.renderDone == false) {
                    ITRACE("Surface is referenced by other surface buffer.");
                    acquired = false;
                    break;
                }
            }
        }
        if (acquired) {
            break;
        }
        nextAcquire++;
        if (nextAcquire == mNumSurfaces) {
            nextAcquire = 0;
        }
        if (nextAcquire == mSurfaceAcquirePos) {
            return DECODE_NO_SURFACE;
        }
    }

    if (acquired == false) {
        return DECODE_NO_SURFACE;
    }

    mAcquiredBuffer = acquiredBuffer;
    mSurfaceAcquirePos = nextAcquire;

    // set surface again as surface maybe reset by skipped frame.
    // skipped frame is a "non-coded frame" and decoder needs to duplicate the previous reference frame as the output.
    mAcquiredBuffer->renderBuffer.surface = mSurfaces[mSurfaceAcquirePos];
    if (mSurfaceUserPtr && mAcquiredBuffer->mappedData) {
        mAcquiredBuffer->mappedData->data = mSurfaceUserPtr[mSurfaceAcquirePos];
    }
    mAcquiredBuffer->renderBuffer.timeStamp = INVALID_PTS;
    mAcquiredBuffer->renderBuffer.display = mVADisplay;
    mAcquiredBuffer->renderBuffer.flag = 0;
    mAcquiredBuffer->renderBuffer.renderDone = false;
    mAcquiredBuffer->asReferernce = false;
    mAcquiredBuffer->renderBuffer.errBuf.errorNumber = 0;
    mAcquiredBuffer->renderBuffer.errBuf.timeStamp = INVALID_PTS;

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::outputSurfaceBuffer(void) {
    Decode_Status status;
    if (mAcquiredBuffer == NULL) {
        ETRACE("mAcquiredBuffer is NULL. Implementation bug.");
        return DECODE_FAIL;
    }

    if (mRawOutput) {
        status = getRawDataFromSurface();
        CHECK_STATUS();
    }

    // frame is successfly decoded to the current surface,  it is ready for output
    if (mShowFrame) {
        mAcquiredBuffer->renderBuffer.renderDone = false;
    } else {
        mAcquiredBuffer->renderBuffer.renderDone = true;
    }

    // decoder must set "asReference and referenceFrame" flags properly

    // update reference frames
    if (mAcquiredBuffer->referenceFrame) {
        if (mManageReference) {
            // managing reference for MPEG4/H.263/WMV.
            // AVC should manage reference frame in a different way
            if (mForwardReference != NULL) {
                // this foward reference is no longer needed
                mForwardReference->asReferernce = false;
            }
            // Forware reference for either P or B frame prediction
            mForwardReference = mLastReference;
            mAcquiredBuffer->asReferernce = true;
        }

        // the last reference frame.
        mLastReference = mAcquiredBuffer;
    }
    // add to the output list
    if (mShowFrame) {
        if (mOutputHead == NULL) {
            mOutputHead = mAcquiredBuffer;
        } else {
            mOutputTail->next = mAcquiredBuffer;
        }
        mOutputTail = mAcquiredBuffer;
        mOutputTail->next = NULL;
    }

    //VTRACE("Pushing POC %d to queue (pts = %.2f)", mAcquiredBuffer->pictureOrder, mAcquiredBuffer->renderBuffer.timeStamp/1E6);

    mAcquiredBuffer = NULL;
    mSurfaceAcquirePos = (mSurfaceAcquirePos  + 1 ) % mNumSurfaces;
    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::releaseSurfaceBuffer(void) {
    if (mAcquiredBuffer == NULL) {
        // this is harmless error
        return DECODE_SUCCESS;
    }

    // frame is not decoded to the acquired buffer, current surface is invalid, and can't be output.
    mAcquiredBuffer->asReferernce = false;
    mAcquiredBuffer->renderBuffer.renderDone = true;
    mAcquiredBuffer = NULL;
    return DECODE_SUCCESS;
}

void VideoDecoderBase::flushSurfaceBuffers(void) {
    endDecodingFrame(true);
    VideoSurfaceBuffer *p = NULL;
    while (mOutputHead) {
        mOutputHead->renderBuffer.renderDone = true;
        p = mOutputHead;
        mOutputHead = mOutputHead->next;
        p->next = NULL;
    }
    mOutputHead = NULL;
    mOutputTail = NULL;
}

Decode_Status VideoDecoderBase::endDecodingFrame(bool dropFrame) {
    Decode_Status status = DECODE_SUCCESS;
    VAStatus vaStatus;

    if (mDecodingFrame == false) {
        if (mAcquiredBuffer != NULL) {
            //ETRACE("mAcquiredBuffer is not NULL. Implementation bug.");
            releaseSurfaceBuffer();
            status = DECODE_FAIL;
        }
        return status;
    }
    // return through exit label to reset mDecodingFrame
    if (mAcquiredBuffer == NULL) {
        ETRACE("mAcquiredBuffer is NULL. Implementation bug.");
        status = DECODE_FAIL;
        goto exit;
    }

    vaStatus = vaEndPicture(mVADisplay, mVAContext);
    if (vaStatus != VA_STATUS_SUCCESS) {
        releaseSurfaceBuffer();
        ETRACE("vaEndPicture failed. vaStatus = %d", vaStatus);
        status = DECODE_DRIVER_FAIL;
        goto exit;
    }

    if (dropFrame) {
        // we are asked to drop this decoded picture
        VTRACE("Frame dropped in endDecodingFrame");
        vaStatus = vaSyncSurface(mVADisplay, mAcquiredBuffer->renderBuffer.surface);
        releaseSurfaceBuffer();
        goto exit;
    }
    status = outputSurfaceBuffer();
    // fall through
exit:
    mDecodingFrame = false;
    return status;
}


Decode_Status VideoDecoderBase::setupVA(uint32_t numSurface, VAProfile profile, uint32_t numExtraSurface) {
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    Decode_Status status;

    if (mVAStarted) {
        return DECODE_SUCCESS;
    }

    mRotationDegrees = 0;
    if (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER){
#ifdef TARGET_HAS_ISV
        if (mVideoFormatInfo.actualBufferNeeded > mConfigBuffer.surfaceNumber - mConfigBuffer.vppBufferNum)
#else
        if (mVideoFormatInfo.actualBufferNeeded > mConfigBuffer.surfaceNumber)
#endif
            return DECODE_FORMAT_CHANGE;

        numSurface = mConfigBuffer.surfaceNumber;
        // if format has been changed in USE_NATIVE_GRAPHIC_BUFFER mode,
        // we can not setupVA here when the graphic buffer resolution is smaller than the resolution decoder really needs
        if (mSizeChanged) {
            if (mStoreMetaData || (!mStoreMetaData && (mVideoFormatInfo.surfaceWidth < mVideoFormatInfo.width || mVideoFormatInfo.surfaceHeight < mVideoFormatInfo.height))) {
                mSizeChanged = false;
                return DECODE_FORMAT_CHANGE;
            }
        }
    }

    // TODO: validate profile
    if (numSurface == 0) {
        return DECODE_FAIL;
    }

    if (mConfigBuffer.flag & HAS_MINIMUM_SURFACE_NUMBER) {
        if (numSurface < mConfigBuffer.surfaceNumber) {
            WTRACE("surface to allocated %d is less than minimum number required %d",
                    numSurface, mConfigBuffer.surfaceNumber);
            numSurface = mConfigBuffer.surfaceNumber;
        }
    }

    if (mVADisplay != NULL) {
        ETRACE("VA is partially started.");
        return DECODE_FAIL;
    }

    // Display is defined as "unsigned int"
#ifndef USE_HYBRID_DRIVER
    mDisplay = new Display;
    *mDisplay = ANDROID_DISPLAY_HANDLE;
#else
    if (profile >= VAProfileH264Baseline && profile <= VAProfileVC1Advanced) {
        ITRACE("Using GEN driver");
        mDisplay = "libva_driver_name=i965";
        mUseGEN = true;
    } else {
        ITRACE("Using PVR driver");
        mDisplay = "libva_driver_name=pvr";
        mUseGEN = false;
    }
#endif
    mVADisplay = vaGetDisplay(mDisplay);
    if (mVADisplay == NULL) {
        ETRACE("vaGetDisplay failed.");
        return DECODE_DRIVER_FAIL;
    }

    int majorVersion, minorVersion;
    vaStatus = vaInitialize(mVADisplay, &majorVersion, &minorVersion);
    CHECK_VA_STATUS("vaInitialize");

    if ((int32_t)profile != VAProfileSoftwareDecoding) {

        status = checkHardwareCapability();
        CHECK_STATUS("checkHardwareCapability");

#if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING)
        status = getCodecSpecificConfigs(profile, &mVAConfig);
        CHECK_STATUS("getCodecSpecificAttributes");
#else
        VAConfigAttrib attrib;
        //We are requesting RT attributes
        attrib.type = VAConfigAttribRTFormat;
        attrib.value = VA_RT_FORMAT_YUV420;

        vaStatus = vaCreateConfig(
                mVADisplay,
                profile,
                VAEntrypointVLD,
                &attrib,
                1,
                &mVAConfig);
        CHECK_VA_STATUS("vaCreateConfig");
#endif
    }

    mNumSurfaces = numSurface;
    mNumExtraSurfaces = numExtraSurface;
    mSurfaces = new VASurfaceID [mNumSurfaces + mNumExtraSurfaces];
    mExtraSurfaces = mSurfaces + mNumSurfaces;
    for (int i = 0; i < mNumSurfaces + mNumExtraSurfaces; ++i) {
        mSurfaces[i] = VA_INVALID_SURFACE;
    }
    if (mSurfaces == NULL) {
        return DECODE_MEMORY_FAIL;
    }

    setRenderRect();
    setColorSpaceInfo(mVideoFormatInfo.colorMatrix, mVideoFormatInfo.videoRange);

    int32_t format = VA_RT_FORMAT_YUV420;
    if (mConfigBuffer.flag & WANT_SURFACE_PROTECTION) {
#ifndef USE_AVC_SHORT_FORMAT
        format |= VA_RT_FORMAT_PROTECTED;
        WTRACE("Surface is protected.");
#endif
    }
    if (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) {
        if (!mStoreMetaData) {
            VASurfaceAttrib attribs[2];
            mVASurfaceAttrib = new VASurfaceAttribExternalBuffers;
            if (mVASurfaceAttrib == NULL) {
                return DECODE_MEMORY_FAIL;
            }

            mVASurfaceAttrib->buffers= (unsigned long *)malloc(sizeof(unsigned long)*mNumSurfaces);
            if (mVASurfaceAttrib->buffers == NULL) {
                return DECODE_MEMORY_FAIL;
            }
            mVASurfaceAttrib->num_buffers = mNumSurfaces;
            mVASurfaceAttrib->pixel_format = VA_FOURCC_NV12;
            mVASurfaceAttrib->width = mVideoFormatInfo.surfaceWidth;
            mVASurfaceAttrib->height = mVideoFormatInfo.surfaceHeight;
            mVASurfaceAttrib->data_size = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride * 1.5;
            mVASurfaceAttrib->num_planes = 2;
            mVASurfaceAttrib->pitches[0] = mConfigBuffer.graphicBufferHStride;
            mVASurfaceAttrib->pitches[1] = mConfigBuffer.graphicBufferHStride;
            mVASurfaceAttrib->pitches[2] = 0;
            mVASurfaceAttrib->pitches[3] = 0;
            mVASurfaceAttrib->offsets[0] = 0;
            mVASurfaceAttrib->offsets[1] = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride;
            mVASurfaceAttrib->offsets[2] = 0;
            mVASurfaceAttrib->offsets[3] = 0;
            mVASurfaceAttrib->private_data = (void *)mConfigBuffer.nativeWindow;
            mVASurfaceAttrib->flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
            if (mConfigBuffer.flag & USE_TILING_MEMORY)
                mVASurfaceAttrib->flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING;

            for (int i = 0; i < mNumSurfaces; i++) {
                mVASurfaceAttrib->buffers[i] = (unsigned long)mConfigBuffer.graphicBufferHandler[i];
            }

            attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
            attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
            attribs[0].value.type = VAGenericValueTypeInteger;
            attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;

            attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
            attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
            attribs[1].value.type = VAGenericValueTypePointer;
            attribs[1].value.value.p = (void *)mVASurfaceAttrib;

            vaStatus = vaCreateSurfaces(
                mVADisplay,
                format,
                mVideoFormatInfo.surfaceWidth,
                mVideoFormatInfo.surfaceHeight,
                mSurfaces,
                mNumSurfaces,
                attribs,
                2);
        }
    } else {
        vaStatus = vaCreateSurfaces(
            mVADisplay,
            format,
            mVideoFormatInfo.width,
            mVideoFormatInfo.height,
            mSurfaces,
            mNumSurfaces,
            NULL,
            0);
        mVideoFormatInfo.surfaceWidth = mVideoFormatInfo.width;
        mVideoFormatInfo.surfaceHeight = mVideoFormatInfo.height;
    }
    CHECK_VA_STATUS("vaCreateSurfaces");

    if (mNumExtraSurfaces != 0) {
        vaStatus = vaCreateSurfaces(
            mVADisplay,
            format,
            mVideoFormatInfo.surfaceWidth,
            mVideoFormatInfo.surfaceHeight,
            mExtraSurfaces,
            mNumExtraSurfaces,
            NULL,
            0);
        CHECK_VA_STATUS("vaCreateSurfaces");
    }

    mVideoFormatInfo.surfaceNumber = mNumSurfaces;
    mVideoFormatInfo.ctxSurfaces = mSurfaces;

    if ((int32_t)profile != VAProfileSoftwareDecoding) {
        if (mStoreMetaData) {
            if (mUseGEN) {
                vaStatus = vaCreateContext(
                    mVADisplay,
                    mVAConfig,
                    mVideoFormatInfo.surfaceWidth,
                    mVideoFormatInfo.surfaceHeight,
                    0,
                    NULL,
                    0,
                    &mVAContext);
            } else {
                vaStatus = vaCreateContext(
                    mVADisplay,
                    mVAConfig,
                    mVideoFormatInfo.surfaceWidth,
                    mVideoFormatInfo.surfaceHeight,
                    0,
                    NULL,
                    mNumSurfaces + mNumExtraSurfaces,
                    &mVAContext);
            }
        } else {
            vaStatus = vaCreateContext(
                mVADisplay,
                mVAConfig,
                mVideoFormatInfo.surfaceWidth,
                mVideoFormatInfo.surfaceHeight,
                0,
                mSurfaces,
                mNumSurfaces + mNumExtraSurfaces,
                &mVAContext);
        }
        CHECK_VA_STATUS("vaCreateContext");
    }

    mSurfaceBuffers = new VideoSurfaceBuffer [mNumSurfaces];
    if (mSurfaceBuffers == NULL) {
        return DECODE_MEMORY_FAIL;
    }
    initSurfaceBuffer(true);

    if ((int32_t)profile == VAProfileSoftwareDecoding) {
        // derive user pointer from surface for direct access
        status = mapSurface();
        CHECK_STATUS("mapSurface")
    }

    setRotationDegrees(mConfigBuffer.rotationDegrees);

    mVAStarted = true;

    pthread_mutex_lock(&mLock);
    if (mStoreMetaData) {
        for (uint32_t i = 0; i < mMetaDataBuffersNum; i++) {
            status = createSurfaceFromHandle(i);
            CHECK_STATUS("createSurfaceFromHandle");
            mSurfaceBuffers[i].renderBuffer.graphicBufferIndex = i;
        }
    }
    pthread_mutex_unlock(&mLock);

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::terminateVA(void) {
    mSignalBufferSize = 0;
    for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) {
         mSignalBufferPre[i] = NULL;
    }

    if (mVAStarted == false) {
        // VA hasn't been started yet
        return DECODE_SUCCESS;
    }

    if (mSurfaceBuffers) {
        for (int32_t i = 0; i < mNumSurfaces; i++) {
            if (mSurfaceBuffers[i].renderBuffer.rawData) {
                if (mSurfaceBuffers[i].renderBuffer.rawData->data) {
                    delete [] mSurfaceBuffers[i].renderBuffer.rawData->data;
                }
                delete mSurfaceBuffers[i].renderBuffer.rawData;
            }
            if (mSurfaceBuffers[i].mappedData) {
                // don't  delete data pointer as it is mapped from surface
                delete mSurfaceBuffers[i].mappedData;
            }
        }
        delete [] mSurfaceBuffers;
        mSurfaceBuffers = NULL;
    }

    if (mVASurfaceAttrib) {
        if (mVASurfaceAttrib->buffers) free(mVASurfaceAttrib->buffers);
        delete mVASurfaceAttrib;
        mVASurfaceAttrib = NULL;
    }


    if (mSurfaceUserPtr) {
        delete [] mSurfaceUserPtr;
        mSurfaceUserPtr = NULL;
    }

    if (mSurfaces) {
        vaDestroySurfaces(mVADisplay, mSurfaces, mStoreMetaData ? mMetaDataBuffersNum : (mNumSurfaces + mNumExtraSurfaces));
        delete [] mSurfaces;
        mSurfaces = NULL;
    }

    if (mVAContext != VA_INVALID_ID) {
         vaDestroyContext(mVADisplay, mVAContext);
         mVAContext = VA_INVALID_ID;
    }

    if (mVAConfig != VA_INVALID_ID) {
        vaDestroyConfig(mVADisplay, mVAConfig);
        mVAConfig = VA_INVALID_ID;
    }

    if (mVADisplay) {
        vaTerminate(mVADisplay);
        mVADisplay = NULL;
    }

    if (mDisplay) {
#ifndef USE_HYBRID_DRIVER
        delete mDisplay;
#endif
        mDisplay = NULL;
    }

    mVAStarted = false;
    mInitialized = false;
    mErrReportEnabled = false;
    if (mStoreMetaData) {
        mMetaDataBuffersNum = 0;
        mSurfaceAcquirePos = 0;
    }
    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::parseBuffer(uint8_t *buffer, int32_t size, bool config, void** vbpData) {
     // DON'T check if mVAStarted == true
    if (mParserHandle == NULL) {
        return DECODE_NO_PARSER;
    }

    uint32_t vbpStatus;
    if (buffer == NULL || size <= 0) {
        return DECODE_INVALID_DATA;
    }

    uint8_t configFlag = config ? 1 : 0;
    vbpStatus = mParserParse(mParserHandle, buffer, size, configFlag);
    CHECK_VBP_STATUS("vbp_parse");

    vbpStatus = mParserQuery(mParserHandle, vbpData);
    CHECK_VBP_STATUS("vbp_query");

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::mapSurface(void) {
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    VAImage image;
    uint8_t *userPtr;
    mSurfaceUserPtr = new uint8_t* [mNumSurfaces];
    if (mSurfaceUserPtr == NULL) {
        return DECODE_MEMORY_FAIL;
    }

    for (int32_t i = 0; i< mNumSurfaces; i++) {
        vaStatus = vaDeriveImage(mVADisplay, mSurfaces[i], &image);
        CHECK_VA_STATUS("vaDeriveImage");
        vaStatus = vaMapBuffer(mVADisplay, image.buf, (void**)&userPtr);
        CHECK_VA_STATUS("vaMapBuffer");
        mSurfaceUserPtr[i] = userPtr;
        mSurfaceBuffers[i].mappedData = new VideoFrameRawData;
        if (mSurfaceBuffers[i].mappedData == NULL) {
            return DECODE_MEMORY_FAIL;
        }
        mSurfaceBuffers[i].mappedData->own = false; // derived from surface so can't be released
        mSurfaceBuffers[i].mappedData->data = NULL;  // specified during acquireSurfaceBuffer
        mSurfaceBuffers[i].mappedData->fourcc = image.format.fourcc;
        mSurfaceBuffers[i].mappedData->width = mVideoFormatInfo.width;
        mSurfaceBuffers[i].mappedData->height = mVideoFormatInfo.height;
        mSurfaceBuffers[i].mappedData->size = image.data_size;
        for (int pi = 0; pi < 3; pi++) {
            mSurfaceBuffers[i].mappedData->pitch[pi] = image.pitches[pi];
            mSurfaceBuffers[i].mappedData->offset[pi] = image.offsets[pi];
        }
        // debug information
        if (image.pitches[0] != image.pitches[1] ||
            image.width != mVideoFormatInfo.width ||
            image.height != mVideoFormatInfo.height ||
            image.offsets[0] != 0) {
            WTRACE("Unexpected VAImage format, w = %d, h = %d, offset = %d", image.width, image.height, image.offsets[0]);
        }
        // TODO: do we need to unmap buffer?
        //vaStatus = vaUnmapBuffer(mVADisplay, image.buf);
        //CHECK_VA_STATUS("vaMapBuffer");
        vaStatus = vaDestroyImage(mVADisplay,image.image_id);
        CHECK_VA_STATUS("vaDestroyImage");

    }
    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::getRawDataFromSurface(VideoRenderBuffer *renderBuffer, uint8_t *pRawData, uint32_t *pSize, bool internal) {
    if (internal) {
        if (mAcquiredBuffer == NULL) {
            return DECODE_FAIL;
        }
        renderBuffer = &(mAcquiredBuffer->renderBuffer);
    }

    VAStatus vaStatus;
    VAImage vaImage;
    vaStatus = vaSyncSurface(renderBuffer->display, renderBuffer->surface);
    CHECK_VA_STATUS("vaSyncSurface");

    vaStatus = vaDeriveImage(renderBuffer->display, renderBuffer->surface, &vaImage);
    CHECK_VA_STATUS("vaDeriveImage");

    void *pBuf = NULL;
    vaStatus = vaMapBuffer(renderBuffer->display, vaImage.buf, &pBuf);
    CHECK_VA_STATUS("vaMapBuffer");


    // size in NV12 format
    uint32_t cropWidth = mVideoFormatInfo.width - (mVideoFormatInfo.cropLeft + mVideoFormatInfo.cropRight);
    uint32_t cropHeight = mVideoFormatInfo.height - (mVideoFormatInfo.cropBottom + mVideoFormatInfo.cropTop);
    if (strcasecmp(mVideoFormatInfo.mimeType,"video/avc") == 0 ||
        strcasecmp(mVideoFormatInfo.mimeType,"video/avc-secure") == 0 ||
        strcasecmp(mVideoFormatInfo.mimeType,"video/h264") == 0) {
        cropHeight = mVideoFormatInfo.height;
        cropWidth = mVideoFormatInfo.width;
    }
    int32_t size = cropWidth  * cropHeight * 3 / 2;

    if (internal) {
        VideoFrameRawData *rawData = NULL;
        if (renderBuffer->rawData == NULL) {
            rawData = new VideoFrameRawData;
            if (rawData == NULL) {
                return DECODE_MEMORY_FAIL;
            }
            memset(rawData, 0, sizeof(VideoFrameRawData));
            renderBuffer->rawData = rawData;
        } else {
            rawData = renderBuffer->rawData;
        }

        if (rawData->data != NULL && rawData->size != size) {
            delete [] rawData->data;
            rawData->data = NULL;
            rawData->size = 0;
        }
        if (rawData->data == NULL) {
            rawData->data = new uint8_t [size];
            if (rawData->data == NULL) {
                return DECODE_MEMORY_FAIL;
            }
        }

        rawData->own = true; // allocated by this library
        rawData->width = cropWidth;
        rawData->height = cropHeight;
        rawData->pitch[0] = cropWidth;
        rawData->pitch[1] = cropWidth;
        rawData->pitch[2] = 0;  // interleaved U/V, two planes
        rawData->offset[0] = 0;
        rawData->offset[1] = cropWidth * cropHeight;
        rawData->offset[2] = cropWidth * cropHeight * 3 / 2;
        rawData->size = size;
        rawData->fourcc = 'NV12';

        pRawData = rawData->data;
    } else {
        *pSize = size;
    }

    if (size == (int32_t)vaImage.data_size) {
#ifdef  __SSE4_1__
        stream_memcpy(pRawData, pBuf, size);
#else
        memcpy(pRawData, pBuf, size);
#endif
    } else {
        // copy Y data
        uint8_t *src = (uint8_t*)pBuf;
        uint8_t *dst = pRawData;
        uint32_t row = 0;
        for (row = 0; row < cropHeight; row++) {
#ifdef  __SSE4_1__
            stream_memcpy(dst, src, cropWidth);
#else
            memcpy(dst, src, cropWidth);
#endif
            dst += cropWidth;
            src += vaImage.pitches[0];
        }
        // copy interleaved V and  U data
        src = (uint8_t*)pBuf + vaImage.offsets[1];
        for (row = 0; row < cropHeight / 2; row++) {
#ifdef  __SSE4_1__
            stream_memcpy(dst, src, cropWidth);
#else
            memcpy(dst, src, cropWidth);
#endif
            dst += cropWidth;
            src += vaImage.pitches[1];
        }
    }

    vaStatus = vaUnmapBuffer(renderBuffer->display, vaImage.buf);
    CHECK_VA_STATUS("vaUnmapBuffer");

    vaStatus = vaDestroyImage(renderBuffer->display, vaImage.image_id);
    CHECK_VA_STATUS("vaDestroyImage");

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::createSurfaceFromHandle(int index) {
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    Decode_Status status;

    int32_t format = VA_RT_FORMAT_YUV420;
    if (mConfigBuffer.flag & WANT_SURFACE_PROTECTION) {
#ifndef USE_AVC_SHORT_FORMAT
        format |= VA_RT_FORMAT_PROTECTED;
        WTRACE("Surface is protected.");
#endif
    }
    VASurfaceAttrib attribs[2];
    VASurfaceAttribExternalBuffers surfExtBuf;
    surfExtBuf.num_buffers = 1;
    surfExtBuf.pixel_format = VA_FOURCC_NV12;
    surfExtBuf.width = mVideoFormatInfo.surfaceWidth;
    surfExtBuf.height = mVideoFormatInfo.surfaceHeight;
    surfExtBuf.data_size = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride * 1.5;
    surfExtBuf.num_planes = 2;
    surfExtBuf.pitches[0] = mConfigBuffer.graphicBufferHStride;
    surfExtBuf.pitches[1] = mConfigBuffer.graphicBufferHStride;
    surfExtBuf.pitches[2] = 0;
    surfExtBuf.pitches[3] = 0;
    surfExtBuf.offsets[0] = 0;
    surfExtBuf.offsets[1] = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride;
    surfExtBuf.offsets[2] = 0;
    surfExtBuf.offsets[3] = 0;
    surfExtBuf.private_data = (void *)mConfigBuffer.nativeWindow;
    surfExtBuf.flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
    if (mConfigBuffer.flag & USE_TILING_MEMORY) {
        surfExtBuf.flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING;
    }

    surfExtBuf.buffers = (long unsigned int*)&(mConfigBuffer.graphicBufferHandler[index]);

    attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
    attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
    attribs[0].value.type = VAGenericValueTypeInteger;
    attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;

    attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
    attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
    attribs[1].value.type = VAGenericValueTypePointer;
    attribs[1].value.value.p = (void *)&surfExtBuf;

    vaStatus = vaCreateSurfaces(
            mVADisplay,
            format,
            mVideoFormatInfo.surfaceWidth,
            mVideoFormatInfo.surfaceHeight,
            &(mSurfaces[index]),
            1,
            attribs,
        2);
    CHECK_VA_STATUS("vaCreateSurfaces");

    return DECODE_SUCCESS;
}

void VideoDecoderBase::initSurfaceBuffer(bool reset) {
    bool useGraphicBuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;
    if (useGraphicBuffer && reset) {
        pthread_mutex_lock(&mLock);
    }
    for (int32_t i = 0; i < mNumSurfaces; i++) {
        mSurfaceBuffers[i].renderBuffer.display = mVADisplay;
        mSurfaceBuffers[i].renderBuffer.surface = VA_INVALID_SURFACE;  // set in acquireSurfaceBuffer
        mSurfaceBuffers[i].renderBuffer.flag = 0;
        mSurfaceBuffers[i].renderBuffer.scanFormat = VA_FRAME_PICTURE;
        mSurfaceBuffers[i].renderBuffer.timeStamp = 0;
        mSurfaceBuffers[i].referenceFrame = false;
        mSurfaceBuffers[i].asReferernce= false;
        mSurfaceBuffers[i].pictureOrder = 0;
        mSurfaceBuffers[i].next = NULL;
        if (reset == true) {
            mSurfaceBuffers[i].renderBuffer.rawData = NULL;
            mSurfaceBuffers[i].mappedData = NULL;
        }
        if (useGraphicBuffer) {
            if (reset) {
               mSurfaceBuffers[i].renderBuffer.graphicBufferHandle = mConfigBuffer.graphicBufferHandler[i];
               mSurfaceBuffers[i].renderBuffer.renderDone = false; //default false
               for (uint32_t j = 0; j < mSignalBufferSize; j++) {
                   if(mSignalBufferPre[j] != NULL && mSignalBufferPre[j] == mSurfaceBuffers[i].renderBuffer.graphicBufferHandle) {
                      mSurfaceBuffers[i].renderBuffer.renderDone = true;
                      VTRACE("initSurfaceBuffer set renderDone = true index = %d", i);
                      mSignalBufferPre[j] = NULL;
                      break;
                   }
               }
            } else {
               mSurfaceBuffers[i].renderBuffer.renderDone = false;
            }
        } else {
            mSurfaceBuffers[i].renderBuffer.graphicBufferHandle = NULL;
            mSurfaceBuffers[i].renderBuffer.renderDone = true;
        }
        mSurfaceBuffers[i].renderBuffer.graphicBufferIndex = i;
    }

    if (useGraphicBuffer && reset) {
        mInitialized = true;
        mSignalBufferSize = 0;
        pthread_mutex_unlock(&mLock);
    }
}

Decode_Status VideoDecoderBase::signalRenderDone(void * graphichandler, bool isNew) {
    Decode_Status status;
    if (graphichandler == NULL) {
        return DECODE_SUCCESS;
    }
    pthread_mutex_lock(&mLock);
    bool graphicBufferMode = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;
    if (mStoreMetaData) {
        if (!graphicBufferMode) {
            pthread_mutex_unlock(&mLock);
            return DECODE_SUCCESS;
        }

        if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) {
            mConfigBuffer.graphicBufferHandler[mMetaDataBuffersNum] = graphichandler;
            if (mInitialized) {
                mSurfaceBuffers[mMetaDataBuffersNum].renderBuffer.graphicBufferHandle = graphichandler;
                mSurfaceBuffers[mMetaDataBuffersNum].renderBuffer.graphicBufferIndex = mMetaDataBuffersNum;
            }
        }
    }
    int i = 0;
    if (!mInitialized) {
        if (mSignalBufferSize >= MAX_GRAPHIC_BUFFER_NUM) {
            pthread_mutex_unlock(&mLock);
            return DECODE_INVALID_DATA;
        }
        mSignalBufferPre[mSignalBufferSize++] = graphichandler;
        VTRACE("SignalRenderDoneFlag mInitialized = false graphichandler = %p, mSignalBufferSize = %d", graphichandler, mSignalBufferSize);
    } else {
        if (!graphicBufferMode) {
            pthread_mutex_unlock(&mLock);
            return DECODE_SUCCESS;
        }
        if (mStoreMetaData) {
            if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) {
                if (mVAStarted) {
                    status = createSurfaceFromHandle(mMetaDataBuffersNum);
                    CHECK_STATUS("createSurfaceFromHandle")
                }
            }
        }
        for (i = 0; i < mNumSurfaces; i++) {
            if (mSurfaceBuffers[i].renderBuffer.graphicBufferHandle == graphichandler) {
                mSurfaceBuffers[i].renderBuffer.renderDone = true;
                VTRACE("SignalRenderDoneFlag mInitialized = true index = %d", i);
               break;
           }
        }
    }

    if (mStoreMetaData) {
        if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) {
            mMetaDataBuffersNum++;
        }
    }

    pthread_mutex_unlock(&mLock);

    return DECODE_SUCCESS;

}

void VideoDecoderBase::querySurfaceRenderStatus(VideoSurfaceBuffer* surface) {
    VASurfaceStatus surfStat = VASurfaceReady;
    VAStatus    vaStat = VA_STATUS_SUCCESS;

    if (!surface) {
        LOGW("SurfaceBuffer not ready yet");
        return;
    }
    surface->renderBuffer.driverRenderDone = true;

#ifndef USE_GEN_HW
    if (surface->renderBuffer.surface != VA_INVALID_SURFACE &&
       (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER)) {

        vaStat = vaQuerySurfaceStatus(mVADisplay, surface->renderBuffer.surface, &surfStat);

        if ((vaStat == VA_STATUS_SUCCESS) && (surfStat != VASurfaceReady))
            surface->renderBuffer.driverRenderDone = false;

    }
#endif

}

// This function should be called before start() to load different type of parsers
#if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING)
Decode_Status VideoDecoderBase::setParserType(_vbp_parser_type type) {
    if ((int32_t)type != VBP_INVALID) {
        ITRACE("Parser Type = %d", (int32_t)type);
        mParserType = type;
        return DECODE_SUCCESS;
    } else {
        ETRACE("Invalid parser type = %d", (int32_t)type);
        return DECODE_NO_PARSER;
    }
}

Decode_Status VideoDecoderBase::updateBuffer(uint8_t *buffer, int32_t size, void** vbpData) {
    if (mParserHandle == NULL) {
        return DECODE_NO_PARSER;
    }

    uint32_t vbpStatus;
    if (buffer == NULL || size <= 0) {
        return DECODE_INVALID_DATA;
    }

    vbpStatus = mParserUpdate(mParserHandle, buffer, size, vbpData);
    CHECK_VBP_STATUS("vbp_update");

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::queryBuffer(void** vbpData) {
    if (mParserHandle == NULL) {
        return DECODE_NO_PARSER;
    }

    uint32_t vbpStatus;
    vbpStatus = mParserQuery(mParserHandle, vbpData);
    CHECK_VBP_STATUS("vbp_query");

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderBase::getCodecSpecificConfigs(VAProfile profile, VAConfigID *config) {
    VAStatus vaStatus;
    VAConfigAttrib attrib;
    attrib.type = VAConfigAttribRTFormat;
    attrib.value = VA_RT_FORMAT_YUV420;

    if (config == NULL) {
        ETRACE("Invalid parameter!");
        return DECODE_FAIL;
    }

    vaStatus = vaCreateConfig(
            mVADisplay,
            profile,
            VAEntrypointVLD,
            &attrib,
            1,
            config);

    CHECK_VA_STATUS("vaCreateConfig");

    return DECODE_SUCCESS;
}
#endif
Decode_Status VideoDecoderBase::checkHardwareCapability() {
    return DECODE_SUCCESS;
}

void VideoDecoderBase::drainDecodingErrors(VideoErrorBuffer *outErrBuf, VideoRenderBuffer *currentSurface) {
    if (mErrReportEnabled && outErrBuf && currentSurface) {
        memcpy(outErrBuf, &(currentSurface->errBuf), sizeof(VideoErrorBuffer));

        currentSurface->errBuf.errorNumber = 0;
        currentSurface->errBuf.timeStamp = INVALID_PTS;
    }
    if (outErrBuf)
        VTRACE("%s: error number is %d", __FUNCTION__, outErrBuf->errorNumber);
}

void VideoDecoderBase::fillDecodingErrors(VideoRenderBuffer *currentSurface) {
    VAStatus ret;

    if (mErrReportEnabled) {
        currentSurface->errBuf.timeStamp = currentSurface->timeStamp;
        // TODO: is 10 a suitable number?
        VASurfaceDecodeMBErrors *err_drv_output = NULL;
        ret = vaQuerySurfaceError(mVADisplay, currentSurface->surface, VA_STATUS_ERROR_DECODING_ERROR, (void **)&err_drv_output);
        if (ret || !err_drv_output) {
            WTRACE("vaQuerySurfaceError failed.");
            return;
        }

        int offset =  0x1 & currentSurface->errBuf.errorNumber;// offset is either 0 or 1
        for (int i = 0; i < MAX_ERR_NUM - offset; i++) {
            if (err_drv_output[i].status != -1) {
                currentSurface->errBuf.errorNumber++;
                currentSurface->errBuf.errorArray[i + offset].type = DecodeMBError;
                currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.start_mb = err_drv_output[i].start_mb;
                currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.end_mb = err_drv_output[i].end_mb;
                currentSurface->errBuf.errorArray[i + offset].num_mbs = err_drv_output[i].end_mb - err_drv_output[i].start_mb + 1;
                ITRACE("Error Index[%d]: type = %d, start_mb = %d, end_mb = %d",
                    currentSurface->errBuf.errorNumber - 1,
                    currentSurface->errBuf.errorArray[i + offset].type,
                    currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.start_mb,
                    currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.end_mb);
            } else break;
        }
        ITRACE("%s: error number of current surface is %d, timestamp @%llu",
            __FUNCTION__, currentSurface->errBuf.errorNumber, currentSurface->timeStamp);
    }
}

void VideoDecoderBase::setRotationDegrees(int32_t rotationDegrees) {
    if (mRotationDegrees == rotationDegrees) {
        return;
    }

    ITRACE("set new rotation degree: %d", rotationDegrees);
    VADisplayAttribute rotate;
    rotate.type = VADisplayAttribRotation;
    rotate.value = VA_ROTATION_NONE;
    if (rotationDegrees == 0)
        rotate.value = VA_ROTATION_NONE;
    else if (rotationDegrees == 90)
        rotate.value = VA_ROTATION_90;
    else if (rotationDegrees == 180)
        rotate.value = VA_ROTATION_180;
    else if (rotationDegrees == 270)
        rotate.value = VA_ROTATION_270;

    VAStatus ret = vaSetDisplayAttributes(mVADisplay, &rotate, 1);
    if (ret) {
        ETRACE("Failed to set rotation degree.");
    }
    mRotationDegrees = rotationDegrees;
}

void VideoDecoderBase::setRenderRect() {

    if (!mVADisplay)
        return;

    VAStatus ret;
    VARectangle rect;
    rect.x = mVideoFormatInfo.cropLeft;
    rect.y = mVideoFormatInfo.cropTop;
    rect.width = mVideoFormatInfo.width - (mVideoFormatInfo.cropLeft + mVideoFormatInfo.cropRight);
    rect.height = mVideoFormatInfo.height - (mVideoFormatInfo.cropBottom + mVideoFormatInfo.cropTop);
    if (strcasecmp(mVideoFormatInfo.mimeType,"video/avc") == 0 ||
        strcasecmp(mVideoFormatInfo.mimeType,"video/avc-secure") == 0 ||
        strcasecmp(mVideoFormatInfo.mimeType,"video/h264") == 0) {
        rect.height = mVideoFormatInfo.height;
        rect.width = mVideoFormatInfo.width;
    }

    VADisplayAttribute render_rect;
    render_rect.type = VADisplayAttribRenderRect;
    render_rect.attrib_ptr = &rect;

    ret = vaSetDisplayAttributes(mVADisplay, &render_rect, 1);
    if (ret) {
        ETRACE("Failed to set rotation degree.");
    }
}

void VideoDecoderBase::setColorSpaceInfo(int32_t colorMatrix, int32_t videoRange) {
    ITRACE("set colorMatrix: 0x%x ", colorMatrix);
    VADisplayAttribute cm;
    cm.type = VADisplayAttribCSCMatrix;
    if (colorMatrix == VA_SRC_BT601) {
        cm.attrib_ptr = &s601;
    } else if (colorMatrix == VA_SRC_BT709) {
        cm.attrib_ptr = &s709;
    } else {
      // if we can't get the color matrix or it's not BT601 or BT709
      // we decide the color matrix according to clip resolution
      if (mVideoFormatInfo.width < 1280 && mVideoFormatInfo.height < 720)
          cm.attrib_ptr = &s601;
      else
          cm.attrib_ptr = &s709;
    }

    VAStatus ret = vaSetDisplayAttributes(mVADisplay, &cm, 1);

    if (ret) {
        ETRACE("Failed to set colorMatrix.");
    }

    // 1: full range, 0: reduced range
    ITRACE("set videoRange: %d ", videoRange);
    VADisplayAttribute vr;
    vr.type = VADisplayAttribColorRange;
    vr.value = (videoRange == 1) ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED;

    ret = vaSetDisplayAttributes(mVADisplay, &vr, 1);

    if (ret) {
        ETRACE("Failed to set videoRange.");
    }
}