Coverage Report

Created: 2026-09-26 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibMedia/Video/VP9/Context.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/Array.h>
11
#include <AK/BitStream.h>
12
#include <AK/Error.h>
13
#include <AK/FixedArray.h>
14
#include <AK/MemoryStream.h>
15
#include <LibGfx/Size.h>
16
#include <LibMedia/Color/CodingIndependentCodePoints.h>
17
#include <LibMedia/DecoderError.h>
18
#include <LibMedia/Subsampling.h>
19
20
#include "BooleanDecoder.h"
21
#include "ContextStorage.h"
22
#include "Enums.h"
23
#include "LookupTables.h"
24
#include "MotionVector.h"
25
#include "SyntaxElementCounter.h"
26
#include "Utilities.h"
27
28
namespace Media::Video::VP9 {
29
30
enum class FrameShowMode {
31
    CreateAndShowNewFrame,
32
    ShowExistingFrame,
33
    DoNotShowFrame,
34
};
35
36
struct Quantizers {
37
    u16 y_ac_quantizer { 0 };
38
    u16 uv_ac_quantizer { 0 };
39
40
    u16 y_dc_quantizer { 0 };
41
    u16 uv_dc_quantizer { 0 };
42
};
43
44
struct FrameContext {
45
public:
46
    static ErrorOr<FrameContext> create(ReadonlyBytes data,
47
        Vector2D<FrameBlockContext>& contexts)
48
2.20k
    {
49
2.20k
        return FrameContext(
50
2.20k
            data,
51
2.20k
            TRY(try_make<FixedMemoryStream>(data)),
52
2.20k
            TRY(try_make<SyntaxElementCounter>()),
53
2.20k
            contexts);
54
2.20k
    }
55
56
    FrameContext(FrameContext const&) = delete;
57
8.11k
    FrameContext(FrameContext&&) = default;
58
59
    ReadonlyBytes stream_data;
60
    NonnullOwnPtr<FixedMemoryStream> stream;
61
    BigEndianInputBitStream bit_stream;
62
63
    DecoderErrorOr<BooleanDecoder> create_range_decoder(size_t size)
64
4.41k
    {
65
4.41k
        if (size > stream->remaining())
66
25
            return DecoderError::corrupted("Range decoder size invalid"sv);
67
68
4.38k
        auto compressed_header_data = ReadonlyBytes(stream_data.data() + stream->offset(), size);
69
70
        // 9.2.1: The Boolean decoding process specified in section 9.2.2 is invoked to read a marker syntax element from the
71
        //        bitstream. It is a requirement of bitstream conformance that the value read is equal to 0.
72
4.38k
        auto decoder = DECODER_TRY(DecoderErrorCategory::Corrupted, BooleanDecoder::initialize(compressed_header_data));
73
4.38k
        if (decoder.read_bool(128))
74
41
            return DecoderError::corrupted("Range decoder marker was non-zero"sv);
75
76
4.38k
        DECODER_TRY(DecoderErrorCategory::Corrupted, bit_stream.discard(size));
77
4.34k
        return decoder;
78
4.34k
    }
79
80
    NonnullOwnPtr<SyntaxElementCounter> counter;
81
82
    u8 profile { 0 };
83
84
    FrameType type { FrameType::KeyFrame };
85
1.42M
    bool is_inter_predicted() const { return type == FrameType::InterFrame; }
86
87
    bool error_resilient_mode { false };
88
    bool parallel_decoding_mode { false };
89
    bool should_replace_probability_context { false };
90
91
5.09k
    bool shows_a_frame() const { return m_frame_show_mode != FrameShowMode::DoNotShowFrame; }
92
0
    bool shows_a_new_frame() const { return m_frame_show_mode == FrameShowMode::CreateAndShowNewFrame; }
93
4.75k
    bool shows_existing_frame() const { return m_frame_show_mode == FrameShowMode::ShowExistingFrame; }
94
932
    void set_frame_hidden() { m_frame_show_mode = FrameShowMode::DoNotShowFrame; }
95
    void set_existing_frame_to_show(u8 index)
96
8
    {
97
8
        m_frame_show_mode = FrameShowMode::ShowExistingFrame;
98
8
        m_existing_frame_index = index;
99
8
    }
100
0
    u8 existing_frame_index() const { return m_existing_frame_index; }
101
102
    bool use_previous_frame_motion_vectors { false };
103
104
    ColorConfig color_config {};
105
106
    u8 reference_frames_to_update_flags { 0 };
107
14.7k
    bool should_update_reference_frame_at_index(u8 index) const { return (reference_frames_to_update_flags & (1 << index)) != 0; }
108
109
    u8 probability_context_index { 0 };
110
111
58.3k
    Gfx::Size<u32> size() const { return m_size; }
112
    ErrorOr<void> set_size(Gfx::Size<u32> size)
113
2.14k
    {
114
2.14k
        m_size = size;
115
116
        // From spec, compute_image_size( )
117
2.14k
        m_rows = pixels_to_blocks(size.height() + 7u);
118
2.14k
        m_columns = pixels_to_blocks(size.width() + 7u);
119
2.14k
        return m_block_contexts.try_resize(m_rows, m_columns);
120
2.14k
    }
121
32.2M
    u32 rows() const { return m_rows; }
122
31.7M
    u32 columns() const { return m_columns; }
123
0
    u32 superblock_rows() const { return blocks_ceiled_to_superblocks(rows()); }
124
4.24k
    u32 superblock_columns() const { return blocks_ceiled_to_superblocks(columns()); }
125
    // Calculates the output size for each plane in the frame.
126
    Gfx::Size<u32> decoded_size(bool uv) const
127
26.9M
    {
128
26.9M
        if (uv) {
129
11.3M
            return {
130
11.3M
                Subsampling::subsampled_size(color_config.subsampling_y, blocks_to_pixels(columns())),
131
11.3M
                Subsampling::subsampled_size(color_config.subsampling_y, blocks_to_pixels(rows())),
132
11.3M
            };
133
11.3M
        }
134
15.6M
        return {
135
15.6M
            blocks_to_pixels(columns()),
136
15.6M
            blocks_to_pixels(rows()),
137
15.6M
        };
138
26.9M
    }
139
140
13.0M
    Vector2D<FrameBlockContext> const& block_contexts() const { return m_block_contexts; }
141
142
    Gfx::Size<u32> render_size { 0, 0 };
143
    Gfx::Size<u16> log2_of_tile_counts { 0, 0 };
144
145
    // This group of fields is only needed for inter-predicted frames.
146
    Array<u8, 3> reference_frame_indices;
147
    Array<bool, ReferenceFrameType::LastFrame + 3> reference_frame_sign_biases;
148
    bool high_precision_motion_vectors_allowed { false };
149
    InterpolationFilter interpolation_filter { InterpolationFilter::Switchable };
150
151
    u8 loop_filter_level { 0 };
152
    u8 loop_filter_sharpness { 0 };
153
    bool loop_filter_delta_enabled { false };
154
    Array<i8, MAX_REF_FRAMES> loop_filter_reference_deltas;
155
    Array<i8, 2> loop_filter_mode_deltas;
156
157
    // Set based on quantization_params( ) in the spec.
158
    bool lossless { false };
159
    Array<Quantizers, MAX_SEGMENTS> segment_quantizers;
160
161
    bool segmentation_enabled { false };
162
    // Note: We can use Optional<Array<...>> for these tree probabilities, but unfortunately it seems to have measurable performance overhead.
163
    bool use_full_segment_id_tree { false };
164
    Array<u8, 7> full_segment_id_tree_probabilities;
165
    bool use_predicted_segment_id_tree { false };
166
    Array<u8, 3> predicted_segment_id_tree_probabilities;
167
    bool should_use_absolute_segment_base_quantizer { false };
168
    SegmentationFeatures segmentation_features;
169
    SegmentFeatureStatus get_segment_feature(u8 segment_id, SegmentFeature feature) const
170
779k
    {
171
779k
        return segmentation_features[segment_id][to_underlying(feature)];
172
779k
    }
173
174
    u16 header_size_in_bytes { 0 };
175
176
    TransformMode transform_mode;
177
178
    // This group also is only needed for inter-predicted frames.
179
    ReferenceMode reference_mode;
180
    ReferenceFrameType fixed_reference_type;
181
    ReferenceFramePair variable_reference_types;
182
183
private:
184
    friend struct TileContext;
185
186
    FrameContext(ReadonlyBytes data,
187
        NonnullOwnPtr<FixedMemoryStream> stream,
188
        NonnullOwnPtr<SyntaxElementCounter> counter,
189
        Vector2D<FrameBlockContext>& contexts)
190
2.20k
        : stream_data(data)
191
2.20k
        , stream(move(stream))
192
2.20k
        , bit_stream(MaybeOwned<Stream>(*this->stream))
193
2.20k
        , counter(move(counter))
194
2.20k
        , m_block_contexts(contexts)
195
2.20k
    {
196
2.20k
    }
197
198
    FrameShowMode m_frame_show_mode { FrameShowMode::CreateAndShowNewFrame };
199
    u8 m_existing_frame_index { 0 };
200
201
    Gfx::Size<u32> m_size { 0, 0 };
202
    u32 m_rows { 0 };
203
    u32 m_columns { 0 };
204
    // FIXME: From spec: NOTE – We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory
205
    //        consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D
206
    //        arrays instead.
207
    //        I think should also apply to other fields that are only accessed relative to the current block. Worth looking
208
    //        into how much of this context needs to be stored for the whole frame vs a row or column from the current tile.
209
    Vector2D<FrameBlockContext>& m_block_contexts;
210
};
211
212
static ErrorOr<NonZeroTokens> create_non_zero_tokens(u32 size_in_sub_blocks, bool subsampling)
213
4.32k
{
214
4.32k
    return NonZeroTokens {
215
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks)),
216
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks >>= subsampling)),
217
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks)),
218
4.32k
    };
219
4.32k
}
Unexecuted instantiation: Decoder.cpp:Media::Video::VP9::create_non_zero_tokens(unsigned int, bool)
Parser.cpp:Media::Video::VP9::create_non_zero_tokens(unsigned int, bool)
Line
Count
Source
213
4.32k
{
214
4.32k
    return NonZeroTokens {
215
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks)),
216
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks >>= subsampling)),
217
4.32k
        TRY(FixedArray<bool>::create(size_in_sub_blocks)),
218
4.32k
    };
219
4.32k
}
Unexecuted instantiation: TreeParser.cpp:Media::Video::VP9::create_non_zero_tokens(unsigned int, bool)
220
221
template<typename T>
222
static Span<T> safe_slice(Span<T> span, u32 start, u32 size)
223
5.11M
{
224
5.11M
    return span.slice(start, min(size, span.size() - start));
225
5.11M
}
Unexecuted instantiation: Decoder.cpp:AK::Span<bool> Media::Video::VP9::safe_slice<bool>(AK::Span<bool>, unsigned int, unsigned int)
Unexecuted instantiation: Decoder.cpp:AK::Span<unsigned char> Media::Video::VP9::safe_slice<unsigned char>(AK::Span<unsigned char>, unsigned int, unsigned int)
Parser.cpp:AK::Span<bool> Media::Video::VP9::safe_slice<bool>(AK::Span<bool>, unsigned int, unsigned int)
Line
Count
Source
223
3.83M
{
224
3.83M
    return span.slice(start, min(size, span.size() - start));
225
3.83M
}
Parser.cpp:AK::Span<unsigned char> Media::Video::VP9::safe_slice<unsigned char>(AK::Span<unsigned char>, unsigned int, unsigned int)
Line
Count
Source
223
1.27M
{
224
1.27M
    return span.slice(start, min(size, span.size() - start));
225
1.27M
}
Unexecuted instantiation: TreeParser.cpp:AK::Span<bool> Media::Video::VP9::safe_slice<bool>(AK::Span<bool>, unsigned int, unsigned int)
Unexecuted instantiation: TreeParser.cpp:AK::Span<unsigned char> Media::Video::VP9::safe_slice<unsigned char>(AK::Span<unsigned char>, unsigned int, unsigned int)
226
227
static NonZeroTokensView create_non_zero_tokens_view(NonZeroTokensView non_zero_tokens, u32 start_in_sub_blocks, u32 size_in_sub_blocks, bool subsampling)
228
1.27M
{
229
1.27M
    NonZeroTokensView result;
230
    // Y plane
231
1.27M
    result[0] = safe_slice(non_zero_tokens[0], start_in_sub_blocks, size_in_sub_blocks);
232
    // UV planes
233
1.27M
    start_in_sub_blocks >>= subsampling;
234
1.27M
    size_in_sub_blocks >>= subsampling;
235
1.27M
    result[1] = safe_slice(non_zero_tokens[1], start_in_sub_blocks, size_in_sub_blocks);
236
1.27M
    result[2] = safe_slice(non_zero_tokens[2], start_in_sub_blocks, size_in_sub_blocks);
237
1.27M
    return result;
238
1.27M
}
Unexecuted instantiation: Decoder.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::Span<bool>, 3ul>, unsigned int, unsigned int, bool)
Parser.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::Span<bool>, 3ul>, unsigned int, unsigned int, bool)
Line
Count
Source
228
1.27M
{
229
1.27M
    NonZeroTokensView result;
230
    // Y plane
231
1.27M
    result[0] = safe_slice(non_zero_tokens[0], start_in_sub_blocks, size_in_sub_blocks);
232
    // UV planes
233
1.27M
    start_in_sub_blocks >>= subsampling;
234
1.27M
    size_in_sub_blocks >>= subsampling;
235
1.27M
    result[1] = safe_slice(non_zero_tokens[1], start_in_sub_blocks, size_in_sub_blocks);
236
1.27M
    result[2] = safe_slice(non_zero_tokens[2], start_in_sub_blocks, size_in_sub_blocks);
237
1.27M
    return result;
238
1.27M
}
Unexecuted instantiation: TreeParser.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::Span<bool>, 3ul>, unsigned int, unsigned int, bool)
239
240
static NonZeroTokensView create_non_zero_tokens_view(NonZeroTokens& non_zero_tokens, u32 start_in_sub_blocks, u32 size_in_sub_blocks, bool subsampling)
241
640k
{
242
640k
    return create_non_zero_tokens_view({ non_zero_tokens[0], non_zero_tokens[1], non_zero_tokens[2] }, start_in_sub_blocks, size_in_sub_blocks, subsampling);
243
640k
}
Unexecuted instantiation: Decoder.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::FixedArray<bool>, 3ul>&, unsigned int, unsigned int, bool)
Parser.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::FixedArray<bool>, 3ul>&, unsigned int, unsigned int, bool)
Line
Count
Source
241
640k
{
242
640k
    return create_non_zero_tokens_view({ non_zero_tokens[0], non_zero_tokens[1], non_zero_tokens[2] }, start_in_sub_blocks, size_in_sub_blocks, subsampling);
243
640k
}
Unexecuted instantiation: TreeParser.cpp:Media::Video::VP9::create_non_zero_tokens_view(AK::Array<AK::FixedArray<bool>, 3ul>&, unsigned int, unsigned int, bool)
244
245
struct TileContext {
246
public:
247
    static DecoderErrorOr<TileContext> try_create(FrameContext& frame_context, u32 tile_size, u32 rows_start, u32 rows_end, u32 columns_start, u32 columns_end, PartitionContextView above_partition_context, NonZeroTokensView above_non_zero_tokens, SegmentationPredictionContextView above_segmentation_ids)
248
2.27k
    {
249
2.27k
        auto width = columns_end - columns_start;
250
2.27k
        auto height = rows_end - rows_start;
251
2.27k
        auto context_view = frame_context.m_block_contexts.view(rows_start, columns_start, height, width);
252
253
2.27k
        return TileContext {
254
2.27k
            frame_context,
255
2.27k
            TRY(frame_context.create_range_decoder(tile_size)),
256
2.22k
            DECODER_TRY_ALLOC(try_make<SyntaxElementCounter>()),
257
2.22k
            rows_start,
258
2.22k
            rows_end,
259
2.22k
            columns_start,
260
2.22k
            columns_end,
261
2.22k
            context_view,
262
2.22k
            above_partition_context,
263
2.22k
            above_non_zero_tokens,
264
2.22k
            above_segmentation_ids,
265
2.22k
            DECODER_TRY_ALLOC(PartitionContext::create(superblocks_to_blocks(blocks_ceiled_to_superblocks(height)))),
266
2.22k
            DECODER_TRY_ALLOC(create_non_zero_tokens(blocks_to_sub_blocks(height), frame_context.color_config.subsampling_y)),
267
2.22k
            DECODER_TRY_ALLOC(SegmentationPredictionContext::create(height)),
268
2.22k
        };
269
2.22k
    }
270
271
1.14M
    Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
272
273
    FrameContext const& frame_context;
274
    BooleanDecoder decoder;
275
    NonnullOwnPtr<SyntaxElementCounter> counter;
276
    u32 rows_start { 0 };
277
    u32 rows_end { 0 };
278
    u32 columns_start { 0 };
279
    u32 columns_end { 0 };
280
0
    u32 rows() const { return rows_end - rows_start; }
281
0
    u32 columns() const { return columns_end - columns_start; }
282
    Vector2DView<FrameBlockContext> block_contexts_view;
283
284
    PartitionContextView above_partition_context;
285
    NonZeroTokensView above_non_zero_tokens;
286
    SegmentationPredictionContextView above_segmentation_ids;
287
    PartitionContext left_partition_context;
288
    NonZeroTokens left_non_zero_tokens;
289
    SegmentationPredictionContext left_segmentation_ids;
290
};
291
292
struct BlockContext {
293
    static BlockContext create(TileContext& tile_context, u32 row, u32 column, BlockSubsize size)
294
638k
    {
295
638k
        auto contexts_view = tile_context.block_contexts_view.view(
296
638k
            row - tile_context.rows_start,
297
638k
            column - tile_context.columns_start,
298
638k
            min<u32>(num_8x8_blocks_high_lookup[size], tile_context.frame_context.rows() - row),
299
638k
            min<u32>(num_8x8_blocks_wide_lookup[size], tile_context.frame_context.columns() - column));
300
301
638k
        auto size_in_blocks = block_size_to_blocks(size);
302
638k
        auto size_in_sub_blocks = block_size_to_sub_blocks(get_subsampled_block_size(size, false, false));
303
304
638k
        return BlockContext {
305
638k
            .frame_context = tile_context.frame_context,
306
638k
            .tile_context = tile_context,
307
638k
            .decoder = tile_context.decoder,
308
638k
            .counter = *tile_context.counter,
309
638k
            .row = row,
310
638k
            .column = column,
311
638k
            .size = size,
312
638k
            .contexts_view = contexts_view,
313
638k
            .above_non_zero_tokens = create_non_zero_tokens_view(tile_context.above_non_zero_tokens, blocks_to_sub_blocks(column - tile_context.columns_start), size_in_sub_blocks.width(), tile_context.frame_context.color_config.subsampling_x),
314
638k
            .above_segmentation_ids = safe_slice(tile_context.above_segmentation_ids, column - tile_context.columns_start, size_in_blocks.width()),
315
638k
            .left_non_zero_tokens = create_non_zero_tokens_view(tile_context.left_non_zero_tokens, blocks_to_sub_blocks(row - tile_context.rows_start), size_in_sub_blocks.height(), tile_context.frame_context.color_config.subsampling_y),
316
638k
            .left_segmentation_ids = safe_slice(tile_context.left_segmentation_ids.span(), row - tile_context.rows_start, size_in_blocks.height()),
317
638k
        };
318
638k
    }
319
320
11.8M
    Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
321
322
    FrameContext const& frame_context;
323
    TileContext const& tile_context;
324
    BooleanDecoder& decoder;
325
    SyntaxElementCounter& counter;
326
    u32 row { 0 };
327
    u32 column { 0 };
328
    BlockSubsize size;
329
    Gfx::Size<u8> get_size_in_sub_blocks() const
330
183k
    {
331
183k
        return block_size_to_sub_blocks(size);
332
183k
    }
333
334
    Vector2DView<FrameBlockContext> contexts_view;
335
336
    u8 segment_id { 0 };
337
    bool should_skip_residuals { false };
338
339
    TransformSize transform_size { Transform_4x4 };
340
341
    ReferenceFramePair reference_frame_types {};
342
47.7M
    bool is_inter_predicted() const { return reference_frame_types.primary != ReferenceFrameType::None; }
343
251k
    bool is_compound() const { return reference_frame_types.secondary != ReferenceFrameType::None; }
344
345
    Array<PredictionMode, 4> sub_block_prediction_modes {};
346
13.9M
    PredictionMode y_prediction_mode() const { return sub_block_prediction_modes.last(); }
347
11.7M
    PredictionMode& y_prediction_mode() { return sub_block_prediction_modes.last(); }
348
    PredictionMode uv_prediction_mode { 0 };
349
350
    InterpolationFilter interpolation_filter { EightTap };
351
    Array<MotionVectorPair, 4> sub_block_motion_vectors {};
352
353
    Array<i32, 1024> residual_tokens {};
354
355
    // Indexed by ReferenceFrame enum.
356
    Array<u8, 4> mode_context {};
357
358
    NonZeroTokensView above_non_zero_tokens;
359
    SegmentationPredictionContextView above_segmentation_ids;
360
    NonZeroTokensView left_non_zero_tokens;
361
    SegmentationPredictionContextView left_segmentation_ids;
362
363
    SegmentFeatureStatus get_segment_feature(SegmentFeature feature) const
364
762k
    {
365
762k
        return frame_context.get_segment_feature(segment_id, feature);
366
762k
    }
367
};
368
369
struct BlockMotionVectorCandidateSet {
370
    MotionVector near_vector;
371
    MotionVector nearest_vector;
372
    MotionVector best_vector;
373
};
374
375
struct MotionVectorCandidate {
376
    ReferenceFrameType type;
377
    MotionVector vector;
378
};
379
380
}