blob: 52a5285ab2a88a0c8a5a0b5763b71e2cf726c2ca [file] [log] [blame]
/*
* 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;
}