| /* |
| * 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 "VideoDecoderMPEG2.h" |
| #include "VideoDecoderTrace.h" |
| #include <string.h> |
| |
| #define MAX_PICTURE_WIDTH_MPEG2 1920 |
| #define MAX_PICTURE_HEIGHT_MPEG2 1088 |
| |
| VideoDecoderMPEG2::VideoDecoderMPEG2(const char *mimeType) |
| : VideoDecoderBase(mimeType, VBP_MPEG2), |
| mBufferIDs(NULL), |
| mNumBufferIDs(0) { |
| //do nothing |
| } |
| |
| VideoDecoderMPEG2::~VideoDecoderMPEG2() { |
| stop(); |
| } |
| |
| Decode_Status VideoDecoderMPEG2::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_mpeg2 *data = NULL; |
| status = VideoDecoderBase::parseBuffer( |
| buffer->data, |
| buffer->size, |
| true, // config flag |
| (void**)&data); |
| CHECK_STATUS("VideoDecoderBase::parseBuffer"); |
| |
| if (data->codec_data->frame_width > MAX_PICTURE_WIDTH_MPEG2 || |
| data->codec_data->frame_height > MAX_PICTURE_HEIGHT_MPEG2) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| status = startVA(data); |
| return status; |
| } |
| |
| void VideoDecoderMPEG2::stop(void) { |
| if (mBufferIDs) { |
| delete [] mBufferIDs; |
| mBufferIDs = NULL; |
| } |
| mNumBufferIDs = 0; |
| |
| VideoDecoderBase::stop(); |
| } |
| |
| Decode_Status VideoDecoderMPEG2::decode(VideoDecodeBuffer *buffer) { |
| Decode_Status status; |
| vbp_data_mpeg2 *data = NULL; |
| bool useGraphicbuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER; |
| |
| if (buffer == NULL) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| #ifdef DUMP_INPUT_BUFFER |
| if (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) { |
| DumpInputBuffer(buffer, "mpeg2"); |
| } |
| #endif |
| |
| buffer->ext = NULL; |
| status = VideoDecoderBase::parseBuffer( |
| buffer->data, |
| buffer->size, |
| false, // config flag |
| (void**)&data); |
| CHECK_STATUS("VideoDecoderBase::parseBuffer"); |
| |
| if (data->codec_data->frame_width > MAX_PICTURE_WIDTH_MPEG2 || |
| data->codec_data->frame_height > MAX_PICTURE_HEIGHT_MPEG2) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| if (!mVAStarted) { |
| status = startVA(data); |
| CHECK_STATUS("startVA"); |
| } |
| |
| if (mSizeChanged && !useGraphicbuffer) { |
| // some container has the incorrect width/height. |
| // send the format change to OMX to update the crop info. |
| mSizeChanged = false; |
| ITRACE("Video size is changed during startVA"); |
| return DECODE_FORMAT_CHANGE; |
| } |
| |
| if ((mVideoFormatInfo.width != (uint32_t)data->codec_data->frame_width || |
| mVideoFormatInfo.height != (uint32_t)data->codec_data->frame_height) && |
| (data->codec_data->frame_width > 0) && (data->codec_data->frame_height)) { |
| // update encoded image size |
| ITRACE("Video size is changed. from %dx%d to %dx%d\n",mVideoFormatInfo.width,mVideoFormatInfo.height, data->codec_data->frame_width,data->codec_data->frame_height); |
| if (useGraphicbuffer && mStoreMetaData) { |
| pthread_mutex_lock(&mFormatLock); |
| } |
| mVideoFormatInfo.width = data->codec_data->frame_width; |
| mVideoFormatInfo.height = data->codec_data->frame_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); |
| } |
| } |
| |
| if (needFlush) { |
| if (mStoreMetaData) { |
| status = endDecodingFrame(false); |
| CHECK_STATUS("endDecodingFrame"); |
| } else { |
| flushSurfaceBuffers(); |
| } |
| mSizeChanged = false; |
| return DECODE_FORMAT_CHANGE; |
| } else { |
| mSizeChanged = true; |
| } |
| |
| setRenderRect(); |
| } else { |
| if (useGraphicbuffer && mStoreMetaData) { |
| mVideoFormatInfo.valid = true; |
| } |
| } |
| |
| VideoDecoderBase::setRotationDegrees(buffer->rotationDegrees); |
| |
| status = decodeFrame(buffer, data); |
| CHECK_STATUS("decodeFrame"); |
| |
| return status; |
| } |
| |
| void VideoDecoderMPEG2::flush(void) { |
| VideoDecoderBase::flush(); |
| } |
| |
| Decode_Status VideoDecoderMPEG2::decodeFrame(VideoDecodeBuffer *buffer, vbp_data_mpeg2 *data) { |
| Decode_Status status; |
| // check if any slice is parsed, we may just receive configuration data |
| if (data->num_pictures == 0 || data->pic_data == NULL) { |
| WTRACE("Number of pictures is 0, buffer contains configuration data only?"); |
| 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 == MPEG2_PICTURE_TYPE_I || frameType == MPEG2_PICTURE_TYPE_P); |
| |
| if (data->num_pictures > 1) { |
| if (data->pic_data[0].pic_parms->picture_coding_extension.bits.picture_structure == MPEG2_PIC_STRUCT_TOP) |
| { |
| mAcquiredBuffer->renderBuffer.scanFormat = VA_TOP_FIELD; |
| } else { |
| mAcquiredBuffer->renderBuffer.scanFormat = VA_BOTTOM_FIELD; |
| } |
| } else { |
| 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; |
| } |
| |
| for (uint32_t index = 0; index < data->num_pictures; index++) { |
| status = decodePicture(data, index); |
| if (status != DECODE_SUCCESS) { |
| endDecodingFrame(true); |
| return status; |
| } |
| } |
| |
| // if sample is successfully decoded, call outputSurfaceBuffer(); otherwise |
| // call releaseSurfacebuffer(); |
| status = outputSurfaceBuffer(); |
| return status; |
| } |
| |
| Decode_Status VideoDecoderMPEG2::decodePicture(vbp_data_mpeg2 *data, int picIndex) { |
| Decode_Status status; |
| VAStatus vaStatus; |
| uint32_t bufferIDCount = 0; |
| |
| vbp_picture_data_mpeg2 *picData = &(data->pic_data[picIndex]); |
| VAPictureParameterBufferMPEG2 *picParam = picData->pic_parms; |
| |
| status = allocateVABufferIDs(picData->num_slices * 2 + 2); |
| CHECK_STATUS("allocateVABufferIDs") |
| |
| // send picture parametre for each slice |
| status = setReference(picParam); |
| 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(VAPictureParameterBufferMPEG2), |
| 1, |
| picParam, |
| &mBufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreatePictureParameterBuffer"); |
| bufferIDCount++; |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VAIQMatrixBufferType, |
| sizeof(VAIQMatrixBufferMPEG2), |
| 1, |
| data->iq_matrix_buffer, |
| &mBufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateIQMatrixBuffer"); |
| bufferIDCount++; |
| |
| for (uint32_t i = 0; i < picData->num_slices; i++) { |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceParameterBufferType, |
| sizeof(VASliceParameterBufferMPEG2), |
| 1, |
| &(picData->slice_data[i].slice_param), |
| &mBufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateSliceParameterBuffer"); |
| bufferIDCount++; |
| |
| // 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 VASliceParameterBufferMPEG2 |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceDataBufferType, |
| picData->slice_data[i].slice_size, //size |
| 1, //num_elements |
| picData->slice_data[i].buffer_addr + picData->slice_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 VideoDecoderMPEG2::setReference(VAPictureParameterBufferMPEG2 *picParam) { |
| switch (picParam->picture_coding_type) { |
| case MPEG2_PICTURE_TYPE_I: |
| picParam->forward_reference_picture = VA_INVALID_SURFACE; |
| picParam->backward_reference_picture = VA_INVALID_SURFACE; |
| break; |
| case MPEG2_PICTURE_TYPE_P: |
| if (mLastReference != NULL) { |
| picParam->forward_reference_picture = mLastReference->renderBuffer.surface; |
| } else { |
| VTRACE("%s: no reference frame, but keep decoding", __FUNCTION__); |
| picParam->forward_reference_picture = VA_INVALID_SURFACE; |
| } |
| picParam->backward_reference_picture = VA_INVALID_SURFACE; |
| break; |
| case MPEG2_PICTURE_TYPE_B: |
| if (mLastReference == NULL || mForwardReference == NULL) { |
| return DECODE_NO_REFERENCE; |
| } else { |
| picParam->forward_reference_picture = mForwardReference->renderBuffer.surface; |
| picParam->backward_reference_picture = mLastReference->renderBuffer.surface; |
| } |
| break; |
| default: |
| // Will never reach here; |
| return DECODE_PARSER_FAIL; |
| } |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderMPEG2::startVA(vbp_data_mpeg2 *data) { |
| updateFormatInfo(data); |
| |
| VAProfile vaProfile; |
| |
| // profile_and_level_indication is 8-bit field |
| // | x | x x x | x x x x| |
| // profile level |
| // profile: 101 - simple |
| // 100 - main |
| // level: 1010 - low |
| // 1000 - main |
| // 0100 - high |
| // 0110 - high 1440 |
| if ((data->codec_data->profile_and_level_indication & 0x70) == 0x50) { |
| vaProfile = VAProfileMPEG2Simple; |
| } else { |
| vaProfile = VAProfileMPEG2Main; |
| } |
| |
| return VideoDecoderBase::setupVA(MPEG2_SURFACE_NUMBER, vaProfile); |
| } |
| |
| Decode_Status VideoDecoderMPEG2::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; |
| } |
| |
| void VideoDecoderMPEG2::updateFormatInfo(vbp_data_mpeg2 *data) { |
| ITRACE("updateFormatInfo: current size: %d x %d, new size: %d x %d", |
| mVideoFormatInfo.width, mVideoFormatInfo.height, |
| data->codec_data->frame_width, |
| data->codec_data->frame_height); |
| |
| mVideoFormatInfo.cropBottom = (data->codec_data->frame_height > mVideoFormatInfo.height) ? |
| (data->codec_data->frame_height - mVideoFormatInfo.height) : 0; |
| mVideoFormatInfo.cropRight = (data->codec_data->frame_width > mVideoFormatInfo.width) ? |
| (data->codec_data->frame_width - mVideoFormatInfo.width) : 0; |
| |
| if ((mVideoFormatInfo.width != (uint32_t)data->codec_data->frame_width || |
| mVideoFormatInfo.height != (uint32_t)data->codec_data->frame_height) && |
| (data->codec_data->frame_width > 0) && (data->codec_data->frame_height)) { |
| // update encoded image size |
| mVideoFormatInfo.width = data->codec_data->frame_width; |
| mVideoFormatInfo.height = data->codec_data->frame_height; |
| mSizeChanged = true; |
| ITRACE("Video size is changed."); |
| } |
| |
| // video_range has default value of 0. Y ranges from 16 to 235. |
| mVideoFormatInfo.videoRange = data->codec_data->video_range; |
| |
| switch (data->codec_data->matrix_coefficients) { |
| case 1: |
| mVideoFormatInfo.colorMatrix = VA_SRC_BT709; |
| break; |
| |
| // ITU-R Recommendation BT.470-6 System B, G (MP4), same as |
| // SMPTE 170M/BT601 |
| case 5: |
| 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->codec_data->par_width; |
| mVideoFormatInfo.aspectY = data->codec_data->par_height; |
| mVideoFormatInfo.bitrate = data->codec_data->bit_rate; |
| mVideoFormatInfo.valid = true; |
| |
| setRenderRect(); |
| } |
| |
| Decode_Status VideoDecoderMPEG2::checkHardwareCapability() { |
| VAStatus vaStatus; |
| VAConfigAttrib cfgAttribs[2]; |
| cfgAttribs[0].type = VAConfigAttribMaxPictureWidth; |
| cfgAttribs[1].type = VAConfigAttribMaxPictureHeight; |
| vaStatus = vaGetConfigAttributes(mVADisplay, |
| VAProfileMPEG2Main, |
| 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; |
| } |