Support for RFC3640 - mpeg4-generic RTP packet type, AAC-lbr and AAC-hbr.

Change-Id: Ied92ea8c2448a2cb1a732c72c21c69da1913dbc8
related-to-bug: 2556656
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 <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/Utils.h>
 
+#include <ctype.h>
 #include <stdint.h>
 
 #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<AMessage> &notify)
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
+        const sp<AMessage> &notify, const AString &desc, const AString &params)
     : 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<ARTPSource> &source) {
     List<sp<ABuffer> > *queue = source->queue();
@@ -85,8 +216,116 @@
     }
     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<AUHeader> 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<AUHeader>::iterator it = headers.begin();
+             it != headers.end(); ++it) {
+            const AUHeader &header = *it;
+
+            CHECK_LE(offset + header.mSize, buffer->size());
+
+            sp<ABuffer> 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 <media/stagefright/foundation/AString.h>
+
 #include <utils/List.h>
 #include <utils/RefBase.h>
 
@@ -29,7 +31,9 @@
 struct AMessage;
 
 struct AMPEG4ElementaryAssembler : public ARTPAssembler {
-    AMPEG4ElementaryAssembler(const sp<AMessage> &notify);
+    AMPEG4ElementaryAssembler(
+            const sp<AMessage> &notify, const AString &desc,
+            const AString &params);
 
 protected:
     virtual ~AMPEG4ElementaryAssembler();
@@ -40,6 +44,18 @@
 
 private:
     sp<AMessage> 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 @@
     return csd;
 }
 
+// From mpeg4-generic configuration data.
+sp<ABuffer> 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<ABuffer> 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<ABuffer> 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 @@
 
         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<ABuffer> 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 @@
         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();