Loading media/extractors/mp4/MPEG4Extractor.cpp +16 −32 Original line number Diff line number Diff line Loading @@ -1127,15 +1127,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { void *data; size_t size; if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size) && size >= 24) { const uint8_t *ptr = (const uint8_t *)data + (size - 24); && size >= 5) { const uint8_t *ptr = (const uint8_t *)data; const uint8_t profile = ptr[2] >> 1; const uint8_t bl_compatibility_id = (ptr[4]) >> 4; const uint8_t blCompatibilityId = (ptr[4]) >> 4; bool create_two_tracks = false; if (bl_compatibility_id && bl_compatibility_id != 15) { if (blCompatibilityId && blCompatibilityId != 15) { create_two_tracks = true; } Loading Loading @@ -1168,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-0 key from the metadata, but // we want to remove the csd-2 key from the metadata, but // don't have an AMediaFormat_* function to do so. Settle // for replacing this csd-0 with an empty csd-0. // for replacing this csd-2 with an empty csd-2. uint8_t emptybuffer[8] = {}; AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0, AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_2, emptybuffer, 0); if (4 == profile || 7 == profile || 8 == profile ) { Loading @@ -1184,8 +1184,6 @@ 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; Loading Loading @@ -2618,22 +2616,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; 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, AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, buffer.get(), chunk_data_size); } AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION); Loading Loading @@ -4511,12 +4495,12 @@ 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_0, &data, &size) || size < 24) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size) || size != 24) { return NULL; } const uint8_t *ptr = (const uint8_t *)data + (size - 24); const uint8_t *ptr = (const uint8_t *)data; // dv_major.dv_minor Should be 1.0 or 2.1 if ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)) { return NULL; Loading Loading @@ -4596,7 +4580,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_0, &data, &size)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { Loading Loading @@ -5172,11 +5156,11 @@ MPEG4Source::MPEG4Source( ALOGV("%s DolbyVision stream detected", __FUNCTION__); void *data; size_t size; CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_0, &data, &size)); CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size)); const uint8_t *ptr = (const uint8_t *)data + (size - 24); const uint8_t *ptr = (const uint8_t *)data; 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))); Loading media/libstagefright/Utils.cpp +170 −149 Original line number Diff line number Diff line Loading @@ -243,26 +243,8 @@ static void parseAvcProfileLevelFromAvcc(const uint8_t *ptr, size_t size, sp<AMe } } static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) { // 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))) { ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]); return; } const uint8_t profile = ptr[2] >> 1; const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f); const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01; const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01; const uint8_t bl_present_flag = (ptr[3] & 0x01); const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4); ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d", profile, level, bl_compatibility_id); // All Dolby Profiles will have profile and level info in MediaFormat // Profile 8 and 9 will have bl_compatibility_id too. const static ALookup<uint8_t, int32_t> profiles{ static const ALookup<uint8_t, int32_t>& getDolbyVisionProfileTable() { static const ALookup<uint8_t, int32_t> profileTable = { {1, DolbyVisionProfileDvavPen}, {3, DolbyVisionProfileDvheDen}, {4, DolbyVisionProfileDvheDtr}, Loading @@ -273,8 +255,11 @@ static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size {9, DolbyVisionProfileDvavSe}, {10, DolbyVisionProfileDvav110}, }; return profileTable; } const static ALookup<uint8_t, int32_t> levels{ static const ALookup<uint8_t, int32_t>& getDolbyVisionLevelsTable() { static const ALookup<uint8_t, int32_t> levelsTable = { {0, DolbyVisionLevelUnknown}, {1, DolbyVisionLevelHd24}, {2, DolbyVisionLevelHd30}, Loading @@ -289,6 +274,30 @@ static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size {11, DolbyVisionLevel8k30}, {12, DolbyVisionLevel8k60}, }; return levelsTable; } static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) { // 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))) { ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]); return; } const uint8_t profile = ptr[2] >> 1; const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f); const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01; const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01; const uint8_t bl_present_flag = (ptr[3] & 0x01); const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4); ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d", profile, level, bl_compatibility_id); // All Dolby Profiles will have profile and level info in MediaFormat // Profile 8 and 9 will have bl_compatibility_id too. const ALookup<uint8_t, int32_t> &profiles = getDolbyVisionProfileTable(); const ALookup<uint8_t, int32_t> &levels = getDolbyVisionLevelsTable(); // set rpuAssoc if (rpu_present_flag && el_present_flag && !bl_present_flag) { format->setInt32("rpuAssoc", 1); Loading Loading @@ -1516,30 +1525,18 @@ status_t convertMetaDataToMessage( 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) { const uint8_t *ptr = (const uint8_t *)data; ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size); parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg); sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); if (buffer.get() == nullptr || buffer->base() == nullptr) { 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); msg->setBuffer("csd-2", buffer); } *format = msg; Loading Loading @@ -2041,49 +2038,59 @@ 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) { int32_t needCreateDoviCSD = 0; int32_t profile = 0; uint8_t bl_compatibility = 0; if (msg->findInt32("profile", &profile)) { int32_t profile = -1; uint8_t blCompatibilityId = -1; int32_t level = 0; uint8_t profileVal = -1; uint8_t profileVal1 = -1; uint8_t profileVal2 = -1; constexpr size_t dvccSize = 24; const ALookup<uint8_t, int32_t> &profiles = getDolbyVisionProfileTable(); const ALookup<uint8_t, int32_t> &levels = getDolbyVisionLevelsTable(); if (!msg->findBuffer("csd-2", &csd2)) { // MP4 extractors are expected to generate csd buffer // some encoders might not be generating it, in which // case we populate the track metadata dv (cc|vc|wc) // from the 'profile' and 'level' info. // This is done according to Dolby Vision ISOBMFF spec if (!msg->findInt32("profile", &profile)) { ALOGE("Dolby Vision profile not found"); return BAD_VALUE; } msg->findInt32("level", &level); if (profile == DolbyVisionProfileDvheSt) { profile = 8; bl_compatibility = 4; } else if (profile == DolbyVisionProfileDvavSe) { profile = 9; bl_compatibility = 2; if (!profiles.rlookup(DolbyVisionProfileDvheSt, &profileVal)) { // dvhe.08 ALOGE("Dolby Vision profile lookup error"); return BAD_VALUE; } if (profile == 8 || profile == 9) { needCreateDoviCSD = 1; blCompatibilityId = 4; } else if (profile == DolbyVisionProfileDvavSe) { if (!profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal)) { // dvav.09 ALOGE("Dolby Vision profile lookup error"); return BAD_VALUE; } blCompatibilityId = 2; } else { ALOGW("did not find dolby vision profile"); ALOGE("Dolby Vision profile look up error"); return BAD_VALUE; } // 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}, {DolbyVisionLevelUhd120, 10}, {DolbyVisionLevel8k30, 11}, {DolbyVisionLevel8k60, 12}, }; levels.map(level, &level_val); ALOGV("found dolby vision level: %d, value: %d", level, level_val); profile = (int32_t) profileVal; uint8_t level_val = 0; if (!levels.map(level, &level_val)) { ALOGE("Dolby Vision level lookup error"); return BAD_VALUE; } std::vector<uint8_t> dvcc(dvccSize); dvcc[0] = 1; // major version dvcc[1] = 0; // minor version dvcc[2] = (uint8_t)((profile & 0x7f) << 1); // dolby vision profile Loading @@ -2091,51 +2098,57 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { 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); dvcc[4] = (uint8_t)(blCompatibilityId << 4); // bl_compatibility id profiles.rlookup(DolbyVisionProfileDvav110, &profileVal); profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1); if (profile > (int32_t) profileVal) { meta->setData(kKeyDVWC, kTypeDVWC, dvcc.data(), dvccSize); } else if (profile > (int32_t) profileVal1) { meta->setData(kKeyDVVC, kTypeDVVC, dvcc.data(), dvccSize); } 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); meta->setData(kKeyDVCC, kTypeDVCC, dvcc.data(), dvccSize); } } else { // we have csd-2, just use that to populate dvcc if (csd2->size() == dvccSize) { uint8_t *dvcc = csd2->data(); profile = dvcc[2] >> 1; profiles.rlookup(DolbyVisionProfileDvav110, &profileVal); profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1); if (profile > (int32_t) profileVal) { meta->setData(kKeyDVWC, kTypeDVWC, csd2->data(), csd2->size()); } else if (profile > (int32_t) profileVal1) { meta->setData(kKeyDVVC, kTypeDVVC, csd2->data(), csd2->size()); } else { meta->setData(kKeyDVCC, kTypeDVCC, dvconfig, 24); meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size()); } } else { ALOGE("Convert MessageToMetadata csd-2 is present but not valid"); 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) { } profiles.rlookup(DolbyVisionProfileDvavPen, &profileVal); profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal1); profiles.rlookup(DolbyVisionProfileDvav110, &profileVal2); if ((profile > (int32_t) profileVal) && (profile < (int32_t) profileVal1)) { 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 (profile == 9) { } else if (profile == (int32_t) profileVal2) { meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size()); } else { sp<ABuffer> csd1; if (msg->findBuffer("csd-avc", &csd)) { meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size()); } else if (msg->findBuffer("csd-1", &csd1)) { 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 } else { // for dolby vision avc, csd0 also holds csd1 size_t i = 0; int csd0realsize = 0; do { Loading Loading @@ -2166,9 +2179,6 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { 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 if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); Loading Loading @@ -2216,6 +2226,17 @@ 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 Loading Loading
media/extractors/mp4/MPEG4Extractor.cpp +16 −32 Original line number Diff line number Diff line Loading @@ -1127,15 +1127,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { void *data; size_t size; if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size) && size >= 24) { const uint8_t *ptr = (const uint8_t *)data + (size - 24); && size >= 5) { const uint8_t *ptr = (const uint8_t *)data; const uint8_t profile = ptr[2] >> 1; const uint8_t bl_compatibility_id = (ptr[4]) >> 4; const uint8_t blCompatibilityId = (ptr[4]) >> 4; bool create_two_tracks = false; if (bl_compatibility_id && bl_compatibility_id != 15) { if (blCompatibilityId && blCompatibilityId != 15) { create_two_tracks = true; } Loading Loading @@ -1168,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-0 key from the metadata, but // we want to remove the csd-2 key from the metadata, but // don't have an AMediaFormat_* function to do so. Settle // for replacing this csd-0 with an empty csd-0. // for replacing this csd-2 with an empty csd-2. uint8_t emptybuffer[8] = {}; AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0, AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_2, emptybuffer, 0); if (4 == profile || 7 == profile || 8 == profile ) { Loading @@ -1184,8 +1184,6 @@ 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; Loading Loading @@ -2618,22 +2616,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; 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, AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, buffer.get(), chunk_data_size); } AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION); Loading Loading @@ -4511,12 +4495,12 @@ 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_0, &data, &size) || size < 24) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size) || size != 24) { return NULL; } const uint8_t *ptr = (const uint8_t *)data + (size - 24); const uint8_t *ptr = (const uint8_t *)data; // dv_major.dv_minor Should be 1.0 or 2.1 if ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)) { return NULL; Loading Loading @@ -4596,7 +4580,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_0, &data, &size)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { Loading Loading @@ -5172,11 +5156,11 @@ MPEG4Source::MPEG4Source( ALOGV("%s DolbyVision stream detected", __FUNCTION__); void *data; size_t size; CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_0, &data, &size)); CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size)); const uint8_t *ptr = (const uint8_t *)data + (size - 24); const uint8_t *ptr = (const uint8_t *)data; 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))); Loading
media/libstagefright/Utils.cpp +170 −149 Original line number Diff line number Diff line Loading @@ -243,26 +243,8 @@ static void parseAvcProfileLevelFromAvcc(const uint8_t *ptr, size_t size, sp<AMe } } static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) { // 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))) { ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]); return; } const uint8_t profile = ptr[2] >> 1; const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f); const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01; const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01; const uint8_t bl_present_flag = (ptr[3] & 0x01); const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4); ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d", profile, level, bl_compatibility_id); // All Dolby Profiles will have profile and level info in MediaFormat // Profile 8 and 9 will have bl_compatibility_id too. const static ALookup<uint8_t, int32_t> profiles{ static const ALookup<uint8_t, int32_t>& getDolbyVisionProfileTable() { static const ALookup<uint8_t, int32_t> profileTable = { {1, DolbyVisionProfileDvavPen}, {3, DolbyVisionProfileDvheDen}, {4, DolbyVisionProfileDvheDtr}, Loading @@ -273,8 +255,11 @@ static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size {9, DolbyVisionProfileDvavSe}, {10, DolbyVisionProfileDvav110}, }; return profileTable; } const static ALookup<uint8_t, int32_t> levels{ static const ALookup<uint8_t, int32_t>& getDolbyVisionLevelsTable() { static const ALookup<uint8_t, int32_t> levelsTable = { {0, DolbyVisionLevelUnknown}, {1, DolbyVisionLevelHd24}, {2, DolbyVisionLevelHd30}, Loading @@ -289,6 +274,30 @@ static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size {11, DolbyVisionLevel8k30}, {12, DolbyVisionLevel8k60}, }; return levelsTable; } static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) { // 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))) { ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]); return; } const uint8_t profile = ptr[2] >> 1; const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f); const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01; const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01; const uint8_t bl_present_flag = (ptr[3] & 0x01); const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4); ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d", profile, level, bl_compatibility_id); // All Dolby Profiles will have profile and level info in MediaFormat // Profile 8 and 9 will have bl_compatibility_id too. const ALookup<uint8_t, int32_t> &profiles = getDolbyVisionProfileTable(); const ALookup<uint8_t, int32_t> &levels = getDolbyVisionLevelsTable(); // set rpuAssoc if (rpu_present_flag && el_present_flag && !bl_present_flag) { format->setInt32("rpuAssoc", 1); Loading Loading @@ -1516,30 +1525,18 @@ status_t convertMetaDataToMessage( 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) { const uint8_t *ptr = (const uint8_t *)data; ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size); parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg); sp<ABuffer> buffer = new (std::nothrow) ABuffer(size); if (buffer.get() == nullptr || buffer->base() == nullptr) { 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); msg->setBuffer("csd-2", buffer); } *format = msg; Loading Loading @@ -2041,49 +2038,59 @@ 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) { int32_t needCreateDoviCSD = 0; int32_t profile = 0; uint8_t bl_compatibility = 0; if (msg->findInt32("profile", &profile)) { int32_t profile = -1; uint8_t blCompatibilityId = -1; int32_t level = 0; uint8_t profileVal = -1; uint8_t profileVal1 = -1; uint8_t profileVal2 = -1; constexpr size_t dvccSize = 24; const ALookup<uint8_t, int32_t> &profiles = getDolbyVisionProfileTable(); const ALookup<uint8_t, int32_t> &levels = getDolbyVisionLevelsTable(); if (!msg->findBuffer("csd-2", &csd2)) { // MP4 extractors are expected to generate csd buffer // some encoders might not be generating it, in which // case we populate the track metadata dv (cc|vc|wc) // from the 'profile' and 'level' info. // This is done according to Dolby Vision ISOBMFF spec if (!msg->findInt32("profile", &profile)) { ALOGE("Dolby Vision profile not found"); return BAD_VALUE; } msg->findInt32("level", &level); if (profile == DolbyVisionProfileDvheSt) { profile = 8; bl_compatibility = 4; } else if (profile == DolbyVisionProfileDvavSe) { profile = 9; bl_compatibility = 2; if (!profiles.rlookup(DolbyVisionProfileDvheSt, &profileVal)) { // dvhe.08 ALOGE("Dolby Vision profile lookup error"); return BAD_VALUE; } if (profile == 8 || profile == 9) { needCreateDoviCSD = 1; blCompatibilityId = 4; } else if (profile == DolbyVisionProfileDvavSe) { if (!profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal)) { // dvav.09 ALOGE("Dolby Vision profile lookup error"); return BAD_VALUE; } blCompatibilityId = 2; } else { ALOGW("did not find dolby vision profile"); ALOGE("Dolby Vision profile look up error"); return BAD_VALUE; } // 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}, {DolbyVisionLevelUhd120, 10}, {DolbyVisionLevel8k30, 11}, {DolbyVisionLevel8k60, 12}, }; levels.map(level, &level_val); ALOGV("found dolby vision level: %d, value: %d", level, level_val); profile = (int32_t) profileVal; uint8_t level_val = 0; if (!levels.map(level, &level_val)) { ALOGE("Dolby Vision level lookup error"); return BAD_VALUE; } std::vector<uint8_t> dvcc(dvccSize); dvcc[0] = 1; // major version dvcc[1] = 0; // minor version dvcc[2] = (uint8_t)((profile & 0x7f) << 1); // dolby vision profile Loading @@ -2091,51 +2098,57 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { 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); dvcc[4] = (uint8_t)(blCompatibilityId << 4); // bl_compatibility id profiles.rlookup(DolbyVisionProfileDvav110, &profileVal); profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1); if (profile > (int32_t) profileVal) { meta->setData(kKeyDVWC, kTypeDVWC, dvcc.data(), dvccSize); } else if (profile > (int32_t) profileVal1) { meta->setData(kKeyDVVC, kTypeDVVC, dvcc.data(), dvccSize); } 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); meta->setData(kKeyDVCC, kTypeDVCC, dvcc.data(), dvccSize); } } else { // we have csd-2, just use that to populate dvcc if (csd2->size() == dvccSize) { uint8_t *dvcc = csd2->data(); profile = dvcc[2] >> 1; profiles.rlookup(DolbyVisionProfileDvav110, &profileVal); profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1); if (profile > (int32_t) profileVal) { meta->setData(kKeyDVWC, kTypeDVWC, csd2->data(), csd2->size()); } else if (profile > (int32_t) profileVal1) { meta->setData(kKeyDVVC, kTypeDVVC, csd2->data(), csd2->size()); } else { meta->setData(kKeyDVCC, kTypeDVCC, dvconfig, 24); meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size()); } } else { ALOGE("Convert MessageToMetadata csd-2 is present but not valid"); 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) { } profiles.rlookup(DolbyVisionProfileDvavPen, &profileVal); profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal1); profiles.rlookup(DolbyVisionProfileDvav110, &profileVal2); if ((profile > (int32_t) profileVal) && (profile < (int32_t) profileVal1)) { 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 (profile == 9) { } else if (profile == (int32_t) profileVal2) { meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size()); } else { sp<ABuffer> csd1; if (msg->findBuffer("csd-avc", &csd)) { meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size()); } else if (msg->findBuffer("csd-1", &csd1)) { 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 } else { // for dolby vision avc, csd0 also holds csd1 size_t i = 0; int csd0realsize = 0; do { Loading Loading @@ -2166,9 +2179,6 @@ status_t convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { 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 if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); Loading Loading @@ -2216,6 +2226,17 @@ 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 Loading