| /* INTEL CONFIDENTIAL |
| * Copyright (c) 2009-2011 Intel Corporation. All rights reserved. |
| * |
| * The source code contained or described herein and all documents |
| * related to the source code ("Material") are owned by Intel |
| * Corporation or its suppliers or licensors. Title to the |
| * Material remains with Intel Corporation or its suppliers and |
| * licensors. The Material contains trade secrets and proprietary |
| * and confidential information of Intel or its suppliers and |
| * licensors. The Material is protected by worldwide copyright and |
| * trade secret laws and treaty provisions. No part of the Material |
| * may be used, copied, reproduced, modified, published, uploaded, |
| * posted, transmitted, distributed, or disclosed in any way without |
| * Intel's prior express written permission. |
| * |
| * No license under any patent, copyright, trade secret or other |
| * intellectual property right is granted to or conferred upon you |
| * by disclosure or delivery of the Materials, either expressly, by |
| * implication, inducement, estoppel or otherwise. Any license |
| * under such intellectual property rights must be express and |
| * approved by Intel in writing. |
| * |
| */ |
| |
| #include "VideoDecoderAVCSecure.h" |
| #include "VideoDecoderTrace.h" |
| #include <string.h> |
| |
| |
| #define STARTCODE_00 0x00 |
| #define STARTCODE_01 0x01 |
| #define STARTCODE_PREFIX_LEN 3 |
| #define NALU_TYPE_MASK 0x1F |
| |
| |
| // mask for little endian, to mast the second and fourth bytes in the byte stream |
| #define STARTCODE_MASK0 0xFF000000 //0x00FF0000 |
| #define STARTCODE_MASK1 0x0000FF00 //0x000000FF |
| |
| |
| typedef enum { |
| NAL_UNIT_TYPE_unspecified0 = 0, |
| NAL_UNIT_TYPE_SLICE, |
| NAL_UNIT_TYPE_DPA, |
| NAL_UNIT_TYPE_DPB, |
| NAL_UNIT_TYPE_DPC, |
| NAL_UNIT_TYPE_IDR, |
| NAL_UNIT_TYPE_SEI, |
| NAL_UNIT_TYPE_SPS, |
| NAL_UNIT_TYPE_PPS, |
| NAL_UNIT_TYPE_Acc_unit_delimiter, |
| NAL_UNIT_TYPE_EOSeq, |
| NAL_UNIT_TYPE_EOstream, |
| NAL_UNIT_TYPE_filler_data, |
| NAL_UNIT_TYPE_SPS_extension, |
| NAL_UNIT_TYPE_Reserved14, |
| NAL_UNIT_TYPE_Reserved15, |
| NAL_UNIT_TYPE_Reserved16, |
| NAL_UNIT_TYPE_Reserved17, |
| NAL_UNIT_TYPE_Reserved18, |
| NAL_UNIT_TYPE_ACP, |
| NAL_UNIT_TYPE_Reserved20, |
| NAL_UNIT_TYPE_Reserved21, |
| NAL_UNIT_TYPE_Reserved22, |
| NAL_UNIT_TYPE_Reserved23, |
| NAL_UNIT_TYPE_unspecified24, |
| } NAL_UNIT_TYPE; |
| |
| #ifndef min |
| #define min(X, Y) ((X) <(Y) ? (X) : (Y)) |
| #endif |
| |
| |
| static const uint8_t startcodePrefix[STARTCODE_PREFIX_LEN] = {0x00, 0x00, 0x01}; |
| |
| |
| VideoDecoderAVCSecure::VideoDecoderAVCSecure(const char *mimeType) |
| : VideoDecoderAVC(mimeType), |
| mNaluHeaderBuffer(NULL), |
| mInputBuffer(NULL) { |
| |
| memset(&mMetadata, 0, sizeof(NaluMetadata)); |
| memset(&mByteStream, 0, sizeof(NaluByteStream)); |
| } |
| |
| VideoDecoderAVCSecure::~VideoDecoderAVCSecure() { |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::start(VideoConfigBuffer *buffer) { |
| Decode_Status status = VideoDecoderAVC::start(buffer); |
| if (status != DECODE_SUCCESS) { |
| return status; |
| } |
| |
| mMetadata.naluInfo = new NaluInfo [MAX_NALU_NUMBER]; |
| mByteStream.byteStream = new uint8_t [MAX_NALU_HEADER_BUFFER]; |
| mNaluHeaderBuffer = new uint8_t [MAX_NALU_HEADER_BUFFER]; |
| |
| if (mMetadata.naluInfo == NULL || |
| mByteStream.byteStream == NULL || |
| mNaluHeaderBuffer == NULL) { |
| ETRACE("Failed to allocate memory."); |
| // TODO: release all allocated memory |
| return DECODE_MEMORY_FAIL; |
| } |
| return status; |
| } |
| |
| void VideoDecoderAVCSecure::stop(void) { |
| VideoDecoderAVC::stop(); |
| |
| if (mMetadata.naluInfo) { |
| delete [] mMetadata.naluInfo; |
| mMetadata.naluInfo = NULL; |
| } |
| |
| if (mByteStream.byteStream) { |
| delete [] mByteStream.byteStream; |
| mByteStream.byteStream = NULL; |
| } |
| |
| if (mNaluHeaderBuffer) { |
| delete [] mNaluHeaderBuffer; |
| mNaluHeaderBuffer = NULL; |
| } |
| } |
| |
| Decode_Status VideoDecoderAVCSecure::decode(VideoDecodeBuffer *buffer) { |
| Decode_Status status; |
| int32_t sizeAccumulated = 0; |
| int32_t sizeLeft = 0; |
| uint8_t *pByteStream = NULL; |
| NaluInfo *pNaluInfo = mMetadata.naluInfo; |
| |
| if (buffer->flag & IS_SECURE_DATA) { |
| pByteStream = buffer->data; |
| sizeLeft = buffer->size; |
| mInputBuffer = NULL; |
| } else { |
| status = parseAnnexBStream(buffer->data, buffer->size, &mByteStream); |
| CHECK_STATUS("parseAnnexBStream"); |
| pByteStream = mByteStream.byteStream; |
| sizeLeft = mByteStream.streamPos; |
| mInputBuffer = buffer->data; |
| } |
| if (sizeLeft < 4) { |
| ETRACE("Not enough data to read number of NALU."); |
| return DECODE_INVALID_DATA; |
| } |
| |
| // read number of NALU |
| memcpy(&(mMetadata.naluNumber), pByteStream, sizeof(int32_t)); |
| pByteStream += 4; |
| sizeLeft -= 4; |
| |
| if (mMetadata.naluNumber == 0) { |
| WTRACE("Number of NALU is ZERO!"); |
| return DECODE_SUCCESS; |
| } |
| |
| for (int32_t i = 0; i < mMetadata.naluNumber; i++) { |
| if (sizeLeft < 12) { |
| ETRACE("Not enough data to parse NALU offset, size, header length for NALU %d, left = %d", i, sizeLeft); |
| return DECODE_INVALID_DATA; |
| } |
| sizeLeft -= 12; |
| // read NALU offset |
| memcpy(&(pNaluInfo->naluOffset), pByteStream, sizeof(int32_t)); |
| pByteStream += 4; |
| |
| // read NALU size |
| memcpy(&(pNaluInfo->naluLen), pByteStream, sizeof(int32_t)); |
| pByteStream += 4; |
| |
| // read NALU header length |
| memcpy(&(pNaluInfo->naluHeaderLen), pByteStream, sizeof(int32_t)); |
| pByteStream += 4; |
| |
| if (sizeLeft < pNaluInfo->naluHeaderLen) { |
| ETRACE("Not enough data to copy NALU header for %d, left = %d, header len = %d", i, sizeLeft, pNaluInfo->naluHeaderLen); |
| return DECODE_INVALID_DATA; |
| } |
| |
| sizeLeft -= pNaluInfo->naluHeaderLen; |
| |
| if (pNaluInfo->naluHeaderLen) { |
| // copy start code prefix to buffer |
| memcpy(mNaluHeaderBuffer + sizeAccumulated, |
| startcodePrefix, |
| STARTCODE_PREFIX_LEN); |
| sizeAccumulated += STARTCODE_PREFIX_LEN; |
| |
| // copy NALU header |
| memcpy(mNaluHeaderBuffer + sizeAccumulated, pByteStream, pNaluInfo->naluHeaderLen); |
| pByteStream += pNaluInfo->naluHeaderLen; |
| |
| sizeAccumulated += pNaluInfo->naluHeaderLen; |
| } else { |
| WTRACE("header len is zero for NALU %d", i); |
| } |
| |
| // for next NALU |
| pNaluInfo++; |
| } |
| |
| buffer->data = mNaluHeaderBuffer; |
| buffer->size = sizeAccumulated; |
| |
| return VideoDecoderAVC::decode(buffer); |
| } |
| |
| |
| 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[4]; |
| |
| 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); |
| |
| 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; |
| } |
| |
| // Check there is no reference frame loss before decoding a frame |
| |
| // Update the reference frames and surface IDs for DPB and current frame |
| status = updateDPB(picParam); |
| CHECK_STATUS("updateDPB"); |
| |
| //We have to provide a hacked DPB rather than complete DPB for libva as workaround |
| status = updateReferenceFrames(picData); |
| CHECK_STATUS("updateReferenceFrames"); |
| |
| 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++; |
| } |
| |
| status = setReference(sliceParam); |
| CHECK_STATUS("setReference"); |
| |
| // find which naluinfo is correlated to current slice |
| int naluIndex = 0; |
| uint32_t accumulatedHeaderLen = 0; |
| uint32_t headerLen = 0; |
| for (; naluIndex < mMetadata.naluNumber; naluIndex++) { |
| headerLen = mMetadata.naluInfo[naluIndex].naluHeaderLen; |
| if (headerLen == 0) { |
| WTRACE("lenght of current NAL unit is 0."); |
| continue; |
| } |
| accumulatedHeaderLen += STARTCODE_PREFIX_LEN; |
| if (accumulatedHeaderLen + headerLen > sliceData->slice_offset) { |
| break; |
| } |
| accumulatedHeaderLen += headerLen; |
| } |
| |
| if (sliceData->slice_offset != accumulatedHeaderLen) { |
| WTRACE("unexpected slice offset %d, accumulatedHeaderLen = %d", sliceData->slice_offset, accumulatedHeaderLen); |
| } |
| |
| sliceParam->slice_data_size = mMetadata.naluInfo[naluIndex].naluLen; |
| sliceData->slice_size = sliceParam->slice_data_size; |
| |
| // no need to update: |
| // sliceParam->slice_data_offset - 0 always |
| // sliceParam->slice_data_bit_offset - relative to sliceData->slice_offset |
| |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceParameterBufferType, |
| sizeof(VASliceParameterBufferH264), |
| 1, |
| sliceParam, |
| &bufferIDs[bufferIDCount]); |
| CHECK_VA_STATUS("vaCreateSliceParameterBuffer"); |
| bufferIDCount++; |
| |
| // sliceData->slice_offset - accumulatedHeaderLen is the absolute offset to start codes of current NAL unit |
| // offset points to first byte of NAL unit |
| uint32_t sliceOffset = mMetadata.naluInfo[naluIndex].naluOffset; |
| if (mInputBuffer != NULL) { |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VASliceDataBufferType, |
| sliceData->slice_size, //size |
| 1, //num_elements |
| mInputBuffer + sliceOffset, |
| &bufferIDs[bufferIDCount]); |
| } else { |
| vaStatus = vaCreateBuffer( |
| mVADisplay, |
| mVAContext, |
| VAProtectedSliceDataBufferType, |
| sliceData->slice_size, //size |
| 1, //num_elements |
| (uint8_t*)sliceOffset, // IMR offset |
| &bufferIDs[bufferIDCount]); |
| } |
| CHECK_VA_STATUS("vaCreateSliceDataBuffer"); |
| bufferIDCount++; |
| |
| vaStatus = vaRenderPicture( |
| mVADisplay, |
| mVAContext, |
| bufferIDs, |
| bufferIDCount); |
| CHECK_VA_STATUS("vaRenderPicture"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| |
| // Parse byte string pattern "0x000001" (3 bytes) in the current buffer. |
| // Returns offset of position following the pattern in the buffer if pattern is found or -1 if not found. |
| int32_t VideoDecoderAVCSecure::findNalUnitOffset(uint8_t *stream, int32_t offset, int32_t length) { |
| uint8_t *ptr; |
| uint32_t left = 0, data = 0, phase = 0; |
| uint8_t mask1 = 0, mask2 = 0; |
| |
| /* Meaning of phase: |
| 0: initial status, "0x000001" bytes are not found so far; |
| 1: one "0x00" byte is found; |
| 2: two or more consecutive "0x00" bytes" are found; |
| 3: "0x000001" patten is found ; |
| 4: if there is one more byte after "0x000001"; |
| */ |
| |
| left = length; |
| ptr = (uint8_t *) (stream + offset); |
| phase = 0; |
| |
| // parse until there is more data and start code not found |
| while ((left > 0) && (phase < 3)) { |
| // Check if the address is 32-bit aligned & phase=0, if thats the case we can check 4 bytes instead of one byte at a time. |
| if (((((uint32_t)ptr) & 0x3) == 0) && (phase == 0)) { |
| while (left > 3) { |
| data = *((uint32_t *)ptr); |
| mask1 = (STARTCODE_00 != (data & STARTCODE_MASK0)); |
| mask2 = (STARTCODE_00 != (data & STARTCODE_MASK1)); |
| // If second byte and fourth byte are not zero's then we cannot have a start code here, |
| // as we need two consecutive zero bytes for a start code pattern. |
| if (mask1 && mask2) { |
| // skip 4 bytes and start over |
| ptr += 4; |
| left -=4; |
| continue; |
| } else { |
| break; |
| } |
| } |
| } |
| |
| // At this point either data is not on a 32-bit boundary or phase > 0 so we look at one byte at a time |
| if (left > 0) { |
| if (*ptr == STARTCODE_00) { |
| phase++; |
| if (phase > 2) { |
| // more than 2 consecutive '0x00' bytes is found |
| phase = 2; |
| } |
| } else if ((*ptr == STARTCODE_01) && (phase == 2)) { |
| // start code is found |
| phase = 3; |
| } else { |
| // reset lookup |
| phase = 0; |
| } |
| ptr++; |
| left--; |
| } |
| } |
| |
| if ((left > 0) && (phase == 3)) { |
| phase = 4; |
| // return offset of position following the pattern in the buffer which matches "0x000001" byte string |
| return (int32_t)(ptr - stream); |
| } |
| return -1; |
| } |
| |
| |
| Decode_Status VideoDecoderAVCSecure::copyNaluHeader(uint8_t *stream, NaluByteStream *naluStream) { |
| uint8_t naluType; |
| int32_t naluHeaderLen; |
| |
| naluType = *(uint8_t *)(stream + naluStream->naluOffset); |
| naluType &= NALU_TYPE_MASK; |
| // first update nalu header length based on nalu type |
| if (naluType >= NAL_UNIT_TYPE_SLICE && naluType <= NAL_UNIT_TYPE_IDR) { |
| // coded slice, return only up to MAX_SLICE_HEADER_SIZE bytes |
| naluHeaderLen = min(naluStream->naluLen, MAX_SLICE_HEADER_SIZE); |
| } else if (naluType >= NAL_UNIT_TYPE_SEI && naluType <= NAL_UNIT_TYPE_PPS) { |
| //sps, pps, sei, etc, return the entire NAL unit in clear |
| naluHeaderLen = naluStream->naluLen; |
| } else { |
| return DECODE_FRAME_DROPPED; |
| } |
| |
| memcpy(naluStream->byteStream + naluStream->streamPos, &(naluStream->naluOffset), sizeof(int32_t)); |
| naluStream->streamPos += 4; |
| |
| memcpy(naluStream->byteStream + naluStream->streamPos, &(naluStream->naluLen), sizeof(int32_t)); |
| naluStream->streamPos += 4; |
| |
| memcpy(naluStream->byteStream + naluStream->streamPos, &naluHeaderLen, sizeof(int32_t)); |
| naluStream->streamPos += 4; |
| |
| if (naluHeaderLen) { |
| memcpy(naluStream->byteStream + naluStream->streamPos, (uint8_t*)(stream + naluStream->naluOffset), naluHeaderLen); |
| naluStream->streamPos += naluHeaderLen; |
| } |
| return DECODE_SUCCESS; |
| } |
| |
| |
| // parse start-code prefixed stream, also knowns as Annex B byte stream, commonly used in AVI, ES, MPEG2 TS container |
| Decode_Status VideoDecoderAVCSecure::parseAnnexBStream(uint8_t *stream, int32_t length, NaluByteStream *naluStream) { |
| int32_t naluOffset, offset, left; |
| NaluInfo *info; |
| uint32_t ret = DECODE_SUCCESS; |
| |
| naluOffset = 0; |
| offset = 0; |
| left = length; |
| |
| // leave 4 bytes to copy nalu count |
| naluStream->streamPos = 4; |
| naluStream->naluCount = 0; |
| memset(naluStream->byteStream, 0, MAX_NALU_HEADER_BUFFER); |
| |
| for (; ;) { |
| naluOffset = findNalUnitOffset(stream, offset, left); |
| if (naluOffset == -1) { |
| break; |
| } |
| |
| if (naluStream->naluCount == 0) { |
| naluStream->naluOffset = naluOffset; |
| } else { |
| naluStream->naluLen = naluOffset - naluStream->naluOffset - STARTCODE_PREFIX_LEN; |
| ret = copyNaluHeader(stream, naluStream); |
| if (ret != DECODE_SUCCESS && ret != DECODE_FRAME_DROPPED) { |
| LOGW("copyNaluHeader returned %d", ret); |
| return ret; |
| } |
| // starting position for next NALU |
| naluStream->naluOffset = naluOffset; |
| } |
| |
| if (ret == DECODE_SUCCESS) { |
| naluStream->naluCount++; |
| } |
| |
| // update next lookup position and length |
| offset = naluOffset + 1; // skip one byte of NAL unit type |
| left = length - offset; |
| } |
| |
| if (naluStream->naluCount > 0) { |
| naluStream->naluLen = length - naluStream->naluOffset; |
| memcpy(naluStream->byteStream, &(naluStream->naluCount), sizeof(int32_t)); |
| // ignore return value, either DECODE_SUCCESS or DECODE_FRAME_DROPPED |
| copyNaluHeader(stream, naluStream); |
| return DECODE_SUCCESS; |
| } |
| |
| LOGW("number of valid NALU is 0!"); |
| return DECODE_SUCCESS; |
| } |
| |