| /* |
| * 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."); |
| } |
| |
| // scaling has been performed on the decoded image. |
| mVideoFormatInfo.videoRange = 1; |
| |
| 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(); |
| } |
| |
| 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; |
| } |
| |
| |