From 393cfdd5c5023a038e430f7c52293a814b3dba0d Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Fri, 11 Nov 2022 17:14:27 -0600 Subject: [PATCH] LibVideo: Read Matroska lazily so that large files can start quickly The Demuxer class was changed to return errors for more functions so that all of the underlying reading can be done lazily. Other than that, the demuxer interface is unchanged, and only the underlying reader was modified. The MatroskaDocument class is no more, and MatroskaReader's getter functions replace it. Every MatroskaReader getter beyond the Segment element's position is parsed lazily from the file as needed. This means that all getter functions can return DecoderErrors which must be handled by callers. --- Meta/Lagom/Fuzzers/FuzzMatroskaReader.cpp | 8 +- Tests/LibVideo/TestVP9Decode.cpp | 40 +- .../Libraries/LibVideo/Containers/Demuxer.h | 4 +- .../LibVideo/Containers/Matroska/Document.h | 61 +-- .../Containers/Matroska/MatroskaDemuxer.cpp | 76 ++- .../Containers/Matroska/MatroskaDemuxer.h | 18 +- .../LibVideo/Containers/Matroska/Reader.cpp | 478 +++++++++++++----- .../LibVideo/Containers/Matroska/Reader.h | 107 +++- .../Libraries/LibVideo/PlaybackManager.cpp | 7 +- Userland/Libraries/LibVideo/Sample.h | 6 +- Userland/Utilities/matroska.cpp | 89 ++-- 11 files changed, 580 insertions(+), 314 deletions(-) diff --git a/Meta/Lagom/Fuzzers/FuzzMatroskaReader.cpp b/Meta/Lagom/Fuzzers/FuzzMatroskaReader.cpp index dff87bf2cc509466f448a6434d973c4f6bf7f611..762a7032f4ca458818d5645f06d209cea1127d10 100644 --- a/Meta/Lagom/Fuzzers/FuzzMatroskaReader.cpp +++ b/Meta/Lagom/Fuzzers/FuzzMatroskaReader.cpp @@ -9,8 +9,12 @@ extern "C" int LLVMFuzzerTestOneInput(u8 const* data, size_t size) { - auto matroska_document = Video::Matroska::Reader::parse_matroska_from_data(data, size); - if (!matroska_document) + auto matroska_reader_result = Video::Matroska::Reader::from_data({ data, size }); + if (matroska_reader_result.is_error()) + return -1; + if (auto result = matroska_reader_result.value().segment_information(); result.is_error()) + return -1; + if (auto result = matroska_reader_result.value().track_count(); result.is_error()) return -1; return 0; } diff --git a/Tests/LibVideo/TestVP9Decode.cpp b/Tests/LibVideo/TestVP9Decode.cpp index ffcea9539b0d81be1e6cce3dcde43623e057bf77..9db538a8616e64e461b16c22b228059b694e3ba9 100644 --- a/Tests/LibVideo/TestVP9Decode.cpp +++ b/Tests/LibVideo/TestVP9Decode.cpp @@ -11,29 +11,33 @@ static void decode_video(StringView path, size_t expected_frame_count) { - auto matroska_document = MUST(Video::Matroska::Reader::parse_matroska_from_file(path)); - auto video_track_optional = matroska_document->track_for_track_type(Video::Matroska::TrackEntry::TrackType::Video); - VERIFY(video_track_optional.has_value()); - auto video_track_entry = video_track_optional.value(); - + auto matroska_reader = MUST(Video::Matroska::Reader::from_file(path)); + u64 video_track = 0; + MUST(matroska_reader.for_each_track_of_type(Video::Matroska::TrackEntry::TrackType::Video, [&](Video::Matroska::TrackEntry const& track_entry) -> Video::DecoderErrorOr { + video_track = track_entry.track_number(); + return IterationDecision::Break; + })); + VERIFY(video_track != 0); + + auto iterator = MUST(matroska_reader.create_sample_iterator(video_track)); size_t frame_count = 0; - size_t cluster_index, block_index, frame_index; Video::VP9::Decoder vp9_decoder; - for (cluster_index = 0; cluster_index < matroska_document->clusters().size(); cluster_index++) { - auto const& cluster = matroska_document->clusters()[cluster_index]; - for (block_index = 0; block_index < cluster.blocks().size(); block_index++) { - auto const& block = cluster.blocks()[block_index]; - if (block.track_number() != video_track_entry.track_number()) - continue; - - for (frame_index = 0; frame_index < block.frames().size(); frame_index++) { - MUST(vp9_decoder.receive_sample(block.frames()[frame_index])); - frame_count++; - } + while (frame_count <= expected_frame_count) { + auto block_result = iterator.next_block(); + if (block_result.is_error() && block_result.error().category() == Video::DecoderErrorCategory::EndOfStream) { + VERIFY(frame_count == expected_frame_count); + return; + } + + auto block = block_result.release_value(); + for (auto const& frame : block.frames()) { + MUST(vp9_decoder.receive_sample(frame)); + frame_count++; } } - VERIFY(frame_count == expected_frame_count); + + VERIFY_NOT_REACHED(); } TEST_CASE(webm_in_vp9) diff --git a/Userland/Libraries/LibVideo/Containers/Demuxer.h b/Userland/Libraries/LibVideo/Containers/Demuxer.h index 9b1ea757eeffcd07ccd42fe71da4fb4dcd80755c..3ed163b7f31b0dbd1a277500a3f6f2f74a7a0e05 100644 --- a/Userland/Libraries/LibVideo/Containers/Demuxer.h +++ b/Userland/Libraries/LibVideo/Containers/Demuxer.h @@ -18,7 +18,7 @@ class Demuxer { public: virtual ~Demuxer() = default; - virtual Vector get_tracks_for_type(TrackType type) = 0; + virtual DecoderErrorOr> get_tracks_for_type(TrackType type) = 0; DecoderErrorOr> get_next_video_sample_for_track(Track track) { @@ -30,7 +30,7 @@ public: virtual DecoderErrorOr seek_to_most_recent_keyframe(Track track, size_t timestamp) = 0; - virtual Time duration() = 0; + virtual DecoderErrorOr