diff options
author | Rajesh Poornachandran <rajesh.poornachandran@intel.com> | 2015-04-13 11:25:58 -0700 |
---|---|---|
committer | Patrick Tjin <pattjin@google.com> | 2015-04-30 10:47:53 -0700 |
commit | ce5d181857065512540466b8d6b8944979291ef9 (patch) | |
tree | ef2d46a2271addace25811aa83f7183718714dcb | |
parent | 3417872a45d599ef169a36ce556e2e0fbdf18303 (diff) | |
download | android_hardware_intel_common_omx-components-ce5d181857065512540466b8d6b8944979291ef9.tar.gz android_hardware_intel_common_omx-components-ce5d181857065512540466b8d6b8944979291ef9.tar.bz2 android_hardware_intel_common_omx-components-ce5d181857065512540466b8d6b8944979291ef9.zip |
Porting ProtectedDataBuffer changes in OMXVideoDecoderAVCSecure.
Bug: 19964594
Change-Id: Iebbdda9bda3cf7a541a286c4bed7cee5f80fe748
Signed-off-by: Rajesh Poornachandran <rajesh.poornachandran@intel.com>
4 files changed, 564 insertions, 59 deletions
diff --git a/videocodec/securevideo/moorefield/LogDumpHelper.h b/videocodec/securevideo/moorefield/LogDumpHelper.h new file mode 100644 index 0000000..3a12977 --- /dev/null +++ b/videocodec/securevideo/moorefield/LogDumpHelper.h @@ -0,0 +1,302 @@ +/* +* Copyright (c) 2014 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. +*/ + +#ifndef LOG_DUMP_HELPER_INCLUDED +#define LOG_DUMP_HELPER_INCLUDED + +#ifndef LOG_TAG +#error "Before including this file, must #define LOG_TAG and #include <utils/Log.h>" +#endif + +#if LOG_NDEBUG == 0 + +// We have dump routines for the structures defined in the following files: +#include "VideoFrameInfo.h" +#include "ProtectedDataBuffer.h" + +// The following helps to use these dump routines from command line unit tests: +#ifdef ANDROID +#define DUMP_EOL "" +#else +#define LOGV printf +#define LOGE printf +#define DUMP_EOL "\n" +#endif // ANDROID + +#ifndef log_helper_min +#define log_helper_min(a,b) ((a) < (b) ? (a) : (b)) +#endif // log_helper_min + +static inline void Copy4Bytes(void* dst, const void* src) +{ + // Don't check input pointers for NULL: this is internal function, + // and if you pass NULL to it, your code deserves to crash. + + uint8_t* bdst = (uint8_t*) dst ; + const uint8_t* bsrc = (const uint8_t*) src ; + + *bdst++ = *bsrc++ ; + *bdst++ = *bsrc++ ; + *bdst++ = *bsrc++ ; + *bdst = *bsrc ; +} +// End of Copy4Bytes() + +static void DumpBufferToString(char* str, uint32_t strSize, const uint8_t* start, uint32_t size) +{ + if (str == NULL || strSize == 0 || start == NULL || size == 0) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + char* s = str ; + char* send = str + strSize ; + + const uint8_t* byte = start ; + const uint8_t* end = start + size ; + + while (byte < end && s < send) + { + s += snprintf(s, strSize - (s - str), "%02x ", *byte) ; + ++byte ; + } +} +// End of DumpBufferToString() + +static void DumpNaluDataBuffer(uint32_t nalu, const uint8_t* start, uint32_t size) +{ + if (start == NULL || size == 0) + { + LOGV("NALU-dump: error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + const uint32_t STR_SIZE = 1024 ; + char str[STR_SIZE] = {0} ; + + DumpBufferToString(str, STR_SIZE, start, size) ; + + LOGV("NALU-dump(nalu %u): data: %s" DUMP_EOL, nalu, str) ; +} +// End of DumpNaluDataBuffer() + +static void DumpBuffer(const char* prefix, const uint8_t* start, uint32_t size) +{ + if (start == NULL || size == 0) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + if (prefix == NULL) + { + prefix = "" ; + } + + const uint32_t STR_SIZE = 1024 ; + char str[STR_SIZE] = {0} ; + + DumpBufferToString(str, STR_SIZE, start, size) ; + + LOGV("%s: ptr=%p, size=%u, data=%s" DUMP_EOL, prefix, start, size, str) ; +} +// End of DumpBuffer() + +static void DumpNaluHeaderBuffer(const uint8_t* const start, uint32_t size) +{ + if (start == NULL || size == 0) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + const uint8_t* current = start ; + + uint32_t numNALUs = 0 ; + Copy4Bytes(&numNALUs, current) ; + current += sizeof(numNALUs) ; + + LOGV("NALU-dump: num NALUs = %u\n", numNALUs) ; + + if (numNALUs > MAX_NALUS_IN_FRAME) + { + LOGE("NALU-dump: ERROR, num NALUs is too big (%u)" DUMP_EOL, numNALUs) ; + } + + for (uint32_t nalu = 0; nalu < numNALUs; ++nalu) + { + uint32_t imr_offset = 0 ; + Copy4Bytes(&imr_offset, current) ; + current += sizeof(imr_offset) ; + + uint32_t nalu_size = 0 ; + Copy4Bytes(&nalu_size, current) ; + current += sizeof(nalu_size) ; + + uint32_t data_size = 0 ; + Copy4Bytes(&data_size, current) ; + current += sizeof(data_size) ; + + LOGV("NALU-dump(nalu %u): imr_offset = %u, nalu_size = %u, data_size = %u" DUMP_EOL, + nalu, imr_offset, nalu_size, data_size) ; + + DumpNaluDataBuffer(nalu, current, data_size) ; + + // Skip past the data + current += data_size ; + } + // End of for +} +// End of DumpNaluHeaderBuffer() + +static const char* DrmSchemeToString(uint32_t drmScheme) +{ + switch(drmScheme) + { + case DRM_SCHEME_NONE: + return "None" ; + + case DRM_SCHEME_WV_CLASSIC: + return "WV Classic" ; + + case DRM_SCHEME_WV_MODULAR: + return "WV Modular" ; + +#ifdef DRM_SCHEME_MCAST_SINK + case DRM_SCHEME_MCAST_SINK: + return "MCast Sink" ; +#endif + +#ifdef DRM_SCHEME_PLAYREADY_ASF + case DRM_SCHEME_PLAYREADY_ASF: + return "PlayReady/ASF" ; +#endif + + default: + return "unknown" ; + } +} +// End of DrmSchemeToString() + +static void DumpBuffer2(const char* prefix, const uint8_t* start, uint32_t size) +{ + if (start == NULL || size == 0) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + if (prefix == NULL) + { + prefix = "" ; + } + + const uint32_t STR_SIZE = 1024 ; + char str[STR_SIZE] = {0} ; + + DumpBufferToString(str, STR_SIZE, start, size) ; + + LOGV("%s%s" DUMP_EOL, prefix, str) ; +} +// End of DumpBuffer2() + +static void DumpProtectedDataBuffer(const char* prefix, ProtectedDataBuffer* buf) +{ + if (buf == NULL) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + if (prefix == NULL) { prefix = "" ; } + + const uint32_t MAX_BUFFER_DUMP_LENGTH = 32 ; + + if (buf->magic != PROTECTED_DATA_BUFFER_MAGIC) + { + const uint8_t* p = (uint8_t*) &buf->magic ; + LOGV("%sWrong magic: %02x %02x %02x %02x" DUMP_EOL, prefix, p[0], p[1], p[2], p[3]) ; + return ; + } + + LOGV("%smagic: ok, drmScheme: %u (%s), clear: %u, size: %u, num PES: %u" DUMP_EOL, prefix, + buf->drmScheme, DrmSchemeToString(buf->drmScheme), buf->clear, buf->size, buf->numPesBuffers) ; + + if (buf->numPesBuffers == 0) + { + uint32_t dumpLength = log_helper_min(buf->size, MAX_BUFFER_DUMP_LENGTH) ; + DumpBuffer2("data: ", buf->data, dumpLength) ; + } + else + { + for (uint32_t i = 0; i < buf->numPesBuffers; ++i) + { + const uint32_t STR_SIZE = 1024 ; + char str[STR_SIZE] = {0} ; + + uint32_t dumpLength = log_helper_min(buf->pesBuffers[i].pesSize, MAX_BUFFER_DUMP_LENGTH) ; + + DumpBufferToString(str, STR_SIZE, + buf->data + buf->pesBuffers[i].pesDataOffset, dumpLength) ; + + LOGV("PES %u: streamCounter: %u, inputCounter: %llu, offset: %u, size: %u, PES data: %s" DUMP_EOL, + i, buf->pesBuffers[i].streamCounter, buf->pesBuffers[i].inputCounter, + buf->pesBuffers[i].pesDataOffset, buf->pesBuffers[i].pesSize, str) ; + } + } +} +// End of DumpProtectedDataBuffer + +static void DumpVideoFrameInfo(frame_info_t* fInfo) +{ + if (fInfo == NULL) + { + LOGV("Error: invalid parameters to %s", __FUNCTION__) ; + return ; + } + + LOGV("frame_info_t: data = %p, size = %u, num_nalus = %u", fInfo->data, fInfo->size, fInfo->num_nalus) ; + + for (uint32_t i = 0; i < fInfo->num_nalus; ++i) + { + LOGV("nalu_info_t: type = %#x, offset = %u (%#x), data = %p, length = %u", + fInfo->nalus[i].type, fInfo->nalus[i].offset, fInfo->nalus[i].offset, + fInfo->nalus[i].data, fInfo->nalus[i].length) ; + + if (fInfo->nalus[i].data != NULL && fInfo->nalus[i].length > 0) + { + DumpBuffer2("nalu_info_t::data: ", fInfo->nalus[i].data, fInfo->nalus[i].length) ; + } + } +} +// End of DumpVideoFrameInfo() + +#else + +// Avoid #ifdef around the dump code + +#define DumpBuffer(...) +#define DumpBuffer2(...) +#define DumpNaluHeaderBuffer(...) +#define DumpProtectedDataBuffer(...) +#define DumpVideoFrameInfo(...) + +#define DUMP_EOL + +#endif // LOG_NDEBUG == 0 + +#endif // LOG_DUMP_HELPER_INCLUDED diff --git a/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.cpp b/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.cpp index 1e81645..bf346f5 100755 --- a/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.cpp +++ b/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.cpp @@ -23,18 +23,21 @@ #include <signal.h> #include <pthread.h> +#include "LogDumpHelper.h" #include "VideoFrameInfo.h" +#include "ProtectedDataBuffer.h" + // Be sure to have an equal string in VideoDecoderHost.cpp (libmix) static const char* AVC_MIME_TYPE = "video/avc"; static const char* AVC_SECURE_MIME_TYPE = "video/avc-secure"; +#define INPORT_BUFFER_SIZE sizeof(ProtectedDataBuffer) #define DATA_BUFFER_INITIAL_OFFSET 0 //1024 #define DATA_BUFFER_SIZE (8 * 1024 * 1024) #define KEEP_ALIVE_INTERVAL 5 // seconds #define DRM_KEEP_ALIVE_TIMER 1000000 #define WV_SESSION_ID 0x00000011 -#define NALU_BUFFER_SIZE 8192 #define NALU_HEADER_LENGTH 1024 // THis should be changed to 4K #define FLUSH_WAIT_INTERVAL (30 * 1000) //30 ms @@ -43,17 +46,6 @@ static const char* AVC_SECURE_MIME_TYPE = "video/avc-secure"; #define DRM_SCHEME_CENC 2 #define DRM_SCHEME_PRASF 3 -//#pragma pack(push, 1) -struct DataBuffer { - uint32_t size; - uint8_t *data; - uint8_t clear; - uint32_t drmScheme; - uint32_t session_id; //used by PR only - uint32_t flags; //used by PR only -}; -//#pragma pack(pop) - bool OMXVideoDecoderAVCSecure::EnableIEDSession(bool enable) { if (mDrmDevFd <= 0) { @@ -67,6 +59,7 @@ bool OMXVideoDecoderAVCSecure::EnableIEDSession(bool enable) OMXVideoDecoderAVCSecure::OMXVideoDecoderAVCSecure() : mKeepAliveTimer(0), + mNumInportBuffers(0), mSessionPaused(false){ ALOGV("OMXVideoDecoderAVCSecure is constructed."); if (drm_vendor_api_init(&drm_vendor_api)) { @@ -116,11 +109,6 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::InitInputPortFormatSpecific(OMX_PARAM_PO this->ports[INPORT_INDEX]->SetMemAllocator(MemAllocDataBuffer, MemFreeDataBuffer, this); - for (int i = 0; i < INPORT_ACTUAL_BUFFER_COUNT; i++) { - mDataBufferSlot[i].offset = DATA_BUFFER_INITIAL_OFFSET + i * INPORT_BUFFER_SIZE; - mDataBufferSlot[i].owner = NULL; - } - return OMX_ErrorNone; } @@ -180,7 +168,24 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::ProcessorProcess( int ret_value; OMX_BUFFERHEADERTYPE *pInput = *pBuffers[INPORT_INDEX]; - DataBuffer *dataBuffer = (DataBuffer *)pInput->pBuffer; + ProtectedDataBuffer *dataBuffer = (ProtectedDataBuffer *)pInput->pBuffer; + // Check that we are dealing with the right buffer + if (dataBuffer->magic != PROTECTED_DATA_BUFFER_MAGIC) + { + if (pInput->nFlags & OMX_BUFFERFLAG_CODECCONFIG) + { + // Processing codec data, which is not in ProtectedDataBuffer format + ALOGV("%s: received AVC codec data (%lu bytes).", __FUNCTION__, pInput->nFilledLen); + DumpBuffer2("OMX: AVC codec data: ", pInput->pBuffer, pInput->nFilledLen); + return OMX_ErrorNone; + } + else + { + // Processing non-codec data, but this buffer is not in ProtectedDataBuffer format + ALOGE("%s: protected data buffer pointer %p doesn't have the right magic", __FUNCTION__, dataBuffer); + return OMX_ErrorBadParameter; + } + } if((dataBuffer->drmScheme == DRM_SCHEME_WVC) && (!mKeepAliveTimer)){ struct sigevent sev; @@ -266,7 +271,7 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::PrepareWVCDecodeBuffer(OMX_BUFFERHEADERT ALOGW("buffer offset %u is not zero!!!", buffer->nOffset); } - DataBuffer *dataBuffer = (DataBuffer *)buffer->pBuffer; + ProtectedDataBuffer *dataBuffer = (ProtectedDataBuffer *)buffer->pBuffer; if (dataBuffer->clear) { p->data = dataBuffer->data + buffer->nOffset; p->size = buffer->nFilledLen; @@ -341,7 +346,7 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::PrepareCENCDecodeBuffer(OMX_BUFFERHEADER ALOGW("buffer offset %u is not zero!!!", buffer->nOffset); } - DataBuffer *dataBuffer = (DataBuffer *)buffer->pBuffer; + ProtectedDataBuffer *dataBuffer = (ProtectedDataBuffer *)buffer->pBuffer; p->data = dataBuffer->data; p->size = sizeof(frame_info_t); p->flag |= IS_SECURE_DATA; @@ -366,7 +371,7 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::PreparePRASFDecodeBuffer(OMX_BUFFERHEADE ALOGW("PR:buffer offset %u is not zero!!!", buffer->nOffset); } - DataBuffer *dataBuffer = (DataBuffer *)buffer->pBuffer; + ProtectedDataBuffer *dataBuffer = (ProtectedDataBuffer *)buffer->pBuffer; if (dataBuffer->clear) { p->data = dataBuffer->data + buffer->nOffset; p->size = buffer->nFilledLen; @@ -435,7 +440,26 @@ OMX_ERRORTYPE OMXVideoDecoderAVCSecure::PrepareDecodeBuffer(OMX_BUFFERHEADERTYPE return OMX_ErrorNone; } - DataBuffer *dataBuffer = (DataBuffer *)buffer->pBuffer; + ProtectedDataBuffer *dataBuffer = (ProtectedDataBuffer *)buffer->pBuffer; + // Check that we are dealing with the right buffer + if (dataBuffer->magic != PROTECTED_DATA_BUFFER_MAGIC) + { + if (buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG) + { + // Processing codec data, which is not in ProtectedDataBuffer format + ALOGI("%s: received AVC codec data (%lu bytes).", __FUNCTION__, buffer->nFilledLen); + DumpBuffer2("OMX: AVC codec data: ", buffer->pBuffer, buffer->nFilledLen) ; + return OMX_ErrorNone; + } + else + { + // Processing non-codec data, but this buffer is not in ProtectedDataBuffer format + ALOGE("%s: protected data buffer pointer %p doesn't have the right magic", __FUNCTION__, dataBuffer); + return OMX_ErrorBadParameter; + } + } + // End of magic check + if(dataBuffer->drmScheme == DRM_SCHEME_WVC){ // OMX_BUFFERFLAG_CODECCONFIG is an optional flag @@ -539,54 +563,60 @@ void OMXVideoDecoderAVCSecure::MemFreeDataBuffer(OMX_U8 *pBuffer, OMX_PTR pUserD } OMX_U8* OMXVideoDecoderAVCSecure::MemAllocDataBuffer(OMX_U32 nSizeBytes) { - if (nSizeBytes > INPORT_BUFFER_SIZE) { - ALOGE("Invalid size (%u) of memory to allocate.", nSizeBytes); + + ALOGW_IF(nSizeBytes != INPORT_BUFFER_SIZE, + "%s: size of memory to allocate is %lu, but will allocate %u", + __FUNCTION__, nSizeBytes, sizeof(ProtectedDataBuffer)); + + if (mNumInportBuffers >= INPORT_ACTUAL_BUFFER_COUNT) + { + ALOGE("%s: cannot allocate buffer: number of inport buffers is %u, which is already at maximum", + __FUNCTION__, mNumInportBuffers); + return NULL; + } + + ProtectedDataBuffer *pBuffer = new ProtectedDataBuffer; + if (pBuffer == NULL) + { + ALOGE("%s: failed to allocate memory.", __FUNCTION__); return NULL; } - ALOGW_IF(nSizeBytes != INPORT_BUFFER_SIZE, "Size of memory to allocate is %u", nSizeBytes); - for (int i = 0; i < INPORT_ACTUAL_BUFFER_COUNT; i++) { - if (mDataBufferSlot[i].owner == NULL) { - DataBuffer *pBuffer = new DataBuffer; - if (pBuffer == NULL) { - ALOGE("Failed to allocate memory."); - return NULL; - } - pBuffer->data = new uint8_t [INPORT_BUFFER_SIZE]; - if (pBuffer->data == NULL) { - delete pBuffer; - ALOGE("Failed to allocate memory, size to allocate %d.", INPORT_BUFFER_SIZE); - return NULL; - } + ++mNumInportBuffers; - // Is this required for classic or not? - // pBuffer->offset = mDataBufferSlot[i].offset; - pBuffer->size = INPORT_BUFFER_SIZE; - mDataBufferSlot[i].owner = (OMX_U8 *)pBuffer; + Init_ProtectedDataBuffer(pBuffer); + + pBuffer->size = INPORT_BUFFER_SIZE; - ALOGV("Allocating buffer = %#x, Data offset = %#x, data = %#x", (uint32_t)pBuffer, mDataBufferSlot[i].offset, (uint32_t)pBuffer->data); - return (OMX_U8 *) pBuffer; - } - } - ALOGE("Data buffer slot is not available."); - return NULL; + ALOGV("Allocating buffer = %#x, data = %#x", (uint32_t)pBuffer, (uint32_t)pBuffer->data); + return (OMX_U8 *) pBuffer; } void OMXVideoDecoderAVCSecure::MemFreeDataBuffer(OMX_U8 *pBuffer) { - DataBuffer *p = (DataBuffer*) pBuffer; - if (p == NULL) { + + if (pBuffer == NULL) + { + ALOGE("%s: trying to free NULL pointer", __FUNCTION__); return; } - for (int i = 0; i < INPORT_ACTUAL_BUFFER_COUNT; i++) { - if (pBuffer == mDataBufferSlot[i].owner) { - ALOGV("Freeing Data buffer offset = %d, data = %#x", mDataBufferSlot[i].offset, (uint32_t)p->data); - delete [] p->data; - delete p; - mDataBufferSlot[i].owner = NULL; - return; - } + + if (mNumInportBuffers == 0) + { + ALOGE("%s: allocated inport buffer count is already 0, cannot delete buffer %p", + __FUNCTION__, pBuffer); + return; } - ALOGE("Invalid buffer %#x to de-allocate", (uint32_t)pBuffer); + + ProtectedDataBuffer *p = (ProtectedDataBuffer*) pBuffer; + if (p->magic != PROTECTED_DATA_BUFFER_MAGIC) + { + ALOGE("%s: attempting to free buffer with a wrong magic 0x%08x", __FUNCTION__, p->magic); + return; + } + + ALOGV("Freeing Data buffer %p with data = %p", p, p->data); + delete p; + --mNumInportBuffers; } void OMXVideoDecoderAVCSecure::KeepAliveTimerCallback(sigval v) { diff --git a/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.h b/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.h index b46a923..2a99ef5 100755 --- a/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.h +++ b/videocodec/securevideo/moorefield/OMXVideoDecoderAVCSecure.h @@ -87,6 +87,8 @@ private: timer_t mKeepAliveTimer; + // Keep track of number of allocated inport buffers + uint32_t mNumInportBuffers; bool mSessionPaused; struct drm_vendor_api drm_vendor_api; int mDrmDevFd; diff --git a/videocodec/securevideo/moorefield/ProtectedDataBuffer.h b/videocodec/securevideo/moorefield/ProtectedDataBuffer.h new file mode 100644 index 0000000..8260648 --- /dev/null +++ b/videocodec/securevideo/moorefield/ProtectedDataBuffer.h @@ -0,0 +1,171 @@ +/* +* Copyright (c) 2014 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. +*/ + +#ifndef PROTECTED_DATA_BUFFER_H +#define PROTECTED_DATA_BUFFER_H + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE: this size takes into account the space used by DRM +// schemes with full sample encryption (e.g., WV Classic) or +// subsample encryption (e.g., WV Modular, which uses 2KB for +// frame info data). +#define NALU_BUFFER_SIZE (4 * 1024) + +// Either start code + type (00 00 00 01 <type byte>) or 4 byte length + type. +#define NALU_HEADER_SIZE 5 + +// This should be able to fit compressed 1080p video I-frame, use half +// of NV12 1080p frame, which on average uses 12 bits per pixel. +#define MAX_COMPRESSED_FRAME_SIZE (1920 * 1080 * 3 / 4) + +#define MAX_PROT_BUFFER_DATA_SIZE (MAX_COMPRESSED_FRAME_SIZE + NALU_BUFFER_SIZE) + +#define MAX_PES_BUFFER_SIZE (64*1024) + +// TODO: it's not clear, how to calculate this value, since PES packet may contain +// less than 64KB worth of data. +#define MAX_PES_PACKETS_PER_FRAME 64 + +// Video decoder defines maximum number of NALUs per frame as 16. +// (At least, as of June of 2014.) Use the same value here. +#define MAX_NALUS_IN_FRAME 16 + +// Integer, which "PDBF", but no 0 terminator +#define PROTECTED_DATA_BUFFER_MAGIC (0UL | ('F' << 24) | ('B' << 16) | ('D' << 8) | 'P') + +#define DRM_SCHEME_NONE 0 +#define DRM_SCHEME_WV_CLASSIC 1 +#define DRM_SCHEME_WV_MODULAR 2 +#define DRM_SCHEME_MCAST_SINK 3 +#define DRM_SCHEME_PLAYREADY_ASF 4 + +// Flag to indicate if Last Subsample flag is received for MDRM +#define PDB_FLAG_COMPLETE_FRAME 0x1000 + +#pragma pack(push, 4) + +typedef struct ProtectedPESBuffer_tag { + + // AES CTR stream counter, needed for HDCP decryption. + // If ProtectedDataBuffer::clear is 1, streamCounter is ignored. + uint32_t streamCounter ; + + // AES CTR input counter, needed for HDCP decryption + // If ProtectedDataBuffer::clear is 1, inputCounter is ignored. + uint64_t inputCounter ; + + // Offset within ProtectedDataBuffer::data buffer, to the start + // of this PES packet's data. + // + // IMPORTANT: for protected content (ProtectedDataBuffer::clear is 0), + // this offset must be divisible by 16 (AES block size). This is to allow + // for in-place transcryption from AES CTR to IED (AES ECB). OMX will + // check that the offset is divisible by 16, and will abort + // playback, if the offset is NOT divisible by 16. For this reason, + // the offset is used and not a byte pointer. + uint32_t pesDataOffset ; + + // Size of the PES data, pointed to by pesData + uint32_t pesSize ; +} +ProtectedPESBuffer ; + +typedef struct ProtectedDataBuffer_tag { + + // Must be set to PROTECTED_DATA_BUFFER_MAGIC. Must be the first + // member of ProtectedDataBuffer structure. + uint32_t magic; + + // See DRM_SCHEME_* defines above + uint32_t drmScheme; + + // 1 if clear, 0 if encrypted + uint32_t clear; + + // Session ID, used by some DRM schemes (e.g. PlayReady) + uint32_t session_id ; + + // Flags, used by some DRM schemes + // MDRM uses it to indicate Complete Frame received + // Miracast Sink uses it to indicate if Transcription is required + uint32_t flags ; + + // Information about the PES data buffers. Used for DRM_SCHEME_MCAST_SINK. + // Reserve space for one more PES data buffer for sentinel value, for + // ease of implementation. + // + ProtectedPESBuffer pesBuffers[MAX_PES_PACKETS_PER_FRAME + 1] ; + + // Number of filled-out entries in pesBuffers array. + // Used for DRM_SCHEME_MCAST_SINK. If data buffer is not partitioned + // into PES packet buffers, set numPesBuffers must be 0. + // + uint32_t numPesBuffers ; + + // Size of the data buffer. + uint32_t size ; + + // For clear content, this is the space for clear data. + // For encrypted content, this space is occupied by IED encrypted + // data or HDCP encrypted data (payloads only, no PES headers), + // depending on the DRM scheme. + // + // A space is made at the end of encrypted data for + // decrypted SPS/PPS headers. + // + // NOTE: data must be last, to allow for flexibility not + // to copy the whole ProtectedDataBuffer, if not whole data + // buffer is filled. + // + uint8_t data[MAX_PROT_BUFFER_DATA_SIZE]; +} +ProtectedDataBuffer; + +#pragma pack(pop) + +#define PDBUFFER_DATA_OFFSET offsetof(ProtectedDataBuffer, data) + +static inline void Init_ProtectedDataBuffer(ProtectedDataBuffer* buf) +{ + // This is internal helper function. If you pass invalid (e.g. NULL) + // pointer to it, you deserve to crash. + + // Perform initialization of certain members, ignore the data + // areas, which will be overwritten in the course of the + // normal usage. + + buf->magic = PROTECTED_DATA_BUFFER_MAGIC ; + buf->drmScheme = DRM_SCHEME_NONE ; + buf->clear = 0 ; + buf->size = 0 ; + buf->numPesBuffers = 0 ; + buf->session_id = 0 ; + buf->flags = 0 ; +} +// End of Init_ProtectedDataBuffer() + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // PROTECTED_DATA_BUFFER_H |