Merge "Support multiple PPS and SPS in avcC box" into gingerbread

This commit is contained in:
James Dong
2010-08-09 13:35:54 -07:00
committed by Android (Google) Code Review

View File

@ -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<MediaSource> &source);
@ -111,6 +114,20 @@ private:
};
List<SttsTableEntry> 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<AVCParamSet> mSeqParamSets;
List<AVCParamSet> 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, &paramSetLen);
} 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, &paramSetLen);
} 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<AVCParamSet>::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<AVCParamSet>::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;
}