Loading media/codec2/core/include/C2Config.h +1 −0 Original line number Diff line number Diff line Loading @@ -521,6 +521,7 @@ enum C2Config::profile_t : uint32_t { PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7, ///< Dolby Vision dvhe.07 profile PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8, ///< Dolby Vision dvhe.08 profile PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9, ///< Dolby Vision dvav.09 profile PROFILE_DV_AV1_10 = _C2_PL_DV_BASE + 10, ///< Dolby Vision dav1.10 profile // AV1 profiles PROFILE_AV1_0 = _C2_PL_AV1_BASE, ///< AV1 Profile 0 (4:2:0, 8 to 10 bit) Loading media/codec2/sfplugin/utils/Codec2Mapper.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -190,6 +190,7 @@ ALookup<C2Config::profile_t, int32_t> sDolbyVisionProfiles = { { C2Config::PROFILE_DV_HE_07, DolbyVisionProfileDvheDtb }, { C2Config::PROFILE_DV_HE_08, DolbyVisionProfileDvheSt }, { C2Config::PROFILE_DV_AV_09, DolbyVisionProfileDvavSe }, { C2Config::PROFILE_DV_AV1_10, DolbyVisionProfileDvav110 }, }; ALookup<C2Config::level_t, int32_t> sH263Levels = { Loading media/extractors/mp4/MPEG4Extractor.cpp +154 −4 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ private: bool mIsAVC; bool mIsHEVC; bool mIsDolbyVision; bool mIsAC4; bool mIsPcm; size_t mNALLengthSize; Loading Loading @@ -337,6 +338,14 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC("hvc1"): case FOURCC("hev1"): return MEDIA_MIMETYPE_VIDEO_HEVC; case FOURCC("dvav"): case FOURCC("dva1"): case FOURCC("dvhe"): case FOURCC("dvh1"): case FOURCC("dav1"): return MEDIA_MIMETYPE_VIDEO_DOLBY_VISION; case FOURCC("ac-4"): return MEDIA_MIMETYPE_AUDIO_AC4; case FOURCC("Opus"): Loading Loading @@ -1062,6 +1071,62 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->mTx3gBuffer = NULL; } const char *mime; AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { void *data; size_t size; if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; const uint8_t profile = ptr[2] >> 1; const uint8_t bl_compatibility_id = (ptr[4]) >> 4; if (4 == profile || 7 == profile || (profile >= 8 && profile < 11 && bl_compatibility_id)) { // we need a backward compatible track ALOGV("Adding new backward compatible track"); Track *track_b = new Track; track_b->timescale = mLastTrack->timescale; track_b->sampleTable = mLastTrack->sampleTable; track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata; track_b->skipTrack = mLastTrack->skipTrack; track_b->has_elst = mLastTrack->has_elst; track_b->elst_media_time = mLastTrack->elst_media_time; track_b->elst_segment_duration = mLastTrack->elst_segment_duration; track_b->elstShiftStartTicks = mLastTrack->elstShiftStartTicks; track_b->subsample_encryption = mLastTrack->subsample_encryption; track_b->mTx3gBuffer = mLastTrack->mTx3gBuffer; track_b->mTx3gSize = mLastTrack->mTx3gSize; track_b->mTx3gFilled = mLastTrack->mTx3gFilled; track_b->meta = AMediaFormat_new(); AMediaFormat_copy(track_b->meta, mLastTrack->meta); mLastTrack->next = track_b; track_b->next = NULL; auto id = track_b->meta->mFormat->findEntryByName(AMEDIAFORMAT_KEY_CSD_2); track_b->meta->mFormat->removeEntryAt(id); if (4 == profile || 7 == profile || 8 == profile ) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_HEVC); } else if (9 == profile) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC); } else if (10 == profile) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); } // Should never get to else part mLastTrack = track_b; } } } } else if (chunk_type == FOURCC("moov")) { mInitCheck = OK; Loading Loading @@ -1830,6 +1895,11 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("avc1"): case FOURCC("hvc1"): case FOURCC("hev1"): case FOURCC("dvav"): case FOURCC("dva1"): case FOURCC("dvhe"): case FOURCC("dvh1"): case FOURCC("dav1"): case FOURCC("av01"): { uint8_t buffer[78]; Loading Loading @@ -1984,7 +2054,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // for audio, use 128KB max_size = 1024 * 128; } else if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { // AVC & HEVC requires compression ratio of at least 2, and uses // macroblocks max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192; Loading Loading @@ -2315,6 +2386,30 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { *offset += chunk_size; break; } case FOURCC("dvcC"): case FOURCC("dvvC"): { auto buffer = heapbuffer<uint8_t>(chunk_data_size); if (buffer.get() == NULL) { ALOGE("b/28471206"); return NO_MEMORY; } if (mDataSource->readAt(data_offset, buffer.get(), chunk_data_size) < chunk_data_size) { return ERROR_IO; } if (mLastTrack == NULL) return ERROR_MALFORMED; 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); *offset += chunk_size; break; } case FOURCC("d263"): { *offset += chunk_size; Loading Loading @@ -4127,6 +4222,19 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) { itemTable = mItemTable; } } 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)) { return NULL; } const uint8_t *ptr = (const uint8_t *)data; // 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))) { return NULL; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { void *data; size_t size; Loading Loading @@ -4172,6 +4280,10 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) { return ERROR_MALFORMED; Loading Loading @@ -4659,6 +4771,7 @@ MPEG4Source::MPEG4Source( mCurrentSampleInfoOffsets(NULL), mIsAVC(false), mIsHEVC(false), mIsDolbyVision(false), mIsAC4(false), mIsPcm(false), mNALLengthSize(0), Loading Loading @@ -4698,6 +4811,7 @@ MPEG4Source::MPEG4Source( mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4); mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION); if (mIsAVC) { void *data; Loading @@ -4722,6 +4836,42 @@ MPEG4Source::MPEG4Source( CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 mNALLengthSize = 1 + (ptr[14 + 7] & 3); } else if (mIsDolbyVision) { ALOGV("%s DolbyVision stream detected", __FUNCTION__); void *data; size_t size; CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; 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))); const uint8_t profile = ptr[2] >> 1; // profile == (unknown,1,9) --> AVC; profile = (2,3,4,5,6,7,8) --> HEVC; // profile == (10) --> AV1 if (profile > 1 && profile < 9) { CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; CHECK(size >= 22); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 mNALLengthSize = 1 + (ptr[14 + 7] & 3); } else if (10 == profile) { /* AV1 profile nothing to do */ } else { CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; CHECK(size >= 7); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 // The number of bytes used to encode the length of a NAL unit. mNALLengthSize = 1 + (ptr[4] & 3); } } mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW); Loading Loading @@ -5789,7 +5939,7 @@ media_status_t MPEG4Source::read( } } if (!mIsAVC && !mIsHEVC && !mIsAC4) { if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize) && !mIsAC4) { if (newBuffer) { if (mIsPcm) { // The twos' PCM block reader assumes that all samples has the same size. Loading Loading @@ -6179,7 +6329,7 @@ media_status_t MPEG4Source::fragmentedRead( AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_IV, iv, ivlength); } if (!mIsAVC && !mIsHEVC) { if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize)) { if (newBuffer) { if (!isInRange((size_t)0u, mBuffer->size(), size)) { mBuffer->release(); Loading media/libstagefright/MPEG4Writer.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -2213,8 +2213,10 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) { mMeta->findData(kKeyHVCC, &type, &data, &size); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { mMeta->findData(kKeyDVCC, &type, &data, &size); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { if (mMeta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds(data, size); if (esds.getCodecSpecificInfo(&data, &size) == OK && Loading media/libstagefright/Utils.cpp +105 −1 Original line number Diff line number Diff line Loading @@ -227,6 +227,68 @@ 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, OMX_VIDEO_DOLBYVISIONPROFILETYPE> profiles{ {1, OMX_VIDEO_DolbyVisionProfileDvavPen}, {3, OMX_VIDEO_DolbyVisionProfileDvheDen}, {4, OMX_VIDEO_DolbyVisionProfileDvheDtr}, {5, OMX_VIDEO_DolbyVisionProfileDvheStn}, {6, OMX_VIDEO_DolbyVisionProfileDvheDth}, {7, OMX_VIDEO_DolbyVisionProfileDvheDtb}, {8, OMX_VIDEO_DolbyVisionProfileDvheSt}, {9, OMX_VIDEO_DolbyVisionProfileDvavSe}, {10, OMX_VIDEO_DolbyVisionProfileDvav110}, }; const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONLEVELTYPE> levels{ {0, OMX_VIDEO_DolbyVisionLevelUnknown}, {1, OMX_VIDEO_DolbyVisionLevelHd24}, {2, OMX_VIDEO_DolbyVisionLevelHd30}, {3, OMX_VIDEO_DolbyVisionLevelFhd24}, {4, OMX_VIDEO_DolbyVisionLevelFhd30}, {5, OMX_VIDEO_DolbyVisionLevelFhd60}, {6, OMX_VIDEO_DolbyVisionLevelUhd24}, {7, OMX_VIDEO_DolbyVisionLevelUhd30}, {8, OMX_VIDEO_DolbyVisionLevelUhd48}, {9, OMX_VIDEO_DolbyVisionLevelUhd60}, }; // set rpuAssoc if (rpu_present_flag && el_present_flag && !bl_present_flag) { format->setInt32("rpuAssoc", 1); } // set profile & level if they are recognized OMX_VIDEO_DOLBYVISIONPROFILETYPE codecProfile; OMX_VIDEO_DOLBYVISIONLEVELTYPE codecLevel; if (profiles.map(profile, &codecProfile)) { format->setInt32("profile", codecProfile); if (codecProfile == OMX_VIDEO_DolbyVisionProfileDvheSt || codecProfile == OMX_VIDEO_DolbyVisionProfileDvavSe) { format->setInt32("bl_compatibility_id", bl_compatibility_id); } if (levels.map(level, &codecLevel)) { format->setInt32("level", codecLevel); } } } static void parseH263ProfileLevelFromD263(const uint8_t *ptr, size_t size, sp<AMessage> &format) { if (size < 7) { return; Loading Loading @@ -1411,6 +1473,12 @@ status_t convertMetaDataToMessage( msg->setBuffer("csd-0", buffer); } if (meta->findData(kKeyDVCC, &type, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size); parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg); } *format = msg; return OK; Loading Loading @@ -1839,6 +1907,32 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize); } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) { meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) { if (msg->findBuffer("csd-2", &csd2)) { meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size()); size_t dvcc_size = 1024; uint8_t dvcc[dvcc_size]; memcpy(dvcc, csd2->data(), dvcc_size); const uint8_t profile = dvcc[2] >> 1; if (profile > 1 && profile < 9) { 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 { sp<ABuffer> 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 { ALOGW("We need csd-2!!. %s", msg->debugString().c_str()); } } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) { Loading Loading @@ -1885,8 +1979,18 @@ void 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 #if 0 Loading Loading
media/codec2/core/include/C2Config.h +1 −0 Original line number Diff line number Diff line Loading @@ -521,6 +521,7 @@ enum C2Config::profile_t : uint32_t { PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7, ///< Dolby Vision dvhe.07 profile PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8, ///< Dolby Vision dvhe.08 profile PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9, ///< Dolby Vision dvav.09 profile PROFILE_DV_AV1_10 = _C2_PL_DV_BASE + 10, ///< Dolby Vision dav1.10 profile // AV1 profiles PROFILE_AV1_0 = _C2_PL_AV1_BASE, ///< AV1 Profile 0 (4:2:0, 8 to 10 bit) Loading
media/codec2/sfplugin/utils/Codec2Mapper.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -190,6 +190,7 @@ ALookup<C2Config::profile_t, int32_t> sDolbyVisionProfiles = { { C2Config::PROFILE_DV_HE_07, DolbyVisionProfileDvheDtb }, { C2Config::PROFILE_DV_HE_08, DolbyVisionProfileDvheSt }, { C2Config::PROFILE_DV_AV_09, DolbyVisionProfileDvavSe }, { C2Config::PROFILE_DV_AV1_10, DolbyVisionProfileDvav110 }, }; ALookup<C2Config::level_t, int32_t> sH263Levels = { Loading
media/extractors/mp4/MPEG4Extractor.cpp +154 −4 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ private: bool mIsAVC; bool mIsHEVC; bool mIsDolbyVision; bool mIsAC4; bool mIsPcm; size_t mNALLengthSize; Loading Loading @@ -337,6 +338,14 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC("hvc1"): case FOURCC("hev1"): return MEDIA_MIMETYPE_VIDEO_HEVC; case FOURCC("dvav"): case FOURCC("dva1"): case FOURCC("dvhe"): case FOURCC("dvh1"): case FOURCC("dav1"): return MEDIA_MIMETYPE_VIDEO_DOLBY_VISION; case FOURCC("ac-4"): return MEDIA_MIMETYPE_AUDIO_AC4; case FOURCC("Opus"): Loading Loading @@ -1062,6 +1071,62 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->mTx3gBuffer = NULL; } const char *mime; AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { void *data; size_t size; if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; const uint8_t profile = ptr[2] >> 1; const uint8_t bl_compatibility_id = (ptr[4]) >> 4; if (4 == profile || 7 == profile || (profile >= 8 && profile < 11 && bl_compatibility_id)) { // we need a backward compatible track ALOGV("Adding new backward compatible track"); Track *track_b = new Track; track_b->timescale = mLastTrack->timescale; track_b->sampleTable = mLastTrack->sampleTable; track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata; track_b->skipTrack = mLastTrack->skipTrack; track_b->has_elst = mLastTrack->has_elst; track_b->elst_media_time = mLastTrack->elst_media_time; track_b->elst_segment_duration = mLastTrack->elst_segment_duration; track_b->elstShiftStartTicks = mLastTrack->elstShiftStartTicks; track_b->subsample_encryption = mLastTrack->subsample_encryption; track_b->mTx3gBuffer = mLastTrack->mTx3gBuffer; track_b->mTx3gSize = mLastTrack->mTx3gSize; track_b->mTx3gFilled = mLastTrack->mTx3gFilled; track_b->meta = AMediaFormat_new(); AMediaFormat_copy(track_b->meta, mLastTrack->meta); mLastTrack->next = track_b; track_b->next = NULL; auto id = track_b->meta->mFormat->findEntryByName(AMEDIAFORMAT_KEY_CSD_2); track_b->meta->mFormat->removeEntryAt(id); if (4 == profile || 7 == profile || 8 == profile ) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_HEVC); } else if (9 == profile) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC); } else if (10 == profile) { AMediaFormat_setString(track_b->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); } // Should never get to else part mLastTrack = track_b; } } } } else if (chunk_type == FOURCC("moov")) { mInitCheck = OK; Loading Loading @@ -1830,6 +1895,11 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("avc1"): case FOURCC("hvc1"): case FOURCC("hev1"): case FOURCC("dvav"): case FOURCC("dva1"): case FOURCC("dvhe"): case FOURCC("dvh1"): case FOURCC("dav1"): case FOURCC("av01"): { uint8_t buffer[78]; Loading Loading @@ -1984,7 +2054,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // for audio, use 128KB max_size = 1024 * 128; } else if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { // AVC & HEVC requires compression ratio of at least 2, and uses // macroblocks max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192; Loading Loading @@ -2315,6 +2386,30 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { *offset += chunk_size; break; } case FOURCC("dvcC"): case FOURCC("dvvC"): { auto buffer = heapbuffer<uint8_t>(chunk_data_size); if (buffer.get() == NULL) { ALOGE("b/28471206"); return NO_MEMORY; } if (mDataSource->readAt(data_offset, buffer.get(), chunk_data_size) < chunk_data_size) { return ERROR_IO; } if (mLastTrack == NULL) return ERROR_MALFORMED; 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); *offset += chunk_size; break; } case FOURCC("d263"): { *offset += chunk_size; Loading Loading @@ -4127,6 +4222,19 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) { itemTable = mItemTable; } } 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)) { return NULL; } const uint8_t *ptr = (const uint8_t *)data; // 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))) { return NULL; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { void *data; size_t size; Loading Loading @@ -4172,6 +4280,10 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) { return ERROR_MALFORMED; } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) { return ERROR_MALFORMED; Loading Loading @@ -4659,6 +4771,7 @@ MPEG4Source::MPEG4Source( mCurrentSampleInfoOffsets(NULL), mIsAVC(false), mIsHEVC(false), mIsDolbyVision(false), mIsAC4(false), mIsPcm(false), mNALLengthSize(0), Loading Loading @@ -4698,6 +4811,7 @@ MPEG4Source::MPEG4Source( mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4); mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION); if (mIsAVC) { void *data; Loading @@ -4722,6 +4836,42 @@ MPEG4Source::MPEG4Source( CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 mNALLengthSize = 1 + (ptr[14 + 7] & 3); } else if (mIsDolbyVision) { ALOGV("%s DolbyVision stream detected", __FUNCTION__); void *data; size_t size; CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; 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))); const uint8_t profile = ptr[2] >> 1; // profile == (unknown,1,9) --> AVC; profile = (2,3,4,5,6,7,8) --> HEVC; // profile == (10) --> AV1 if (profile > 1 && profile < 9) { CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; CHECK(size >= 22); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 mNALLengthSize = 1 + (ptr[14 + 7] & 3); } else if (10 == profile) { /* AV1 profile nothing to do */ } else { CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)); const uint8_t *ptr = (const uint8_t *)data; CHECK(size >= 7); CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 // The number of bytes used to encode the length of a NAL unit. mNALLengthSize = 1 + (ptr[4] & 3); } } mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW); Loading Loading @@ -5789,7 +5939,7 @@ media_status_t MPEG4Source::read( } } if (!mIsAVC && !mIsHEVC && !mIsAC4) { if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize) && !mIsAC4) { if (newBuffer) { if (mIsPcm) { // The twos' PCM block reader assumes that all samples has the same size. Loading Loading @@ -6179,7 +6329,7 @@ media_status_t MPEG4Source::fragmentedRead( AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_IV, iv, ivlength); } if (!mIsAVC && !mIsHEVC) { if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize)) { if (newBuffer) { if (!isInRange((size_t)0u, mBuffer->size(), size)) { mBuffer->release(); Loading
media/libstagefright/MPEG4Writer.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -2213,8 +2213,10 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) { mMeta->findData(kKeyHVCC, &type, &data, &size); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) { mMeta->findData(kKeyDVCC, &type, &data, &size); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { if (mMeta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds(data, size); if (esds.getCodecSpecificInfo(&data, &size) == OK && Loading
media/libstagefright/Utils.cpp +105 −1 Original line number Diff line number Diff line Loading @@ -227,6 +227,68 @@ 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, OMX_VIDEO_DOLBYVISIONPROFILETYPE> profiles{ {1, OMX_VIDEO_DolbyVisionProfileDvavPen}, {3, OMX_VIDEO_DolbyVisionProfileDvheDen}, {4, OMX_VIDEO_DolbyVisionProfileDvheDtr}, {5, OMX_VIDEO_DolbyVisionProfileDvheStn}, {6, OMX_VIDEO_DolbyVisionProfileDvheDth}, {7, OMX_VIDEO_DolbyVisionProfileDvheDtb}, {8, OMX_VIDEO_DolbyVisionProfileDvheSt}, {9, OMX_VIDEO_DolbyVisionProfileDvavSe}, {10, OMX_VIDEO_DolbyVisionProfileDvav110}, }; const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONLEVELTYPE> levels{ {0, OMX_VIDEO_DolbyVisionLevelUnknown}, {1, OMX_VIDEO_DolbyVisionLevelHd24}, {2, OMX_VIDEO_DolbyVisionLevelHd30}, {3, OMX_VIDEO_DolbyVisionLevelFhd24}, {4, OMX_VIDEO_DolbyVisionLevelFhd30}, {5, OMX_VIDEO_DolbyVisionLevelFhd60}, {6, OMX_VIDEO_DolbyVisionLevelUhd24}, {7, OMX_VIDEO_DolbyVisionLevelUhd30}, {8, OMX_VIDEO_DolbyVisionLevelUhd48}, {9, OMX_VIDEO_DolbyVisionLevelUhd60}, }; // set rpuAssoc if (rpu_present_flag && el_present_flag && !bl_present_flag) { format->setInt32("rpuAssoc", 1); } // set profile & level if they are recognized OMX_VIDEO_DOLBYVISIONPROFILETYPE codecProfile; OMX_VIDEO_DOLBYVISIONLEVELTYPE codecLevel; if (profiles.map(profile, &codecProfile)) { format->setInt32("profile", codecProfile); if (codecProfile == OMX_VIDEO_DolbyVisionProfileDvheSt || codecProfile == OMX_VIDEO_DolbyVisionProfileDvavSe) { format->setInt32("bl_compatibility_id", bl_compatibility_id); } if (levels.map(level, &codecLevel)) { format->setInt32("level", codecLevel); } } } static void parseH263ProfileLevelFromD263(const uint8_t *ptr, size_t size, sp<AMessage> &format) { if (size < 7) { return; Loading Loading @@ -1411,6 +1473,12 @@ status_t convertMetaDataToMessage( msg->setBuffer("csd-0", buffer); } if (meta->findData(kKeyDVCC, &type, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size); parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg); } *format = msg; return OK; Loading Loading @@ -1839,6 +1907,32 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize); } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) { meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) { if (msg->findBuffer("csd-2", &csd2)) { meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size()); size_t dvcc_size = 1024; uint8_t dvcc[dvcc_size]; memcpy(dvcc, csd2->data(), dvcc_size); const uint8_t profile = dvcc[2] >> 1; if (profile > 1 && profile < 9) { 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 { sp<ABuffer> 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 { ALOGW("We need csd-2!!. %s", msg->debugString().c_str()); } } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) { Loading Loading @@ -1885,8 +1979,18 @@ void 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 #if 0 Loading