You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
496 lines
18 KiB
496 lines
18 KiB
/* |
|
* 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> |
|
|
|
#define MAX_PICTURE_WIDTH_VP8 1920 |
|
#define MAX_PICTURE_HEIGHT_VP8 1088 |
|
|
|
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"); |
|
|
|
if (data->codec_data->frame_width > MAX_PICTURE_WIDTH_VP8 || |
|
data->codec_data->frame_height > MAX_PICTURE_HEIGHT_VP8) { |
|
return DECODE_INVALID_DATA; |
|
} |
|
|
|
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"); |
|
|
|
if (data->codec_data->frame_width > MAX_PICTURE_WIDTH_VP8 || |
|
data->codec_data->frame_height > MAX_PICTURE_HEIGHT_VP8) { |
|
return DECODE_INVALID_DATA; |
|
} |
|
|
|
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; |
|
} |
|
|
|
|