/*
* 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 "VideoDecoderWMV.h"
#include "VideoDecoderTrace.h"
#include <string.h>
VideoDecoderWMV::VideoDecoderWMV(const char *mimeType)
: VideoDecoderBase(mimeType, VBP_VC1),
mBufferIDs(NULL),
mNumBufferIDs(0),
mConfigDataParsed(false),
mRangeMapped(false),
mDeblockedCurrPicIndex(0),
mDeblockedLastPicIndex(1),
mDeblockedForwardPicIndex(2) {
}
VideoDecoderWMV::~VideoDecoderWMV() {
stop();
}
Decode_Status VideoDecoderWMV::start(VideoConfigBuffer *buffer) {
Decode_Status status;
status = VideoDecoderBase::start(buffer);
CHECK_STATUS("VideoDecoderBase::start");
if (buffer->data == NULL || buffer->size == 0) {
WTRACE("No config data to start VA.");
return DECODE_SUCCESS;
}
vbp_data_vc1 *data = NULL;
status = parseBuffer(buffer->data, buffer->size, &data);
CHECK_STATUS("parseBuffer");
status = startVA(data);
return status;
}
void VideoDecoderWMV::stop(void) {
if (mBufferIDs) {
delete [] mBufferIDs;
mBufferIDs = NULL;
}
mNumBufferIDs = 0;
mConfigDataParsed = false;
mRangeMapped = false;
mDeblockedCurrPicIndex = 0;
mDeblockedLastPicIndex = 1;
mDeblockedForwardPicIndex = 2;
VideoDecoderBase::stop();
}
void VideoDecoderWMV::flush(void) {
VideoDecoderBase::flush();
mRangeMapped = false;
mDeblockedCurrPicIndex = 0;
mDeblockedLastPicIndex = 1;
mDeblockedForwardPicIndex = 2;
}
Decode_Status VideoDecoderWMV::decode(VideoDecodeBuffer *buffer) {
Decode_Status status;
vbp_data_vc1 *data = NULL;
bool useGraphicbuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;
if (buffer == NULL) {
return DECODE_INVALID_DATA;
}
status = parseBuffer(buffer->data, buffer->size, &data);
CHECK_STATUS("parseBuffer");
if (!mVAStarted) {
status = startVA(data);
CHECK_STATUS("startVA");
}
if (mSizeChanged && !useGraphicbuffer) {
mSizeChanged = false;
return DECODE_FORMAT_CHANGE;
}
if ((mVideoFormatInfo.width != data->se_data->CODED_WIDTH ||
mVideoFormatInfo.height != data->se_data->CODED_HEIGHT) &&
data->se_data->CODED_WIDTH &&
data->se_data->CODED_HEIGHT) {
ITRACE("video size is changed from %dx%d to %dx%d", mVideoFormatInfo.width, mVideoFormatInfo.height,
data->se_data->CODED_WIDTH, data->se_data->CODED_HEIGHT);
if (useGraphicbuffer && mStoreMetaData) {
pthread_mutex_lock(&mFormatLock);
}
mVideoFormatInfo.width = data->se_data->CODED_WIDTH;
mVideoFormatInfo.height = data->se_data->CODED_HEIGHT;
bool needFlush = false;
if (useGraphicbuffer) {
if (mStoreMetaData) {
needFlush = true;
mVideoFormatInfo.valid = false;
pthread_mutex_unlock(&mFormatLock);
} else {
needFlush = (mVideoFormatInfo.width > mVideoFormatInfo.surfaceWidth)
|| (mVideoFormatInfo.height > mVideoFormatInfo.surfaceHeight);
}
}
setRenderRect();
if (needFlush) {
if (mStoreMetaData) {
status = endDecodingFrame(false);
CHECK_STATUS("endDecodingFrame");
} else {
flushSurfaceBuffers();
}
mSizeChanged = false;
return DECODE_FORMAT_CHANGE;
} else {
mSizeChanged = true;
}
} else {
if (useGraphicbuffer && mStoreMetaData) {
mVideoFormatInfo.valid = true;
}
}
status = decodeFrame(buffer, data);
CHECK_STATUS("decodeFrame");
return status;
}
Decode_Status VideoDecoderWMV::decodeFrame(VideoDecodeBuffer* buffer, vbp_data_vc1 *data) {
Decode_Status status;
mCurrentPTS = buffer->timeStamp;
if (0 == data->num_pictures || NULL == data->pic_data) {
WTRACE("Number of pictures is 0, buffer contains configuration data only?");
return DECODE_SUCCESS;
}
if (data->pic_data[0].picture_is_skipped == VC1_PTYPE_SKIPPED) {
// Do nothing for skip frame as the last frame will be rendered agian by natively
// No needs to handle reference frame neither
return DECODE_SUCCESS;
#if 0
//use the last P or I frame surface for skipped frame and treat it as P frame
if (mLastReference == NULL) {
// TODO: handle this case
WTRACE("The last reference is unavailable to construct skipped frame.");
return DECODE_SUCCESS;
}
status = acquireSurfaceBuffer();
CHECK_STATUS("acquireSurfaceBuffer");
mAcquiredBuffer->renderBuffer.timeStamp = mCurrentPTS;
mAcquiredBuffer->renderBuffer.flag = 0;
mAcquiredBuffer->renderBuffer.scanFormat = mLastReference->renderBuffer.scanFormat;
mAcquiredBuffer->renderBuffer.surface = mLastReference->renderBuffer.surface;
// No need to update mappedData for HW decoding
//mAcquiredBuffer->mappedData.data = mLastReference->mappedData.data;
mAcquiredBuffer->referenceFrame = true;
// let outputSurfaceBuffer handle "asReference" for VC1
status = outputSurfaceBuffer();
return status;
#endif
}
status = acquireSurfaceBuffer();
CHECK_STATUS("acquireSurfaceBuffer");
mAcquiredBuffer->renderBuffer.timeStamp = buffer->timeStamp;
if (buffer->flag & HAS_DISCONTINUITY) {
mAcquiredBuffer->renderBuffer.flag |= HAS_DISCONTINUITY;
}
if (buffer->flag & WANT_DECODE_ONLY) {
mAcquiredBuffer->renderBuffer.flag |= WANT_DECODE_ONLY;
}
if (mSizeChanged) {
mSizeChanged = false;
mAcquiredBuffer->renderBuffer.flag |= IS_RESOLUTION_CHANGE;
}
if (data->num_pictures > 1) {
if (data->pic_data[0].pic_parms->picture_fields.bits.is_first_field) {
mAcquiredBuffer->renderBuffer.scanFormat = VA_TOP_FIELD;
} else {
mAcquiredBuffer->renderBuffer.scanFormat = VA_BOTTOM_FIELD;
}
} else {
mAcquiredBuffer->renderBuffer.scanFormat = VA_FRAME_PICTURE;
}
mRangeMapped = (data->se_data->RANGE_MAPY_FLAG || data->se_data->RANGE_MAPUV_FLAG || data->se_data->RANGERED);
int frameType = data->pic_data[0].pic_parms->picture_fields.bits.picture_type;
mAcquiredBuffer->referenceFrame = (frameType == VC1_PTYPE_I || frameType == VC1_PTYPE_P);
// TODO: handle multiple frames parsed from a sample buffer
int numPictures = (data->num_pictures > 1) ? 2 : 1;
for (int index = 0; index < numPictures; index++) {
status = decodePicture(data, index);
if (status != DECODE_SUCCESS) {
endDecodingFrame(true);
return status;
}
}
if (mRangeMapped) {
updateDeblockedPicIndexes(frameType);
}
// let outputSurfaceBuffer handle "asReference" for VC1
status = outputSurfaceBuffer();
return status;
}
Decode_Status VideoDecoderWMV::decodePicture(vbp_data_vc1 *data, int32_t picIndex) {
VAStatus vaStatus = VA_STATUS_SUCCESS;
Decode_Status status;
int32_t bufferIDCount = 0;
vbp_picture_data_vc1 *picData = &(data->pic_data[picIndex]);
VAPictureParameterBufferVC1 *picParams = picData->pic_parms;
if (picParams == NULL) {
return DECODE_PARSER_FAIL;
}
status = allocateVABufferIDs(picData->num_slices * 2 + 2);
CHECK_STATUS("allocateVABufferIDs");
status = setReference(picParams, picIndex, mAcquiredBuffer->renderBuffer.surface);
CHECK_STATUS("setReference");
if (mRangeMapped) {
// keep the destination surface for the picture after decoding and in-loop filtering
picParams->inloop_decoded_picture = mExtraSurfaces[mDeblockedCurrPicIndex];
} else {
picParams->inloop_decoded_picture = VA_INVALID_SURFACE;
}
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(VAPictureParameterBufferVC1),
1,
picParams,
&mBufferIDs[bufferIDCount]);
CHECK_VA_STATUS("vaCreatePictureParameterBuffer");
bufferIDCount++;
if (picParams->bitplane_present.value) {
vaStatus = vaCreateBuffer(
mVADisplay,
mVAContext,
VABitPlaneBufferType,
picData->size_bitplanes,
1,
picData->packed_bitplanes,
&mBufferIDs[bufferIDCount]);
CHECK_VA_STATUS("vaCreateBitPlaneBuffer");
bufferIDCount++;
}
for (uint32_t i = 0; i < picData->num_slices; i++) {
vaStatus = vaCreateBuffer(
mVADisplay,
mVAContext,
VASliceParameterBufferType,
sizeof(VASliceParameterBufferVC1),
1,
&(picData->slc_data[i].slc_parms),
&mBufferIDs[bufferIDCount]);
CHECK_VA_STATUS("vaCreateSliceParameterBuffer");
bufferIDCount++;
vaStatus = vaCreateBuffer(
mVADisplay,
mVAContext,
VASliceDataBufferType,
//size
picData->slc_data[i].slice_size,
//num_elements
1,
//slice data buffer pointer
//Note that this is the original data buffer ptr;
// offset to the actual slice data is provided in
// slice_data_offset in VASliceParameterBufferVC1
picData->slc_data[i].buffer_addr + picData->slc_data[i].slice_offset,
&mBufferIDs[bufferIDCount]);
CHECK_VA_STATUS("vaCreateSliceDataBuffer");
bufferIDCount++;
}
vaStatus = vaRenderPicture(
mVADisplay,
mVAContext,
mBufferIDs,
bufferIDCount);
CHECK_VA_STATUS("vaRenderPicture");
vaStatus = vaEndPicture(mVADisplay, mVAContext);
mDecodingFrame = false;
CHECK_VA_STATUS("vaRenderPicture");
return DECODE_SUCCESS;
}
Decode_Status VideoDecoderWMV::setReference(
VAPictureParameterBufferVC1 *params,
int32_t picIndex,
VASurfaceID current) {
int frameType = params->picture_fields.bits.picture_type;
switch (frameType) {
case VC1_PTYPE_I:
params->forward_reference_picture = current;
params->backward_reference_picture = current;
break;
case VC1_PTYPE_P:
// check REFDIST in the picture parameter buffer
if (0 != params->reference_fields.bits.reference_distance_flag &&
0 != params->reference_fields.bits.reference_distance) {
/* The previous decoded frame (distance is up to 16 but not 0) is used
for reference. Not supported here.
*/
return DECODE_NO_REFERENCE;
}
if (1 == picIndex) {
// handle interlace field coding case
if (1 == params->reference_fields.bits.num_reference_pictures ||
1 == params->reference_fields.bits.reference_field_pic_indicator) {
/*
two reference fields or the second closest I/P field is used for
prediction. Set forward reference picture to INVALID so it will be
updated to a valid previous reconstructed reference frame later.
*/
params->forward_reference_picture = VA_INVALID_SURFACE;
} else {
/* the closest I/P is used for reference so it must be the
complementary field in the same surface.
*/
params->forward_reference_picture = current;
}
}
if (VA_INVALID_SURFACE == params->forward_reference_picture) {
if (mLastReference == NULL) {
return DECODE_NO_REFERENCE;
}
params->forward_reference_picture = mLastReference->renderBuffer.surface;
}
params->backward_reference_picture = VA_INVALID_SURFACE;
break;
case VC1_PTYPE_B:
if (mForwardReference == NULL || mLastReference == NULL) {
return DECODE_NO_REFERENCE;
}
params->forward_reference_picture = mForwardReference->renderBuffer.surface;
params->backward_reference_picture = mLastReference->renderBuffer.surface;
break;
case VC1_PTYPE_BI:
params->forward_reference_picture = VA_INVALID_SURFACE;
params->backward_reference_picture = VA_INVALID_SURFACE;
break;
case VC1_PTYPE_SKIPPED:
//Will never happen here
break;
default:
break;
}
return DECODE_SUCCESS;
}
void VideoDecoderWMV::updateDeblockedPicIndexes(int frameType) {
int32_t curPicIndex = mDeblockedCurrPicIndex;
/* Out Loop (range map) buffers */
if (frameType != VC1_PTYPE_SKIPPED) {
if ((frameType == VC1_PTYPE_I) || (frameType == VC1_PTYPE_P)) {
mDeblockedCurrPicIndex = mDeblockedLastPicIndex;
mDeblockedLastPicIndex = curPicIndex;
} else {
mDeblockedCurrPicIndex = mDeblockedForwardPicIndex;
mDeblockedForwardPicIndex = curPicIndex;
}
}
}
Decode_Status VideoDecoderWMV::updateConfigData(
uint8_t *configData,
int32_t configDataLen,
uint8_t **newConfigData,
int32_t* newConfigDataLen) {
int32_t i = 0;
uint8_t *p = configData;
/* Check for start codes. If one exist, then this is VC-1 and not WMV. */
while (i < configDataLen - 2) {
if ((p[i] == 0) &&
(p[i + 1] == 0) &&
(p[i + 2] == 1)) {
*newConfigData = NULL;
*newConfigDataLen = 0;
return DECODE_SUCCESS;
}
i++;
}
*newConfigDataLen = configDataLen + 9;
p = *newConfigData = new uint8_t [*newConfigDataLen];
if (!p) {
return DECODE_MEMORY_FAIL;
}
/* If we get here we have 4+ bytes of codec data that must be formatted */
/* to pass through as an RCV sequence header. */
p[0] = 0;
p[1] = 0;
p[2] = 1;
p[3] = 0x0f; /* Start code. */
p[4] = (mVideoFormatInfo.width >> 8) & 0x0ff;
p[5] = mVideoFormatInfo.width & 0x0ff;
p[6] = (mVideoFormatInfo.height >> 8) & 0x0ff;
p[7] = mVideoFormatInfo.height & 0x0ff;
memcpy(p + 8, configData, configDataLen);
*(p + configDataLen + 8) = 0x80;
return DECODE_SUCCESS;
}
Decode_Status VideoDecoderWMV::startVA(vbp_data_vc1 *data) {
updateFormatInfo(data);
VAProfile vaProfile;
switch (data->se_data->PROFILE) {
case 0:
vaProfile = VAProfileVC1Simple;
break;
case 1:
vaProfile = VAProfileVC1Main;
break;
default:
vaProfile = VAProfileVC1Advanced;
break;
}
return VideoDecoderBase::setupVA(VC1_SURFACE_NUMBER, vaProfile, VC1_EXTRA_SURFACE_NUMBER);
}
void VideoDecoderWMV::updateFormatInfo(vbp_data_vc1 *data) {
ITRACE("updateFormatInfo: current size: %d x %d, new size: %d x %d",
mVideoFormatInfo.width, mVideoFormatInfo.height,
data->se_data->CODED_WIDTH, data->se_data->CODED_HEIGHT);
mVideoFormatInfo.cropBottom = data->se_data->CODED_HEIGHT > mVideoFormatInfo.height ?
data->se_data->CODED_HEIGHT - mVideoFormatInfo.height : 0;
mVideoFormatInfo.cropRight = data->se_data->CODED_WIDTH > mVideoFormatInfo.width ?
data->se_data->CODED_WIDTH - mVideoFormatInfo.width : 0;
if ((mVideoFormatInfo.width != data->se_data->CODED_WIDTH ||
mVideoFormatInfo.height != data->se_data->CODED_HEIGHT) &&
data->se_data->CODED_WIDTH &&
data->se_data->CODED_HEIGHT) {
// encoded image size
mVideoFormatInfo.width = data->se_data->CODED_WIDTH;
mVideoFormatInfo.height = data->se_data->CODED_HEIGHT;
mSizeChanged = true;
ITRACE("Video size is changed.");
}
// video_range has default value of 0. Y ranges from 16 to 235.
mVideoFormatInfo.videoRange = 0;
switch (data->se_data->MATRIX_COEF) {
case 1:
mVideoFormatInfo.colorMatrix = VA_SRC_BT709;
break;
// ITU-R BT.1700, ITU-R BT.601-5, and SMPTE 293M-1996.
case 6:
mVideoFormatInfo.colorMatrix = VA_SRC_BT601;
break;
default:
// unknown color matrix, set to 0 so color space flag will not be set.
mVideoFormatInfo.colorMatrix = 0;
break;
}
mVideoFormatInfo.aspectX = data->se_data->ASPECT_HORIZ_SIZE;
mVideoFormatInfo.aspectY = data->se_data->ASPECT_VERT_SIZE;
mVideoFormatInfo.bitrate = 0; //data->se_data->bitrate;
mVideoFormatInfo.valid = true;
setRenderRect();
setColorSpaceInfo(mVideoFormatInfo.colorMatrix, mVideoFormatInfo.videoRange);
}
Decode_Status VideoDecoderWMV::allocateVABufferIDs(int32_t number) {
if (mNumBufferIDs > number) {
return DECODE_SUCCESS;
}
if (mBufferIDs) {
delete [] mBufferIDs;
}
mBufferIDs = NULL;
mNumBufferIDs = 0;
mBufferIDs = new VABufferID [number];
if (mBufferIDs == NULL) {
return DECODE_MEMORY_FAIL;
}
mNumBufferIDs = number;
return DECODE_SUCCESS;
}
Decode_Status VideoDecoderWMV::parseBuffer(uint8_t *data, int32_t size, vbp_data_vc1 **vbpData) {
Decode_Status status;
if (data == NULL || size == 0) {
return DECODE_INVALID_DATA;
}
if (mConfigDataParsed) {
status = VideoDecoderBase::parseBuffer(data, size, false, (void**)vbpData);
CHECK_STATUS("VideoDecoderBase::parseBuffer");
} else {
uint8_t *newData = NULL;
int32_t newSize = 0;
status = updateConfigData(data, size, &newData, &newSize);
CHECK_STATUS("updateConfigData");
if (newSize) {
status = VideoDecoderBase::parseBuffer(newData, newSize, true, (void**)vbpData);
delete [] newData;
} else {
status = VideoDecoderBase::parseBuffer(data, size, true, (void**)vbpData);
}
CHECK_STATUS("VideoDecoderBase::parseBuffer");
mConfigDataParsed = true;
}
return DECODE_SUCCESS;
}
Decode_Status VideoDecoderWMV::checkHardwareCapability() {
#ifndef USE_GEN_HW
VAStatus vaStatus;
VAConfigAttrib cfgAttribs[2];
cfgAttribs[0].type = VAConfigAttribMaxPictureWidth;
cfgAttribs[1].type = VAConfigAttribMaxPictureHeight;
vaStatus = vaGetConfigAttributes(mVADisplay, VAProfileVC1Advanced,
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;
}
#endif
return DECODE_SUCCESS;
}