From 4dba3e90f211eb5f5af19b10c5d3fc8c967b0086 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 31 Aug 2010 14:25:36 -0700 Subject: Support for RFC3640 - mpeg4-generic RTP packet type, AAC-lbr and AAC-hbr. Change-Id: Ied92ea8c2448a2cb1a732c72c21c69da1913dbc8 related-to-bug: 2556656 --- .../rtsp/AMPEG4ElementaryAssembler.cpp | 245 ++++++++++++++++++++- .../rtsp/AMPEG4ElementaryAssembler.h | 18 +- media/libstagefright/rtsp/APacketSource.cpp | 83 +++++++ media/libstagefright/rtsp/ARTPSource.cpp | 5 +- 4 files changed, 345 insertions(+), 6 deletions(-) (limited to 'media/libstagefright/rtsp') diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index 7dd3e3f..f68a35b 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -18,29 +18,160 @@ #include "ARTPSource.h" +#include #include #include #include #include +#include +#include #include #define BE_VERBOSE 0 namespace android { +static bool GetAttribute(const char *s, const char *key, AString *value) { + value->clear(); + + size_t keyLen = strlen(key); + + for (;;) { + while (isspace(*s)) { + ++s; + } + + const char *colonPos = strchr(s, ';'); + + size_t len = + (colonPos == NULL) ? strlen(s) : colonPos - s; + + if (len >= keyLen + 1 && s[keyLen] == '=' + && !strncasecmp(s, key, keyLen)) { + value->setTo(&s[keyLen + 1], len - keyLen - 1); + return true; + } + + if (colonPos == NULL) { + return false; + } + + s = colonPos + 1; + } +} + +static bool GetIntegerAttribute( + const char *s, const char *key, unsigned *x) { + *x = 0; + + AString val; + if (!GetAttribute(s, key, &val)) { + return false; + } + + s = val.c_str(); + char *end; + unsigned y = strtoul(s, &end, 10); + + if (end == s || *end != '\0') { + return false; + } + + *x = y; + + return true; +} + // static -AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp ¬ify) +AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler( + const sp ¬ify, const AString &desc, const AString ¶ms) : mNotifyMsg(notify), + mIsGeneric(false), + mParams(params), + mSizeLength(0), + mIndexLength(0), + mIndexDeltaLength(0), + mCTSDeltaLength(0), + mDTSDeltaLength(0), + mRandomAccessIndication(false), + mStreamStateIndication(0), + mAuxiliaryDataSizeLength(0), + mHasAUHeader(false), mAccessUnitRTPTime(0), mNextExpectedSeqNoValid(false), mNextExpectedSeqNo(0), mAccessUnitDamaged(false) { + mIsGeneric = desc.startsWith("mpeg4-generic/"); + + if (mIsGeneric) { + AString value; + CHECK(GetAttribute(params.c_str(), "mode", &value)); + + if (!GetIntegerAttribute(params.c_str(), "sizeLength", &mSizeLength)) { + mSizeLength = 0; + } + + if (!GetIntegerAttribute( + params.c_str(), "indexLength", &mIndexLength)) { + mIndexLength = 0; + } + + if (!GetIntegerAttribute( + params.c_str(), "indexDeltaLength", &mIndexDeltaLength)) { + mIndexDeltaLength = 0; + } + + if (!GetIntegerAttribute( + params.c_str(), "CTSDeltaLength", &mCTSDeltaLength)) { + mCTSDeltaLength = 0; + } + + if (!GetIntegerAttribute( + params.c_str(), "DTSDeltaLength", &mDTSDeltaLength)) { + mDTSDeltaLength = 0; + } + + unsigned x; + if (!GetIntegerAttribute( + params.c_str(), "randomAccessIndication", &x)) { + mRandomAccessIndication = false; + } else { + CHECK(x == 0 || x == 1); + mRandomAccessIndication = (x != 0); + } + + if (!GetIntegerAttribute( + params.c_str(), "streamStateIndication", + &mStreamStateIndication)) { + mStreamStateIndication = 0; + } + + if (!GetIntegerAttribute( + params.c_str(), "auxiliaryDataSizeLength", + &mAuxiliaryDataSizeLength)) { + mAuxiliaryDataSizeLength = 0; + } + + mHasAUHeader = + mSizeLength > 0 + || mIndexLength > 0 + || mIndexDeltaLength > 0 + || mCTSDeltaLength > 0 + || mDTSDeltaLength > 0 + || mRandomAccessIndication + || mStreamStateIndication > 0; + } } AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() { } +struct AUHeader { + unsigned mSize; + unsigned mSerial; +}; + ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket( const sp &source) { List > *queue = source->queue(); @@ -85,8 +216,116 @@ ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket( } mAccessUnitRTPTime = rtpTime; - mPackets.push_back(buffer); - // hexdump(buffer->data(), buffer->size()); + if (!mIsGeneric) { + mPackets.push_back(buffer); + } else { + // hexdump(buffer->data(), buffer->size()); + + CHECK_GE(buffer->size(), 2u); + unsigned AU_headers_length = U16_AT(buffer->data()); // in bits + + CHECK_GE(buffer->size(), 2 + (AU_headers_length + 7) / 8); + + List headers; + + ABitReader bits(buffer->data() + 2, buffer->size() - 2); + unsigned numBitsLeft = AU_headers_length; + + unsigned AU_serial = 0; + for (;;) { + if (numBitsLeft < mSizeLength) { break; } + + unsigned AU_size = bits.getBits(mSizeLength); + numBitsLeft -= mSizeLength; + + size_t n = headers.empty() ? mIndexLength : mIndexDeltaLength; + if (numBitsLeft < n) { break; } + + unsigned AU_index = bits.getBits(n); + numBitsLeft -= n; + + if (headers.empty()) { + AU_serial = AU_index; + } else { + AU_serial += 1 + AU_index; + } + + if (mCTSDeltaLength > 0) { + if (numBitsLeft < 1) { + break; + } + --numBitsLeft; + if (bits.getBits(1)) { + if (numBitsLeft < mCTSDeltaLength) { + break; + } + bits.skipBits(mCTSDeltaLength); + numBitsLeft -= mCTSDeltaLength; + } + } + + if (mDTSDeltaLength > 0) { + if (numBitsLeft < 1) { + break; + } + --numBitsLeft; + if (bits.getBits(1)) { + if (numBitsLeft < mDTSDeltaLength) { + break; + } + bits.skipBits(mDTSDeltaLength); + numBitsLeft -= mDTSDeltaLength; + } + } + + if (mRandomAccessIndication) { + if (numBitsLeft < 1) { + break; + } + bits.skipBits(1); + --numBitsLeft; + } + + if (mStreamStateIndication > 0) { + if (numBitsLeft < mStreamStateIndication) { + break; + } + bits.skipBits(mStreamStateIndication); + } + + AUHeader header; + header.mSize = AU_size; + header.mSerial = AU_serial; + headers.push_back(header); + } + + size_t offset = 2 + (AU_headers_length + 7) / 8; + + if (mAuxiliaryDataSizeLength > 0) { + ABitReader bits(buffer->data() + offset, buffer->size() - offset); + + unsigned auxSize = bits.getBits(mAuxiliaryDataSizeLength); + + offset += (mAuxiliaryDataSizeLength + auxSize + 7) / 8; + } + + for (List::iterator it = headers.begin(); + it != headers.end(); ++it) { + const AUHeader &header = *it; + + CHECK_LE(offset + header.mSize, buffer->size()); + + sp accessUnit = new ABuffer(header.mSize); + memcpy(accessUnit->data(), buffer->data() + offset, header.mSize); + + offset += header.mSize; + + CopyTimes(accessUnit, buffer); + mPackets.push_back(accessUnit); + } + + CHECK_EQ(offset, buffer->size()); + } queue->erase(queue->begin()); ++mNextExpectedSeqNo; diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h index 1566d00..794bbcc 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h @@ -20,6 +20,8 @@ #include "ARTPAssembler.h" +#include + #include #include @@ -29,7 +31,9 @@ struct ABuffer; struct AMessage; struct AMPEG4ElementaryAssembler : public ARTPAssembler { - AMPEG4ElementaryAssembler(const sp ¬ify); + AMPEG4ElementaryAssembler( + const sp ¬ify, const AString &desc, + const AString ¶ms); protected: virtual ~AMPEG4ElementaryAssembler(); @@ -40,6 +44,18 @@ protected: private: sp mNotifyMsg; + bool mIsGeneric; + AString mParams; + + unsigned mSizeLength; + unsigned mIndexLength; + unsigned mIndexDeltaLength; + unsigned mCTSDeltaLength; + unsigned mDTSDeltaLength; + bool mRandomAccessIndication; + unsigned mStreamStateIndication; + unsigned mAuxiliaryDataSizeLength; + bool mHasAUHeader; uint32_t mAccessUnitRTPTime; bool mNextExpectedSeqNoValid; diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index 2d7738b..75b4571 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -247,6 +247,65 @@ sp MakeAACCodecSpecificData(const char *params) { return csd; } +// From mpeg4-generic configuration data. +sp MakeAACCodecSpecificData2(const char *params) { + AString val; + unsigned long objectType; + if (GetAttribute(params, "objectType", &val)) { + const char *s = val.c_str(); + char *end; + objectType = strtoul(s, &end, 10); + CHECK(end > s && *end == '\0'); + } else { + objectType = 0x40; // Audio ISO/IEC 14496-3 + } + + CHECK(GetAttribute(params, "config", &val)); + + sp config = decodeHex(val); + CHECK(config != NULL); + + // Make sure size fits into a single byte and doesn't have to + // be encoded. + CHECK_LT(20 + config->size(), 128u); + + const uint8_t *data = config->data(); + + static const uint8_t kStaticESDS[] = { + 0x03, 22, + 0x00, 0x00, // ES_ID + 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag + + 0x04, 17, + 0x40, // Audio ISO/IEC 14496-3 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x05, 2, + // AudioSpecificInfo follows + }; + + sp csd = new ABuffer(sizeof(kStaticESDS) + config->size()); + uint8_t *dst = csd->data(); + *dst++ = 0x03; + *dst++ = 20 + config->size(); + *dst++ = 0x00; // ES_ID + *dst++ = 0x00; + *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag + *dst++ = 0x04; + *dst++ = 15 + config->size(); + *dst++ = objectType; + for (int i = 0; i < 12; ++i) { *dst++ = 0x00; } + *dst++ = 0x05; + *dst++ = config->size(); + memcpy(dst, config->data(), config->size()); + + // hexdump(csd->data(), csd->size()); + + return csd; +} + static size_t GetSizeWidth(size_t x) { size_t n = 1; while (x > 127) { @@ -560,6 +619,30 @@ APacketSource::APacketSource( mFormat->setInt32(kKeyWidth, width); mFormat->setInt32(kKeyHeight, height); + } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + AString val; + if (!GetAttribute(params.c_str(), "mode", &val) + || (strcasecmp(val.c_str(), "AAC-lbr") + && strcasecmp(val.c_str(), "AAC-hbr"))) { + mInitCheck = ERROR_UNSUPPORTED; + return; + } + + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + + int32_t sampleRate, numChannels; + ASessionDescription::ParseFormatDesc( + desc.c_str(), &sampleRate, &numChannels); + + mFormat->setInt32(kKeySampleRate, sampleRate); + mFormat->setInt32(kKeyChannelCount, numChannels); + + sp codecSpecificData = + MakeAACCodecSpecificData2(params.c_str()); + + mFormat->setData( + kKeyESDS, 0, + codecSpecificData->data(), codecSpecificData->size()); } else { mInitCheck = ERROR_UNSUPPORTED; } diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp index 775c4ee..9656ba2 100644 --- a/media/libstagefright/rtsp/ARTPSource.cpp +++ b/media/libstagefright/rtsp/ARTPSource.cpp @@ -64,8 +64,9 @@ ARTPSource::ARTPSource( mAssembler = new AAMRAssembler(notify, false /* isWide */, params); } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) { mAssembler = new AAMRAssembler(notify, true /* isWide */, params); - } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) { - mAssembler = new AMPEG4ElementaryAssembler(notify); + } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8) + || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params); mIssueFIRRequests = true; } else { TRESPASS(); -- cgit v1.1