| /* |
| * 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 ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) && mStoreMetaData) { |
| pthread_mutex_lock(&mFormatLock); |
| } |
| |
| 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; |
| } |
| |
| // video_range has default value of 0. Y ranges from 16 to 235. |
| mVideoFormatInfo.videoRange = 0; |
| |
| switch (data->codec_data->clr_type) { |
| case 0: |
| mVideoFormatInfo.colorMatrix = VA_SRC_BT601; |
| break; |
| case 1: |
| default: |
| mVideoFormatInfo.colorMatrix = 0; |
| break; |
| } |
| |
| 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); |
| |
| if ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) && mStoreMetaData) { |
| if (mSizeChanged) { |
| mVideoFormatInfo.valid = false; |
| } else { |
| mVideoFormatInfo.valid = true; |
| } |
| |
| pthread_mutex_unlock(&mFormatLock); |
| } else { |
| mVideoFormatInfo.valid = true; |
| } |
| |
| setRenderRect(); |
| setColorSpaceInfo(mVideoFormatInfo.colorMatrix, mVideoFormatInfo.videoRange); |
| } |
| |
| 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) { |
| updateFormatInfo(data); |
| if (mSizeChanged && !(mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER)) { |
| mSizeChanged = false; |
| return DECODE_FORMAT_CHANGE; |
| } |
| |
| bool needFlush = false; |
| if (useGraphicbuffer) { |
| // For VP8 in adaptive playback legacy mode, |
| // force buffer reallocation. |
| needFlush = mSizeChanged; |
| } |
| if (needFlush) { |
| if (mStoreMetaData) { |
| status = endDecodingFrame(false); |
| CHECK_STATUS("endDecodingFrame"); |
| } else { |
| 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; |
| } |
| |