Loading media/module/extractors/mkv/MatroskaExtractor.cpp +33 −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,13 @@ 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) { ALOGW("ignoring error %d in synthesizeVP9", err); } } // 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 +171 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,177 @@ bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t si return true; } // Check if the next 24 bits are VP9 SYNC_CODE static bool isVp9SyncCode(ABitReader &bits) { if (bits.numBitsLeft() < 24) { return false; } return bits.getBits(24) == 0x498342; } // This parses bitdepth and subsampling in a VP9 uncompressed header // (refer section bitdepth_colorspace_sampling in 6.2 of the VP9 bitstream spec) static bool getVp9BitdepthChromaSubSampling(ABitReader &bits, int32_t profile, int32_t *bitDepth, int32_t *chromaSubsampling) { 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; } *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; } } 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; } int32_t frame_type = bits.getBits(1); // 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() < 2) { return false; } int32_t show_frame = bits.getBits(1); int32_t error_resilient_mode = bits.getBits(1); int32_t bitDepth = 8; int32_t chromaSubsampling = -1; if (frame_type == 0 /* KEY_FRAME */) { // Check for sync code. if (!isVp9SyncCode(bits)) { return false; } if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) { return false; } } else { int32_t intra_only = 0; if (!show_frame) { if (bits.numBitsLeft() < 1) { return false; } intra_only = bits.getBits(1); } if (!error_resilient_mode) { if (bits.numBitsLeft() < 2) { return false; } // ignore reset_frame_context bits.skipBits(2); } if (!intra_only) { // Require first frame to be either KEY_FRAME or INTER_FRAME with intra_only set to true return false; } // Check for sync code. if (!isVp9SyncCode(bits)) { return false; } if (profile > 0) { if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) { return false; } } else { bitDepth = 8; chromaSubsampling = 3; } } 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 +33 −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,13 @@ 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) { ALOGW("ignoring error %d in synthesizeVP9", err); } } // 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 +171 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,177 @@ bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t si return true; } // Check if the next 24 bits are VP9 SYNC_CODE static bool isVp9SyncCode(ABitReader &bits) { if (bits.numBitsLeft() < 24) { return false; } return bits.getBits(24) == 0x498342; } // This parses bitdepth and subsampling in a VP9 uncompressed header // (refer section bitdepth_colorspace_sampling in 6.2 of the VP9 bitstream spec) static bool getVp9BitdepthChromaSubSampling(ABitReader &bits, int32_t profile, int32_t *bitDepth, int32_t *chromaSubsampling) { 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; } *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; } } 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; } int32_t frame_type = bits.getBits(1); // 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() < 2) { return false; } int32_t show_frame = bits.getBits(1); int32_t error_resilient_mode = bits.getBits(1); int32_t bitDepth = 8; int32_t chromaSubsampling = -1; if (frame_type == 0 /* KEY_FRAME */) { // Check for sync code. if (!isVp9SyncCode(bits)) { return false; } if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) { return false; } } else { int32_t intra_only = 0; if (!show_frame) { if (bits.numBitsLeft() < 1) { return false; } intra_only = bits.getBits(1); } if (!error_resilient_mode) { if (bits.numBitsLeft() < 2) { return false; } // ignore reset_frame_context bits.skipBits(2); } if (!intra_only) { // Require first frame to be either KEY_FRAME or INTER_FRAME with intra_only set to true return false; } // Check for sync code. if (!isVp9SyncCode(bits)) { return false; } if (profile > 0) { if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) { return false; } } else { bitDepth = 8; chromaSubsampling = 3; } } 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_