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

Commit 6c955303 authored by Harish Mahendrakar's avatar Harish Mahendrakar Committed by Ray Essick
Browse files

stagefright: Updates to Opus unified CSD syntax

Added a marker and length field before OpusHead data as well.
This will prevent any potential truncation of OpusHead, when
data inside OpusHead matches one of the markers defined

Added checks to validate sizes parsed.
If the sizes are not as expected, then treat that as an error

OggWriter updated to not write codec config data as frame data

Test: With a local AMediaCodec api based application to decode
 opus encoder's output
Test: Test with mediamuxer api based application to mux encoders output
 to ogg file
Test: cts-tradefed run cts -m CtsMediaTestCases \
 -t android.media.cts.EncoderTest#testOpusEncoders

Bug: 123581317
Bug: 124053011
Change-Id: Ic3c7613ff47855e16be39dc60939e1e715522bc6
parent be32a246
Loading
Loading
Loading
Loading
+14 −9
Original line number Diff line number Diff line
@@ -252,20 +252,25 @@ void C2SoftOpusDec::process(
    const uint8_t *data = rView.data() + inOffset;
    if (mInputBufferCount < 3) {
        if (mInputBufferCount == 0) {
            size_t opusHeadSize = inSize;
            size_t opusHeadSize = 0;
            size_t codecDelayBufSize = 0;
            size_t seekPreRollBufSize = 0;
            void *opusHeadBuf = (void *)data;
            void *opusHeadBuf = NULL;
            void *codecDelayBuf = NULL;
            void *seekPreRollBuf = NULL;

            GetOpusHeaderBuffers(data, inSize, &opusHeadBuf,
            if (!GetOpusHeaderBuffers(data, inSize, &opusHeadBuf,
                                     &opusHeadSize, &codecDelayBuf,
                                     &codecDelayBufSize, &seekPreRollBuf,
                                &seekPreRollBufSize);
                                     &seekPreRollBufSize)) {
                ALOGE("%s encountered error in GetOpusHeaderBuffers", __func__);
                mSignalledError = true;
                work->result = C2_CORRUPTED;
                return;
            }

            if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) {
                ALOGE("Encountered error while Parsing Opus Header.");
                ALOGE("%s Encountered error while Parsing Opus Header.", __func__);
                mSignalledError = true;
                work->result = C2_CORRUPTED;
                return;
@@ -304,14 +309,14 @@ void C2SoftOpusDec::process(
                return;
            }

            if (codecDelayBuf && codecDelayBufSize == 8) {
            if (codecDelayBuf && codecDelayBufSize == sizeof(uint64_t)) {
                uint64_t value;
                memcpy(&value, codecDelayBuf, sizeof(uint64_t));
                mCodecDelay = ns_to_samples(value, kRate);
                mSamplesToDiscard = mCodecDelay;
                ++mInputBufferCount;
            }
            if (seekPreRollBuf && seekPreRollBufSize == 8) {
            if (seekPreRollBuf && seekPreRollBufSize == sizeof(uint64_t)) {
                uint64_t value;
                memcpy(&value, codecDelayBuf, sizeof(uint64_t));
                mSeekPreRoll = ns_to_samples(value, kRate);
+12 −0
Original line number Diff line number Diff line
@@ -295,6 +295,18 @@ status_t OggWriter::threadFunc() {
                  mEstimatedSizeBytes, mMaxFileSizeLimitBytes);
            break;
        }

        int32_t isCodecSpecific;
        if ((buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecSpecific)
             && isCodecSpecific)
            || IsOpusHeader((uint8_t*)buffer->data() + buffer->range_offset(),
                         buffer->range_length())) {
            ALOGV("Drop codec specific info buffer");
            buffer->release();
            buffer = nullptr;
            continue;
        }

        int64_t timestampUs;
        CHECK(buffer->meta_data().findInt64(kKeyTime, &timestampUs));
        if (timestampUs > mEstimatedDurationUs) {
+120 −31
Original line number Diff line number Diff line
@@ -15,9 +15,9 @@
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "SoftOpus"
#include <algorithm>
#define LOG_TAG "OpusHeader"
#include <cstring>
#include <inttypes.h>
#include <stdint.h>

#include <log/log.h>
@@ -91,6 +91,9 @@ static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_of

// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header
bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header) {
    if (data == NULL) {
        return false;
    }
    if (data_size < kOpusHeaderSize) {
        ALOGV("Header size is too small.");
        return false;
@@ -183,53 +186,88 @@ int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate,
        ALOGD("Buffer not large enough to hold unified OPUS CSD");
        return -1;
    }
    int headerLen = 0;

    // Add opus header
    /*
      Following is the CSD syntax for signalling OpusHeader
      (http://wiki.xiph.org/OggOpus#ID_Header)

      Marker (8 bytes) | Length (8 bytes) | OpusHeader

      Markers supported:
      AOPUS_CSD_OPUS_HEADER_MARKER - Signals Opus Header

      Length should be a value within AOPUS_OPUSHEAD_MINSIZE and AOPUS_OPUSHEAD_MAXSIZE.
    */

    memcpy(output + headerLen, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE);
    headerLen += AOPUS_MARKER_SIZE;

    int headerLen = WriteOpusHeader(header, inputSampleRate, output,
    // Place holder for opusHeader Size
    headerLen += AOPUS_LENGTH_SIZE;

    int headerSize = WriteOpusHeader(header, inputSampleRate, output + headerLen,
        outputSize);
    if (headerLen < 0) {
        ALOGD("WriteOpusHeader failed");
        return -1;
    }
    if (headerLen >= (outputSize - 2 * AOPUS_TOTAL_CSD_SIZE)) {
        ALOGD("Buffer not large enough to hold codec delay and seek pre roll");
    if (headerSize < 0) {
        ALOGD("%s: WriteOpusHeader failed", __func__);
        return -1;
    }
    headerLen += headerSize;

    uint64_t length = AOPUS_LENGTH;
    // Update opus headerSize after AOPUS_CSD_OPUS_HEADER_MARKER
    uint64_t length = headerSize;
    memcpy(output + AOPUS_MARKER_SIZE, &length, AOPUS_LENGTH_SIZE);

    /*
      Following is the CSD syntax for signalling codec delay and
      seek pre-roll which is to be appended after OpusHeader

      Marker (8 bytes) | Length (8 bytes) | Samples (8 bytes)
      Marker (8 bytes) | Length (8 bytes) | Samples in ns (8 bytes)

      Markers supported:
      AOPUSDLY - Signals Codec Delay
      AOPUSPRL - Signals seek pre roll
      AOPUS_CSD_CODEC_DELAY_MARKER - codec delay as samples in ns, represented in 8 bytes
      AOPUS_CSD_SEEK_PREROLL_MARKER - preroll adjustment as samples in ns, represented in 8 bytes

      Length should be 8.
    */

    length = sizeof(codecDelay);
    if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) {
        ALOGD("Buffer not large enough to hold codec delay");
        return -1;
    }
    // Add codec delay
    memcpy(output + headerLen, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE);
    headerLen += AOPUS_MARKER_SIZE;
    memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
    headerLen += AOPUS_LENGTH_SIZE;
    memcpy(output + headerLen, &codecDelay, AOPUS_CSD_SIZE);
    headerLen += AOPUS_CSD_SIZE;
    memcpy(output + headerLen, &codecDelay, length);
    headerLen += length;

    length = sizeof(seekPreRoll);
    if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) {
        ALOGD("Buffer not large enough to hold seek pre roll");
        return -1;
    }
    // Add skip pre roll
    memcpy(output + headerLen, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE);
    headerLen += AOPUS_MARKER_SIZE;
    memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE);
    headerLen += AOPUS_LENGTH_SIZE;
    memcpy(output + headerLen, &seekPreRoll, AOPUS_CSD_SIZE);
    headerLen += AOPUS_CSD_SIZE;
    memcpy(output + headerLen, &seekPreRoll, length);
    headerLen += length;

    return headerLen;
}

void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
bool IsOpusHeader(const uint8_t *data, size_t data_size) {
    if (data_size < AOPUS_MARKER_SIZE) {
        return false;
    }

    return !memcmp(data, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE);
}

bool GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
                          void **opusHeadBuf, size_t *opusHeadSize,
                          void **codecDelayBuf, size_t *codecDelaySize,
                          void **seekPreRollBuf, size_t *seekPreRollSize) {
@@ -237,26 +275,77 @@ void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
    *codecDelaySize = 0;
    *seekPreRollBuf = NULL;
    *seekPreRollSize = 0;
    *opusHeadBuf = NULL;
    *opusHeadSize = 0;

    // AOPUS_MARKER_SIZE is 8 "OpusHead" is of size 8
    if (data_size < 8)
        return false;

    // Check if the CSD is in legacy format
    if (!memcmp("OpusHead", data, 8)) {
        if (data_size < AOPUS_OPUSHEAD_MINSIZE || data_size > AOPUS_OPUSHEAD_MAXSIZE) {
            ALOGD("Unexpected size for opusHeadSize %zu", data_size);
            return false;
        }
        *opusHeadBuf = (void *)data;
        *opusHeadSize = data_size;
    if (data_size >= AOPUS_UNIFIED_CSD_MINSIZE) {
        return true;
    } else if (memcmp(AOPUS_CSD_MARKER_PREFIX, data, AOPUS_CSD_MARKER_PREFIX_SIZE) == 0) {
        size_t i = 0;
        while (i < data_size - AOPUS_TOTAL_CSD_SIZE) {
        bool found = false;
        while (i <= data_size - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE) {
            uint8_t *csdBuf = (uint8_t *)data + i;
            if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) {
                *opusHeadSize = std::min(*opusHeadSize, i);
            if (!memcmp(csdBuf, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE)) {
                uint64_t value;
                memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
                if (value < AOPUS_OPUSHEAD_MINSIZE || value > AOPUS_OPUSHEAD_MAXSIZE) {
                    ALOGD("Unexpected size for opusHeadSize %" PRIu64, value);
                    return false;
                }
                i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
                if (i > data_size) {
                    ALOGD("Marker signals a header that is larger than input");
                    return false;
                }
                *opusHeadBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
                *opusHeadSize = value;
                found = true;
            } else if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) {
                uint64_t value;
                memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
                if (value != sizeof(uint64_t)) {
                    ALOGD("Unexpected size for codecDelay %" PRIu64, value);
                    return false;
                }
                i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
                if (i > data_size) {
                    ALOGD("Marker signals a header that is larger than input");
                    return false;
                }
                *codecDelayBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
                *codecDelaySize = AOPUS_CSD_SIZE;
                i += AOPUS_TOTAL_CSD_SIZE;
                *codecDelaySize = value;
            } else if (!memcmp(csdBuf, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE)) {
                *opusHeadSize = std::min(*opusHeadSize, i);
                uint64_t value;
                memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value));
                if (value != sizeof(uint64_t)) {
                    ALOGD("Unexpected size for seekPreRollSize %" PRIu64, value);
                    return false;
                }
                i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value;
                if (i > data_size) {
                    ALOGD("Marker signals a header that is larger than input");
                    return false;
                }
                *seekPreRollBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE;
                *seekPreRollSize = AOPUS_CSD_SIZE;
                i += AOPUS_TOTAL_CSD_SIZE;
                *seekPreRollSize = value;
            } else {
                i++;
            }
        }
        return found;
    } else {
        return false;  // it isn't in either format
    }
}

+29 −13
Original line number Diff line number Diff line
@@ -25,22 +25,37 @@
namespace android {

/* Constants used for delimiting Opus CSD */
#define AOPUS_CSD_CODEC_DELAY_MARKER "AOPUSDLY"
#define AOPUS_CSD_SEEK_PREROLL_MARKER "AOPUSPRL"
#define AOPUS_CSD_SIZE 8
#define AOPUS_LENGTH 8
#define AOPUS_CSD_MARKER_PREFIX "AOPUS"
#define AOPUS_CSD_MARKER_PREFIX_SIZE (sizeof(AOPUS_CSD_MARKER_PREFIX) - 1)
#define AOPUS_CSD_OPUS_HEADER_MARKER AOPUS_CSD_MARKER_PREFIX "HDR"
#define AOPUS_CSD_CODEC_DELAY_MARKER AOPUS_CSD_MARKER_PREFIX "DLY"
#define AOPUS_CSD_SEEK_PREROLL_MARKER AOPUS_CSD_MARKER_PREFIX "PRL"
#define AOPUS_MARKER_SIZE 8
#define AOPUS_LENGTH_SIZE 8
#define AOPUS_TOTAL_CSD_SIZE \
    ((AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_CSD_SIZE))
#define AOPUS_CSD0_MINSIZE 19
#define AOPUS_LENGTH_SIZE sizeof(uint64_t)
#define AOPUS_CSD_CODEC_DELAY_SIZE \
     (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + sizeof(uint64_t)
#define AOPUS_CSD_SEEK_PREROLL_SIZE \
     (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + sizeof(uint64_t)

/* OpusHead csd minimum size is 19 */
#define AOPUS_OPUSHEAD_MINSIZE 19
#define AOPUS_CSD_OPUSHEAD_MINSIZE \
    (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_OPUSHEAD_MINSIZE)

#define AOPUS_UNIFIED_CSD_MINSIZE \
    ((AOPUS_CSD0_MINSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE))
    ((AOPUS_CSD_OPUSHEAD_MINSIZE) + \
     (AOPUS_CSD_CODEC_DELAY_SIZE) + \
     (AOPUS_CSD_SEEK_PREROLL_SIZE))

/* OpusHead csd at max can be AOPUS_CSD_OPUSHEAD_MINSIZE + 2 + max number of channels (255) */
#define AOPUS_OPUSHEAD_MAXSIZE ((AOPUS_OPUSHEAD_MINSIZE) + 2 + 255)
#define AOPUS_CSD_OPUSHEAD_MAXSIZE \
    (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_OPUSHEAD_MAXSIZE)

/* CSD0 at max can be 22 bytes + max number of channels (255) */
#define AOPUS_CSD0_MAXSIZE 277
#define AOPUS_UNIFIED_CSD_MAXSIZE \
    ((AOPUS_CSD0_MAXSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE))
    ((AOPUS_CSD_OPUSHEAD_MAXSIZE) + \
     (AOPUS_CSD_CODEC_DELAY_SIZE) + \
     (AOPUS_CSD_SEEK_PREROLL_SIZE))

struct OpusHeader {
    int channels;
@@ -54,13 +69,14 @@ struct OpusHeader {

bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header);
int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size);
void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
bool GetOpusHeaderBuffers(const uint8_t *data, size_t data_size,
                          void **opusHeadBuf, size_t *opusHeadSize,
                          void **codecDelayBuf, size_t *codecDelaySize,
                          void **seekPreRollBuf, size_t *seekPreRollSize);
int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate,
                     uint8_t* output, size_t outputSize, uint64_t codecDelay,
                     uint64_t seekPreRoll);
bool IsOpusHeader(const uint8_t *data, size_t data_size);
}  // namespace android

#endif  // OPUS_HEADER_H_