/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 | | } |