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

Commit a547a3dd authored by Baoli Yan's avatar Baoli Yan Committed by Lajos Molnar
Browse files

stagefright/extrators: Dolby Vision mp4 extractor update

Add support for 'DvcC' 'DvvC' 'DvwC' boxes.

Change the use of 'csd-2' that was only used by the extractor internally
to instead add that information to 'csd-0';

Change-Id: I5c1378f69e71e8a82f874dc2f961e7384e060e8d
parent 36b4b7dc
Loading
Loading
Loading
Loading
+41 −21
Original line number Diff line number Diff line
@@ -1127,10 +1127,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                    void *data;
                    size_t size;

                    if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
                    if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
                                               &data, &size)
                        && size >= 5) {
                        const uint8_t *ptr = (const uint8_t *)data;
                        && size >= 24) {
                        const uint8_t *ptr = (const uint8_t *)data + (size - 24);
                        const uint8_t profile = ptr[2] >> 1;
                        const uint8_t bl_compatibility_id = (ptr[4]) >> 4;
                        bool create_two_tracks = false;
@@ -1147,13 +1147,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {

                            track_b->timescale = mLastTrack->timescale;
                            track_b->sampleTable = mLastTrack->sampleTable;
                            track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata;
                            track_b->includes_expensive_metadata =
                                mLastTrack->includes_expensive_metadata;
                            track_b->skipTrack = mLastTrack->skipTrack;
                            track_b->elst_needs_processing = mLastTrack->elst_needs_processing;
                            track_b->elst_media_time = mLastTrack->elst_media_time;
                            track_b->elst_segment_duration = mLastTrack->elst_segment_duration;
                            track_b->elst_shift_start_ticks = mLastTrack->elst_shift_start_ticks;
                            track_b->elst_initial_empty_edit_ticks = mLastTrack->elst_initial_empty_edit_ticks;
                            track_b->elst_initial_empty_edit_ticks =
                                mLastTrack->elst_initial_empty_edit_ticks;
                            track_b->subsample_encryption = mLastTrack->subsample_encryption;

                            track_b->mTx3gBuffer = mLastTrack->mTx3gBuffer;
@@ -1166,11 +1168,11 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                            mLastTrack->next = track_b;
                            track_b->next = NULL;

                            // we want to remove the csd-2 key from the metadata, but
                            // we want to remove the csd-0 key from the metadata, but
                            // don't have an AMediaFormat_* function to do so. Settle
                            // for replacing this csd-2 with an empty csd-2.
                            // for replacing this csd-0 with an empty csd-0.
                            uint8_t emptybuffer[8] = {};
                            AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_2,
                            AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0,
                                                   emptybuffer, 0);

                            if (4 == profile || 7 == profile || 8 == profile ) {
@@ -1182,6 +1184,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                            } else if (10 == profile) {
                                AMediaFormat_setString(track_b->meta,
                                        AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1);
                                AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0,
                                    data, size - 24);
                            } // Should never get to else part

                            mLastTrack = track_b;
@@ -2591,9 +2595,11 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
            *offset += chunk_size;
            break;
        }
        case FOURCC("dvcC"):
        case FOURCC("dvvC"): {

        case FOURCC("dvcC"):
        case FOURCC("dvvC"):
        case FOURCC("dvwC"):
        {
            if (chunk_data_size != 24) {
                return ERROR_MALFORMED;
            }
@@ -2612,14 +2618,29 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
            if (mLastTrack == NULL)
                return ERROR_MALFORMED;

            AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
            void *data = nullptr;
            size_t size = 0;
            if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
                //if csd-0 is already present, then append dvcc
                auto csd0_dvcc = heapbuffer<uint8_t>(size + chunk_data_size);

                memcpy(csd0_dvcc.get(), data, size);
                memcpy(csd0_dvcc.get() + size, buffer.get(), chunk_data_size);

                AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
                                    csd0_dvcc.get(), size + chunk_data_size);
            } else {
                //if not set csd-0 directly
                AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
                                    buffer.get(), chunk_data_size);
            }
            AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME,
                                   MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);

            *offset += chunk_size;
            break;
        }

        case FOURCC("d263"):
        {
            *offset += chunk_size;
@@ -4458,7 +4479,6 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
    if (!AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime)) {
        return NULL;
    }

    sp<ItemTable> itemTable;
    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
        void *data;
@@ -4491,14 +4511,14 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
        void *data;
        size_t size;
        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)
                || size < 24) {
            return NULL;
        }

        const uint8_t *ptr = (const uint8_t *)data;

        const uint8_t *ptr = (const uint8_t *)data + (size - 24);
        // dv_major.dv_minor Should be 1.0 or 2.1
        if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
        if ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)) {
            return NULL;
        }
   } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)
@@ -4576,7 +4596,7 @@ status_t MPEG4Extractor::verifyTrack(Track *track) {
            return ERROR_MALFORMED;
        }
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
            return ERROR_MALFORMED;
        }
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
@@ -5152,11 +5172,11 @@ MPEG4Source::MPEG4Source(
        ALOGV("%s DolbyVision stream detected", __FUNCTION__);
        void *data;
        size_t size;
        CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size));
        CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_0, &data, &size));

        const uint8_t *ptr = (const uint8_t *)data;
        const uint8_t *ptr = (const uint8_t *)data + (size - 24);

        CHECK(size == 24);
        CHECK(size >= 24);

        // dv_major.dv_minor Should be 1.0 or 2.1
        CHECK(!((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)));
+143 −30
Original line number Diff line number Diff line
@@ -1510,7 +1510,30 @@ status_t convertMetaDataToMessage(
        msg->setBuffer("csd-0", buffer);
    }

    if (meta->findData(kKeyDVCC, &type, &data, &size)) {
    if (meta->findData(kKeyDVCC, &type, &data, &size)
            || meta->findData(kKeyDVVC, &type, &data, &size)
            || meta->findData(kKeyDVWC, &type, &data, &size)) {
        sp<ABuffer> buffer, csdOrg;
        if (msg->findBuffer("csd-0", &csdOrg)) {
            buffer = new (std::nothrow) ABuffer(size + csdOrg->size());
            if (buffer.get() == NULL || buffer->base() == NULL) {
                return NO_MEMORY;
            }

            memcpy(buffer->data(), csdOrg->data(), csdOrg->size());
            memcpy(buffer->data() + csdOrg->size(), data, size);
        } else {
            buffer = new (std::nothrow) ABuffer(size);
            if (buffer.get() == NULL || buffer->base() == NULL) {
                return NO_MEMORY;
            }
            memcpy(buffer->data(), data, size);
        }

        buffer->meta()->setInt32("csd", true);
        buffer->meta()->setInt64("timeUs", 0);
        msg->setBuffer("csd-0", buffer);

        const uint8_t *ptr = (const uint8_t *)data;
        ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size);
        parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg);
@@ -2015,30 +2038,131 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
                   mime == MEDIA_MIMETYPE_IMAGE_AVIF) {
            meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
        } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
            if (msg->findBuffer("csd-2", &csd2)) {
                //dvcc should be 24
                if (csd2->size() == 24) {
                    meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
                    uint8_t *dvcc = csd2->data();
                    const uint8_t profile = dvcc[2] >> 1;
            int32_t needCreateDoviCSD = 0;
            int32_t profile = 0;
            uint8_t bl_compatibility = 0;
            if (msg->findInt32("profile", &profile)) {
                if (profile == DolbyVisionProfileDvheSt) {
                    profile = 8;
                    bl_compatibility = 4;
                } else if (profile == DolbyVisionProfileDvavSe) {
                    profile = 9;
                    bl_compatibility = 2;
                }
                if (profile == 8 || profile == 9) {
                    needCreateDoviCSD = 1;
                }
            } else {
                ALOGW("did not find dolby vision profile");
            }
            // No dovi csd data, need to create it
            if (needCreateDoviCSD) {
                uint8_t dvcc[24];
                int32_t level = 0;
                uint8_t level_val = 0;

                if (msg->findInt32("level", &level)) {
                    const static ALookup<int32_t, uint8_t> levels {
                        {DolbyVisionLevelUnknown, 0},
                        {DolbyVisionLevelHd24, 1},
                        {DolbyVisionLevelHd30, 2},
                        {DolbyVisionLevelFhd24, 3},
                        {DolbyVisionLevelFhd30, 4},
                        {DolbyVisionLevelFhd60, 5},
                        {DolbyVisionLevelUhd24, 6},
                        {DolbyVisionLevelUhd30, 7},
                        {DolbyVisionLevelUhd48, 8},
                        {DolbyVisionLevelUhd60, 9},
                    };
                    levels.map(level, &level_val);
                    ALOGV("found dolby vision level: %d, value: %d", level, level_val);
                }

                dvcc[0] = 1; // major version
                dvcc[1] = 0; // minor version
                dvcc[2] = (uint8_t)((profile & 0x7f) << 1);// dolby vision profile
                dvcc[2] = (uint8_t)((dvcc[2] | (uint8_t)((level_val >> 5) & 0x1)) & 0xff);
                dvcc[3] = (uint8_t)((level_val & 0x1f) << 3); // dolby vision level
                dvcc[3] = (uint8_t)(dvcc[3] | (1 << 2)); // rpu_present_flag
                dvcc[3] = (uint8_t)(dvcc[3] | (1)); // bl_present_flag
                dvcc[4] = (uint8_t)(bl_compatibility << 4);// bl_compatibility id

                std::vector<uint8_t> dvcc_data(24);
                memcpy(dvcc_data.data(), dvcc, 24);
                if (profile > 10) {
                    meta->setData(kKeyDVWC, kTypeDVWC, dvcc_data.data(), 24);
                } else if (profile > 7) {
                    meta->setData(kKeyDVVC, kTypeDVVC, dvcc_data.data(), 24);
                } else {
                    meta->setData(kKeyDVCC, kTypeDVCC, dvcc_data.data(), 24);
                }
            } else if (csd0size >= 24) { // have dovi csd, just send it out...
                uint8_t *dvconfig = csd0->data() + (csd0size -24);
                profile = dvconfig[2] >> 1;
                if (profile > 10) {
                    meta->setData(kKeyDVWC, kTypeDVWC, dvconfig, 24);
                } else if (profile > 7) {
                    meta->setData(kKeyDVVC, kTypeDVVC, dvconfig, 24);
                } else {
                    meta->setData(kKeyDVCC, kTypeDVCC, dvconfig, 24);
                }
            } else {
                return BAD_VALUE;
            }

            // Send the avc/hevc/av1 csd data...
            if (csd0size >= 24) {
                sp<ABuffer> csd;
                if ( profile > 1 && profile < 9) {
                    if (msg->findBuffer("csd-hevc", &csd)) {
                        meta->setData(kKeyHVCC, kTypeHVCC, csd->data(), csd->size());
                    } else if (csd0size > 24) {
                        std::vector<uint8_t> hvcc(csd0size + 1024);
                        size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
                        meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
                    } else if (DolbyVisionProfileDvav110 == profile) {
                        meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
                    } else {
                    }
                } else if (profile == 9) {
                    sp<ABuffer> csd1;
                        if (msg->findBuffer("csd-1", &csd1)) {
                    if (msg->findBuffer("csd-avc", &csd)) {
                        meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
                    } else if (msg->findBuffer("csd-1", &csd1)) {
                        std::vector<char> avcc(csd0size + csd1->size() + 1024);
                        size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
                        meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
                    } else { // for dolby vision avc, csd0 also holds csd1
                        size_t i = 0;
                        int csd0realsize = 0;
                        do {
                            i = findNextNalStartCode(csd0->data() + i,
                                            csd0->size() - i) - csd0->data();
                            if (i > 0) {
                                csd0realsize = i;
                                break;
                            }
                            i += 4;
                        } while(i < csd0->size());
                        // buffer0 -> csd0
                        sp<ABuffer> buffer0 = new (std::nothrow) ABuffer(csd0realsize);
                        if (buffer0.get() == NULL || buffer0->base() == NULL) {
                            return NO_MEMORY;
                        }
                        memcpy(buffer0->data(), csd0->data(), csd0realsize);
                        // buffer1 -> csd1
                        sp<ABuffer> buffer1 = new (std::nothrow)
                                ABuffer(csd0->size() - csd0realsize);
                        if (buffer1.get() == NULL || buffer1->base() == NULL) {
                            return NO_MEMORY;
                        }
                        memcpy(buffer1->data(), csd0->data()+csd0realsize,
                                    csd0->size() - csd0realsize);

                        std::vector<char> avcc(csd0->size() + 1024);
                        size_t outsize = reassembleAVCC(buffer0, buffer1, avcc.data());
                        meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
                    }
                } else if (profile == 10) {
                    meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size() - 24);
                }
            } else {
                ALOGE("We need csd-2!!. %s", msg->debugString().c_str());
                return BAD_VALUE;
            }
        } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
            meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
@@ -2086,17 +2210,6 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
        meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
    } else if (msg->findBuffer("d263", &csd0)) {
        meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
    } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION && msg->findBuffer("csd-2", &csd2)) {
        meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());

        // Remove CSD-2 from the data here to avoid duplicate data in meta
        meta->remove(kKeyOpaqueCSD2);

        if (msg->findBuffer("csd-avc", &csd0)) {
            meta->setData(kKeyAVCC, kTypeAVCC, csd0->data(), csd0->size());
        } else if (msg->findBuffer("csd-hevc", &csd0)) {
            meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
        }
    }
    // XXX TODO add whatever other keys there are

+4 −0
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ enum {
    kKeyAVCC              = 'avcc',  // raw data
    kKeyHVCC              = 'hvcc',  // raw data
    kKeyDVCC              = 'dvcc',  // raw data
    kKeyDVVC              = 'dvvc',  // raw data
    kKeyDVWC              = 'dvwc',  // raw data
    kKeyAV1C              = 'av1c',  // raw data
    kKeyThumbnailHVCC     = 'thvc',  // raw data
    kKeyThumbnailAV1C     = 'tav1',  // raw data
@@ -283,6 +285,8 @@ enum {
    kTypeHVCC        = 'hvcc',
    kTypeAV1C        = 'av1c',
    kTypeDVCC        = 'dvcc',
    kTypeDVVC        = 'dvvc',
    kTypeDVWC        = 'dvwc',
    kTypeD263        = 'd263',
    kTypeHCOS        = 'hcos',
};