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

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

Better support for MP4A-LATM RTP disassembly. This used to fail if...

Better support for MP4A-LATM RTP disassembly. This used to fail if mNumSubFrames > 1 and the sub frames did not align with RTP packet boundaries.

Change-Id: I20e3b86f52b7f0f41663ffe8bc1f4db92280e884
parent c8b3ca3c
Loading
Loading
Loading
Loading
+367 −17
Original line number Diff line number Diff line
@@ -18,18 +18,381 @@

#include "ARTPSource.h"

#include <media/stagefright/foundation/hexdump.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/MediaErrors.h>

#include <ctype.h>

namespace android {

AMPEG4AudioAssembler::AMPEG4AudioAssembler(const sp<AMessage> &notify)
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] == '=' && !strncmp(s, key, keyLen)) {
            value->setTo(&s[keyLen + 1], len - keyLen - 1);
            return true;
        }

        if (colonPos == NULL) {
            return false;
        }

        s = colonPos + 1;
    }
}

static sp<ABuffer> decodeHex(const AString &s) {
    if ((s.size() % 2) != 0) {
        return NULL;
    }

    size_t outLen = s.size() / 2;
    sp<ABuffer> buffer = new ABuffer(outLen);
    uint8_t *out = buffer->data();

    uint8_t accum = 0;
    for (size_t i = 0; i < s.size(); ++i) {
        char c = s.c_str()[i];
        unsigned value;
        if (c >= '0' && c <= '9') {
            value = c - '0';
        } else if (c >= 'a' && c <= 'f') {
            value = c - 'a' + 10;
        } else if (c >= 'A' && c <= 'F') {
            value = c - 'A' + 10;
        } else {
            return NULL;
        }

        accum = (accum << 4) | value;

        if (i & 1) {
            *out++ = accum;

            accum = 0;
        }
    }

    return buffer;
}

static status_t parseAudioObjectType(
        ABitReader *bits, unsigned *audioObjectType) {
    *audioObjectType = bits->getBits(5);
    if ((*audioObjectType) == 31) {
        *audioObjectType = 32 + bits->getBits(6);
    }

    return OK;
}

static status_t parseGASpecificConfig(
        ABitReader *bits,
        unsigned audioObjectType, unsigned channelConfiguration) {
    unsigned frameLengthFlag = bits->getBits(1);
    unsigned dependsOnCoreCoder = bits->getBits(1);
    if (dependsOnCoreCoder) {
        /* unsigned coreCoderDelay = */bits->getBits(1);
    }
    unsigned extensionFlag = bits->getBits(1);

    if (!channelConfiguration) {
        // program_config_element
        return ERROR_UNSUPPORTED;  // XXX to be implemented
    }

    if (audioObjectType == 6 || audioObjectType == 20) {
        /* unsigned layerNr = */bits->getBits(3);
    }

    if (extensionFlag) {
        if (audioObjectType == 22) {
            /* unsigned numOfSubFrame = */bits->getBits(5);
            /* unsigned layerLength = */bits->getBits(11);
        } else if (audioObjectType == 17 || audioObjectType == 19
                || audioObjectType == 20 || audioObjectType == 23) {
            /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1);
            /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1);
            /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1);
        }

        unsigned extensionFlag3 = bits->getBits(1);
        CHECK_EQ(extensionFlag3, 0u);  // TBD in version 3
    }

    return OK;
}

static status_t parseAudioSpecificConfig(ABitReader *bits) {
    unsigned audioObjectType;
    CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);

    unsigned samplingFreqIndex = bits->getBits(4);
    if (samplingFreqIndex == 0x0f) {
        /* unsigned samplingFrequency = */bits->getBits(24);
    }

    unsigned channelConfiguration = bits->getBits(4);

    unsigned extensionAudioObjectType = 0;
    unsigned sbrPresent = 0;

    if (audioObjectType == 5) {
        extensionAudioObjectType = audioObjectType;
        sbrPresent = 1;
        unsigned extensionSamplingFreqIndex = bits->getBits(4);
        if (extensionSamplingFreqIndex == 0x0f) {
            /* unsigned extensionSamplingFrequency = */bits->getBits(24);
        }
        CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
    }

    CHECK((audioObjectType >= 1 && audioObjectType <= 4)
        || (audioObjectType >= 6 && audioObjectType <= 7)
        || audioObjectType == 17
        || (audioObjectType >= 19 && audioObjectType <= 23));

    CHECK_EQ(parseGASpecificConfig(
                bits, audioObjectType, channelConfiguration), (status_t)OK);

    if (audioObjectType == 17
            || (audioObjectType >= 19 && audioObjectType <= 27)) {
        unsigned epConfig = bits->getBits(2);
        if (epConfig == 2 || epConfig == 3) {
            // ErrorProtectionSpecificConfig
            return ERROR_UNSUPPORTED;  // XXX to be implemented

            if (epConfig == 3) {
                unsigned directMapping = bits->getBits(1);
                CHECK_EQ(directMapping, 1u);
            }
        }
    }

#if 0
    // This is not supported here as the upper layers did not explicitly
    // signal the length of AudioSpecificConfig.

    if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
        unsigned syncExtensionType = bits->getBits(11);
        if (syncExtensionType == 0x2b7) {
            CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
                     (status_t)OK);

            sbrPresent = bits->getBits(1);

            if (sbrPresent == 1) {
                unsigned extensionSamplingFreqIndex = bits->getBits(4);
                if (extensionSamplingFreqIndex == 0x0f) {
                    /* unsigned extensionSamplingFrequency = */bits->getBits(24);
                }
            }
        }
    }
#endif

    return OK;
}

static status_t parseStreamMuxConfig(
        ABitReader *bits,
        unsigned *numSubFrames,
        unsigned *frameLengthType,
        bool *otherDataPresent,
        unsigned *otherDataLenBits) {
    unsigned audioMuxVersion = bits->getBits(1);

    unsigned audioMuxVersionA = 0;
    if (audioMuxVersion == 1) {
        audioMuxVersionA = bits->getBits(1);
    }

    CHECK_EQ(audioMuxVersionA, 0u);  // otherwise future spec

    if (audioMuxVersion != 0) {
        return ERROR_UNSUPPORTED;  // XXX to be implemented;
    }
    CHECK_EQ(audioMuxVersion, 0u);  // XXX to be implemented

    unsigned allStreamsSameTimeFraming = bits->getBits(1);
    CHECK_EQ(allStreamsSameTimeFraming, 1u);  // There's only one stream.

    *numSubFrames = bits->getBits(6);
    unsigned numProgram = bits->getBits(4);
    CHECK_EQ(numProgram, 0u);  // disabled in RTP LATM

    unsigned numLayer = bits->getBits(3);
    CHECK_EQ(numLayer, 0u);  // disabled in RTP LATM

    if (audioMuxVersion == 0) {
        // AudioSpecificConfig
        CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
    } else {
        TRESPASS();  // XXX to be implemented
    }

    *frameLengthType = bits->getBits(3);
    switch (*frameLengthType) {
        case 0:
        {
            /* unsigned bufferFullness = */bits->getBits(8);

            // The "coreFrameOffset" does not apply since there's only
            // a single layer.
            break;
        }

        case 1:
        {
            /* unsigned frameLength = */bits->getBits(9);
            break;
        }

        case 3:
        case 4:
        case 5:
        {
            /* unsigned CELPframeLengthTableIndex = */bits->getBits(6);
            break;
        }

        case 6:
        case 7:
        {
            /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1);
            break;
        }

        default:
            break;
    }

    *otherDataPresent = bits->getBits(1);
    *otherDataLenBits = 0;
    if (*otherDataPresent) {
        if (audioMuxVersion == 1) {
            TRESPASS();  // XXX to be implemented
        } else {
            *otherDataLenBits = 0;

            unsigned otherDataLenEsc;
            do {
                (*otherDataLenBits) <<= 8;
                otherDataLenEsc = bits->getBits(1);
                unsigned otherDataLenTmp = bits->getBits(8);
                (*otherDataLenBits) += otherDataLenTmp;
            } while (otherDataLenEsc);
        }
    }

    unsigned crcCheckPresent = bits->getBits(1);
    if (crcCheckPresent) {
        /* unsigned crcCheckSum = */bits->getBits(8);
    }

    return OK;
}

sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) {
    CHECK(!mMuxConfigPresent);  // XXX to be implemented

    sp<ABuffer> out = new ABuffer(buffer->size());
    out->setRange(0, 0);

    size_t offset = 0;
    uint8_t *ptr = buffer->data();

    for (size_t i = 0; i <= mNumSubFrames; ++i) {
        // parse PayloadLengthInfo

        unsigned payloadLength = 0;

        switch (mFrameLengthType) {
            case 0:
            {
                unsigned muxSlotLengthBytes = 0;
                unsigned tmp;
                do {
                    CHECK_LT(offset, buffer->size());
                    tmp = ptr[offset++];
                    muxSlotLengthBytes += tmp;
                } while (tmp == 0xff);

                payloadLength = muxSlotLengthBytes;
                break;
            }

            default:
                TRESPASS();  // XXX to be implemented
                break;
        }

        CHECK_LE(offset + payloadLength, buffer->size());

        memcpy(out->data() + out->size(), &ptr[offset], payloadLength);
        out->setRange(0, out->size() + payloadLength);

        offset += payloadLength;

        if (mOtherDataPresent) {
            // We want to stay byte-aligned.

            CHECK((mOtherDataLenBits % 8) == 0);
            CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size());
            offset += mOtherDataLenBits / 8;
        }
    }

    CHECK_EQ(offset, buffer->size());

    return out;
}

AMPEG4AudioAssembler::AMPEG4AudioAssembler(
        const sp<AMessage> &notify, const AString &params)
    : mNotifyMsg(notify),
      mMuxConfigPresent(false),
      mAccessUnitRTPTime(0),
      mNextExpectedSeqNoValid(false),
      mNextExpectedSeqNo(0),
      mAccessUnitDamaged(false) {
    AString val;
    if (!GetAttribute(params.c_str(), "cpresent", &val)) {
        mMuxConfigPresent = true;
    } else if (val == "0") {
        mMuxConfigPresent = false;
    } else {
        CHECK(val == "1");
        mMuxConfigPresent = true;
    }

    CHECK(GetAttribute(params.c_str(), "config", &val));

    sp<ABuffer> config = decodeHex(val);
    CHECK(config != NULL);

    ABitReader bits(config->data(), config->size());
    status_t err = parseStreamMuxConfig(
            &bits, &mNumSubFrames, &mFrameLengthType,
            &mOtherDataPresent, &mOtherDataLenBits);

    CHECK_EQ(err, (status_t)NO_ERROR);
}

AMPEG4AudioAssembler::~AMPEG4AudioAssembler() {
@@ -108,13 +471,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
    while (it != mPackets.end()) {
        const sp<ABuffer> &unit = *it;

        size_t n = 0;
        while (unit->data()[n] == 0xff) {
            ++n;
        }
        ++n;

        totalSize += unit->size() - n;
        totalSize += unit->size();
        ++it;
    }

@@ -124,20 +481,13 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
    while (it != mPackets.end()) {
        const sp<ABuffer> &unit = *it;

        size_t n = 0;
        while (unit->data()[n] == 0xff) {
            ++n;
        }
        ++n;

        memcpy((uint8_t *)accessUnit->data() + offset,
               unit->data() + n, unit->size() - n);

        offset += unit->size() - n;
               unit->data(), unit->size());

        ++it;
    }

    accessUnit = removeLATMFraming(accessUnit);
    CopyTimes(accessUnit, *mPackets.begin());

#if 0
+12 −1
Original line number Diff line number Diff line
@@ -27,9 +27,11 @@
namespace android {

struct AMessage;
struct AString;

struct AMPEG4AudioAssembler : public ARTPAssembler {
    AMPEG4AudioAssembler(const sp<AMessage> &notify);
    AMPEG4AudioAssembler(
            const sp<AMessage> &notify, const AString &params);

protected:
    virtual ~AMPEG4AudioAssembler();
@@ -40,6 +42,13 @@ protected:

private:
    sp<AMessage> mNotifyMsg;

    bool mMuxConfigPresent;
    unsigned mNumSubFrames;
    unsigned mFrameLengthType;
    bool mOtherDataPresent;
    unsigned mOtherDataLenBits;

    uint32_t mAccessUnitRTPTime;
    bool mNextExpectedSeqNoValid;
    uint32_t mNextExpectedSeqNo;
@@ -49,6 +58,8 @@ private:
    AssemblyStatus addPacket(const sp<ARTPSource> &source);
    void submitAccessUnit();

    sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);

    DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
};

+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ ARTPSource::ARTPSource(
        mAssembler = new AAVCAssembler(notify);
        mIssueFIRRequests = true;
    } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
        mAssembler = new AMPEG4AudioAssembler(notify);
        mAssembler = new AMPEG4AudioAssembler(notify, params);
    } else if (!strncmp(desc.c_str(), "H263-1998/", 10)
            || !strncmp(desc.c_str(), "H263-2000/", 10)) {
        mAssembler = new AH263Assembler(notify);
+2 −1
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ bool ASessionDescription::parse(const void *data, size_t size) {
    mFormats.push(AString("[root]"));

    AString desc((const char *)data, size);
    LOGI("%s", desc.c_str());

    size_t i = 0;
    for (;;) {
@@ -76,6 +75,8 @@ bool ASessionDescription::parse(const void *data, size_t size) {
            return false;
        }

        LOGI("%s", line.c_str());

        switch (line.c_str()[0]) {
            case 'v':
            {