C++程序  |  450行  |  16.18 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 "VideoDecoderVP8.h"
#include "VideoDecoderTrace.h"
#include <string.h>

VideoDecoderVP8::VideoDecoderVP8(const char *mimeType)
    : VideoDecoderBase(mimeType, VBP_VP8) {
    invalidateReferenceFrames(0);
    invalidateReferenceFrames(1);
}

VideoDecoderVP8::~VideoDecoderVP8() {
    stop();
}

void VideoDecoderVP8::invalidateReferenceFrames(int toggle) {
    ReferenceFrameBuffer *p = mRFBs[toggle];
    for (int i = 0; i < VP8_REF_SIZE; i++) {
        p->index = (uint32_t) -1;
        p->surfaceBuffer = NULL;
        p++;
    }
}

void VideoDecoderVP8::clearAsReference(int toggle, int ref_type) {
    ReferenceFrameBuffer ref = mRFBs[toggle][ref_type];
    if (ref.surfaceBuffer) {
        ref.surfaceBuffer->asReferernce = false;
    }
}

void VideoDecoderVP8::updateFormatInfo(vbp_data_vp8 *data) {
    uint32_t width = data->codec_data->frame_width;
    uint32_t height = data->codec_data->frame_height;
    ITRACE("updateFormatInfo: current size: %d x %d, new size: %d x %d",
            mVideoFormatInfo.width, mVideoFormatInfo.height, width, height);

    if ((mVideoFormatInfo.width != width ||
            mVideoFormatInfo.height != height) &&
            width && height) {
        if ((VideoDecoderBase::alignMB(mVideoFormatInfo.width) != width) ||
            (VideoDecoderBase::alignMB(mVideoFormatInfo.height) != height)) {
            mSizeChanged = true;
            ITRACE("Video size is changed.");
        }
        mVideoFormatInfo.width = width;
        mVideoFormatInfo.height = height;
    }

    mVideoFormatInfo.cropLeft = data->codec_data->crop_left;
    mVideoFormatInfo.cropRight = data->codec_data->crop_right;
    mVideoFormatInfo.cropTop = data->codec_data->crop_top;
    mVideoFormatInfo.cropBottom = data->codec_data->crop_bottom;
    ITRACE("Cropping: left = %d, top = %d, right = %d, bottom = %d", data->codec_data->crop_left, data->codec_data->crop_top, data->codec_data->crop_right, data->codec_data->crop_bottom);

    mVideoFormatInfo.valid = true;

    setRenderRect();
}

Decode_Status VideoDecoderVP8::startVA(vbp_data_vp8 *data) {
    updateFormatInfo(data);

    VAProfile vaProfile = VAProfileVP8Version0_3;
    if (data->codec_data->version_num > 3) {
        return DECODE_PARSER_FAIL;
    }

    enableLowDelayMode(true);

    return VideoDecoderBase::setupVA(VP8_SURFACE_NUMBER + VP8_REF_SIZE, vaProfile);
}

Decode_Status VideoDecoderVP8::start(VideoConfigBuffer *buffer) {
    Decode_Status status;

    status = VideoDecoderBase::start(buffer);
    CHECK_STATUS("VideoDecoderBase::start");

    // We don't want base class to manage reference.
    VideoDecoderBase::ManageReference(false);

    if (buffer->data == NULL || buffer->size == 0) {
        WTRACE("No config data to start VA.");
        return DECODE_SUCCESS;
    }

    vbp_data_vp8 *data = NULL;
    status = VideoDecoderBase::parseBuffer(buffer->data, buffer->size, true, (void**)&data);
    CHECK_STATUS("VideoDecoderBase::parseBuffer");

    status = startVA(data);
    return status;
}

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

    invalidateReferenceFrames(0);
    invalidateReferenceFrames(1);
}

void VideoDecoderVP8::flush(void) {
    VideoDecoderBase::flush();

    invalidateReferenceFrames(0);
    invalidateReferenceFrames(1);
}

Decode_Status VideoDecoderVP8::decode(VideoDecodeBuffer *buffer) {
    Decode_Status status;
    vbp_data_vp8 *data = NULL;
    if (buffer == NULL) {
        ETRACE("VideoDecodeBuffer is NULL.");
        return DECODE_INVALID_DATA;
    }

    status = VideoDecoderBase::parseBuffer(
                 buffer->data,
                 buffer->size,
                 false,
                 (void**)&data);
    CHECK_STATUS("VideoDecoderBase::parseBuffer");

    mShowFrame = data->codec_data->show_frame;

    if (!mVAStarted) {
        status = startVA(data);
        CHECK_STATUS("startVA");
    }

    VideoDecoderBase::setRotationDegrees(buffer->rotationDegrees);

    status = decodeFrame(buffer, data);

    return status;
}

Decode_Status VideoDecoderVP8::decodeFrame(VideoDecodeBuffer* buffer, vbp_data_vp8 *data) {
    Decode_Status status;
    bool useGraphicbuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;
    mCurrentPTS = buffer->timeStamp;
    if (0 == data->num_pictures || NULL == data->pic_data) {
        WTRACE("Number of pictures is 0.");
        return DECODE_SUCCESS;
    }

    if (VP8_KEY_FRAME == data->codec_data->frame_type) {
        if (mSizeChanged && !useGraphicbuffer){
            mSizeChanged = false;
            return DECODE_FORMAT_CHANGE;
        } else {
            updateFormatInfo(data);
            bool noNeedFlush = false;
            if (useGraphicbuffer) {
                noNeedFlush = (mVideoFormatInfo.width <= mVideoFormatInfo.surfaceWidth)
                        && (mVideoFormatInfo.height <= mVideoFormatInfo.surfaceHeight);
            }
            if (mSizeChanged == true && !noNeedFlush) {
                flushSurfaceBuffers();
                mSizeChanged = false;
                return DECODE_FORMAT_CHANGE;
            }
        }
    }

    if (data->codec_data->frame_type == VP8_SKIPPED_FRAME) {
        // Do nothing for skip frame as the last frame will be rendered agian by natively
        return DECODE_SUCCESS;
    }

    status = acquireSurfaceBuffer();
    CHECK_STATUS("acquireSurfaceBuffer");

    // set referenceFrame to true if frame decoded is I/P frame, false otherwise.
    int frameType = data->codec_data->frame_type;
    mAcquiredBuffer->referenceFrame = (frameType == VP8_KEY_FRAME || frameType == VP8_INTER_FRAME);
    // assume it is frame picture.
    mAcquiredBuffer->renderBuffer.scanFormat = VA_FRAME_PICTURE;
    mAcquiredBuffer->renderBuffer.timeStamp = buffer->timeStamp;
    mAcquiredBuffer->renderBuffer.flag = 0;
    if (buffer->flag & WANT_DECODE_ONLY) {
        mAcquiredBuffer->renderBuffer.flag |= WANT_DECODE_ONLY;
    }
    if (mSizeChanged) {
        mSizeChanged = false;
        mAcquiredBuffer->renderBuffer.flag |= IS_RESOLUTION_CHANGE;
    }

    // Here data->num_pictures is always equal to 1
    for (uint32_t index = 0; index < data->num_pictures; index++) {
        status = decodePicture(data, index);
        if (status != DECODE_SUCCESS) {
            endDecodingFrame(true);
            return status;
        }
    }

    if (frameType != VP8_SKIPPED_FRAME) {
        updateReferenceFrames(data);
    }

    // if sample is successfully decoded, call outputSurfaceBuffer(); otherwise
    // call releaseSurfacebuffer();
    status = outputSurfaceBuffer();
    return status;
}

Decode_Status VideoDecoderVP8::decodePicture(vbp_data_vp8 *data, int32_t picIndex) {
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    Decode_Status status;
    uint32_t bufferIDCount = 0;
    VABufferID bufferIDs[5];

    vbp_picture_data_vp8 *picData = &(data->pic_data[picIndex]);
    VAPictureParameterBufferVP8 *picParams = picData->pic_parms;

    status = setReference(picParams);
    CHECK_STATUS("setReference");

    vaStatus = vaBeginPicture(mVADisplay, mVAContext, mAcquiredBuffer->renderBuffer.surface);
    CHECK_VA_STATUS("vaBeginPicture");
    // setting mDecodingFrame to true so vaEndPicture will be invoked to end the picture decoding.
    mDecodingFrame = true;

    vaStatus = vaCreateBuffer(
                   mVADisplay,
                   mVAContext,
                   VAPictureParameterBufferType,
                   sizeof(VAPictureParameterBufferVP8),
                   1,
                   picParams,
                   &bufferIDs[bufferIDCount]);
    CHECK_VA_STATUS("vaCreatePictureParameterBuffer");
    bufferIDCount++;

    vaStatus = vaCreateBuffer(
                   mVADisplay,
                   mVAContext,
                   VAProbabilityBufferType,
                   sizeof(VAProbabilityDataBufferVP8),
                   1,
                   data->prob_data,
                   &bufferIDs[bufferIDCount]);
    CHECK_VA_STATUS("vaCreateProbabilityBuffer");
    bufferIDCount++;

    vaStatus = vaCreateBuffer(
                   mVADisplay,
                   mVAContext,
                   VAIQMatrixBufferType,
                   sizeof(VAIQMatrixBufferVP8),
                   1,
                   data->IQ_matrix_buf,
                   &bufferIDs[bufferIDCount]);
    CHECK_VA_STATUS("vaCreateIQMatrixBuffer");
    bufferIDCount++;

    /* Here picData->num_slices is always equal to 1 */
    for (uint32_t i = 0; i < picData->num_slices; i++) {
        vaStatus = vaCreateBuffer(
                       mVADisplay,
                       mVAContext,
                       VASliceParameterBufferType,
                       sizeof(VASliceParameterBufferVP8),
                       1,
                       &(picData->slc_data[i].slc_parms),
                       &bufferIDs[bufferIDCount]);
        CHECK_VA_STATUS("vaCreateSliceParameterBuffer");
        bufferIDCount++;

        vaStatus = vaCreateBuffer(
                       mVADisplay,
                       mVAContext,
                       VASliceDataBufferType,
                       picData->slc_data[i].slice_size, //size
                       1,        //num_elements
                       picData->slc_data[i].buffer_addr + picData->slc_data[i].slice_offset,
                       &bufferIDs[bufferIDCount]);
        CHECK_VA_STATUS("vaCreateSliceDataBuffer");
        bufferIDCount++;
    }

    vaStatus = vaRenderPicture(
                   mVADisplay,
                   mVAContext,
                   bufferIDs,
                   bufferIDCount);
    CHECK_VA_STATUS("vaRenderPicture");

    vaStatus = vaEndPicture(mVADisplay, mVAContext);
    mDecodingFrame = false;
    CHECK_VA_STATUS("vaEndPicture");

    return DECODE_SUCCESS;
}

Decode_Status VideoDecoderVP8::setReference(VAPictureParameterBufferVP8 *picParam) {
    int frameType = picParam->pic_fields.bits.key_frame;
    switch (frameType) {
    case VP8_KEY_FRAME:
        picParam->last_ref_frame = VA_INVALID_SURFACE;
        picParam->alt_ref_frame = VA_INVALID_SURFACE;
        picParam->golden_ref_frame = VA_INVALID_SURFACE;
        break;
    case VP8_INTER_FRAME:
        if (mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer   == NULL ||
                mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer    == NULL ||
                mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer == NULL) {
            mAcquiredBuffer->renderBuffer.errBuf.errorNumber = 1;
            mAcquiredBuffer->renderBuffer.errBuf.errorArray[0].type = DecodeRefMissing;
            return DECODE_NO_REFERENCE;
        }
        //mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer = mLastReference;
        picParam->last_ref_frame = mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer->renderBuffer.surface;
        picParam->alt_ref_frame = mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer->renderBuffer.surface;
        picParam->golden_ref_frame = mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer->renderBuffer.surface;
        break;
    case VP8_SKIPPED_FRAME:
        // will never happen here
        break;
    default:
        return DECODE_PARSER_FAIL;
    }

    return DECODE_SUCCESS;
}

void VideoDecoderVP8::updateReferenceFrames(vbp_data_vp8 *data) {
    /* Refresh last frame reference buffer using the currently reconstructed frame */
    refreshLastReference(data);

    /* Refresh golden frame reference buffer using the currently reconstructed frame */
    refreshGoldenReference(data);

    /* Refresh alternative frame reference buffer using the currently reconstructed frame */
    refreshAltReference(data);

    /* Update reference frames */
    for (int i = 0; i < VP8_REF_SIZE; i++) {
        VideoSurfaceBuffer *p = mRFBs[1][i].surfaceBuffer;
        int j;
        for (j = 0; j < VP8_REF_SIZE; j++) {
            if (p == mRFBs[0][j].surfaceBuffer) {
                break;
            }
        }
        if (j == VP8_REF_SIZE) {
            clearAsReference(1, i);
        }
    }
}

void VideoDecoderVP8::refreshLastReference(vbp_data_vp8 *data) {
    /* Save previous last reference */
    mRFBs[1][VP8_LAST_REF_PIC].surfaceBuffer = mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer;
    mRFBs[1][VP8_LAST_REF_PIC].index = mRFBs[0][VP8_LAST_REF_PIC].index;

    /* For key frame, this is always true */
    if (data->codec_data->refresh_last_frame) {
        mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer = mAcquiredBuffer;
        mRFBs[0][VP8_LAST_REF_PIC].index = mAcquiredBuffer->renderBuffer.surface;
        mRFBs[0][VP8_LAST_REF_PIC].surfaceBuffer->asReferernce = true;
    }
}

void VideoDecoderVP8::refreshGoldenReference(vbp_data_vp8 *data) {
    /* Save previous golden reference */
    mRFBs[1][VP8_GOLDEN_REF_PIC].surfaceBuffer = mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer;
    mRFBs[1][VP8_GOLDEN_REF_PIC].index = mRFBs[0][VP8_GOLDEN_REF_PIC].index;

    if (data->codec_data->golden_copied != BufferCopied_NoneToGolden) {
        if (data->codec_data->golden_copied == BufferCopied_LastToGolden) {
            /* LastFrame is copied to GoldenFrame */
            mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer = mRFBs[1][VP8_LAST_REF_PIC].surfaceBuffer;
            mRFBs[0][VP8_GOLDEN_REF_PIC].index = mRFBs[1][VP8_LAST_REF_PIC].index;
        } else if (data->codec_data->golden_copied == BufferCopied_AltRefToGolden) {
            /* AltRefFrame is copied to GoldenFrame */
            mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer = mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer;
            mRFBs[0][VP8_GOLDEN_REF_PIC].index = mRFBs[0][VP8_ALT_REF_PIC].index;
        }
    }

    /* For key frame, this is always true */
    if (data->codec_data->refresh_golden_frame) {
        mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer = mAcquiredBuffer;
        mRFBs[0][VP8_GOLDEN_REF_PIC].index = mAcquiredBuffer->renderBuffer.surface;
        mRFBs[0][VP8_GOLDEN_REF_PIC].surfaceBuffer->asReferernce = true;
    }
}

void VideoDecoderVP8::refreshAltReference(vbp_data_vp8 *data) {
    /* Save previous alternative reference */
    mRFBs[1][VP8_ALT_REF_PIC].surfaceBuffer = mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer;
    mRFBs[1][VP8_ALT_REF_PIC].index = mRFBs[0][VP8_ALT_REF_PIC].index;

    if (data->codec_data->altref_copied != BufferCopied_NoneToAltRef) {
        if (data->codec_data->altref_copied == BufferCopied_LastToAltRef) {
            /* LastFrame is copied to AltRefFrame */
            mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer = mRFBs[1][VP8_LAST_REF_PIC].surfaceBuffer;
            mRFBs[0][VP8_ALT_REF_PIC].index = mRFBs[1][VP8_LAST_REF_PIC].index;
        } else if (data->codec_data->altref_copied == BufferCopied_GoldenToAltRef) {
            /* GoldenFrame is copied to AltRefFrame */
            mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer = mRFBs[1][VP8_GOLDEN_REF_PIC].surfaceBuffer;
            mRFBs[0][VP8_ALT_REF_PIC].index = mRFBs[1][VP8_GOLDEN_REF_PIC].index;
        }
    }

    /* For key frame, this is always true */
    if (data->codec_data->refresh_alt_frame) {
        mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer = mAcquiredBuffer;
        mRFBs[0][VP8_ALT_REF_PIC].index = mAcquiredBuffer->renderBuffer.surface;
        mRFBs[0][VP8_ALT_REF_PIC].surfaceBuffer->asReferernce = true;
    }
}


Decode_Status VideoDecoderVP8::checkHardwareCapability() {
    VAStatus vaStatus;
    VAConfigAttrib cfgAttribs[2];
    cfgAttribs[0].type = VAConfigAttribMaxPictureWidth;
    cfgAttribs[1].type = VAConfigAttribMaxPictureHeight;
    vaStatus = vaGetConfigAttributes(mVADisplay, VAProfileVP8Version0_3,
            VAEntrypointVLD, cfgAttribs, 2);
    CHECK_VA_STATUS("vaGetConfigAttributes");
    if (cfgAttribs[0].value * cfgAttribs[1].value < (uint32_t)mVideoFormatInfo.width * (uint32_t)mVideoFormatInfo.height) {
        ETRACE("hardware supports resolution %d * %d smaller than the clip resolution %d * %d",
                cfgAttribs[0].value, cfgAttribs[1].value, mVideoFormatInfo.width, mVideoFormatInfo.height);
        return DECODE_DRIVER_FAIL;
    }

    return DECODE_SUCCESS;
}