michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "OMXCodecDescriptorUtil.h" michael@0: michael@0: namespace android { michael@0: michael@0: // The utility functions in this file concatenate two AVC/H.264 parameter sets, michael@0: // sequence parameter set(SPS) and picture parameter set(PPS), into byte stream michael@0: // format or construct AVC decoder config descriptor blob from them. michael@0: // michael@0: // * NAL unit defined in ISO/IEC 14496-10 7.3.1 michael@0: // * SPS defined ISO/IEC 14496-10 7.3.2.1.1 michael@0: // * PPS defined in ISO/IEC 14496-10 7.3.2.2 michael@0: // michael@0: // Byte stream format: michael@0: // Start code <0x00 0x00 0x00 0x01> (4 bytes) michael@0: // --- (SPS) NAL unit --- michael@0: // ... (3 bits) michael@0: // NAL unit type <0x07> (5 bits) michael@0: // SPS (3+ bytes) michael@0: // Profile (1 byte) michael@0: // Compatible profiles (1 byte) michael@0: // Level (1 byte) michael@0: // ... michael@0: // --- End --- michael@0: // Start code <0x00 0x00 0x00 0x01> (4 bytes) michael@0: // --- (PPS) NAL unit --- michael@0: // ... (3 bits) michael@0: // NAL unit type <0x08> (5 bits) michael@0: // PPS (1+ bytes) michael@0: // ... michael@0: // --- End --- michael@0: // michael@0: // Descriptor format: (defined in ISO/IEC 14496-15 5.2.4.1.1) michael@0: // --- Header (5 bytes) --- michael@0: // Version <0x01> (1 byte) michael@0: // Profile (1 byte) michael@0: // Compatible profiles (1 byte) michael@0: // Level (1 byte) michael@0: // Reserved <111111> (6 bits) michael@0: // NAL length type (2 bits) michael@0: // --- Parameter sets --- michael@0: // Reserved <111> (3 bits) michael@0: // Number of SPS (5 bits) michael@0: // SPS (3+ bytes) michael@0: // Length (2 bytes) michael@0: // SPS NAL unit (1+ bytes) michael@0: // ... michael@0: // Number of PPS (1 byte) michael@0: // PPS (3+ bytes) michael@0: // Length (2 bytes) michael@0: // PPS NAL unit (1+ bytes) michael@0: // ... michael@0: // --- End --- michael@0: michael@0: // NAL unit start code. michael@0: static const uint8_t kNALUnitStartCode[] = { 0x00, 0x00, 0x00, 0x01 }; michael@0: michael@0: // NAL unit types. michael@0: enum { michael@0: kNALUnitTypeSPS = 0x07, // Value for sequence parameter set. michael@0: kNALUnitTypePPS = 0x08, // Value for picture parameter set. michael@0: kNALUnitTypeBad = -1, // Malformed data. michael@0: }; michael@0: michael@0: // Sequence parameter set or picture parameter set. michael@0: struct AVCParamSet { michael@0: AVCParamSet(const uint8_t* aPtr, const size_t aSize) michael@0: : mPtr(aPtr) michael@0: , mSize(aSize) michael@0: { michael@0: MOZ_ASSERT(mPtr && mSize > 0); michael@0: } michael@0: michael@0: size_t Size() { michael@0: return mSize + 2; // 2 more bytes for length value. michael@0: } michael@0: michael@0: // Append 2 bytes length value and NAL unit bitstream to aOutputBuf. michael@0: void AppendTo(nsTArray* aOutputBuf) michael@0: { michael@0: // 2 bytes length value. michael@0: uint8_t size[] = { michael@0: (mSize & 0xFF00) >> 8, // MSB. michael@0: mSize & 0x00FF, // LSB. michael@0: }; michael@0: aOutputBuf->AppendElements(size, sizeof(size)); michael@0: michael@0: aOutputBuf->AppendElements(mPtr, mSize); michael@0: } michael@0: michael@0: const uint8_t* mPtr; // Pointer to NAL unit. michael@0: const size_t mSize; // NAL unit length in bytes. michael@0: }; michael@0: michael@0: // Convert SPS and PPS data into decoder config descriptor blob. The generated michael@0: // blob will be appended to aOutputBuf. michael@0: static status_t michael@0: ConvertParamSetsToDescriptorBlob(sp& aSPS, sp& aPPS, michael@0: nsTArray* aOutputBuf) michael@0: { michael@0: // Strip start code in the input. michael@0: AVCParamSet sps(aSPS->data() + sizeof(kNALUnitStartCode), michael@0: aSPS->size() - sizeof(kNALUnitStartCode)); michael@0: AVCParamSet pps(aPPS->data() + sizeof(kNALUnitStartCode), michael@0: aPPS->size() - sizeof(kNALUnitStartCode)); michael@0: size_t paramSetsSize = sps.Size() + pps.Size(); michael@0: michael@0: // Profile/level info in SPS. michael@0: uint8_t* info = aSPS->data() + 5; michael@0: michael@0: uint8_t header[] = { michael@0: 0x01, // Version. michael@0: info[0], // Profile. michael@0: info[1], // Compatible profiles. michael@0: info[2], // Level. michael@0: 0xFF, // 6 bits reserved value <111111> + 2 bits NAL length type <11> michael@0: }; michael@0: michael@0: // Reserve 1 byte for number of SPS & another 1 for number of PPS. michael@0: aOutputBuf->SetCapacity(sizeof(header) + paramSetsSize + 2); michael@0: // Build the blob. michael@0: aOutputBuf->AppendElements(header, sizeof(header)); // 5 bytes Header. michael@0: aOutputBuf->AppendElement(0xE0 | 1); // 3 bits <111> + 5 bits number of SPS. michael@0: sps.AppendTo(aOutputBuf); // SPS NALU data. michael@0: aOutputBuf->AppendElement(1); // 1 byte number of PPS. michael@0: pps.AppendTo(aOutputBuf); // PPS NALU data. michael@0: michael@0: return OK; michael@0: } michael@0: michael@0: static int michael@0: NALType(sp& aBuffer) michael@0: { michael@0: if (aBuffer == nullptr) { michael@0: return kNALUnitTypeBad; michael@0: } michael@0: // Start code? michael@0: uint8_t* data = aBuffer->data(); michael@0: if (aBuffer->size() <= 4 || michael@0: memcmp(data, kNALUnitStartCode, sizeof(kNALUnitStartCode))) { michael@0: return kNALUnitTypeBad; michael@0: } michael@0: michael@0: return data[4] & 0x1F; michael@0: } michael@0: michael@0: // Generate AVC/H.264 decoder config blob. michael@0: // See MPEG4Writer::Track::makeAVCCodecSpecificData() and michael@0: // MPEG4Writer::Track::writeAvccBox() implementation in libstagefright. michael@0: status_t michael@0: GenerateAVCDescriptorBlob(sp& aConfigData, michael@0: nsTArray* aOutputBuf, michael@0: OMXVideoEncoder::BlobFormat aFormat) michael@0: { michael@0: // Search for parameter sets using key "csd-0" and "csd-1". michael@0: char key[6] = "csd-"; michael@0: sp sps; michael@0: sp pps; michael@0: for (int i = 0; i < 2; i++) { michael@0: snprintf(key + 4, 2, "%d", i); michael@0: sp paramSet; michael@0: bool found = aConfigData->findBuffer(key, ¶mSet); michael@0: int type = NALType(paramSet); michael@0: bool valid = ((type == kNALUnitTypeSPS) || (type == kNALUnitTypePPS)); michael@0: michael@0: MOZ_ASSERT(found && valid); michael@0: if (!found || !valid) { michael@0: return ERROR_MALFORMED; michael@0: } michael@0: michael@0: switch (type) { michael@0: case kNALUnitTypeSPS: michael@0: sps = paramSet; michael@0: break; michael@0: case kNALUnitTypePPS: michael@0: pps = paramSet; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Should not get here!"); michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSERT(sps != nullptr && pps != nullptr); michael@0: if (sps == nullptr || pps == nullptr) { michael@0: return ERROR_MALFORMED; michael@0: } michael@0: michael@0: status_t result = OK; michael@0: if (aFormat == OMXVideoEncoder::BlobFormat::AVC_NAL) { michael@0: // SPS + PPS. michael@0: aOutputBuf->AppendElements(sps->data(), sps->size()); michael@0: aOutputBuf->AppendElements(pps->data(), pps->size()); michael@0: return OK; michael@0: } else { michael@0: status_t result = ConvertParamSetsToDescriptorBlob(sps, pps, aOutputBuf); michael@0: MOZ_ASSERT(result == OK); michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: } // namespace android