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

Commit 68e6fe1f authored by Previr Rangroo's avatar Previr Rangroo Committed by Robert Shih
Browse files

Add EAC3 support to MediaExtractor

Report EAC3 mime, sample-rate and channel count
for MPEG4 and TS.

This commit supersedes the commit on  p-fs-release  branch.
https://partner-android-review.googlesource.com/c/platform/frameworks/av/+/1057810



Change-Id: I9e14d821b5e78a03028bc52d8ee838c23859e9e9
Signed-off-by: default avatarPrevir Rangroo <prang@dolby.com>
parent 7f1732ee
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -650,7 +650,8 @@ static void dumpCodecProfiles(bool queryDecoders) {
        MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
        MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
        MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
        MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4
        MEDIA_MIMETYPE_VIDEO_DOLBY_VISION,
        MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4
    };

    const char *codecType = queryDecoders? "decoder" : "encoder";
+196 −44
Original line number Diff line number Diff line
@@ -313,6 +313,9 @@ static const char *FourCC2MIME(uint32_t fourcc) {
        case FOURCC('s', 'a', 'w', 'b'):
            return MEDIA_MIMETYPE_AUDIO_AMR_WB;

        case FOURCC('e', 'c', '-', '3'):
            return MEDIA_MIMETYPE_AUDIO_EAC3;

        case FOURCC('m', 'p', '4', 'v'):
            return MEDIA_MIMETYPE_VIDEO_MPEG4;

@@ -2438,13 +2441,19 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
        case FOURCC('a', 'c', '-', '3'):
        {
            *offset += chunk_size;
            return parseAC3SampleEntry(data_offset);
            return parseAC3SpecificBox(data_offset);
        }

        case FOURCC('e', 'c', '-', '3'):
        {
            *offset += chunk_size;
            return parseEAC3SpecificBox(data_offset);
        }

        case FOURCC('a', 'c', '-', '4'):
        {
            *offset += chunk_size;
            return parseAC4SampleEntry(data_offset);
            return parseAC4SpecificBox(data_offset);
        }

        case FOURCC('f', 't', 'y', 'p'):
@@ -2518,43 +2527,43 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
    return OK;
}

status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) {
status_t MPEG4Extractor::parseChannelCountSampleRate(
        off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate) {
    // skip 16 bytes:
    //  + 6-byte reserved,
    //  + 2-byte data reference index,
    //  + 8-byte reserved
    offset += 16;
    uint16_t channelCount;
    if (!mDataSource->getUInt16(offset, &channelCount)) {
        ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count");
    *offset += 16;
    if (!mDataSource->getUInt16(*offset, channelCount)) {
        ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read channel count");
        return ERROR_MALFORMED;
    }
    // skip 8 bytes:
    //  + 2-byte channelCount,
    //  + 2-byte sample size,
    //  + 4-byte reserved
    offset += 8;
    uint16_t sampleRate;
    if (!mDataSource->getUInt16(offset, &sampleRate)) {
        ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate");
    *offset += 8;
    if (!mDataSource->getUInt16(*offset, sampleRate)) {
        ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read sample rate");
        return ERROR_MALFORMED;
    }

    // skip 4 bytes:
    //  + 2-byte sampleRate,
    //  + 2-byte reserved
    offset += 4;
    *offset += 4;
    return OK;
}

status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
    if (mLastTrack == NULL) {
        return ERROR_MALFORMED;
    }
    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
    return parseAC4SpecificBox(offset);
}

status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
    uint16_t sampleRate, channelCount;
    status_t status;
    if ((status = parseChannelCountSampleRate(&offset, &channelCount, &sampleRate)) != OK) {
        return status;
    }
    uint32_t size;
    // + 4-byte size
    // + 4-byte type
@@ -2593,39 +2602,185 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
        return ERROR_MALFORMED;
    }

    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
    return OK;
}

status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
    // skip 16 bytes:
    //  + 6-byte reserved,
    //  + 2-byte data reference index,
    //  + 8-byte reserved
    offset += 16;
    uint16_t channelCount;
    if (!mDataSource->getUInt16(offset, &channelCount)) {
status_t MPEG4Extractor::parseEAC3SpecificBox(off64_t offset) {
    if (mLastTrack == NULL) {
        return ERROR_MALFORMED;
    }
    // skip 8 bytes:
    //  + 2-byte channelCount,
    //  + 2-byte sample size,
    //  + 4-byte reserved
    offset += 8;
    uint16_t sampleRate;
    if (!mDataSource->getUInt16(offset, &sampleRate)) {
        ALOGE("MPEG4Extractor: error while reading ac-3 block: cannot read sample rate");

    uint16_t sampleRate, channels;
    status_t status;
    if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) {
        return status;
    }
    uint32_t size;
    // + 4-byte size
    // + 4-byte type
    // + 3-byte payload
    const uint32_t kEAC3SpecificBoxMinSize = 11;
    // 13 + 3 + (8 * (2 + 5 + 5 + 3 + 1 + 3 + 4 + (14 * 9 + 1))) bits == 152 bytes theoretical max
    // calculated from the required bits read below as well as the maximum number of independent
    // and dependant sub streams you can have
    const uint32_t kEAC3SpecificBoxMaxSize = 152;
    if (!mDataSource->getUInt32(offset, &size) ||
        size < kEAC3SpecificBoxMinSize ||
        size > kEAC3SpecificBoxMaxSize) {
        ALOGE("MPEG4Extractor: error while reading eac-3 block: cannot read specific box size");
        return ERROR_MALFORMED;
    }

    // skip 4 bytes:
    //  + 2-byte sampleRate,
    //  + 2-byte reserved
    offset += 4;
    return parseAC3SpecificBox(offset, sampleRate);
    uint32_t type;
    if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'e', 'c', '3')) {
        ALOGE("MPEG4Extractor: error while reading eac-3 specific block: header not dec3");
        return ERROR_MALFORMED;
    }

    offset += 4;
    uint8_t* chunk = new (std::nothrow) uint8_t[size];
    if (chunk == NULL) {
        return ERROR_MALFORMED;
    }

    if (mDataSource->readAt(offset, chunk, size) != (ssize_t)size) {
        ALOGE("MPEG4Extractor: error while reading eac-3 specific block: bitstream fields");
        delete[] chunk;
        return ERROR_MALFORMED;
    }

    ABitReader br(chunk, size);
    static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5};
    static const unsigned sampleRateTable[] = {48000, 44100, 32000};

    if (br.numBitsLeft() < 16) {
        delete[] chunk;
        return ERROR_MALFORMED;
    }
    unsigned data_rate = br.getBits(13);
    ALOGV("EAC3 data rate = %d", data_rate);

status_t MPEG4Extractor::parseAC3SpecificBox(
        off64_t offset, uint16_t sampleRate) {
    unsigned num_ind_sub = br.getBits(3) + 1;
    ALOGV("EAC3 independant substreams = %d", num_ind_sub);
    if (br.numBitsLeft() < (num_ind_sub * 23)) {
        delete[] chunk;
        return ERROR_MALFORMED;
    }

    unsigned channelCount = 0;
    for (unsigned i = 0; i < num_ind_sub; i++) {
        unsigned fscod = br.getBits(2);
        if (fscod == 3) {
            ALOGE("Incorrect fscod (3) in EAC3 header");
            delete[] chunk;
            return ERROR_MALFORMED;
        }
        unsigned boxSampleRate = sampleRateTable[fscod];
        if (boxSampleRate != sampleRate) {
            ALOGE("sample rate mismatch: boxSampleRate = %d, sampleRate = %d",
                boxSampleRate, sampleRate);
            delete[] chunk;
            return ERROR_MALFORMED;
        }

        unsigned bsid = br.getBits(5);
        if (bsid < 8) {
            ALOGW("Incorrect bsid in EAC3 header. Possibly AC-3?");
            delete[] chunk;
            return ERROR_MALFORMED;
        }

        // skip
        br.skipBits(2);
        unsigned bsmod = br.getBits(3);
        unsigned acmod = br.getBits(3);
        unsigned lfeon = br.getBits(1);
        // we currently only support the first stream
        if (i == 0)
            channelCount = channelCountTable[acmod] + lfeon;
        ALOGV("bsmod = %d, acmod = %d, lfeon = %d", bsmod, acmod, lfeon);

        br.skipBits(3);
        unsigned num_dep_sub = br.getBits(4);
        ALOGV("EAC3 dependant substreams = %d", num_dep_sub);
        if (num_dep_sub != 0) {
            if (br.numBitsLeft() < 9) {
                delete[] chunk;
                return ERROR_MALFORMED;
            }
            static const char* chan_loc_tbl[] = { "Lc/Rc","Lrs/Rrs","Cs","Ts","Lsd/Rsd",
                "Lw/Rw","Lvh/Rvh","Cvh","Lfe2" };
            unsigned chan_loc = br.getBits(9);
            unsigned mask = 1;
            for (unsigned j = 0; j < 9; j++, mask <<= 1) {
                if ((chan_loc & mask) != 0) {
                    // we currently only support the first stream
                    if (i == 0) {
                        channelCount++;
                        // these are 2 channels in the mask
                        if (j == 0 || j == 1 || j == 4 || j == 5 || j == 6) {
                            channelCount++;
                        }
                    }
                    ALOGV(" %s", chan_loc_tbl[j]);
                }
            }
        } else {
            if (br.numBitsLeft() == 0) {
                delete[] chunk;
                return ERROR_MALFORMED;
            }
            br.skipBits(1);
        }
    }

    if (br.numBitsLeft() != 0) {
        if (br.numBitsLeft() < 8) {
            delete[] chunk;
            return ERROR_MALFORMED;
        }
        unsigned mask = br.getBits(8);
        for (unsigned i = 0; i < 8; i++) {
            if (((0x1 << i) && mask) == 0)
                continue;

            if (br.numBitsLeft() < 8) {
                delete[] chunk;
                return ERROR_MALFORMED;
            }
            switch (i) {
                case 0: {
                    unsigned complexity = br.getBits(8);
                    ALOGV("Found a JOC stream with complexity = %d", complexity);
                }break;
                default: {
                    br.skipBits(8);
                }break;
            }
        }
    }
    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3);
    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);

    delete[] chunk;
    return OK;
}

status_t MPEG4Extractor::parseAC3SpecificBox(off64_t offset) {
    if (mLastTrack == NULL) {
        return ERROR_MALFORMED;
    }

    uint16_t sampleRate, channels;
    status_t status;
    if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) {
        return status;
    }
    uint32_t size;
    // + 4-byte size
    // + 4-byte type
@@ -2680,9 +2835,6 @@ status_t MPEG4Extractor::parseAC3SpecificBox(
    unsigned lfeon = br.getBits(1);
    unsigned channelCount = channelCountTable[acmod] + lfeon;

    if (mLastTrack == NULL) {
        return ERROR_MALFORMED;
    }
    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+4 −3
Original line number Diff line number Diff line
@@ -139,9 +139,10 @@ private:

    Track *findTrackByMimePrefix(const char *mimePrefix);

    status_t parseAC3SampleEntry(off64_t offset);
    status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate);
    status_t parseAC4SampleEntry(off64_t offset);
    status_t parseChannelCountSampleRate(
            off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate);
    status_t parseAC3SpecificBox(off64_t offset);
    status_t parseEAC3SpecificBox(off64_t offset);
    status_t parseAC4SpecificBox(off64_t offset);

    MPEG4Extractor(const MPEG4Extractor &);
+1 −1
Original line number Diff line number Diff line
@@ -1577,6 +1577,7 @@ static const struct mime_conv_t mimeLookup[] = {
    { MEDIA_MIMETYPE_AUDIO_VORBIS,      AUDIO_FORMAT_VORBIS },
    { MEDIA_MIMETYPE_AUDIO_OPUS,        AUDIO_FORMAT_OPUS},
    { MEDIA_MIMETYPE_AUDIO_AC3,         AUDIO_FORMAT_AC3},
    { MEDIA_MIMETYPE_AUDIO_EAC3,        AUDIO_FORMAT_E_AC3},
    { MEDIA_MIMETYPE_AUDIO_AC4,         AUDIO_FORMAT_AC4},
    { MEDIA_MIMETYPE_AUDIO_FLAC,        AUDIO_FORMAT_FLAC},
    { 0, AUDIO_FORMAT_INVALID }
@@ -1868,4 +1869,3 @@ AString nameForFd(int fd) {
}

}  // namespace android
+5 −0
Original line number Diff line number Diff line
@@ -815,6 +815,10 @@ ATSParser::Stream::Stream(
            mode = ElementaryStreamQueue::AC3;
            break;

        case STREAMTYPE_EAC3:
            mode = ElementaryStreamQueue::EAC3;
            break;

        case STREAMTYPE_PES_PRIVATE_DATA:
            if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) {
                mode = ElementaryStreamQueue::AC4;
@@ -1026,6 +1030,7 @@ bool ATSParser::Stream::isAudio() const {
        case STREAMTYPE_MPEG2_AUDIO_ADTS:
        case STREAMTYPE_LPCM_AC3:
        case STREAMTYPE_AC3:
        case STREAMTYPE_EAC3:
        case STREAMTYPE_AAC_ENCRYPTED:
        case STREAMTYPE_AC3_ENCRYPTED:
            return true;
Loading