Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e536f800 authored by Andreas Huber's avatar Andreas Huber
Browse files

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

Change-Id: Ied92ea8c2448a2cb1a732c72c21c69da1913dbc8
related-to-bug: 2556656
parent 6eb5ac9c
Loading
Loading
Loading
Loading
+242 −3
Original line number Diff line number Diff line
@@ -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,9 +216,117 @@ ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
    }
    mAccessUnitRTPTime = rtpTime;

    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;

+17 −1
Original line number Diff line number Diff line
@@ -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 ABuffer;
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 @@ protected:

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;
+83 −0
Original line number Diff line number Diff line
@@ -247,6 +247,65 @@ sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
    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 @@ 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<ABuffer> codecSpecificData =
            MakeAACCodecSpecificData2(params.c_str());

        mFormat->setData(
                kKeyESDS, 0,
                codecSpecificData->data(), codecSpecificData->size());
    } else {
        mInitCheck = ERROR_UNSUPPORTED;
    }
+3 −2
Original line number Diff line number Diff line
@@ -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();