Loading media/module/extractors/mkv/MatroskaExtractor.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -1769,6 +1769,30 @@ status_t MatroskaExtractor::synthesizeMPEG4(TrackInfo *trackInfo, size_t index) } status_t MatroskaExtractor::synthesizeVP9(TrackInfo* trackInfo, size_t index) { BlockIterator iter(this, trackInfo->mTrackNum, index); if (iter.eos()) { return ERROR_MALFORMED; } const mkvparser::Block* block = iter.block(); if (block->GetFrameCount() <= 0) { return ERROR_MALFORMED; } const mkvparser::Block::Frame& frame = block->GetFrame(0); auto tmpData = heapbuffer<unsigned char>(frame.len); long n = frame.Read(mReader, tmpData.get()); if (n != 0) { return ERROR_MALFORMED; } if (!MakeVP9CodecSpecificData(trackInfo->mMeta, tmpData.get(), frame.len)) { return ERROR_MALFORMED; } return OK; } static inline bool isValidInt32ColourValue(long long value) { return value != mkvparser::Colour::kValueNotPresent Loading Loading @@ -2002,6 +2026,8 @@ void MatroskaExtractor::addTracks() { // specified in http://www.webmproject.org/vp9/profiles/. AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize); } else { isSetCsdFrom1stFrame = true; } } else if (!strcmp("V_AV1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); Loading Loading @@ -2254,6 +2280,14 @@ void MatroskaExtractor::addTracks() { mTracks.pop(); continue; } } else if ((!strcmp("V_VP9", codecID) && codecPrivateSize == 0) || (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_VP9) && isSetCsdFrom1stFrame)) { // Attempt to recover from VP9 track without codec private data err = synthesizeVP9(trackInfo, n); if (err != OK) { mTracks.pop(); continue; } } // the TrackInfo owns the metadata now meta = nullptr; Loading media/module/extractors/mkv/include/MatroskaExtractor.h +1 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ private: status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index); status_t synthesizeMPEG2(TrackInfo *trackInfo, size_t index); status_t synthesizeMPEG4(TrackInfo *trackInfo, size_t index); status_t synthesizeVP9(TrackInfo* trackInfo, size_t index); status_t initTrackInfo( const mkvparser::Track *track, AMediaFormat *meta, Loading media/module/metadatautils/MetaDataUtils.cpp +118 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,124 @@ bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t si return true; } // The param data contains the first frame data, starting with the uncompressed frame // header. This uncompressed header (refer section 6.2 of the VP9 bitstream spec) is // used to parse profile, bitdepth and subsampling. bool MakeVP9CodecSpecificData(AMediaFormat* meta, const uint8_t* data, size_t size) { if (meta == nullptr || data == nullptr || size == 0) { return false; } ABitReader bits(data, size); // First 2 bits of the uncompressed header should be the frame_marker. if (bits.getBits(2) != 0b10) { return false; } int32_t profileLowBit = bits.getBits(1); int32_t profileHighBit = bits.getBits(1); int32_t profile = profileHighBit * 2 + profileLowBit; // One reserved '0' bit if profile is 3. if (profile == 3 && bits.getBits(1) != 0) { return false; } // If show_existing_frame is set, we get no more data. Since this is // expected to be the first frame, we can return false which will cascade // into ERROR_MALFORMED. if (bits.getBits(1)) { return false; } // This should be the first frame, so expect a KEY_FRAME. // if (frame_type != KEY_FRAME) if (bits.getBits(1) != 0) { return false; } // Upto 7 bits could be read till now, which were guaranteed to be available // since size > 0. Check for bits available before reading them from now on. if (bits.numBitsLeft() < 26) { return false; } // Discard show_frame and error_resilient_mode. bits.skipBits(2); // Check for sync code. if (bits.getBits(24) != 0x498342) { return false; } int32_t bitDepth; if (profile >= 2) { if (bits.numBitsLeft() < 1) { return false; } bitDepth = bits.getBits(1) ? 12 : 10; } else { bitDepth = 8; } uint32_t colorspace; if (!bits.getBitsGraceful(3, &colorspace)) { return false; } int32_t chromaSubsampling = -1; if (colorspace != 7 /*SRGB*/) { // Skip yuv_range_flag if (!bits.skipBits(1)) { return false; } // Check for subsampling only for profiles 1 and 3. if (profile == 1 || profile == 3) { uint32_t ss_x; uint32_t ss_y; if (bits.getBitsGraceful(1, &ss_x) && bits.getBitsGraceful(1, &ss_y)) { chromaSubsampling = ss_x << 1 & ss_y; } else { return false; } } else { chromaSubsampling = 3; } } else { if (profile == 1 || profile == 3) { chromaSubsampling = 0; } } int32_t csdSize = 6; if (chromaSubsampling != -1) { csdSize += 3; } // Create VP9 Codec Feature Metadata (CodecPrivate) that can be parsed // https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate sp<ABuffer> csd = sp<ABuffer>::make(csdSize); uint8_t* csdData = csd->data(); *csdData++ = 0x01 /* FEATURE PROFILE */; *csdData++ = 0x01 /* length */; *csdData++ = profile; *csdData++ = 0x03 /* FEATURE BITDEPTH */; *csdData++ = 0x01 /* length */; *csdData++ = bitDepth; // csdSize more than 6 means chroma subsampling data was found. if (csdSize > 6) { *csdData++ = 0x04 /* FEATURE SUBSAMPLING */; *csdData++ = 0x01 /* length */; *csdData++ = chromaSubsampling; } AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, csd->data(), csd->size()); return true; } bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) { if (data == nullptr || size < 7) { return false; Loading media/module/metadatautils/include/media/stagefright/MetaDataUtils.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ bool MakeAACCodecSpecificData(AMediaFormat *meta, unsigned profile, unsigned sam void parseVorbisComment( AMediaFormat *fileMeta, const char *comment, size_t commentLength); bool MakeVP9CodecSpecificData(AMediaFormat* meta, const uint8_t* data, size_t size); } // namespace android #endif // META_DATA_UTILS_H_ Loading
media/module/extractors/mkv/MatroskaExtractor.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -1769,6 +1769,30 @@ status_t MatroskaExtractor::synthesizeMPEG4(TrackInfo *trackInfo, size_t index) } status_t MatroskaExtractor::synthesizeVP9(TrackInfo* trackInfo, size_t index) { BlockIterator iter(this, trackInfo->mTrackNum, index); if (iter.eos()) { return ERROR_MALFORMED; } const mkvparser::Block* block = iter.block(); if (block->GetFrameCount() <= 0) { return ERROR_MALFORMED; } const mkvparser::Block::Frame& frame = block->GetFrame(0); auto tmpData = heapbuffer<unsigned char>(frame.len); long n = frame.Read(mReader, tmpData.get()); if (n != 0) { return ERROR_MALFORMED; } if (!MakeVP9CodecSpecificData(trackInfo->mMeta, tmpData.get(), frame.len)) { return ERROR_MALFORMED; } return OK; } static inline bool isValidInt32ColourValue(long long value) { return value != mkvparser::Colour::kValueNotPresent Loading Loading @@ -2002,6 +2026,8 @@ void MatroskaExtractor::addTracks() { // specified in http://www.webmproject.org/vp9/profiles/. AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize); } else { isSetCsdFrom1stFrame = true; } } else if (!strcmp("V_AV1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); Loading Loading @@ -2254,6 +2280,14 @@ void MatroskaExtractor::addTracks() { mTracks.pop(); continue; } } else if ((!strcmp("V_VP9", codecID) && codecPrivateSize == 0) || (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_VP9) && isSetCsdFrom1stFrame)) { // Attempt to recover from VP9 track without codec private data err = synthesizeVP9(trackInfo, n); if (err != OK) { mTracks.pop(); continue; } } // the TrackInfo owns the metadata now meta = nullptr; Loading
media/module/extractors/mkv/include/MatroskaExtractor.h +1 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ private: status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index); status_t synthesizeMPEG2(TrackInfo *trackInfo, size_t index); status_t synthesizeMPEG4(TrackInfo *trackInfo, size_t index); status_t synthesizeVP9(TrackInfo* trackInfo, size_t index); status_t initTrackInfo( const mkvparser::Track *track, AMediaFormat *meta, Loading
media/module/metadatautils/MetaDataUtils.cpp +118 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,124 @@ bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t si return true; } // The param data contains the first frame data, starting with the uncompressed frame // header. This uncompressed header (refer section 6.2 of the VP9 bitstream spec) is // used to parse profile, bitdepth and subsampling. bool MakeVP9CodecSpecificData(AMediaFormat* meta, const uint8_t* data, size_t size) { if (meta == nullptr || data == nullptr || size == 0) { return false; } ABitReader bits(data, size); // First 2 bits of the uncompressed header should be the frame_marker. if (bits.getBits(2) != 0b10) { return false; } int32_t profileLowBit = bits.getBits(1); int32_t profileHighBit = bits.getBits(1); int32_t profile = profileHighBit * 2 + profileLowBit; // One reserved '0' bit if profile is 3. if (profile == 3 && bits.getBits(1) != 0) { return false; } // If show_existing_frame is set, we get no more data. Since this is // expected to be the first frame, we can return false which will cascade // into ERROR_MALFORMED. if (bits.getBits(1)) { return false; } // This should be the first frame, so expect a KEY_FRAME. // if (frame_type != KEY_FRAME) if (bits.getBits(1) != 0) { return false; } // Upto 7 bits could be read till now, which were guaranteed to be available // since size > 0. Check for bits available before reading them from now on. if (bits.numBitsLeft() < 26) { return false; } // Discard show_frame and error_resilient_mode. bits.skipBits(2); // Check for sync code. if (bits.getBits(24) != 0x498342) { return false; } int32_t bitDepth; if (profile >= 2) { if (bits.numBitsLeft() < 1) { return false; } bitDepth = bits.getBits(1) ? 12 : 10; } else { bitDepth = 8; } uint32_t colorspace; if (!bits.getBitsGraceful(3, &colorspace)) { return false; } int32_t chromaSubsampling = -1; if (colorspace != 7 /*SRGB*/) { // Skip yuv_range_flag if (!bits.skipBits(1)) { return false; } // Check for subsampling only for profiles 1 and 3. if (profile == 1 || profile == 3) { uint32_t ss_x; uint32_t ss_y; if (bits.getBitsGraceful(1, &ss_x) && bits.getBitsGraceful(1, &ss_y)) { chromaSubsampling = ss_x << 1 & ss_y; } else { return false; } } else { chromaSubsampling = 3; } } else { if (profile == 1 || profile == 3) { chromaSubsampling = 0; } } int32_t csdSize = 6; if (chromaSubsampling != -1) { csdSize += 3; } // Create VP9 Codec Feature Metadata (CodecPrivate) that can be parsed // https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate sp<ABuffer> csd = sp<ABuffer>::make(csdSize); uint8_t* csdData = csd->data(); *csdData++ = 0x01 /* FEATURE PROFILE */; *csdData++ = 0x01 /* length */; *csdData++ = profile; *csdData++ = 0x03 /* FEATURE BITDEPTH */; *csdData++ = 0x01 /* length */; *csdData++ = bitDepth; // csdSize more than 6 means chroma subsampling data was found. if (csdSize > 6) { *csdData++ = 0x04 /* FEATURE SUBSAMPLING */; *csdData++ = 0x01 /* length */; *csdData++ = chromaSubsampling; } AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, csd->data(), csd->size()); return true; } bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) { if (data == nullptr || size < 7) { return false; Loading
media/module/metadatautils/include/media/stagefright/MetaDataUtils.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ bool MakeAACCodecSpecificData(AMediaFormat *meta, unsigned profile, unsigned sam void parseVorbisComment( AMediaFormat *fileMeta, const char *comment, size_t commentLength); bool MakeVP9CodecSpecificData(AMediaFormat* meta, const uint8_t* data, size_t size); } // namespace android #endif // META_DATA_UTILS_H_