Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibMedia/Containers/Matroska/Reader.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
3
 * Copyright (c) 2022, Gregory Bertilson <Zaggy1024@gmail.com>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/IntegralMath.h>
11
#include <AK/Optional.h>
12
#include <AK/OwnPtr.h>
13
#include <LibCore/MappedFile.h>
14
#include <LibMedia/DecoderError.h>
15
16
#include "Document.h"
17
18
namespace Media::Matroska {
19
20
class SampleIterator;
21
class Streamer;
22
23
class Reader {
24
public:
25
    typedef Function<DecoderErrorOr<IterationDecision>(TrackEntry const&)> TrackEntryCallback;
26
27
    static DecoderErrorOr<Reader> from_file(StringView path);
28
    static DecoderErrorOr<Reader> from_mapped_file(NonnullOwnPtr<Core::MappedFile> mapped_file);
29
30
    static DecoderErrorOr<Reader> from_data(ReadonlyBytes data);
31
32
0
    EBMLHeader const& header() const { return m_header.value(); }
33
34
    DecoderErrorOr<SegmentInformation> segment_information();
35
36
    DecoderErrorOr<void> for_each_track(TrackEntryCallback);
37
    DecoderErrorOr<void> for_each_track_of_type(TrackEntry::TrackType, TrackEntryCallback);
38
    DecoderErrorOr<NonnullRefPtr<TrackEntry>> track_for_track_number(u64);
39
    DecoderErrorOr<size_t> track_count();
40
41
    DecoderErrorOr<SampleIterator> create_sample_iterator(u64 track_number);
42
    DecoderErrorOr<SampleIterator> seek_to_random_access_point(SampleIterator, Duration);
43
    DecoderErrorOr<Optional<Vector<CuePoint> const&>> cue_points_for_track(u64 track_number);
44
    DecoderErrorOr<bool> has_cues_for_track(u64 track_number);
45
46
private:
47
    Reader(ReadonlyBytes data)
48
5.57k
        : m_data(data)
49
5.57k
    {
50
5.57k
    }
51
52
    DecoderErrorOr<void> parse_initial_data();
53
54
    DecoderErrorOr<Optional<size_t>> find_first_top_level_element_with_id([[maybe_unused]] StringView element_name, u32 element_id);
55
56
    DecoderErrorOr<void> ensure_tracks_are_parsed();
57
    DecoderErrorOr<void> parse_tracks(Streamer&);
58
59
    DecoderErrorOr<void> parse_cues(Streamer&);
60
    DecoderErrorOr<void> ensure_cues_are_parsed();
61
    DecoderErrorOr<void> seek_to_cue_for_timestamp(SampleIterator&, Duration const&);
62
63
    RefPtr<Core::SharedMappedFile> m_mapped_file;
64
    ReadonlyBytes m_data;
65
66
    Optional<EBMLHeader> m_header;
67
68
    size_t m_segment_contents_position { 0 };
69
    size_t m_segment_contents_size { 0 };
70
71
    HashMap<u32, size_t> m_seek_entries;
72
    size_t m_last_top_level_element_position { 0 };
73
74
    Optional<SegmentInformation> m_segment_information;
75
76
    OrderedHashMap<u64, NonnullRefPtr<TrackEntry>> m_tracks;
77
78
    // The vectors must be sorted by timestamp at all times.
79
    HashMap<u64, Vector<CuePoint>> m_cues;
80
    bool m_cues_have_been_parsed { false };
81
};
82
83
class SampleIterator {
84
public:
85
    DecoderErrorOr<Block> next_block();
86
0
    Cluster const& current_cluster() const { return *m_current_cluster; }
87
0
    Optional<Duration> const& last_timestamp() const { return m_last_timestamp; }
88
0
    TrackEntry const& track() const { return *m_track; }
89
90
private:
91
    friend class Reader;
92
93
    SampleIterator(RefPtr<Core::SharedMappedFile> file, ReadonlyBytes data, TrackEntry& track, u64 timestamp_scale, size_t position)
94
0
        : m_file(move(file))
95
0
        , m_data(data)
96
0
        , m_track(track)
97
0
        , m_segment_timestamp_scale(timestamp_scale)
98
0
        , m_position(position)
99
0
    {
100
0
    }
101
102
    DecoderErrorOr<void> seek_to_cue_point(CuePoint const& cue_point);
103
104
    RefPtr<Core::SharedMappedFile> m_file;
105
    ReadonlyBytes m_data;
106
    NonnullRefPtr<TrackEntry> m_track;
107
    u64 m_segment_timestamp_scale { 0 };
108
109
    // Must always point to an element ID or the end of the stream.
110
    size_t m_position { 0 };
111
112
    Optional<Duration> m_last_timestamp;
113
114
    Optional<Cluster> m_current_cluster;
115
};
116
117
class Streamer {
118
public:
119
    Streamer(ReadonlyBytes data)
120
15.8k
        : m_data(data)
121
15.8k
    {
122
15.8k
    }
123
124
11.1M
    u8 const* data() { return m_data.data() + m_position; }
125
126
11.7k
    char const* data_as_chars() { return reinterpret_cast<char const*>(data()); }
127
128
1.44M
    size_t octets_read() { return m_octets_read.last(); }
129
130
52.0k
    void push_octets_read() { m_octets_read.append(0); }
131
132
    void pop_octets_read()
133
43.9k
    {
134
43.9k
        auto popped = m_octets_read.take_last();
135
43.9k
        if (!m_octets_read.is_empty())
136
43.9k
            m_octets_read.last() += popped;
137
43.9k
    }
138
139
    ErrorOr<u8> read_octet();
140
141
    ErrorOr<i16> read_i16();
142
143
    ErrorOr<u64> read_variable_size_integer(bool mask_length = true);
144
    ErrorOr<i64> read_variable_size_signed_integer();
145
146
    ErrorOr<u64> read_u64();
147
    ErrorOr<double> read_float();
148
149
    ErrorOr<ByteString> read_string();
150
151
    ErrorOr<void> read_unknown_element();
152
153
    ErrorOr<ReadonlyBytes> read_raw_octets(size_t num_octets);
154
155
11.3M
    size_t position() const { return m_position; }
156
11.1M
    size_t remaining() const { return m_data.size() - position(); }
157
158
0
    bool at_end() const { return remaining() == 0; }
159
10.8M
    bool has_octet() const { return remaining() >= 1; }
160
161
    ErrorOr<void> seek_to_position(size_t position);
162
163
private:
164
    ReadonlyBytes m_data;
165
    size_t m_position { 0 };
166
    Vector<size_t> m_octets_read { 0 };
167
};
168
169
}