From 3266b2c04867f687e1e1b7b86088d6eb83077fd0 Mon Sep 17 00:00:00 2001 From: James Dong Date: Fri, 6 Aug 2010 00:29:03 -0700 Subject: Support multiple PPS and SPS in avcC box - Also o do not use the hard-coded profile and levels. Instead, we are using the profile and level found in the codec config data o we are not supporting FRExt profile and levels for now, which requires additional seq parameter set extension for instance. Change-Id: If695b4c996d073d8e48aa45fdd7001e9f016f375 --- media/libstagefright/MPEG4Writer.cpp | 263 +++++++++++++++++++++++++++++++---- 1 file changed, 236 insertions(+), 27 deletions(-) (limited to 'media/libstagefright/MPEG4Writer.cpp') diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 0d8c3c6..20fbc05 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -38,6 +38,9 @@ namespace android { +static const uint8_t kNalUnitTypeSeqParamSet = 0x07; +static const uint8_t kNalUnitTypePicParamSet = 0x08; + class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const sp &source); @@ -111,6 +114,20 @@ private: }; List mSttsTableEntries; + // Sequence parameter set or picture parameter set + struct AVCParamSet { + AVCParamSet(uint16_t length, const uint8_t *data) + : mLength(length), mData(data) {} + + uint16_t mLength; + const uint8_t *mData; + }; + List mSeqParamSets; + List mPicParamSets; + uint8_t mProfileIdc; + uint8_t mProfileCompatible; + uint8_t mLevelIdc; + void *mCodecSpecificData; size_t mCodecSpecificDataSize; bool mGotAllCodecSpecificData; @@ -124,8 +141,15 @@ private: static void *ThreadWrapper(void *me); void threadEntry(); + const uint8_t *parseParamSet( + const uint8_t *data, size_t length, int type, size_t *paramSetLen); + status_t makeAVCCodecSpecificData( const uint8_t *data, size_t size); + status_t copyAVCCodecSpecificData( + const uint8_t *data, size_t size); + status_t parseAVCCodecSpecificData( + const uint8_t *data, size_t size); // Track authoring progress status void trackProgressStatus(int64_t timeUs, status_t err = OK); @@ -1038,6 +1062,174 @@ static void hexdump(const void *_data, size_t size) { } } +static void getNalUnitType(uint8_t byte, uint8_t* type) { + LOGV("getNalUnitType: %d", byte); + + // nal_unit_type: 5-bit unsigned integer + *type = (byte & 0x1F); +} + +static const uint8_t *findNextStartCode( + const uint8_t *data, size_t length) { + + LOGV("findNextStartCode: %p %d", data, length); + + size_t bytesLeft = length; + while (bytesLeft > 4 && + memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) { + --bytesLeft; + } + if (bytesLeft <= 4) { + bytesLeft = 0; // Last parameter set + } + return &data[length - bytesLeft]; +} + +const uint8_t *MPEG4Writer::Track::parseParamSet( + const uint8_t *data, size_t length, int type, size_t *paramSetLen) { + + LOGV("parseParamSet"); + CHECK(type == kNalUnitTypeSeqParamSet || + type == kNalUnitTypePicParamSet); + + const uint8_t *nextStartCode = findNextStartCode(data, length); + *paramSetLen = nextStartCode - data; + if (*paramSetLen == 0) { + LOGE("Param set is malformed, since its length is 0"); + return NULL; + } + + AVCParamSet paramSet(*paramSetLen, data); + if (type == kNalUnitTypeSeqParamSet) { + if (*paramSetLen < 4) { + LOGE("Seq parameter set malformed"); + return NULL; + } + if (mSeqParamSets.empty()) { + mProfileIdc = data[1]; + mProfileCompatible = data[2]; + mLevelIdc = data[3]; + } else { + if (mProfileIdc != data[1] || + mProfileCompatible != data[2] || + mLevelIdc != data[3]) { + LOGE("Inconsistent profile/level found in seq parameter sets"); + return NULL; + } + } + mSeqParamSets.push_back(paramSet); + } else { + mPicParamSets.push_back(paramSet); + } + return nextStartCode; +} + +status_t MPEG4Writer::Track::copyAVCCodecSpecificData( + const uint8_t *data, size_t size) { + LOGV("copyAVCCodecSpecificData"); + + // 2 bytes for each of the parameter set length field + // plus the 7 bytes for the header + if (size < 4 + 7) { + LOGE("Codec specific data length too short: %d", size); + return ERROR_MALFORMED; + } + + mCodecSpecificDataSize = size; + mCodecSpecificData = malloc(size); + memcpy(mCodecSpecificData, data, size); + return OK; +} + +status_t MPEG4Writer::Track::parseAVCCodecSpecificData( + const uint8_t *data, size_t size) { + + LOGV("parseAVCCodecSpecificData"); + // Data starts with a start code. + // SPS and PPS are separated with start codes. + // Also, SPS must come before PPS + uint8_t type = kNalUnitTypeSeqParamSet; + bool gotSps = false; + bool gotPps = false; + const uint8_t *tmp = data; + const uint8_t *nextStartCode = data; + size_t bytesLeft = size; + size_t paramSetLen = 0; + mCodecSpecificDataSize = 0; + while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) { + getNalUnitType(*(tmp + 4), &type); + if (type == kNalUnitTypeSeqParamSet) { + if (gotPps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotSps) { + gotSps = true; + } + nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen); + } else if (type == kNalUnitTypePicParamSet) { + if (!gotSps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotPps) { + gotPps = true; + } + nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen); + } else { + LOGE("Only SPS and PPS Nal units are expected"); + return ERROR_MALFORMED; + } + + if (nextStartCode == NULL) { + return ERROR_MALFORMED; + } + + // Move on to find the next parameter set + bytesLeft -= nextStartCode - tmp; + tmp = nextStartCode; + mCodecSpecificDataSize += (2 + paramSetLen); + } + + { + // Check on the number of seq parameter sets + size_t nSeqParamSets = mSeqParamSets.size(); + if (nSeqParamSets == 0) { + LOGE("Cound not find sequence parameter set"); + return ERROR_MALFORMED; + } + + if (nSeqParamSets > 0x1F) { + LOGE("Too many seq parameter sets (%d) found", nSeqParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the number of pic parameter sets + size_t nPicParamSets = mPicParamSets.size(); + if (nPicParamSets == 0) { + LOGE("Cound not find picture parameter set"); + return ERROR_MALFORMED; + } + if (nPicParamSets > 0xFF) { + LOGE("Too many pic parameter sets (%d) found", nPicParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the profiles + // These profiles requires additional parameter set extensions + if (mProfileIdc == 100 || mProfileIdc == 110 || + mProfileIdc == 122 || mProfileIdc == 144) { + LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc); + return BAD_VALUE; + } + } + + return OK; +} status_t MPEG4Writer::Track::makeAVCCodecSpecificData( const uint8_t *data, size_t size) { @@ -1048,50 +1240,67 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( return ERROR_MALFORMED; } - if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { - LOGE("Must start with a start code"); + if (size < 4) { + LOGE("Codec specific data length too short: %d", size); return ERROR_MALFORMED; } - size_t picParamOffset = 4; - while (picParamOffset + 3 < size - && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { - ++picParamOffset; + // Data is in the form of AVCCodecSpecificData + if (memcmp("\x00\x00\x00\x01", data, 4)) { + return copyAVCCodecSpecificData(data, size); } - if (picParamOffset + 3 >= size) { - LOGE("Could not find start-code for pictureParameterSet"); + if (parseAVCCodecSpecificData(data, size) != OK) { return ERROR_MALFORMED; } - size_t seqParamSetLength = picParamOffset - 4; - size_t picParamSetLength = size - picParamOffset - 4; - - mCodecSpecificDataSize = - 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; - + // ISO 14496-15: AVC file format + mCodecSpecificDataSize += 7; // 7 more bytes in the header mCodecSpecificData = malloc(mCodecSpecificDataSize); uint8_t *header = (uint8_t *)mCodecSpecificData; - header[0] = 1; - header[1] = 0x42; // profile - header[2] = 0x80; - header[3] = 0x1e; // level + header[0] = 1; // version + header[1] = mProfileIdc; // profile indication + header[2] = mProfileCompatible; // profile compatibility + header[3] = mLevelIdc; + // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne #if USE_NALLEN_FOUR header[4] = 0xfc | 3; // length size == 4 bytes #else header[4] = 0xfc | 1; // length size == 2 bytes #endif - header[5] = 0xe0 | 1; - header[6] = seqParamSetLength >> 8; - header[7] = seqParamSetLength & 0xff; - memcpy(&header[8], &data[4], seqParamSetLength); - header += 8 + seqParamSetLength; - header[0] = 1; - header[1] = picParamSetLength >> 8; - header[2] = picParamSetLength & 0xff; - memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); + // 3-bit '111' followed by 5-bit numSequenceParameterSets + int nSequenceParamSets = mSeqParamSets.size(); + header[5] = 0xe0 | nSequenceParamSets; + header += 6; + for (List::iterator it = mSeqParamSets.begin(); + it != mSeqParamSets.end(); ++it) { + // 16-bit sequence parameter set length + uint16_t seqParamSetLength = it->mLength; + header[0] = seqParamSetLength >> 8; + header[1] = seqParamSetLength & 0xff; + + // SPS NAL unit (sequence parameter length bytes) + memcpy(&header[2], it->mData, seqParamSetLength); + header += (2 + seqParamSetLength); + } + + // 8-bit nPictureParameterSets + int nPictureParamSets = mPicParamSets.size(); + header[0] = nPictureParamSets; + header += 1; + for (List::iterator it = mPicParamSets.begin(); + it != mPicParamSets.end(); ++it) { + // 16-bit picture parameter set length + uint16_t picParamSetLength = it->mLength; + header[0] = picParamSetLength >> 8; + header[1] = picParamSetLength & 0xff; + + // PPS Nal unit (picture parameter set length bytes) + memcpy(&header[2], it->mData, picParamSetLength); + header += (2 + picParamSetLength); + } return OK; } -- cgit v1.1