| /* |
| * 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 "va_private.h" |
| #include "VideoDecoderAVCSecure.h" |
| #include "VideoDecoderTrace.h" |
| #include <string.h> |
| |
| #define STARTCODE_PREFIX_LEN 3 |
| #define NALU_TYPE_MASK 0x1F |
| #define MAX_NALU_HEADER_BUFFER 8192 |
| static const uint8_t startcodePrefix[STARTCODE_PREFIX_LEN] = {0x00, 0x00, 0x01}; |
| |
| VideoDecoderAVCSecure::VideoDecoderAVCSecure(const char *mimeType) |
| : VideoDecoderAVC(mimeType), |
| mNaluHeaderBuffer(NULL), |
| mSliceHeaderBuffer(NULL) { |
| setParserType(VBP_H264SECURE); |
| } |
| |
| VideoDecoderAVCSecure::~VideoDecoderAVCSecure() { |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::start(VideoConfigBuffer *buffer) { |
| Decode_Status status = VideoDecoderAVC::start(buffer); |
| if (status != DECODE_SUCCESS) { |
| return status; |
| } |
| |
| mNaluHeaderBuffer = new uint8_t [MAX_NALU_HEADER_BUFFER]; |
| |
| if (mNaluHeaderBuffer == NULL) { |
| ETRACE("Failed to allocate memory for mNaluHeaderBuffer"); |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| mSliceHeaderBuffer = new uint8_t [MAX_NALU_HEADER_BUFFER]; |
| if (mSliceHeaderBuffer == NULL) { |
| ETRACE("Failed to allocate memory for mSliceHeaderBuffer"); |
| if (mNaluHeaderBuffer) { |
| delete [] mNaluHeaderBuffer; |
| mNaluHeaderBuffer = NULL; |
| } |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| return status; |
| } |
| |
| void VideoDecoderAVCSecure::stop(void) { |
| VideoDecoderAVC::stop(); |
| |
| if (mNaluHeaderBuffer) { |
| delete [] mNaluHeaderBuffer; |
| mNaluHeaderBuffer = NULL; |
| } |
| |
| if (mSliceHeaderBuffer) { |
| delete [] mSliceHeaderBuffer; |
| mSliceHeaderBuffer = NULL; |
| } |
| |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::decode(VideoDecodeBuffer *buffer) { |
| Decode_Status status; |
| int32_t sizeAccumulated = 0; |
| int32_t sliceHeaderSize = 0; |
| int32_t sizeLeft = 0; |
| int32_t sliceIdx = 0; |
| uint8_t naluType; |
| frame_info_t* pFrameInfo; |
| |
| mFrameSize = 0; |
| if (buffer->flag & IS_SECURE_DATA) { |
| VTRACE("Decoding protected video ..."); |
| mIsEncryptData = 1; |
| } else { |
| VTRACE("Decoding clear video ..."); |
| mIsEncryptData = 0; |
| return VideoDecoderAVC::decode(buffer); |
| } |
| |
| if (buffer->size != sizeof(frame_info_t)) { |
| ETRACE("Not enough data to read frame_info_t!"); |
| return DECODE_INVALID_DATA; |
| } |
| pFrameInfo = (frame_info_t*) buffer->data; |
| |
| mFrameSize = pFrameInfo->length; |
| VTRACE("mFrameSize = %d", mFrameSize); |
| |
| memcpy(&mEncParam, pFrameInfo->pavp, sizeof(pavp_info_t)); |
| for (int32_t i = 0; i < pFrameInfo->num_nalus; i++) { |
| naluType = pFrameInfo->nalus[i].type & NALU_TYPE_MASK; |
| if (naluType >= h264_NAL_UNIT_TYPE_SLICE && naluType <= h264_NAL_UNIT_TYPE_IDR) { |
| memcpy(mSliceHeaderBuffer + sliceHeaderSize, |
| &sliceIdx, |
| sizeof(int32_t)); |
| sliceHeaderSize += 4; |
| |
| memcpy(mSliceHeaderBuffer + sliceHeaderSize, |
| &pFrameInfo->data, |
| sizeof(uint8_t*)); |
| sliceHeaderSize += sizeof(uint8_t*); |
| |
| memcpy(mSliceHeaderBuffer + sliceHeaderSize, |
| &pFrameInfo->nalus[i].offset, |
| sizeof(uint32_t)); |
| sliceHeaderSize += sizeof(uint32_t); |
| |
| memcpy(mSliceHeaderBuffer + sliceHeaderSize, |
| &pFrameInfo->nalus[i].length, |
| sizeof(uint32_t)); |
| sliceHeaderSize += sizeof(uint32_t); |
| |
| memcpy(mSliceHeaderBuffer + sliceHeaderSize, |
| pFrameInfo->nalus[i].slice_header, |
| sizeof(slice_header_t)); |
| sliceHeaderSize += sizeof(slice_header_t); |
| if (pFrameInfo->nalus[i].type & 0x60) { |
| memcpy(mSliceHeaderBuffer+sliceHeaderSize, pFrameInfo->dec_ref_pic_marking, sizeof(dec_ref_pic_marking_t)); |
| } else { |
| memset(mSliceHeaderBuffer+sliceHeaderSize, 0, sizeof(dec_ref_pic_marking_t)); |
| } |
| sliceHeaderSize += sizeof(dec_ref_pic_marking_t); |
| sliceIdx++; |
| } else if (naluType >= h264_NAL_UNIT_TYPE_SEI && naluType <= h264_NAL_UNIT_TYPE_PPS) { |
| memcpy(mNaluHeaderBuffer + sizeAccumulated, |
| startcodePrefix, |
| STARTCODE_PREFIX_LEN); |
| sizeAccumulated += STARTCODE_PREFIX_LEN; |
| memcpy(mNaluHeaderBuffer + sizeAccumulated, |
| pFrameInfo->nalus[i].data, |
| pFrameInfo->nalus[i].length); |
| sizeAccumulated += pFrameInfo->nalus[i].length; |
| } else { |
| WTRACE("Failure: DECODE_FRAME_DROPPED"); |
| return DECODE_FRAME_DROPPED; |
| } |
| } |
| |
| vbp_data_h264 *data = NULL; |
| int new_sequence_to_handle = 0; |
| |
| if (sizeAccumulated > 0) { |
| status = VideoDecoderBase::parseBuffer( |
| mNaluHeaderBuffer, |
| sizeAccumulated, |
| false, |
| (void**)&data); |
| CHECK_STATUS("VideoDecoderBase::parseBuffer"); |
| |
| // [FIX DRC zoom issue] if one buffer contains more than one nalu |
| // for example SPS+PPS+IDR, new_sps/new_pps flags set in parseBuffer |
| // will be flushed in the following updateBuffer. |
| // So that handleNewSequence will not be handled in decodeFrame() |
| if (data->new_sps || data->new_pps) { |
| new_sequence_to_handle = 1; |
| } |
| } |
| |
| if (sliceHeaderSize > 0) { |
| memset(mSliceHeaderBuffer + sliceHeaderSize, 0xFF, 4); |
| sliceHeaderSize += 4; |
| status = VideoDecoderBase::updateBuffer( |
| mSliceHeaderBuffer, |
| sliceHeaderSize, |
| (void**)&data); |
| CHECK_STATUS("VideoDecoderBase::updateBuffer"); |
| |
| // in case the flags were flushed but indeed new sequence needed to be handled. |
| if ((1 == new_sequence_to_handle) && |
| ((data->new_sps == 0) || (data->new_pps == 0))) { |
| data->new_sps = 1; |
| data->new_pps = 1; |
| } |
| } |
| |
| if (data == NULL) { |
| ETRACE("Invalid data returned by parser!"); |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| if (!mVAStarted) { |
| if (data->has_sps && data->has_pps) { |
| status = startVA(data); |
| CHECK_STATUS("startVA"); |
| } else { |
| WTRACE("Can't start VA as either SPS or PPS is still not available."); |
| return DECODE_SUCCESS; |
| } |
| } |
| status = decodeFrame(buffer, data); |
| return status; |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::decodeSlice(vbp_data_h264 *data, uint32_t picIndex, uint32_t sliceIndex) { |
| Decode_Status status; |
| VAStatus vaStatus; |
| uint32_t bufferIDCount = 0; |
| // maximum 4 buffers to render a slice: picture parameter, IQMatrix, slice parameter, slice data |
| VABufferID bufferIDs[5]; |
| |
| vbp_picture_data_h264 *picData = &(data->pic_data[picIndex]); |
| vbp_slice_data_h264 *sliceData = &(picData->slc_data[sliceIndex]); |
| VAPictureParameterBufferH264 *picParam = picData->pic_parms; |
| VASliceParameterBufferH264 *sliceParam = &(sliceData->slc_parms); |
| VAEncryptionParameterBuffer encryptParam; |
| |
| if (sliceParam->first_mb_in_slice == 0 || mDecodingFrame == false) { |
| // either condition indicates start of a new frame |
| if (sliceParam->first_mb_in_slice != 0) { |
| WTRACE("The first slice is lost."); |
| // TODO: handle the first slice lost |
| } |
| if (mDecodingFrame) { |
| // interlace content, complete decoding the first field |
| vaStatus = vaEndPicture(mVADisplay, mVAContext); |
| CHECK_VA_STATUS("vaEndPicture"); |
| |
| // for interlace content, top field may be valid only after the second field is parsed |
| mAcquiredBuffer->pictureOrder= picParam->CurrPic.TopFieldOrderCnt; |
| } |
| |
| // Update the reference frames and surface IDs for DPB and current frame |
| status = updateDPB(picParam); |
| CHECK_STATUS("updateDPB"); |
| |
| vaStatus = vaBeginPicture(mVADisplay, mVAContext, mAcquiredBuffer->renderBuffer.surface); |
| CHECK_VA_STATUS("vaBeginPicture"); |
| |
| // start decoding a frame |
| mDecodingFrame = true; |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VAPictureParameterBufferType, |
| sizeof(VAPictureParameterBufferH264), |
| 1, |
| picParam, |
| &bufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreatePictureParameterBuffer"); |
| bufferIDCount++; |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VAIQMatrixBufferType, |
| sizeof(VAIQMatrixBufferH264), |
| 1, |
| data->IQ_matrix_buf, |
| &bufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateIQMatrixBuffer"); |
| bufferIDCount++; |
| |
| if (mIsEncryptData) { |
| memset(&encryptParam, 0, sizeof(VAEncryptionParameterBuffer)); |
| encryptParam.pavpCounterMode = 4; |
| encryptParam.pavpEncryptionType = 2; |
| encryptParam.hostEncryptMode = 2; |
| encryptParam.pavpHasBeenEnabled = 1; |
| encryptParam.app_id = 0; |
| memcpy(encryptParam.pavpAesCounter, mEncParam.iv, 16); |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| (VABufferType)VAEncryptionParameterBufferType, |
| sizeof(VAEncryptionParameterBuffer), |
| 1, |
| &encryptParam, |
| &bufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateEncryptionParameterBuffer"); |
| bufferIDCount++; |
| } |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceDataBufferType, |
| mFrameSize, //size |
| 1, //num_elements |
| sliceData->buffer_addr + sliceData->slice_offset, |
| &bufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateSliceDataBuffer"); |
| bufferIDCount++; |
| |
| } |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceParameterBufferType, |
| sizeof(VASliceParameterBufferH264Base), |
| 1, |
| sliceParam, |
| &bufferIDs[bufferIDCount]); |
| |
| CHECK_VA_STATUS("vaCreateSliceParameterBuffer"); |
| bufferIDCount++; |
| |
| vaStatus = vaRenderPicture( |
| mVADisplay, |
| mVAContext, |
| bufferIDs, |
| bufferIDCount); |
| CHECK_VA_STATUS("vaRenderPicture"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::getCodecSpecificConfigs( |
| VAProfile profile, VAConfigID *config) |
| { |
| VAStatus vaStatus; |
| VAConfigAttrib attrib[2]; |
| |
| if (config == NULL) { |
| ETRACE("Invalid parameter!"); |
| return DECODE_FAIL; |
| } |
| |
| attrib[0].type = VAConfigAttribRTFormat; |
| attrib[0].value = VA_RT_FORMAT_YUV420; |
| attrib[1].type = VAConfigAttribDecSliceMode; |
| attrib[1].value = VA_DEC_SLICE_MODE_NORMAL; |
| |
| vaStatus = vaGetConfigAttributes(mVADisplay,profile,VAEntrypointVLD, &attrib[1], 1); |
| |
| if (attrib[1].value & VA_DEC_SLICE_MODE_BASE) |
| { |
| ITRACE("AVC short format used"); |
| attrib[1].value = VA_DEC_SLICE_MODE_BASE; |
| } else if (attrib[1].value & VA_DEC_SLICE_MODE_NORMAL) { |
| ITRACE("AVC long format ssed"); |
| attrib[1].value = VA_DEC_SLICE_MODE_NORMAL; |
| } else { |
| ETRACE("Unsupported Decode Slice Mode!"); |
| return DECODE_FAIL; |
| } |
| |
| vaStatus = vaCreateConfig( |
| mVADisplay, |
| profile, |
| VAEntrypointVLD, |
| &attrib[0], |
| 2, |
| config); |
| CHECK_VA_STATUS("vaCreateConfig"); |
| |
| return DECODE_SUCCESS; |
| } |