/* * Copyright (c) 2021, Hunter Salyer * Copyright (c) 2022, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include "Document.h" namespace Video::Matroska { class SampleIterator; class Streamer; class Reader { public: typedef Function(TrackEntry const&)> TrackEntryCallback; static DecoderErrorOr from_file(StringView path); static DecoderErrorOr from_data(ReadonlyBytes data); EBMLHeader const& header() const { return m_header.value(); } DecoderErrorOr segment_information(); DecoderErrorOr for_each_track(TrackEntryCallback); DecoderErrorOr for_each_track_of_type(TrackEntry::TrackType, TrackEntryCallback); DecoderErrorOr track_for_track_number(u64); DecoderErrorOr track_count(); DecoderErrorOr create_sample_iterator(u64 track_number); DecoderErrorOr seek_to_random_access_point(SampleIterator&, Time); DecoderErrorOr const&>> cue_points_for_track(u64 track_number); DecoderErrorOr has_cues_for_track(u64 track_number); private: Reader(ReadonlyBytes data) : m_data(data) { } DecoderErrorOr parse_initial_data(); DecoderErrorOr> find_first_top_level_element_with_id([[maybe_unused]] StringView element_name, u32 element_id); DecoderErrorOr ensure_tracks_are_parsed(); DecoderErrorOr parse_tracks(Streamer&); DecoderErrorOr parse_cues(Streamer&); DecoderErrorOr ensure_cues_are_parsed(); DecoderErrorOr seek_to_cue_for_timestamp(SampleIterator&, Time const&); RefPtr m_mapped_file; ReadonlyBytes m_data; Optional m_header; size_t m_segment_contents_position { 0 }; size_t m_segment_contents_size { 0 }; HashMap m_seek_entries; size_t m_last_top_level_element_position { 0 }; Optional m_segment_information; OrderedHashMap m_tracks; // The vectors must be sorted by timestamp at all times. HashMap> m_cues; bool m_cues_have_been_parsed { false }; }; class SampleIterator { public: DecoderErrorOr next_block(); Cluster const& current_cluster() { return *m_current_cluster; } Time const& last_timestamp() { return m_last_timestamp; } private: friend class Reader; SampleIterator(RefPtr file, ReadonlyBytes data, TrackEntry track, u64 timestamp_scale, size_t position) : m_file(move(file)) , m_data(data) , m_track(move(track)) , m_segment_timestamp_scale(timestamp_scale) , m_position(position) { } DecoderErrorOr set_position(size_t position); DecoderErrorOr seek_to_cue_point(CuePoint const& cue_point); RefPtr m_file; ReadonlyBytes m_data; TrackEntry m_track; u64 m_segment_timestamp_scale { 0 }; // Must always point to an element ID or the end of the stream. size_t m_position { 0 }; Time m_last_timestamp { Time::min() }; Optional m_current_cluster; }; class Streamer { public: Streamer(ReadonlyBytes data) : m_data(data) { } u8 const* data() { return m_data.data() + m_position; } char const* data_as_chars() { return reinterpret_cast(data()); } size_t octets_read() { return m_octets_read.last(); } void push_octets_read() { m_octets_read.append(0); } void pop_octets_read() { auto popped = m_octets_read.take_last(); if (!m_octets_read.is_empty()) m_octets_read.last() += popped; } ErrorOr read_octet(); ErrorOr read_i16(); ErrorOr read_variable_size_integer(bool mask_length = true); ErrorOr read_variable_size_signed_integer(); ErrorOr read_u64(); ErrorOr read_float(); ErrorOr read_string(); ErrorOr read_unknown_element(); ErrorOr read_raw_octets(size_t num_octets); size_t position() const { return m_position; } size_t remaining() const { return m_data.size() - position(); } bool at_end() const { return remaining() == 0; } bool has_octet() const { return remaining() >= 1; } ErrorOr seek_to_position(size_t position); private: ReadonlyBytes m_data; size_t m_position { 0 }; Vector m_octets_read { 0 }; }; }