LibMedia: Remove the home-grown VP9 decoder

This commit is contained in:
Zaggy1024 2024-06-20 23:11:08 -05:00 committed by Andrew Kaster
parent 3cc1187036
commit c4c91f02b3
Notes: sideshowbarker 2024-07-17 10:08:28 +09:00
27 changed files with 3 additions and 7983 deletions

View file

@ -1,16 +0,0 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibMedia/Video/VP9/Decoder.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(u8 const* data, size_t size)
{
AK::set_debug_enabled(false);
Media::Video::VP9::Decoder vp9_decoder;
(void)vp9_decoder.receive_sample(Duration::zero(), { data, size });
return 0;
}

View file

@ -39,7 +39,6 @@ set(FUZZER_TARGETS
TTF
TinyVGLoader
URL
VP9Decoder
WasmParser
WAVLoader
WebPLoader
@ -94,7 +93,6 @@ set(FUZZER_DEPENDENCIES_TIFFLoader LibGfx)
set(FUZZER_DEPENDENCIES_TTF LibGfx)
set(FUZZER_DEPENDENCIES_TinyVGLoader LibGfx)
set(FUZZER_DEPENDENCIES_URL LibURL)
set(FUZZER_DEPENDENCIES_VP9Decoder LibMedia)
set(FUZZER_DEPENDENCIES_WasmParser LibWasm)
set(FUZZER_DEPENDENCIES_WAVLoader LibAudio)
set(FUZZER_DEPENDENCIES_WebPLoader LibGfx)

View file

@ -14,7 +14,3 @@ install(FILES vp9_4k.webm DESTINATION usr/Tests/LibMedia)
install(FILES vp9_clamp_reference_mvs.webm DESTINATION usr/Tests/LibMedia)
install(FILES vp9_oob_blocks.webm DESTINATION usr/Tests/LibMedia)
install(FILES master_elements_containing_crc32.mkv DESTINATION usr/Tests/LibMedia)
install(FILES oss-fuzz-testcase-52630.vp9 DESTINATION usr/Tests/LibMedia)
install(FILES oss-fuzz-testcase-53977.vp9 DESTINATION usr/Tests/LibMedia)
install(FILES oss-fuzz-testcase-62054.vp9 DESTINATION usr/Tests/LibMedia)
install(FILES oss-fuzz-testcase-63182.vp9 DESTINATION usr/Tests/LibMedia)

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibMedia/Video/VP9/Decoder.h>
#include <LibMedia/FFmpeg/FFmpegVideoDecoder.h>
#include "TestMediaCommon.h"
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::SampleIterator const&)
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::SampleIterator const& iterator)
{
return make<Media::Video::VP9::Decoder>();
return MUST(Media::FFmpeg::FFmpegVideoDecoder::try_create(Media::CodecID::VP9, iterator.track().codec_private_data()));
}
TEST_CASE(webm_in_vp9)
@ -23,23 +23,6 @@ TEST_CASE(vp9_oob_blocks)
decode_video("./vp9_oob_blocks.webm"sv, 240, make_decoder);
}
TEST_CASE(vp9_malformed_frame)
{
Array test_inputs = {
"./oss-fuzz-testcase-52630.vp9"sv,
"./oss-fuzz-testcase-53977.vp9"sv,
"./oss-fuzz-testcase-62054.vp9"sv,
"./oss-fuzz-testcase-63182.vp9"sv
};
for (auto test_input : test_inputs) {
auto file = MUST(Core::MappedFile::map(test_input));
Media::Video::VP9::Decoder vp9_decoder;
auto maybe_decoder_error = vp9_decoder.receive_sample(Duration::zero(), file->bytes());
EXPECT(maybe_decoder_error.is_error());
}
}
BENCHMARK_CASE(vp9_4k)
{
decode_video("./vp9_4k.webm"sv, 2, make_decoder);

View file

@ -1 +0,0 @@
I<EFBFBD>

View file

@ -7,11 +7,6 @@ set(SOURCES
FFmpeg/FFmpegVideoDecoder.cpp
PlaybackManager.cpp
VideoFrame.cpp
Video/VP9/Decoder.cpp
Video/VP9/Parser.cpp
Video/VP9/ProbabilityTables.cpp
Video/VP9/SyntaxElementCounter.cpp
Video/VP9/TreeParser.cpp
)
serenity_lib(LibMedia media)

View file

@ -1,15 +0,0 @@
/*
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/ImageFormats/BooleanDecoder.h>
namespace Media::Video::VP9 {
using BooleanDecoder = Gfx::BooleanDecoder;
}

View file

@ -1,380 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/BitStream.h>
#include <AK/Error.h>
#include <AK/FixedArray.h>
#include <AK/MemoryStream.h>
#include <LibGfx/Size.h>
#include <LibMedia/Color/CodingIndependentCodePoints.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/Subsampling.h>
#include "BooleanDecoder.h"
#include "ContextStorage.h"
#include "Enums.h"
#include "LookupTables.h"
#include "MotionVector.h"
#include "SyntaxElementCounter.h"
#include "Utilities.h"
namespace Media::Video::VP9 {
enum class FrameShowMode {
CreateAndShowNewFrame,
ShowExistingFrame,
DoNotShowFrame,
};
struct Quantizers {
u16 y_ac_quantizer { 0 };
u16 uv_ac_quantizer { 0 };
u16 y_dc_quantizer { 0 };
u16 uv_dc_quantizer { 0 };
};
struct FrameContext {
public:
static ErrorOr<FrameContext> create(ReadonlyBytes data,
Vector2D<FrameBlockContext>& contexts)
{
return FrameContext(
data,
TRY(try_make<FixedMemoryStream>(data)),
TRY(try_make<SyntaxElementCounter>()),
contexts);
}
FrameContext(FrameContext const&) = delete;
FrameContext(FrameContext&&) = default;
ReadonlyBytes stream_data;
NonnullOwnPtr<FixedMemoryStream> stream;
BigEndianInputBitStream bit_stream;
DecoderErrorOr<BooleanDecoder> create_range_decoder(size_t size)
{
if (size > stream->remaining())
return DecoderError::corrupted("Range decoder size invalid"sv);
auto compressed_header_data = ReadonlyBytes(stream_data.data() + stream->offset(), size);
// 9.2.1: The Boolean decoding process specified in section 9.2.2 is invoked to read a marker syntax element from the
// bitstream. It is a requirement of bitstream conformance that the value read is equal to 0.
auto decoder = DECODER_TRY(DecoderErrorCategory::Corrupted, BooleanDecoder::initialize(compressed_header_data));
if (decoder.read_bool(128))
return DecoderError::corrupted("Range decoder marker was non-zero"sv);
DECODER_TRY(DecoderErrorCategory::Corrupted, bit_stream.discard(size));
return decoder;
}
NonnullOwnPtr<SyntaxElementCounter> counter;
u8 profile { 0 };
FrameType type { FrameType::KeyFrame };
bool is_inter_predicted() const { return type == FrameType::InterFrame; }
bool error_resilient_mode { false };
bool parallel_decoding_mode { false };
bool should_replace_probability_context { false };
bool shows_a_frame() const { return m_frame_show_mode != FrameShowMode::DoNotShowFrame; }
bool shows_a_new_frame() const { return m_frame_show_mode == FrameShowMode::CreateAndShowNewFrame; }
bool shows_existing_frame() const { return m_frame_show_mode == FrameShowMode::ShowExistingFrame; }
void set_frame_hidden() { m_frame_show_mode = FrameShowMode::DoNotShowFrame; }
void set_existing_frame_to_show(u8 index)
{
m_frame_show_mode = FrameShowMode::ShowExistingFrame;
m_existing_frame_index = index;
}
u8 existing_frame_index() const { return m_existing_frame_index; }
bool use_previous_frame_motion_vectors { false };
ColorConfig color_config {};
u8 reference_frames_to_update_flags { 0 };
bool should_update_reference_frame_at_index(u8 index) const { return (reference_frames_to_update_flags & (1 << index)) != 0; }
u8 probability_context_index { 0 };
Gfx::Size<u32> size() const { return m_size; }
ErrorOr<void> set_size(Gfx::Size<u32> size)
{
m_size = size;
// From spec, compute_image_size( )
m_rows = pixels_to_blocks(size.height() + 7u);
m_columns = pixels_to_blocks(size.width() + 7u);
return m_block_contexts.try_resize(m_rows, m_columns);
}
u32 rows() const { return m_rows; }
u32 columns() const { return m_columns; }
u32 superblock_rows() const { return blocks_ceiled_to_superblocks(rows()); }
u32 superblock_columns() const { return blocks_ceiled_to_superblocks(columns()); }
// Calculates the output size for each plane in the frame.
Gfx::Size<u32> decoded_size(bool uv) const
{
if (uv) {
return {
Subsampling::subsampled_size(color_config.subsampling_y, blocks_to_pixels(columns())),
Subsampling::subsampled_size(color_config.subsampling_y, blocks_to_pixels(rows())),
};
}
return {
blocks_to_pixels(columns()),
blocks_to_pixels(rows()),
};
}
Vector2D<FrameBlockContext> const& block_contexts() const { return m_block_contexts; }
Gfx::Size<u32> render_size { 0, 0 };
Gfx::Size<u16> log2_of_tile_counts { 0, 0 };
// This group of fields is only needed for inter-predicted frames.
Array<u8, 3> reference_frame_indices;
Array<bool, ReferenceFrameType::LastFrame + 3> reference_frame_sign_biases;
bool high_precision_motion_vectors_allowed { false };
InterpolationFilter interpolation_filter { InterpolationFilter::Switchable };
u8 loop_filter_level { 0 };
u8 loop_filter_sharpness { 0 };
bool loop_filter_delta_enabled { false };
Array<i8, MAX_REF_FRAMES> loop_filter_reference_deltas;
Array<i8, 2> loop_filter_mode_deltas;
// Set based on quantization_params( ) in the spec.
bool lossless { false };
Array<Quantizers, MAX_SEGMENTS> segment_quantizers;
bool segmentation_enabled { false };
// Note: We can use Optional<Array<...>> for these tree probabilities, but unfortunately it seems to have measurable performance overhead.
bool use_full_segment_id_tree { false };
Array<u8, 7> full_segment_id_tree_probabilities;
bool use_predicted_segment_id_tree { false };
Array<u8, 3> predicted_segment_id_tree_probabilities;
bool should_use_absolute_segment_base_quantizer { false };
SegmentationFeatures segmentation_features;
SegmentFeatureStatus get_segment_feature(u8 segment_id, SegmentFeature feature) const
{
return segmentation_features[segment_id][to_underlying(feature)];
}
u16 header_size_in_bytes { 0 };
TransformMode transform_mode;
// This group also is only needed for inter-predicted frames.
ReferenceMode reference_mode;
ReferenceFrameType fixed_reference_type;
ReferenceFramePair variable_reference_types;
private:
friend struct TileContext;
FrameContext(ReadonlyBytes data,
NonnullOwnPtr<FixedMemoryStream> stream,
NonnullOwnPtr<SyntaxElementCounter> counter,
Vector2D<FrameBlockContext>& contexts)
: stream_data(data)
, stream(move(stream))
, bit_stream(MaybeOwned<Stream>(*this->stream))
, counter(move(counter))
, m_block_contexts(contexts)
{
}
FrameShowMode m_frame_show_mode { FrameShowMode::CreateAndShowNewFrame };
u8 m_existing_frame_index { 0 };
Gfx::Size<u32> m_size { 0, 0 };
u32 m_rows { 0 };
u32 m_columns { 0 };
// FIXME: From spec: NOTE We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory
// consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D
// arrays instead.
// I think should also apply to other fields that are only accessed relative to the current block. Worth looking
// into how much of this context needs to be stored for the whole frame vs a row or column from the current tile.
Vector2D<FrameBlockContext>& m_block_contexts;
};
static ErrorOr<NonZeroTokens> create_non_zero_tokens(u32 size_in_sub_blocks, bool subsampling)
{
return NonZeroTokens {
TRY(FixedArray<bool>::create(size_in_sub_blocks)),
TRY(FixedArray<bool>::create(size_in_sub_blocks >>= subsampling)),
TRY(FixedArray<bool>::create(size_in_sub_blocks)),
};
}
template<typename T>
static Span<T> safe_slice(Span<T> span, u32 start, u32 size)
{
return span.slice(start, min(size, span.size() - start));
}
static NonZeroTokensView create_non_zero_tokens_view(NonZeroTokensView non_zero_tokens, u32 start_in_sub_blocks, u32 size_in_sub_blocks, bool subsampling)
{
NonZeroTokensView result;
// Y plane
result[0] = safe_slice(non_zero_tokens[0], start_in_sub_blocks, size_in_sub_blocks);
// UV planes
start_in_sub_blocks >>= subsampling;
size_in_sub_blocks >>= subsampling;
result[1] = safe_slice(non_zero_tokens[1], start_in_sub_blocks, size_in_sub_blocks);
result[2] = safe_slice(non_zero_tokens[2], start_in_sub_blocks, size_in_sub_blocks);
return result;
}
static NonZeroTokensView create_non_zero_tokens_view(NonZeroTokens& non_zero_tokens, u32 start_in_sub_blocks, u32 size_in_sub_blocks, bool subsampling)
{
return create_non_zero_tokens_view({ non_zero_tokens[0].span(), non_zero_tokens[1].span(), non_zero_tokens[2].span() }, start_in_sub_blocks, size_in_sub_blocks, subsampling);
}
struct TileContext {
public:
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)
{
auto width = columns_end - columns_start;
auto height = rows_end - rows_start;
auto context_view = frame_context.m_block_contexts.view(rows_start, columns_start, height, width);
return TileContext {
frame_context,
TRY(frame_context.create_range_decoder(tile_size)),
DECODER_TRY_ALLOC(try_make<SyntaxElementCounter>()),
rows_start,
rows_end,
columns_start,
columns_end,
context_view,
above_partition_context,
above_non_zero_tokens,
above_segmentation_ids,
DECODER_TRY_ALLOC(PartitionContext::create(superblocks_to_blocks(blocks_ceiled_to_superblocks(height)))),
DECODER_TRY_ALLOC(create_non_zero_tokens(blocks_to_sub_blocks(height), frame_context.color_config.subsampling_y)),
DECODER_TRY_ALLOC(SegmentationPredictionContext::create(height)),
};
}
Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
FrameContext const& frame_context;
BooleanDecoder decoder;
NonnullOwnPtr<SyntaxElementCounter> counter;
u32 rows_start { 0 };
u32 rows_end { 0 };
u32 columns_start { 0 };
u32 columns_end { 0 };
u32 rows() const { return rows_end - rows_start; }
u32 columns() const { return columns_end - columns_start; }
Vector2DView<FrameBlockContext> block_contexts_view;
PartitionContextView above_partition_context;
NonZeroTokensView above_non_zero_tokens;
SegmentationPredictionContextView above_segmentation_ids;
PartitionContext left_partition_context;
NonZeroTokens left_non_zero_tokens;
SegmentationPredictionContext left_segmentation_ids;
};
struct BlockContext {
static BlockContext create(TileContext& tile_context, u32 row, u32 column, BlockSubsize size)
{
auto contexts_view = tile_context.block_contexts_view.view(
row - tile_context.rows_start,
column - tile_context.columns_start,
min<u32>(num_8x8_blocks_high_lookup[size], tile_context.frame_context.rows() - row),
min<u32>(num_8x8_blocks_wide_lookup[size], tile_context.frame_context.columns() - column));
auto size_in_blocks = block_size_to_blocks(size);
auto size_in_sub_blocks = block_size_to_sub_blocks(get_subsampled_block_size(size, false, false));
return BlockContext {
.frame_context = tile_context.frame_context,
.tile_context = tile_context,
.decoder = tile_context.decoder,
.counter = *tile_context.counter,
.row = row,
.column = column,
.size = size,
.contexts_view = contexts_view,
.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),
.above_segmentation_ids = safe_slice(tile_context.above_segmentation_ids, column - tile_context.columns_start, size_in_blocks.width()),
.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),
.left_segmentation_ids = safe_slice(tile_context.left_segmentation_ids.span(), row - tile_context.rows_start, size_in_blocks.height()),
};
}
Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); }
FrameContext const& frame_context;
TileContext const& tile_context;
BooleanDecoder& decoder;
SyntaxElementCounter& counter;
u32 row { 0 };
u32 column { 0 };
BlockSubsize size;
Gfx::Size<u8> get_size_in_sub_blocks() const
{
return block_size_to_sub_blocks(size);
}
Vector2DView<FrameBlockContext> contexts_view;
u8 segment_id { 0 };
bool should_skip_residuals { false };
TransformSize transform_size { Transform_4x4 };
ReferenceFramePair reference_frame_types {};
bool is_inter_predicted() const { return reference_frame_types.primary != ReferenceFrameType::None; }
bool is_compound() const { return reference_frame_types.secondary != ReferenceFrameType::None; }
Array<PredictionMode, 4> sub_block_prediction_modes {};
PredictionMode y_prediction_mode() const { return sub_block_prediction_modes.last(); }
PredictionMode& y_prediction_mode() { return sub_block_prediction_modes.last(); }
PredictionMode uv_prediction_mode { 0 };
InterpolationFilter interpolation_filter { EightTap };
Array<MotionVectorPair, 4> sub_block_motion_vectors {};
Array<i32, 1024> residual_tokens {};
// Indexed by ReferenceFrame enum.
Array<u8, 4> mode_context {};
NonZeroTokensView above_non_zero_tokens;
SegmentationPredictionContextView above_segmentation_ids;
NonZeroTokensView left_non_zero_tokens;
SegmentationPredictionContextView left_segmentation_ids;
SegmentFeatureStatus get_segment_feature(SegmentFeature feature) const
{
return frame_context.get_segment_feature(segment_id, feature);
}
};
struct BlockMotionVectorCandidateSet {
MotionVector near_vector;
MotionVector nearest_vector;
MotionVector best_vector;
};
struct MotionVectorCandidate {
ReferenceFrameType type;
MotionVector vector;
};
}

View file

@ -1,290 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/Error.h>
#include <AK/Vector.h>
#include <LibGfx/Size.h>
#include <LibMedia/Color/CodingIndependentCodePoints.h>
#include "Enums.h"
#include "LookupTables.h"
#include "MotionVector.h"
namespace Media::Video::VP9 {
template<typename T>
struct ReferencePair {
T primary;
T secondary;
T& operator[](ReferenceIndex index)
{
switch (index) {
case ReferenceIndex::Primary:
return primary;
case ReferenceIndex::Secondary:
return secondary;
default:
VERIFY_NOT_REACHED();
}
}
T const& operator[](ReferenceIndex index) const
{
return const_cast<ReferencePair<T>&>(*this)[index];
}
};
typedef ReferencePair<ReferenceFrameType> ReferenceFramePair;
typedef ReferencePair<MotionVector> MotionVectorPair;
template<typename T>
class Vector2D;
template<typename T>
class Vector2DView {
public:
u32 top() const { return m_top; }
u32 left() const { return m_left; }
u32 height() const { return m_height; }
u32 width() const { return m_width; }
T const& operator[](size_t index) const { return m_storage[index]; }
size_t size() const { return m_storage->size(); }
T& at(u32 relative_row, u32 relative_column)
{
VERIFY(relative_row < height());
VERIFY(relative_column < width());
return m_storage->at(top() + relative_row, left() + relative_column);
}
T const& at(u32 relative_row, u32 relative_column) const
{
VERIFY(relative_row < height());
VERIFY(relative_column < width());
return m_storage->at(top() + relative_row, left() + relative_column);
}
Vector2DView<T> view(u32 top, u32 left, u32 height, u32 width)
{
VERIFY(top + height <= this->height());
VERIFY(left + width <= this->width());
return Vector2DView<T>(m_storage, this->top() + top, this->left() + left, height, width);
}
private:
friend class Vector2D<T>;
Vector2DView(Vector2D<T>* const storage, u32 top, u32 left, u32 height, u32 width)
: m_storage(storage)
, m_top(top)
, m_left(left)
, m_height(height)
, m_width(width)
{
}
Vector2D<T>* const m_storage;
u32 const m_top { 0 };
u32 const m_left { 0 };
u32 const m_height { 0 };
u32 const m_width { 0 };
};
template<typename T>
class Vector2D {
public:
~Vector2D()
{
clear_storage();
}
ErrorOr<void> try_resize(u32 height, u32 width)
{
if (height != m_height && width != m_width) {
clear_storage();
size_t size = height * width;
auto* new_storage = new (nothrow) T[size];
if (!new_storage)
return Error::from_errno(ENOMEM);
m_storage = new_storage;
m_height = height;
m_width = width;
}
return {};
}
u32 height() const { return m_height; }
u32 width() const { return m_width; }
size_t index_at(u32 row, u32 column) const
{
VERIFY(row < height());
VERIFY(column < width());
return row * width() + column;
}
T& operator[](size_t index) { return m_storage[index]; }
T const& operator[](size_t index) const { return m_storage[index]; }
size_t size() const { return m_height * m_width; }
T& at(u32 row, u32 column)
{
return m_storage[index_at(row, column)];
}
T const& at(u32 row, u32 column) const
{
return m_storage[index_at(row, column)];
}
void assign(u32 row, u32 column, T&& value)
{
new (&m_storage[index_at(row, column)]) T(move(value));
}
template<typename OtherT, typename Function>
void copy_to(Vector2D<OtherT>& other, Function function) const
{
VERIFY(width() <= other.width());
VERIFY(height() <= other.height());
for (u32 row = 0; row < height(); row++) {
for (u32 column = 0; column < width(); column++)
other.at(row, column) = function(at(row, column));
}
}
void copy_to(Vector2D<T>& other) const
{
VERIFY(width() <= other.width());
VERIFY(height() <= other.height());
for (u32 row = 0; row < height(); row++) {
auto other_index = other.index_at(row, 0);
AK::TypedTransfer<T>::copy(&m_storage[index_at(row, 0)], &other[other_index], width());
}
}
template<typename OtherT>
ErrorOr<void> try_resize_to_match_other_vector2d(Vector2D<OtherT> const& other)
{
return try_resize(other.height(), other.width());
}
void reset()
{
for (size_t i = 0; i < size(); i++)
m_storage[i] = T();
}
Vector2DView<T> view(u32 top, u32 left, u32 height, u32 width)
{
VERIFY(top + height <= this->height());
VERIFY(left + width <= this->width());
return Vector2DView<T>(this, top, left, height, width);
}
private:
void clear_storage()
{
delete[] m_storage;
m_storage = nullptr;
m_width = 0;
m_height = 0;
}
u32 m_height { 0 };
u32 m_width { 0 };
T* m_storage { nullptr };
};
// Block context that is kept for the lifetime of a frame.
struct FrameBlockContext {
bool is_intra_predicted() const { return ref_frames.primary == ReferenceFrameType::None; }
bool is_single_reference() const { return ref_frames.secondary == ReferenceFrameType::None; }
MotionVectorPair primary_motion_vector_pair() const { return sub_block_motion_vectors[3]; }
bool is_available { false };
bool skip_coefficients { false };
TransformSize transform_size { Transform_4x4 };
PredictionMode y_mode { PredictionMode::DcPred };
Array<PredictionMode, 4> sub_modes { PredictionMode::DcPred, PredictionMode::DcPred, PredictionMode::DcPred, PredictionMode::DcPred };
InterpolationFilter interpolation_filter { InterpolationFilter::EightTap };
ReferenceFramePair ref_frames { ReferenceFrameType::None, ReferenceFrameType::None };
Array<MotionVectorPair, 4> sub_block_motion_vectors;
u8 segment_id { 0 };
};
// Block context that is kept between frames until explicitly cleared.
struct PersistentBlockContext {
PersistentBlockContext()
: available(false)
{
}
PersistentBlockContext(FrameBlockContext const& frame_context)
: available(frame_context.is_available)
, ref_frames(frame_context.ref_frames)
, primary_motion_vector_pair(frame_context.primary_motion_vector_pair())
, segment_id(frame_context.segment_id)
{
}
bool available { false };
ReferenceFramePair ref_frames { ReferenceFrameType::None, ReferenceFrameType::None };
MotionVectorPair primary_motion_vector_pair {};
u8 segment_id { 0 };
};
struct SegmentFeatureStatus {
bool enabled { false };
u8 value { 0 };
};
using SegmentFeatures = Array<SegmentFeatureStatus, to_underlying(SegmentFeature::Sentinel)>;
using SegmentationFeatures = Array<SegmentFeatures, MAX_SEGMENTS>;
struct ColorConfig {
u8 bit_depth { 8 };
ColorSpace color_space { ColorSpace::Bt601 };
VideoFullRangeFlag color_range { VideoFullRangeFlag::Studio };
bool subsampling_x { true };
bool subsampling_y { true };
};
struct BlockMotionVectorCandidateSet;
using BlockMotionVectorCandidates = ReferencePair<BlockMotionVectorCandidateSet>;
using NonZeroTokens = Array<FixedArray<bool>, 3>;
using NonZeroTokensView = Array<Span<bool>, 3>;
using SegmentationPredictionContext = FixedArray<u8>;
using SegmentationPredictionContextView = Span<u8>;
using PartitionContext = FixedArray<u8>;
using PartitionContextView = Span<u8>;
struct ReferenceFrame {
Gfx::Size<u32> size { 0, 0 };
bool subsampling_x { false };
bool subsampling_y { false };
u8 bit_depth { 0 };
Array<Vector<u16>, 3> frame_planes {};
bool is_valid() const { return bit_depth > 0; }
// These values are set at the start of each inter frame to be used during prediction.
i32 x_scale { 0 };
i32 y_scale { 0 };
i32 scaled_step_x { 0 };
i32 scaled_step_y { 0 };
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,153 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Error.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/Queue.h>
#include <AK/Span.h>
#include <LibMedia/Color/CodingIndependentCodePoints.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/VideoDecoder.h>
#include <LibMedia/VideoFrame.h>
#include "Parser.h"
namespace Media::Video::VP9 {
class Decoder final : public VideoDecoder {
friend class Parser;
public:
Decoder();
~Decoder() override { }
/* (8.1) General */
DecoderErrorOr<void> receive_sample(Duration timestamp, ReadonlyBytes) override;
DecoderErrorOr<NonnullOwnPtr<VideoFrame>> get_decoded_frame() override;
void flush() override;
private:
typedef i32 Intermediate;
// Based on the maximum size resulting from num_4x4_blocks_wide_lookup.
static constexpr size_t maximum_block_dimensions = 64ULL;
static constexpr size_t maximum_block_size = maximum_block_dimensions * maximum_block_dimensions;
// Based on the maximum for TXSize.
static constexpr size_t maximum_transform_size = 32ULL * 32ULL;
DecoderErrorOr<void> decode_frame(Duration timestamp, ReadonlyBytes);
template<typename T>
DecoderErrorOr<void> create_video_frame(Duration timestamp, FrameContext const&);
DecoderErrorOr<void> allocate_buffers(FrameContext const&);
Vector<u16>& get_output_buffer(u8 plane);
/* (8.4) Probability Adaptation Process */
u8 merge_prob(u8 pre_prob, u32 count_0, u32 count_1, u8 count_sat, u8 max_update_factor);
u32 merge_probs(int const* tree, int index, u8* probs, u32* counts, u8 count_sat, u8 max_update_factor);
DecoderErrorOr<void> adapt_coef_probs(FrameContext const&);
DecoderErrorOr<void> adapt_non_coef_probs(FrameContext const&);
void adapt_probs(int const* tree, u8* probs, u32* counts);
u8 adapt_prob(u8 prob, u32 counts[2]);
/* (8.5) Prediction Processes */
// (8.5.1) Intra prediction process
DecoderErrorOr<void> predict_intra(u8 plane, BlockContext const& block_context, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TransformSize transform_size, u32 block_index);
DecoderErrorOr<void> prepare_referenced_frame(Gfx::Size<u32> frame_size, u8 reference_frame_index);
// (8.5.1) Inter prediction process
DecoderErrorOr<void> predict_inter(u8 plane, BlockContext const& block_context, u32 x, u32 y, u32 width, u32 height, u32 block_index);
// (8.5.2.1) Motion vector selection process
MotionVector select_motion_vector(u8 plane, BlockContext const&, ReferenceIndex, u32 block_index);
// (8.5.2.2) Motion vector clamping process
MotionVector clamp_motion_vector(u8 plane, BlockContext const&, u32 block_row, u32 block_column, MotionVector vector);
// From (8.5.1) Inter prediction process, steps 2-5
DecoderErrorOr<void> predict_inter_block(u8 plane, BlockContext const&, ReferenceIndex, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer);
/* (8.6) Reconstruction and Dequantization */
// Returns the quantizer index for the current block
static u8 get_base_quantizer_index(SegmentFeatureStatus alternative_quantizer_feature, bool should_use_absolute_segment_base_quantizer, u8 base_quantizer_index);
// Returns the quantizer value for the dc coefficient for a particular plane
static u16 get_dc_quantizer(u8 bit_depth, u8 base, i8 delta);
// Returns the quantizer value for the ac coefficient for a particular plane
static u16 get_ac_quantizer(u8 bit_depth, u8 base, i8 delta);
// (8.6.2) Reconstruct process
DecoderErrorOr<void> reconstruct(u8 plane, BlockContext const&, u32 transform_block_x, u32 transform_block_y, TransformSize transform_block_size, TransformSet);
template<u8 log2_of_block_size>
DecoderErrorOr<void> reconstruct_templated(u8 plane, BlockContext const&, u32 transform_block_x, u32 transform_block_y, TransformSet);
// (8.7) Inverse transform process
template<u8 log2_of_block_size>
DecoderErrorOr<void> inverse_transform_2d(BlockContext const&, Span<Intermediate> dequantized, TransformSet);
// (8.7.1) 1D Transforms
// (8.7.1.1) Butterfly functions
inline i32 cos64(u8 angle);
inline i32 sin64(u8 angle);
// The function B( a, b, angle, 0 ) performs a butterfly rotation.
inline void butterfly_rotation_in_place(Span<Intermediate> data, size_t index_a, size_t index_b, u8 angle, bool flip);
// The function H( a, b, 0 ) performs a Hadamard rotation.
inline void hadamard_rotation_in_place(Span<Intermediate> data, size_t index_a, size_t index_b, bool flip);
// The function SB( a, b, angle, 0 ) performs a butterfly rotation.
// Spec defines the source as array T, and the destination array as S.
template<typename S, typename D>
inline void butterfly_rotation(Span<S> source, Span<D> destination, size_t index_a, size_t index_b, u8 angle, bool flip);
// The function SH( a, b ) performs a Hadamard rotation and rounding.
// Spec defines the source array as S, and the destination array as T.
template<typename S, typename D>
inline void hadamard_rotation(Span<S> source, Span<D> destination, size_t index_a, size_t index_b);
// (8.7.1.10) This process does an in-place Walsh-Hadamard transform of the array T (of length 4).
inline DecoderErrorOr<void> inverse_walsh_hadamard_transform(Span<Intermediate> data, u8 log2_of_block_size, u8 shift);
// (8.7.1.2) Inverse DCT array permutation process
template<u8 log2_of_block_size>
inline DecoderErrorOr<void> inverse_discrete_cosine_transform_array_permutation(Span<Intermediate> data);
// (8.7.1.3) Inverse DCT process
template<u8 log2_of_block_size>
inline DecoderErrorOr<void> inverse_discrete_cosine_transform(Span<Intermediate> data);
// (8.7.1.4) This process performs the in-place permutation of the array T of length 2 n which is required as the first step of
// the inverse ADST.
template<u8 log2_of_block_size>
inline void inverse_asymmetric_discrete_sine_transform_input_array_permutation(Span<Intermediate> data);
// (8.7.1.5) This process performs the in-place permutation of the array T of length 2 n which is required before the final
// step of the inverse ADST.
template<u8 log2_of_block_size>
inline void inverse_asymmetric_discrete_sine_transform_output_array_permutation(Span<Intermediate> data);
// (8.7.1.6) This process does an in-place transform of the array T to perform an inverse ADST.
inline void inverse_asymmetric_discrete_sine_transform_4(Span<Intermediate> data);
// (8.7.1.7) This process does an in-place transform of the array T using a higher precision array S for intermediate
// results.
inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform_8(Span<Intermediate> data);
// (8.7.1.8) This process does an in-place transform of the array T using a higher precision array S for intermediate
// results.
inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform_16(Span<Intermediate> data);
// (8.7.1.9) This process performs an in-place inverse ADST process on the array T of size 2 n for 2 ≤ n ≤ 4.
template<u8 log2_of_block_size>
inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform(Span<Intermediate> data);
/* (8.10) Reference Frame Update Process */
DecoderErrorOr<void> update_reference_frames(FrameContext const&);
NonnullOwnPtr<Parser> m_parser;
Vector<u16> m_output_buffers[3];
Queue<NonnullOwnPtr<VideoFrame>, 1> m_video_frame_queue;
};
}

View file

@ -1,177 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Symbols.h"
#include <AK/Types.h>
namespace Media::Video::VP9 {
enum class FrameType {
KeyFrame,
IntraOnlyFrame,
InterFrame
};
enum ColorSpace : u8 {
Unknown = 0,
Bt601 = 1,
Bt709 = 2,
Smpte170 = 3,
Smpte240 = 4,
Bt2020 = 5,
Reserved = 6,
RGB = 7
};
enum InterpolationFilter : u8 {
EightTap = 0,
EightTapSmooth = 1,
EightTapSharp = 2,
Bilinear = 3,
Switchable = 4
};
enum ReferenceFrameType : u8 {
// None represents both INTRA_FRAME and NONE in the spec. When the primary reference
// frame type is None, that means that the frame/block is not inter-predicted.
None = 0,
LastFrame = 1,
GoldenFrame = 2,
AltRefFrame = 3,
};
enum class TransformMode : u8 {
Only_4x4 = 0,
Allow_8x8 = 1,
Allow_16x16 = 2,
Allow_32x32 = 3,
Select = 4,
};
enum TransformSize : u8 {
Transform_4x4 = 0,
Transform_8x8 = 1,
Transform_16x16 = 2,
Transform_32x32 = 3,
};
enum class TransformType : u8 {
DCT = 0,
ADST = 1,
};
struct TransformSet {
TransformType first_transform : 1;
TransformType second_transform : 1;
bool operator==(TransformSet const& other) const
{
return first_transform == other.first_transform && second_transform == other.second_transform;
}
};
enum ReferenceMode : u8 {
SingleReference = 0,
CompoundReference = 1,
ReferenceModeSelect = 2,
};
enum class ReferenceIndex : u8 {
Primary = 0,
Secondary = 1,
};
enum BlockSubsize : u8 {
Block_4x4 = 0,
Block_4x8 = 1,
Block_8x4 = 2,
Block_8x8 = 3,
Block_8x16 = 4,
Block_16x8 = 5,
Block_16x16 = 6,
Block_16x32 = 7,
Block_32x16 = 8,
Block_32x32 = 9,
Block_32x64 = 10,
Block_64x32 = 11,
Block_64x64 = 12,
Block_Invalid = BLOCK_INVALID
};
enum Partition : u8 {
PartitionNone = 0,
PartitionHorizontal = 1,
PartitionVertical = 2,
PartitionSplit = 3,
};
enum class PredictionMode : u8 {
DcPred = 0,
VPred = 1,
HPred = 2,
D45Pred = 3,
D135Pred = 4,
D117Pred = 5,
D153Pred = 6,
D207Pred = 7,
D63Pred = 8,
TmPred = 9,
NearestMv = 10,
NearMv = 11,
ZeroMv = 12,
NewMv = 13,
};
enum MvJoint : u8 {
MotionVectorAllZero = 0,
MotionVectorNonZeroColumn = 1,
MotionVectorNonZeroRow = 2,
};
enum MvClass : u8 {
MvClass0 = 0,
MvClass1 = 1,
MvClass2 = 2,
MvClass3 = 3,
MvClass4 = 4,
MvClass5 = 5,
MvClass6 = 6,
MvClass7 = 7,
MvClass8 = 8,
MvClass9 = 9,
MvClass10 = 10,
};
enum Token : u8 {
ZeroToken = 0,
OneToken = 1,
TwoToken = 2,
ThreeToken = 3,
FourToken = 4,
DctValCat1 = 5,
DctValCat2 = 6,
DctValCat3 = 7,
DctValCat4 = 8,
DctValCat5 = 9,
DctValCat6 = 10,
};
enum class SegmentFeature : u8 {
// SEG_LVL_ALT_Q
AlternativeQuantizerBase,
// SEG_LVL_ALT_L
AlternativeLoopFilterBase,
// SEG_LVL_REF_FRAME
ReferenceFrameOverride,
// SEG_LVL_SKIP
SkipResidualsOverride,
// SEG_LVL_MAX
Sentinel,
};
}

File diff suppressed because one or more lines are too long

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace Media::Video::VP9 {
struct MotionVector {
public:
constexpr MotionVector() = default;
constexpr MotionVector(MotionVector const& other) = default;
constexpr MotionVector(i32 row, i32 col)
: m_row(row)
, m_column(col)
{
}
constexpr MotionVector& operator=(MotionVector const& other) = default;
constexpr MotionVector& operator=(MotionVector&& other) = default;
constexpr i32 row() const { return m_row; }
constexpr void set_row(i32 row) { m_row = row; }
constexpr i32 column() const { return m_column; }
constexpr void set_column(i32 col) { m_column = col; }
constexpr MotionVector operator+(MotionVector const& other) const
{
return MotionVector(this->row() + other.row(), this->column() + other.column());
}
constexpr MotionVector& operator+=(MotionVector const& other)
{
*this = *this + other;
return *this;
}
constexpr MotionVector operator*(i32 scalar) const
{
return MotionVector(this->row() * scalar, this->column() * scalar);
}
constexpr MotionVector& operator*=(i32 scalar)
{
*this = *this * scalar;
return *this;
}
constexpr bool operator==(MotionVector const& other) const
{
return this->row() == other.row() && this->column() == other.column();
}
private:
i32 m_row { 0 };
i32 m_column { 0 };
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,147 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/OwnPtr.h>
#include <AK/Span.h>
#include <LibGfx/Size.h>
#include <LibMedia/Color/CodingIndependentCodePoints.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/Forward.h>
#include <LibThreading/Forward.h>
#include "ContextStorage.h"
#include "LookupTables.h"
#include "MotionVector.h"
#include "ProbabilityTables.h"
#include "SyntaxElementCounter.h"
#include "TreeParser.h"
namespace Media::Video::VP9 {
class Decoder;
struct FrameContext;
struct TileContext;
struct BlockContext;
struct MotionVectorCandidate;
struct QuantizationParameters;
class Parser {
friend class TreeParser;
friend class Decoder;
public:
explicit Parser(Decoder&);
~Parser();
DecoderErrorOr<FrameContext> parse_frame(ReadonlyBytes);
private:
/* Annex B: Superframes are a method of storing multiple coded frames into a single chunk
* See also section 5.26. */
Vector<size_t> parse_superframe_sizes(ReadonlyBytes);
DecoderErrorOr<VideoFullRangeFlag> read_video_full_range_flag(BigEndianInputBitStream&);
/* (6.1) Frame Syntax */
bool trailing_bits();
DecoderErrorOr<void> refresh_probs(FrameContext const&);
/* (6.2) Uncompressed Header Syntax */
DecoderErrorOr<void> uncompressed_header(FrameContext& frame_context);
DecoderErrorOr<void> frame_sync_code(BigEndianInputBitStream&);
DecoderErrorOr<ColorConfig> parse_color_config(BigEndianInputBitStream&, u8 profile);
DecoderErrorOr<Gfx::Size<u32>> parse_frame_size(BigEndianInputBitStream&);
DecoderErrorOr<Gfx::Size<u32>> parse_frame_size_with_refs(BigEndianInputBitStream&, Array<u8, 3> const& reference_indices);
DecoderErrorOr<Gfx::Size<u32>> parse_render_size(BigEndianInputBitStream&, Gfx::Size<u32> frame_size);
DecoderErrorOr<void> compute_image_size(FrameContext&);
DecoderErrorOr<InterpolationFilter> read_interpolation_filter(BigEndianInputBitStream&);
DecoderErrorOr<void> loop_filter_params(FrameContext&);
DecoderErrorOr<i8> read_delta_q(BigEndianInputBitStream&);
DecoderErrorOr<void> segmentation_params(FrameContext&);
DecoderErrorOr<u8> read_prob(BigEndianInputBitStream&);
static void precalculate_quantizers(FrameContext& frame_context, QuantizationParameters quantization_parameters);
DecoderErrorOr<void> parse_tile_counts(FrameContext&);
void setup_past_independence();
/* (6.3) Compressed Header Syntax */
DecoderErrorOr<void> compressed_header(FrameContext&);
TransformMode read_tx_mode(BooleanDecoder&, FrameContext const&);
void tx_mode_probs(BooleanDecoder&);
u8 diff_update_prob(BooleanDecoder&, u8 prob);
u8 decode_term_subexp(BooleanDecoder&);
u8 inv_remap_prob(u8 delta_prob, u8 prob);
u8 inv_recenter_nonneg(u8 v, u8 m);
void read_coef_probs(BooleanDecoder&, TransformMode);
void read_skip_prob(BooleanDecoder&);
void read_inter_mode_probs(BooleanDecoder&);
void read_interp_filter_probs(BooleanDecoder&);
void read_is_inter_probs(BooleanDecoder&);
void frame_reference_mode(FrameContext&, BooleanDecoder&);
void frame_reference_mode_probs(BooleanDecoder&, FrameContext const&);
void read_y_mode_probs(BooleanDecoder&);
void read_partition_probs(BooleanDecoder&);
void mv_probs(BooleanDecoder&, FrameContext const&);
u8 update_mv_prob(BooleanDecoder&, u8 prob);
/* (6.4) Decode Tiles Syntax */
DecoderErrorOr<void> decode_tiles(FrameContext&);
DecoderErrorOr<void> decode_tile(TileContext&);
void clear_left_context(TileContext&);
DecoderErrorOr<void> decode_partition(TileContext&, u32 row, u32 column, BlockSubsize subsize);
DecoderErrorOr<void> decode_block(TileContext&, u32 row, u32 column, BlockSubsize subsize);
void mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
void intra_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
void set_intra_segment_id(BlockContext&);
bool read_should_skip_residuals(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
TransformSize read_tx_size(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select);
void inter_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
void set_inter_segment_id(BlockContext&);
u8 get_segment_id(BlockContext const&);
bool read_is_inter(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
void intra_block_mode_info(BlockContext&);
void inter_block_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
void read_ref_frames(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context);
MotionVectorPair get_motion_vector(BlockContext const&, BlockMotionVectorCandidates const&);
MotionVector read_motion_vector(BlockContext const&, BlockMotionVectorCandidates const&, ReferenceIndex);
i32 read_single_motion_vector_component(BooleanDecoder&, SyntaxElementCounter&, u8 component, bool use_high_precision);
DecoderErrorOr<bool> residual(BlockContext&, bool has_block_above, bool has_block_left);
bool tokens(BlockContext&, size_t plane, u32 x, u32 y, TransformSize, TransformSet, Array<u8, 1024> token_cache);
i32 read_coef(BooleanDecoder&, u8 bit_depth, Token token);
/* (6.5) Motion Vector Prediction */
MotionVectorPair find_reference_motion_vectors(BlockContext&, ReferenceFrameType, i32 block);
void select_best_sub_block_reference_motion_vectors(BlockContext&, BlockMotionVectorCandidates&, i32 block, ReferenceIndex);
size_t get_image_index(FrameContext const&, u32 row, u32 column) const;
MotionVectorCandidate get_motion_vector_from_current_or_previous_frame(BlockContext const&, MotionVector candidate_vector, ReferenceIndex, bool use_prev);
void add_motion_vector_if_reference_frame_type_is_same(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, Vector<MotionVector, 2>& list, bool use_prev);
void add_motion_vector_if_reference_frame_type_is_different(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, Vector<MotionVector, 2>& list, bool use_prev);
bool m_is_first_compute_image_size_invoke { true };
Gfx::Size<u32> m_previous_frame_size { 0, 0 };
bool m_previous_show_frame { false };
ColorConfig m_previous_color_config;
FrameType m_previous_frame_type { FrameType::KeyFrame };
Array<i8, MAX_REF_FRAMES> m_previous_loop_filter_ref_deltas;
Array<i8, 2> m_previous_loop_filter_mode_deltas;
bool m_previous_should_use_absolute_segment_base_quantizer;
SegmentationFeatures m_previous_segmentation_features;
ReferenceFrame m_reference_frames[NUM_REF_FRAMES];
Vector2D<FrameBlockContext> m_reusable_frame_block_contexts;
Vector2D<PersistentBlockContext> m_previous_block_contexts;
OwnPtr<ProbabilityTables> m_probability_tables;
Decoder& m_decoder;
Vector<NonnullOwnPtr<Threading::WorkerThread<DecoderError>>> m_worker_threads;
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,127 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/Types.h>
#include "Symbols.h"
namespace Media::Video::VP9 {
typedef u8 ParetoTable[128][8];
typedef u8 KfPartitionProbs[PARTITION_CONTEXTS][PARTITION_TYPES - 1];
typedef u8 KfYModeProbs[INTRA_MODES][INTRA_MODES][INTRA_MODES - 1];
typedef u8 KfUVModeProbs[INTRA_MODES][INTRA_MODES - 1];
typedef u8 PartitionProbs[PARTITION_CONTEXTS][PARTITION_TYPES - 1];
typedef u8 YModeProbs[BLOCK_SIZE_GROUPS][INTRA_MODES - 1];
typedef u8 UVModeProbs[INTRA_MODES][INTRA_MODES - 1];
typedef u8 SkipProb[SKIP_CONTEXTS];
typedef u8 IsInterProb[IS_INTER_CONTEXTS];
typedef u8 CompModeProb[COMP_MODE_CONTEXTS];
typedef u8 CompRefProb[REF_CONTEXTS];
typedef u8 SingleRefProb[REF_CONTEXTS][2];
typedef u8 MvSignProb[2];
typedef u8 MvBitsProb[2][MV_OFFSET_BITS];
typedef u8 MvClass0BitProb[2];
typedef u8 TxProbs[TX_SIZES][TX_SIZE_CONTEXTS][TX_SIZES - 1];
typedef u8 InterModeProbs[INTER_MODE_CONTEXTS][INTER_MODES - 1];
typedef u8 InterpFilterProbs[INTERP_FILTER_CONTEXTS][SWITCHABLE_FILTERS - 1];
typedef u8 MvJointProbs[3];
typedef u8 MvClassProbs[2][MV_CLASSES - 1];
typedef u8 MvClass0FrProbs[2][CLASS0_SIZE][3];
typedef u8 MvClass0HpProbs[2];
typedef u8 MvFrProbs[2][3];
typedef u8 MvHpProb[2];
typedef u8 CoefProbs[TX_SIZES][BLOCK_TYPES][REF_TYPES][COEF_BANDS][PREV_COEF_CONTEXTS][UNCONSTRAINED_NODES];
class ProbabilityTables final {
public:
void save_probs(u8 index);
void reset_probs();
void load_probs(u8 index);
void load_probs2(u8 index);
ParetoTable const& pareto_table() const;
KfPartitionProbs const& kf_partition_probs() const;
KfYModeProbs const& kf_y_mode_probs() const;
KfUVModeProbs const& kf_uv_mode_prob() const;
PartitionProbs& partition_probs() { return m_current_probability_table.partition_probs; }
PartitionProbs const& partition_probs() const { return m_current_probability_table.partition_probs; }
YModeProbs& y_mode_probs() { return m_current_probability_table.y_mode_probs; }
YModeProbs const& y_mode_probs() const { return m_current_probability_table.y_mode_probs; }
UVModeProbs& uv_mode_probs() { return m_current_probability_table.uv_mode_probs; }
UVModeProbs const& uv_mode_probs() const { return m_current_probability_table.uv_mode_probs; }
SkipProb& skip_prob() { return m_current_probability_table.skip_prob; }
SkipProb const& skip_prob() const { return m_current_probability_table.skip_prob; }
IsInterProb& is_inter_prob() { return m_current_probability_table.is_inter_prob; }
IsInterProb const& is_inter_prob() const { return m_current_probability_table.is_inter_prob; }
CompModeProb& comp_mode_prob() { return m_current_probability_table.comp_mode_prob; }
CompModeProb const& comp_mode_prob() const { return m_current_probability_table.comp_mode_prob; }
CompRefProb& comp_ref_prob() { return m_current_probability_table.comp_ref_prob; }
CompRefProb const& comp_ref_prob() const { return m_current_probability_table.comp_ref_prob; }
SingleRefProb& single_ref_prob() { return m_current_probability_table.single_ref_prob; }
SingleRefProb const& single_ref_prob() const { return m_current_probability_table.single_ref_prob; }
MvSignProb& mv_sign_prob() { return m_current_probability_table.mv_sign_prob; }
MvSignProb const& mv_sign_prob() const { return m_current_probability_table.mv_sign_prob; }
MvBitsProb& mv_bits_prob() { return m_current_probability_table.mv_bits_prob; }
MvBitsProb const& mv_bits_prob() const { return m_current_probability_table.mv_bits_prob; }
MvClass0BitProb& mv_class0_bit_prob() { return m_current_probability_table.mv_class0_bit_prob; }
MvClass0BitProb const& mv_class0_bit_prob() const { return m_current_probability_table.mv_class0_bit_prob; }
TxProbs& tx_probs() { return m_current_probability_table.tx_probs; }
TxProbs const& tx_probs() const { return m_current_probability_table.tx_probs; }
InterModeProbs& inter_mode_probs() { return m_current_probability_table.inter_mode_probs; }
InterModeProbs const& inter_mode_probs() const { return m_current_probability_table.inter_mode_probs; }
InterpFilterProbs& interp_filter_probs() { return m_current_probability_table.interp_filter_probs; }
InterpFilterProbs const& interp_filter_probs() const { return m_current_probability_table.interp_filter_probs; }
MvJointProbs& mv_joint_probs() { return m_current_probability_table.mv_joint_probs; }
MvJointProbs const& mv_joint_probs() const { return m_current_probability_table.mv_joint_probs; }
MvClassProbs& mv_class_probs() { return m_current_probability_table.mv_class_probs; }
MvClassProbs const& mv_class_probs() const { return m_current_probability_table.mv_class_probs; }
MvClass0FrProbs& mv_class0_fr_probs() { return m_current_probability_table.mv_class0_fr_probs; }
MvClass0FrProbs const& mv_class0_fr_probs() const { return m_current_probability_table.mv_class0_fr_probs; }
MvClass0HpProbs& mv_class0_hp_prob() { return m_current_probability_table.mv_class0_hp_prob; }
MvClass0HpProbs const& mv_class0_hp_prob() const { return m_current_probability_table.mv_class0_hp_prob; }
MvFrProbs& mv_fr_probs() { return m_current_probability_table.mv_fr_probs; }
MvFrProbs const& mv_fr_probs() const { return m_current_probability_table.mv_fr_probs; }
MvHpProb& mv_hp_prob() { return m_current_probability_table.mv_hp_prob; }
MvHpProb const& mv_hp_prob() const { return m_current_probability_table.mv_hp_prob; }
CoefProbs& coef_probs() { return m_current_probability_table.coef_probs; }
CoefProbs const& coef_probs() const { return m_current_probability_table.coef_probs; }
private:
struct ProbabilityTable {
PartitionProbs partition_probs;
YModeProbs y_mode_probs;
UVModeProbs uv_mode_probs;
SkipProb skip_prob;
IsInterProb is_inter_prob;
CompModeProb comp_mode_prob;
CompRefProb comp_ref_prob;
SingleRefProb single_ref_prob;
MvSignProb mv_sign_prob;
MvBitsProb mv_bits_prob;
MvClass0BitProb mv_class0_bit_prob;
TxProbs tx_probs;
InterModeProbs inter_mode_probs;
InterpFilterProbs interp_filter_probs;
MvJointProbs mv_joint_probs;
MvClassProbs mv_class_probs;
MvClass0FrProbs mv_class0_fr_probs;
MvClass0HpProbs mv_class0_hp_prob;
MvFrProbs mv_fr_probs;
MvHpProb mv_hp_prob;
CoefProbs coef_probs;
};
Array<ProbabilityTable, 4> m_saved_probability_tables;
ProbabilityTable m_current_probability_table;
};
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson<zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Media::Video::VP9 {
// FIXME: These should be placed in logical groupings based on the
// context they are used in, and perhaps split into multiple
// files. While doing so, as many of these as possible should be
// renamed to be more human-readable, and most if not all should
// be constexpr variables rather than preprocessor definitions.
#define REFS_PER_FRAME 3
#define MV_FR_SIZE 4
#define MVREF_NEIGHBORS 8
#define BLOCK_SIZE_GROUPS 4
#define BLOCK_SIZES 13
#define BLOCK_INVALID 14
#define PARTITION_CONTEXTS 16
#define MI_SIZE 8
#define MIN_TILE_WIDTH_B64 4
#define MAX_TILE_WIDTH_B64 64
#define MAX_MV_REF_CANDIDATES 2
#define LOG2_OF_NUM_REF_FRAMES 3
#define NUM_REF_FRAMES 1 << LOG2_OF_NUM_REF_FRAMES
#define MAX_REF_FRAMES 4
#define IS_INTER_CONTEXTS 4
#define COMP_MODE_CONTEXTS 5
#define REF_CONTEXTS 5
#define MAX_SEGMENTS 8
#define BLOCK_TYPES 2
#define REF_TYPES 2
#define COEF_BANDS 6
#define PREV_COEF_CONTEXTS 6
#define UNCONSTRAINED_NODES 3
#define TX_SIZE_CONTEXTS 2
#define SWITCHABLE_FILTERS 3
#define INTERP_FILTER_CONTEXTS 4
#define SKIP_CONTEXTS 3
#define PARTITION_TYPES 4
#define TX_SIZES 4
#define TX_MODES 5
#define MB_MODE_COUNT 14
#define INTRA_MODES 10
#define INTER_MODES 4
#define INTER_MODE_CONTEXTS 7
#define MV_JOINTS 4
#define MV_CLASSES 11
#define CLASS0_SIZE 2
#define MV_OFFSET_BITS 10
#define MAX_PROB 255
#define MAX_MODE_LF_DELTAS 2
#define COMPANDED_MVREF_THRESH 8
#define MAX_LOOP_FILTER 63
#define REF_SCALE_SHIFT 14
// Number of bits of precision when performing inter prediction.
#define SUBPEL_BITS 4
#define SUBPEL_SHIFTS 16
#define SUBPEL_MASK 15
#define MV_BORDER 128
// Value used when clipping motion vectors.
#define INTERP_EXTEND 4
// Value used when clipping motion vectors.
#define BORDERINPIXELS 160
#define MAX_UPDATE_FACTOR 128
#define COUNT_SAT 20
#define BOTH_ZERO 0
#define ZERO_PLUS_PREDICTED 1
#define BOTH_PREDICTED 2
#define NEW_PLUS_NON_INTRA 3
#define BOTH_NEW 4
#define INTRA_PLUS_NON_INTRA 5
#define BOTH_INTRA 6
#define INVALID_CASE 9
}

View file

@ -1,88 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "SyntaxElementCounter.h"
#include <AK/Format.h>
namespace Media::Video::VP9 {
SyntaxElementCounter::SyntaxElementCounter()
{
__builtin_memset(m_counts_intra_mode, 0, sizeof(m_counts_intra_mode));
__builtin_memset(m_counts_uv_mode, 0, sizeof(m_counts_uv_mode));
__builtin_memset(m_counts_partition, 0, sizeof(m_counts_partition));
__builtin_memset(m_counts_interp_filter, 0, sizeof(m_counts_interp_filter));
__builtin_memset(m_counts_inter_mode, 0, sizeof(m_counts_inter_mode));
__builtin_memset(m_counts_tx_size, 0, sizeof(m_counts_tx_size));
__builtin_memset(m_counts_is_inter, 0, sizeof(m_counts_is_inter));
__builtin_memset(m_counts_comp_mode, 0, sizeof(m_counts_comp_mode));
__builtin_memset(m_counts_single_ref, 0, sizeof(m_counts_single_ref));
__builtin_memset(m_counts_comp_ref, 0, sizeof(m_counts_comp_ref));
__builtin_memset(m_counts_skip, 0, sizeof(m_counts_skip));
__builtin_memset(m_counts_mv_joint, 0, sizeof(m_counts_mv_joint));
__builtin_memset(m_counts_mv_sign, 0, sizeof(m_counts_mv_sign));
__builtin_memset(m_counts_mv_class, 0, sizeof(m_counts_mv_class));
__builtin_memset(m_counts_mv_class0_bit, 0, sizeof(m_counts_mv_class0_bit));
__builtin_memset(m_counts_mv_class0_fr, 0, sizeof(m_counts_mv_class0_fr));
__builtin_memset(m_counts_mv_class0_hp, 0, sizeof(m_counts_mv_class0_hp));
__builtin_memset(m_counts_mv_bits, 0, sizeof(m_counts_mv_bits));
__builtin_memset(m_counts_mv_fr, 0, sizeof(m_counts_mv_fr));
__builtin_memset(m_counts_mv_hp, 0, sizeof(m_counts_mv_hp));
__builtin_memset(m_counts_token, 0, sizeof(m_counts_token));
__builtin_memset(m_counts_more_coefs, 0, sizeof(m_counts_more_coefs));
}
template<typename T, size_t size>
static void sum_arrays(T (&destination)[size], T const (&left)[size], T const (&right)[size])
{
for (size_t i = 0; i < size; i++) {
destination[i] = left[i] + right[i];
}
}
template<typename T, size_t size, size_t size_2>
static void sum_arrays(T (&destination)[size][size_2], T const (&left)[size][size_2], T const (&right)[size][size_2])
{
for (size_t i = 0; i < size; i++) {
sum_arrays(destination[i], left[i], right[i]);
}
}
SyntaxElementCounter SyntaxElementCounter::operator+(SyntaxElementCounter const& other) const
{
SyntaxElementCounter result;
sum_arrays(result.m_counts_intra_mode, this->m_counts_intra_mode, other.m_counts_intra_mode);
sum_arrays(result.m_counts_uv_mode, this->m_counts_uv_mode, other.m_counts_uv_mode);
sum_arrays(result.m_counts_partition, this->m_counts_partition, other.m_counts_partition);
sum_arrays(result.m_counts_interp_filter, this->m_counts_interp_filter, other.m_counts_interp_filter);
sum_arrays(result.m_counts_inter_mode, this->m_counts_inter_mode, other.m_counts_inter_mode);
sum_arrays(result.m_counts_tx_size, this->m_counts_tx_size, other.m_counts_tx_size);
sum_arrays(result.m_counts_is_inter, this->m_counts_is_inter, other.m_counts_is_inter);
sum_arrays(result.m_counts_comp_mode, this->m_counts_comp_mode, other.m_counts_comp_mode);
sum_arrays(result.m_counts_single_ref, this->m_counts_single_ref, other.m_counts_single_ref);
sum_arrays(result.m_counts_comp_ref, this->m_counts_comp_ref, other.m_counts_comp_ref);
sum_arrays(result.m_counts_skip, this->m_counts_skip, other.m_counts_skip);
sum_arrays(result.m_counts_mv_joint, this->m_counts_mv_joint, other.m_counts_mv_joint);
sum_arrays(result.m_counts_mv_sign, this->m_counts_mv_sign, other.m_counts_mv_sign);
sum_arrays(result.m_counts_mv_class, this->m_counts_mv_class, other.m_counts_mv_class);
sum_arrays(result.m_counts_mv_class0_bit, this->m_counts_mv_class0_bit, other.m_counts_mv_class0_bit);
sum_arrays(result.m_counts_mv_class0_fr, this->m_counts_mv_class0_fr, other.m_counts_mv_class0_fr);
sum_arrays(result.m_counts_mv_class0_hp, this->m_counts_mv_class0_hp, other.m_counts_mv_class0_hp);
sum_arrays(result.m_counts_mv_bits, this->m_counts_mv_bits, other.m_counts_mv_bits);
sum_arrays(result.m_counts_mv_fr, this->m_counts_mv_fr, other.m_counts_mv_fr);
sum_arrays(result.m_counts_mv_hp, this->m_counts_mv_hp, other.m_counts_mv_hp);
sum_arrays(result.m_counts_token, this->m_counts_token, other.m_counts_token);
sum_arrays(result.m_counts_more_coefs, this->m_counts_more_coefs, other.m_counts_more_coefs);
return result;
}
SyntaxElementCounter& SyntaxElementCounter::operator+=(SyntaxElementCounter const& other)
{
*this = *this + other;
return *this;
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Symbols.h"
#include <AK/Types.h>
namespace Media::Video::VP9 {
class SyntaxElementCounter final {
public:
SyntaxElementCounter();
/* (8.3) Clear Counts Process */
void clear_counts();
u32 m_counts_intra_mode[BLOCK_SIZE_GROUPS][INTRA_MODES];
u32 m_counts_uv_mode[INTRA_MODES][INTRA_MODES];
u32 m_counts_partition[PARTITION_CONTEXTS][PARTITION_TYPES];
u32 m_counts_interp_filter[INTERP_FILTER_CONTEXTS][SWITCHABLE_FILTERS];
u32 m_counts_inter_mode[INTER_MODE_CONTEXTS][INTER_MODES];
u32 m_counts_tx_size[TX_SIZES][TX_SIZE_CONTEXTS][TX_SIZES];
u32 m_counts_is_inter[IS_INTER_CONTEXTS][2];
u32 m_counts_comp_mode[COMP_MODE_CONTEXTS][2];
u32 m_counts_single_ref[REF_CONTEXTS][2][2];
u32 m_counts_comp_ref[REF_CONTEXTS][2];
u32 m_counts_skip[SKIP_CONTEXTS][2];
u32 m_counts_mv_joint[MV_JOINTS];
u32 m_counts_mv_sign[2][2];
u32 m_counts_mv_class[2][MV_CLASSES];
u32 m_counts_mv_class0_bit[2][CLASS0_SIZE];
u32 m_counts_mv_class0_fr[2][CLASS0_SIZE][MV_FR_SIZE];
u32 m_counts_mv_class0_hp[2][2];
u32 m_counts_mv_bits[2][MV_OFFSET_BITS][2];
u32 m_counts_mv_fr[2][MV_FR_SIZE];
u32 m_counts_mv_hp[2][2];
u32 m_counts_token[TX_SIZES][BLOCK_TYPES][REF_TYPES][COEF_BANDS][PREV_COEF_CONTEXTS][UNCONSTRAINED_NODES];
u32 m_counts_more_coefs[TX_SIZES][BLOCK_TYPES][REF_TYPES][COEF_BANDS][PREV_COEF_CONTEXTS][2];
SyntaxElementCounter operator+(SyntaxElementCounter const&) const;
SyntaxElementCounter& operator+=(SyntaxElementCounter const&);
};
}

View file

@ -1,733 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Function.h>
#include "Context.h"
#include "Enums.h"
#include "LookupTables.h"
#include "Parser.h"
#include "TreeParser.h"
#include "Utilities.h"
namespace Media::Video::VP9 {
// Parsing of binary trees is handled here, as defined in sections 9.3.
// Each syntax element is defined in its own section for each overarching section listed here:
// - 9.3.1: Selection of the binary tree to be used.
// - 9.3.2: Probability selection based on context and often the node of the tree.
// - 9.3.4: Counting each syntax element when it is read.
class TreeSelection {
public:
union TreeSelectionValue {
int const* m_tree;
int m_value;
};
constexpr TreeSelection(int const* values)
: m_is_single_value(false)
, m_value { .m_tree = values }
{
}
constexpr TreeSelection(int value)
: m_is_single_value(true)
, m_value { .m_value = value }
{
}
bool is_single_value() const { return m_is_single_value; }
int single_value() const { return m_value.m_value; }
int const* tree() const { return m_value.m_tree; }
private:
bool m_is_single_value;
TreeSelectionValue m_value;
};
template<typename OutputType>
inline OutputType parse_tree(BooleanDecoder& decoder, TreeSelection tree_selection, Function<u8(u8)> const& probability_getter)
{
// 9.3.3: The tree decoding function.
if (tree_selection.is_single_value())
return static_cast<OutputType>(tree_selection.single_value());
int const* tree = tree_selection.tree();
int n = 0;
do {
u8 node = n >> 1;
n = tree[n + decoder.read_bool(probability_getter(node))];
} while (n > 0);
return static_cast<OutputType>(-n);
}
Partition TreeParser::parse_partition(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, bool has_rows, bool has_columns, BlockSubsize block_subsize, u8 num_8x8, PartitionContextView above_partition_context, PartitionContextView left_partition_context, u32 row, u32 column, bool frame_is_intra)
{
// Tree array
TreeSelection tree = { PartitionSplit };
if (has_rows && has_columns)
tree = { partition_tree };
else if (has_rows)
tree = { rows_partition_tree };
else if (has_columns)
tree = { cols_partition_tree };
// Probability array
u32 above = 0;
u32 left = 0;
auto bsl = mi_width_log2_lookup[block_subsize];
auto block_offset = mi_width_log2_lookup[Block_64x64] - bsl;
for (auto i = 0; i < num_8x8; i++) {
above |= above_partition_context[column + i];
left |= left_partition_context[row + i];
}
above = (above & (1 << block_offset)) > 0;
left = (left & (1 << block_offset)) > 0;
auto context = bsl * 4 + left * 2 + above;
u8 const* probabilities = frame_is_intra ? probability_table.kf_partition_probs()[context] : probability_table.partition_probs()[context];
Function<u8(u8)> probability_getter = [&](u8 node) {
if (has_rows && has_columns)
return probabilities[node];
if (has_columns)
return probabilities[1];
return probabilities[2];
};
auto value = parse_tree<Partition>(decoder, tree, probability_getter);
counter.m_counts_partition[context][value]++;
return value;
}
PredictionMode TreeParser::parse_default_intra_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, BlockSubsize mi_size, FrameBlockContext above, FrameBlockContext left, Array<PredictionMode, 4> const& block_sub_modes, u8 index_x, u8 index_y)
{
// FIXME: This should use a struct for the above and left contexts.
// Tree
TreeSelection tree = { intra_mode_tree };
// Probabilities
PredictionMode above_mode, left_mode;
if (mi_size >= Block_8x8) {
above_mode = above.sub_modes[2];
left_mode = left.sub_modes[1];
} else {
if (index_y > 0)
above_mode = block_sub_modes[index_x];
else
above_mode = above.sub_modes[2 + index_x];
if (index_x > 0)
left_mode = block_sub_modes[index_y << 1];
else
left_mode = left.sub_modes[1 + (index_y << 1)];
}
u8 const* probabilities = probability_table.kf_y_mode_probs()[to_underlying(above_mode)][to_underlying(left_mode)];
auto value = parse_tree<PredictionMode>(decoder, tree, [&](u8 node) { return probabilities[node]; });
// Default intra mode is not counted.
return value;
}
PredictionMode TreeParser::parse_default_uv_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, PredictionMode y_mode)
{
// Tree
TreeSelection tree = { intra_mode_tree };
// Probabilities
u8 const* probabilities = probability_table.kf_uv_mode_prob()[to_underlying(y_mode)];
auto value = parse_tree<PredictionMode>(decoder, tree, [&](u8 node) { return probabilities[node]; });
// Default UV mode is not counted.
return value;
}
PredictionMode TreeParser::parse_intra_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, BlockSubsize mi_size)
{
// Tree
TreeSelection tree = { intra_mode_tree };
// Probabilities
auto context = size_group_lookup[mi_size];
u8 const* probabilities = probability_table.y_mode_probs()[context];
auto value = parse_tree<PredictionMode>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_intra_mode[context][to_underlying(value)]++;
return value;
}
PredictionMode TreeParser::parse_sub_intra_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter)
{
// Tree
TreeSelection tree = { intra_mode_tree };
// Probabilities
u8 const* probabilities = probability_table.y_mode_probs()[0];
auto value = parse_tree<PredictionMode>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_intra_mode[0][to_underlying(value)]++;
return value;
}
PredictionMode TreeParser::parse_uv_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, PredictionMode y_mode)
{
// Tree
TreeSelection tree = { intra_mode_tree };
// Probabilities
u8 const* probabilities = probability_table.uv_mode_probs()[to_underlying(y_mode)];
auto value = parse_tree<PredictionMode>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_uv_mode[to_underlying(y_mode)][to_underlying(value)]++;
return value;
}
u8 TreeParser::parse_segment_id(BooleanDecoder& decoder, Array<u8, 7> const& probabilities)
{
auto value = parse_tree<u8>(decoder, { segment_tree }, [&](u8 node) { return probabilities[node]; });
// Segment ID is not counted.
return value;
}
bool TreeParser::parse_segment_id_predicted(BooleanDecoder& decoder, Array<u8, 3> const& probabilities, u8 above_seg_pred_context, u8 left_seg_pred_context)
{
auto context = left_seg_pred_context + above_seg_pred_context;
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probabilities[context]; });
// Segment ID prediction is not counted.
return value;
}
PredictionMode TreeParser::parse_inter_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 mode_context_for_ref_frame_0)
{
// Tree
TreeSelection tree = { inter_mode_tree };
// Probabilities
u8 const* probabilities = probability_table.inter_mode_probs()[mode_context_for_ref_frame_0];
auto value = parse_tree<u8>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_inter_mode[mode_context_for_ref_frame_0][value]++;
return static_cast<PredictionMode>(value + to_underlying(PredictionMode::NearestMv));
}
InterpolationFilter TreeParser::parse_interpolation_filter(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left context should be provided by a struct.
// Tree
TreeSelection tree = { interp_filter_tree };
// Probabilities
// NOTE: SWITCHABLE_FILTERS is not used in the spec for this function. Therefore, the number
// was demystified by referencing the reference codec libvpx:
// https://github.com/webmproject/libvpx/blob/705bf9de8c96cfe5301451f1d7e5c90a41c64e5f/vp9/common/vp9_pred_common.h#L69
u8 left_interp = !left.is_intra_predicted() ? left.interpolation_filter : SWITCHABLE_FILTERS;
u8 above_interp = !above.is_intra_predicted() ? above.interpolation_filter : SWITCHABLE_FILTERS;
u8 context = SWITCHABLE_FILTERS;
if (above_interp == left_interp || above_interp == SWITCHABLE_FILTERS)
context = left_interp;
else if (left_interp == SWITCHABLE_FILTERS)
context = above_interp;
u8 const* probabilities = probability_table.interp_filter_probs()[context];
auto value = parse_tree<InterpolationFilter>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_interp_filter[context][to_underlying(value)]++;
return value;
}
bool TreeParser::parse_skip(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, FrameBlockContext above, FrameBlockContext left)
{
// Probabilities
u8 context = 0;
context += static_cast<u8>(above.skip_coefficients);
context += static_cast<u8>(left.skip_coefficients);
u8 probability = probability_table.skip_prob()[context];
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_skip[context][value]++;
return value;
}
TransformSize TreeParser::parse_tx_size(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, TransformSize max_tx_size, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Tree
TreeSelection tree { tx_size_8_tree };
if (max_tx_size == Transform_16x16)
tree = { tx_size_16_tree };
if (max_tx_size == Transform_32x32)
tree = { tx_size_32_tree };
// Probabilities
auto above_context = max_tx_size;
auto left_context = max_tx_size;
if (above.is_available && !above.skip_coefficients)
above_context = above.transform_size;
if (left.is_available && !left.skip_coefficients)
left_context = left.transform_size;
if (!left.is_available)
left_context = above_context;
if (!above.is_available)
above_context = left_context;
auto context = (above_context + left_context) > max_tx_size;
u8 const* probabilities = probability_table.tx_probs()[max_tx_size][context];
auto value = parse_tree<TransformSize>(decoder, tree, [&](u8 node) { return probabilities[node]; });
counter.m_counts_tx_size[max_tx_size][context][value]++;
return value;
}
bool TreeParser::parse_block_is_inter_predicted(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Probabilities
u8 context = 0;
if (above.is_available && left.is_available)
context = (left.is_intra_predicted() && above.is_intra_predicted()) ? 3 : static_cast<u8>(above.is_intra_predicted() || left.is_intra_predicted());
else if (above.is_available || left.is_available)
context = 2 * static_cast<u8>(above.is_available ? above.is_intra_predicted() : left.is_intra_predicted());
u8 probability = probability_table.is_inter_prob()[context];
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_is_inter[context][value]++;
return value;
}
ReferenceMode TreeParser::parse_comp_mode(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, ReferenceFrameType comp_fixed_ref, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Probabilities
u8 context;
if (above.is_available && left.is_available) {
if (above.is_single_reference() && left.is_single_reference()) {
auto is_above_fixed = above.ref_frames.primary == comp_fixed_ref;
auto is_left_fixed = left.ref_frames.primary == comp_fixed_ref;
context = is_above_fixed ^ is_left_fixed;
} else if (above.is_single_reference()) {
auto is_above_fixed = above.ref_frames.primary == comp_fixed_ref;
context = 2 + static_cast<u8>(is_above_fixed || above.is_intra_predicted());
} else if (left.is_single_reference()) {
auto is_left_fixed = left.ref_frames.primary == comp_fixed_ref;
context = 2 + static_cast<u8>(is_left_fixed || left.is_intra_predicted());
} else {
context = 4;
}
} else if (above.is_available) {
if (above.is_single_reference())
context = above.ref_frames.primary == comp_fixed_ref;
else
context = 3;
} else if (left.is_available) {
if (left.is_single_reference())
context = static_cast<u8>(left.ref_frames.primary == comp_fixed_ref);
else
context = 3;
} else {
context = 1;
}
u8 probability = probability_table.comp_mode_prob()[context];
auto value = parse_tree<ReferenceMode>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_comp_mode[context][value]++;
return value;
}
ReferenceIndex TreeParser::parse_comp_ref(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, ReferenceFrameType comp_fixed_ref, ReferenceFramePair comp_var_ref, ReferenceIndex variable_reference_index, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Probabilities
u8 context;
if (above.is_available && left.is_available) {
if (above.is_intra_predicted() && left.is_intra_predicted()) {
context = 2;
} else if (left.is_intra_predicted()) {
if (above.is_single_reference()) {
context = 1 + 2 * (above.ref_frames.primary != comp_var_ref.secondary);
} else {
context = 1 + 2 * (above.ref_frames[variable_reference_index] != comp_var_ref.secondary);
}
} else if (above.is_intra_predicted()) {
if (left.is_single_reference()) {
context = 1 + 2 * (left.ref_frames.primary != comp_var_ref.secondary);
} else {
context = 1 + 2 * (left.ref_frames[variable_reference_index] != comp_var_ref.secondary);
}
} else {
auto var_ref_above = above.is_single_reference() ? above.ref_frames.primary : above.ref_frames[variable_reference_index];
auto var_ref_left = left.is_single_reference() ? left.ref_frames.primary : left.ref_frames[variable_reference_index];
if (var_ref_above == var_ref_left && comp_var_ref.secondary == var_ref_above) {
context = 0;
} else if (left.is_single_reference() && above.is_single_reference()) {
if ((var_ref_above == comp_fixed_ref && var_ref_left == comp_var_ref.primary)
|| (var_ref_left == comp_fixed_ref && var_ref_above == comp_var_ref.primary)) {
context = 4;
} else if (var_ref_above == var_ref_left) {
context = 3;
} else {
context = 1;
}
} else if (left.is_single_reference() || above.is_single_reference()) {
auto vrfc = left.is_single_reference() ? var_ref_above : var_ref_left;
auto rfs = above.is_single_reference() ? var_ref_above : var_ref_left;
if (vrfc == comp_var_ref.secondary && rfs != comp_var_ref.secondary) {
context = 1;
} else if (rfs == comp_var_ref.secondary && vrfc != comp_var_ref.secondary) {
context = 2;
} else {
context = 4;
}
} else if (var_ref_above == var_ref_left) {
context = 4;
} else {
context = 2;
}
}
} else if (above.is_available) {
if (above.is_intra_predicted()) {
context = 2;
} else {
if (above.is_single_reference()) {
context = 3 * static_cast<u8>(above.ref_frames.primary != comp_var_ref.secondary);
} else {
context = 4 * static_cast<u8>(above.ref_frames[variable_reference_index] != comp_var_ref.secondary);
}
}
} else if (left.is_available) {
if (left.is_intra_predicted()) {
context = 2;
} else {
if (left.is_single_reference()) {
context = 3 * static_cast<u8>(left.ref_frames.primary != comp_var_ref.secondary);
} else {
context = 4 * static_cast<u8>(left.ref_frames[variable_reference_index] != comp_var_ref.secondary);
}
}
} else {
context = 2;
}
u8 probability = probability_table.comp_ref_prob()[context];
auto value = parse_tree<ReferenceIndex>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_comp_ref[context][to_underlying(value)]++;
return value;
}
bool TreeParser::parse_single_ref_part_1(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Probabilities
u8 context;
if (above.is_available && left.is_available) {
if (above.is_intra_predicted() && left.is_intra_predicted()) {
context = 2;
} else if (left.is_intra_predicted()) {
if (above.is_single_reference()) {
context = 4 * (above.ref_frames.primary == ReferenceFrameType::LastFrame);
} else {
context = 1 + (above.ref_frames.primary == ReferenceFrameType::LastFrame || above.ref_frames.secondary == ReferenceFrameType::LastFrame);
}
} else if (above.is_intra_predicted()) {
if (left.is_single_reference()) {
context = 4 * (left.ref_frames.primary == ReferenceFrameType::LastFrame);
} else {
context = 1 + (left.ref_frames.primary == ReferenceFrameType::LastFrame || left.ref_frames.secondary == ReferenceFrameType::LastFrame);
}
} else {
if (left.is_single_reference() && above.is_single_reference()) {
context = 2 * (above.ref_frames.primary == ReferenceFrameType::LastFrame) + 2 * (left.ref_frames.primary == ReferenceFrameType::LastFrame);
} else if (!left.is_single_reference() && !above.is_single_reference()) {
auto above_used_last_frame = above.ref_frames.primary == ReferenceFrameType::LastFrame || above.ref_frames.secondary == ReferenceFrameType::LastFrame;
auto left_used_last_frame = left.ref_frames.primary == ReferenceFrameType::LastFrame || left.ref_frames.secondary == ReferenceFrameType::LastFrame;
context = 1 + (above_used_last_frame || left_used_last_frame);
} else {
auto single_reference_type = above.is_single_reference() ? above.ref_frames.primary : left.ref_frames.primary;
auto compound_reference_a_type = above.is_single_reference() ? left.ref_frames.primary : above.ref_frames.primary;
auto compound_reference_b_type = above.is_single_reference() ? left.ref_frames.secondary : above.ref_frames.secondary;
context = compound_reference_a_type == ReferenceFrameType::LastFrame || compound_reference_b_type == ReferenceFrameType::LastFrame;
if (single_reference_type == ReferenceFrameType::LastFrame)
context += 3;
}
}
} else if (above.is_available) {
if (above.is_intra_predicted()) {
context = 2;
} else {
if (above.is_single_reference()) {
context = 4 * (above.ref_frames.primary == ReferenceFrameType::LastFrame);
} else {
context = 1 + (above.ref_frames.primary == ReferenceFrameType::LastFrame || above.ref_frames.secondary == ReferenceFrameType::LastFrame);
}
}
} else if (left.is_available) {
if (left.is_intra_predicted()) {
context = 2;
} else {
if (left.is_single_reference()) {
context = 4 * (left.ref_frames.primary == ReferenceFrameType::LastFrame);
} else {
context = 1 + (left.ref_frames.primary == ReferenceFrameType::LastFrame || left.ref_frames.secondary == ReferenceFrameType::LastFrame);
}
}
} else {
context = 2;
}
u8 probability = probability_table.single_ref_prob()[context][0];
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_single_ref[context][0][value]++;
return value;
}
bool TreeParser::parse_single_ref_part_2(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, FrameBlockContext above, FrameBlockContext left)
{
// FIXME: Above and left contexts should be in structs.
// Probabilities
u8 context;
if (above.is_available && left.is_available) {
if (above.is_intra_predicted() && left.is_intra_predicted()) {
context = 2;
} else if (left.is_intra_predicted()) {
if (above.is_single_reference()) {
if (above.ref_frames.primary == ReferenceFrameType::LastFrame) {
context = 3;
} else {
context = 4 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame);
}
} else {
context = 1 + 2 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame || above.ref_frames.secondary == ReferenceFrameType::GoldenFrame);
}
} else if (above.is_intra_predicted()) {
if (left.is_single_reference()) {
if (left.ref_frames.primary == ReferenceFrameType::LastFrame) {
context = 3;
} else {
context = 4 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame);
}
} else {
context = 1 + 2 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame || left.ref_frames.secondary == ReferenceFrameType::GoldenFrame);
}
} else {
if (left.is_single_reference() && above.is_single_reference()) {
auto above_last = above.ref_frames.primary == ReferenceFrameType::LastFrame;
auto left_last = left.ref_frames.primary == ReferenceFrameType::LastFrame;
if (above_last && left_last) {
context = 3;
} else if (above_last) {
context = 4 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame);
} else if (left_last) {
context = 4 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame);
} else {
context = 2 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame) + 2 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame);
}
} else if (!left.is_single_reference() && !above.is_single_reference()) {
if (above.ref_frames.primary == left.ref_frames.primary && above.ref_frames.secondary == left.ref_frames.secondary) {
context = 3 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame || above.ref_frames.secondary == ReferenceFrameType::GoldenFrame);
} else {
context = 2;
}
} else {
auto single_reference_type = above.is_single_reference() ? above.ref_frames.primary : left.ref_frames.primary;
auto compound_reference_a_type = above.is_single_reference() ? left.ref_frames.primary : above.ref_frames.primary;
auto compound_reference_b_type = above.is_single_reference() ? left.ref_frames.secondary : above.ref_frames.secondary;
context = compound_reference_a_type == ReferenceFrameType::GoldenFrame || compound_reference_b_type == ReferenceFrameType::GoldenFrame;
if (single_reference_type == ReferenceFrameType::GoldenFrame) {
context += 3;
} else if (single_reference_type != ReferenceFrameType::AltRefFrame) {
context = 1 + (2 * context);
}
}
}
} else if (above.is_available) {
if (above.is_intra_predicted() || (above.ref_frames.primary == ReferenceFrameType::LastFrame && above.is_single_reference())) {
context = 2;
} else if (above.is_single_reference()) {
context = 4 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame);
} else {
context = 3 * (above.ref_frames.primary == ReferenceFrameType::GoldenFrame || above.ref_frames.secondary == ReferenceFrameType::GoldenFrame);
}
} else if (left.is_available) {
if (left.is_intra_predicted() || (left.ref_frames.primary == ReferenceFrameType::LastFrame && left.is_single_reference())) {
context = 2;
} else if (left.is_single_reference()) {
context = 4 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame);
} else {
context = 3 * (left.ref_frames.primary == ReferenceFrameType::GoldenFrame || left.ref_frames.secondary == ReferenceFrameType::GoldenFrame);
}
} else {
context = 2;
}
u8 probability = probability_table.single_ref_prob()[context][1];
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_single_ref[context][1][value]++;
return value;
}
MvJoint TreeParser::parse_motion_vector_joint(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter)
{
auto value = parse_tree<MvJoint>(decoder, { mv_joint_tree }, [&](u8 node) { return probability_table.mv_joint_probs()[node]; });
counter.m_counts_mv_joint[value]++;
return value;
}
bool TreeParser::parse_motion_vector_sign(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component)
{
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability_table.mv_sign_prob()[component]; });
counter.m_counts_mv_sign[component][value]++;
return value;
}
MvClass TreeParser::parse_motion_vector_class(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component)
{
// Spec doesn't mention node, but the probabilities table has an extra dimension
// so we will use node for that.
auto value = parse_tree<MvClass>(decoder, { mv_class_tree }, [&](u8 node) { return probability_table.mv_class_probs()[component][node]; });
counter.m_counts_mv_class[component][value]++;
return value;
}
bool TreeParser::parse_motion_vector_class0_bit(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component)
{
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability_table.mv_class0_bit_prob()[component]; });
counter.m_counts_mv_class0_bit[component][value]++;
return value;
}
u8 TreeParser::parse_motion_vector_class0_fr(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component, bool class_0_bit)
{
auto value = parse_tree<u8>(decoder, { mv_fr_tree }, [&](u8 node) { return probability_table.mv_class0_fr_probs()[component][class_0_bit][node]; });
counter.m_counts_mv_class0_fr[component][class_0_bit][value]++;
return value;
}
bool TreeParser::parse_motion_vector_class0_hp(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component, bool use_hp)
{
TreeSelection tree { 1 };
if (use_hp)
tree = { binary_tree };
auto value = parse_tree<bool>(decoder, tree, [&](u8) { return probability_table.mv_class0_hp_prob()[component]; });
counter.m_counts_mv_class0_hp[component][value]++;
return value;
}
bool TreeParser::parse_motion_vector_bit(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component, u8 bit_index)
{
auto value = parse_tree<bool>(decoder, { binary_tree }, [&](u8) { return probability_table.mv_bits_prob()[component][bit_index]; });
counter.m_counts_mv_bits[component][bit_index][value]++;
return value;
}
u8 TreeParser::parse_motion_vector_fr(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component)
{
auto value = parse_tree<u8>(decoder, { mv_fr_tree }, [&](u8 node) { return probability_table.mv_fr_probs()[component][node]; });
counter.m_counts_mv_fr[component][value]++;
return value;
}
bool TreeParser::parse_motion_vector_hp(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, u8 component, bool use_hp)
{
TreeSelection tree { 1 };
if (use_hp)
tree = { binary_tree };
auto value = parse_tree<u8>(decoder, tree, [&](u8) { return probability_table.mv_hp_prob()[component]; });
counter.m_counts_mv_hp[component][value]++;
return value;
}
TokensContext TreeParser::get_context_for_first_token(NonZeroTokensView above_non_zero_tokens, NonZeroTokensView left_non_zero_tokens_in_block, TransformSize transform_size, u8 plane, u32 sub_block_column, u32 sub_block_row, bool is_inter, u8 band)
{
u8 transform_size_in_sub_blocks = transform_size_to_sub_blocks(transform_size);
bool above_has_non_zero_tokens = false;
for (u8 x = 0; x < transform_size_in_sub_blocks && x < above_non_zero_tokens[plane].size() - sub_block_column; x++) {
if (above_non_zero_tokens[plane][sub_block_column + x]) {
above_has_non_zero_tokens = true;
break;
}
}
bool left_has_non_zero_tokens = false;
for (u8 y = 0; y < transform_size_in_sub_blocks && y < left_non_zero_tokens_in_block[plane].size() - sub_block_row; y++) {
if (left_non_zero_tokens_in_block[plane][sub_block_row + y]) {
left_has_non_zero_tokens = true;
break;
}
}
u8 context = above_has_non_zero_tokens + left_has_non_zero_tokens;
return TokensContext { transform_size, plane > 0, is_inter, band, context };
}
TokensContext TreeParser::get_context_for_other_tokens(Array<u8, 1024> token_cache, TransformSize transform_size, TransformSet transform_set, u8 plane, u16 token_position, bool is_inter, u8 band)
{
auto transform_size_in_pixels = sub_blocks_to_pixels(transform_size_to_sub_blocks(transform_size));
auto log2_of_transform_size = transform_size + 2;
auto pixel_y = token_position >> log2_of_transform_size;
auto pixel_x = token_position - (pixel_y << log2_of_transform_size);
auto above_token_energy = pixel_y > 0 ? (pixel_y - 1) * transform_size_in_pixels + pixel_x : 0;
auto left_token_energy = pixel_y * transform_size_in_pixels + pixel_x - 1;
u32 neighbor_a, neighbor_b;
if (pixel_y > 0 && pixel_x > 0) {
if (transform_set == TransformSet { TransformType::DCT, TransformType::ADST }) {
neighbor_a = above_token_energy;
neighbor_b = above_token_energy;
} else if (transform_set == TransformSet { TransformType::ADST, TransformType::DCT }) {
neighbor_a = left_token_energy;
neighbor_b = left_token_energy;
} else {
neighbor_a = above_token_energy;
neighbor_b = left_token_energy;
}
} else if (pixel_y > 0) {
neighbor_a = above_token_energy;
neighbor_b = above_token_energy;
} else {
neighbor_a = left_token_energy;
neighbor_b = left_token_energy;
}
u8 context = (1 + token_cache[neighbor_a] + token_cache[neighbor_b]) >> 1;
return TokensContext { transform_size, plane > 0, is_inter, band, context };
}
bool TreeParser::parse_more_coefficients(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, TokensContext const& context)
{
auto probability = probability_table.coef_probs()[context.m_tx_size][context.m_is_uv_plane][context.m_is_inter][context.m_band][context.m_context_index][0];
auto value = parse_tree<u8>(decoder, { binary_tree }, [&](u8) { return probability; });
counter.m_counts_more_coefs[context.m_tx_size][context.m_is_uv_plane][context.m_is_inter][context.m_band][context.m_context_index][value]++;
return value;
}
Token TreeParser::parse_token(BooleanDecoder& decoder, ProbabilityTables const& probability_table, SyntaxElementCounter& counter, TokensContext const& context)
{
Function<u8(u8)> probability_getter = [&](u8 node) -> u8 {
auto prob = probability_table.coef_probs()[context.m_tx_size][context.m_is_uv_plane][context.m_is_inter][context.m_band][context.m_context_index][min(2, 1 + node)];
if (node < 2)
return prob;
auto x = (prob - 1) / 2;
auto const& pareto_table = probability_table.pareto_table();
if ((prob & 1) != 0)
return pareto_table[x][node - 2];
return (pareto_table[x][node - 2] + pareto_table[x + 1][node - 2]) >> 1;
};
auto value = parse_tree<Token>(decoder, { token_tree }, probability_getter);
counter.m_counts_token[context.m_tx_size][context.m_is_uv_plane][context.m_is_inter][context.m_band][context.m_context_index][min(2, value)]++;
return value;
}
}

View file

@ -1,79 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "BooleanDecoder.h"
#include "ContextStorage.h"
#include "Enums.h"
#include "ProbabilityTables.h"
#include "SyntaxElementCounter.h"
namespace Media::Video::VP9 {
class Parser;
struct BlockContext;
struct FrameBlockContext;
struct TokensContext {
TransformSize m_tx_size;
bool m_is_uv_plane;
bool m_is_inter;
u8 m_band;
u8 m_context_index;
};
class TreeParser {
public:
static Partition parse_partition(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, bool has_rows, bool has_columns, BlockSubsize block_subsize, u8 num_8x8, PartitionContextView above_partition_context, PartitionContextView left_partition_context, u32 row, u32 column, bool frame_is_intra);
static PredictionMode parse_default_intra_mode(BooleanDecoder&, ProbabilityTables const&, BlockSubsize mi_size, FrameBlockContext above, FrameBlockContext left, Array<PredictionMode, 4> const& block_sub_modes, u8 index_x, u8 index_y);
static PredictionMode parse_default_uv_mode(BooleanDecoder&, ProbabilityTables const&, PredictionMode y_mode);
static PredictionMode parse_intra_mode(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, BlockSubsize mi_size);
static PredictionMode parse_sub_intra_mode(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&);
static PredictionMode parse_uv_mode(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, PredictionMode y_mode);
static u8 parse_segment_id(BooleanDecoder&, Array<u8, 7> const& probabilities);
static bool parse_segment_id_predicted(BooleanDecoder&, Array<u8, 3> const& probabilities, u8 above_seg_pred_context, u8 left_seg_pred_context);
static PredictionMode parse_inter_mode(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 mode_context_for_ref_frame_0);
static InterpolationFilter parse_interpolation_filter(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, FrameBlockContext above, FrameBlockContext left);
static bool parse_skip(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, FrameBlockContext above, FrameBlockContext left);
static TransformSize parse_tx_size(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, TransformSize max_tx_size, FrameBlockContext above, FrameBlockContext left);
static bool parse_block_is_inter_predicted(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, FrameBlockContext above, FrameBlockContext left);
static ReferenceMode parse_comp_mode(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, ReferenceFrameType comp_fixed_ref, FrameBlockContext above, FrameBlockContext left);
static ReferenceIndex parse_comp_ref(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, ReferenceFrameType comp_fixed_ref, ReferenceFramePair comp_var_ref, ReferenceIndex variable_reference_index, FrameBlockContext above, FrameBlockContext left);
static bool parse_single_ref_part_1(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, FrameBlockContext above, FrameBlockContext left);
static bool parse_single_ref_part_2(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, FrameBlockContext above, FrameBlockContext left);
static MvJoint parse_motion_vector_joint(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&);
static bool parse_motion_vector_sign(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component);
static MvClass parse_motion_vector_class(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component);
static bool parse_motion_vector_class0_bit(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component);
static u8 parse_motion_vector_class0_fr(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component, bool class_0_bit);
static bool parse_motion_vector_class0_hp(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component, bool use_hp);
static bool parse_motion_vector_bit(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component, u8 bit_index);
static u8 parse_motion_vector_fr(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component);
static bool parse_motion_vector_hp(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, u8 component, bool use_hp);
static TokensContext get_context_for_first_token(NonZeroTokensView above_non_zero_tokens, NonZeroTokensView left_non_zero_tokens, TransformSize transform_size, u8 plane, u32 sub_block_column, u32 sub_block_row, bool is_inter, u8 band);
static TokensContext get_context_for_other_tokens(Array<u8, 1024> token_cache, TransformSize transform_size, TransformSet transform_set, u8 plane, u16 token_position, bool is_inter, u8 band);
static bool parse_more_coefficients(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, TokensContext const& context);
static Token parse_token(BooleanDecoder&, ProbabilityTables const&, SyntaxElementCounter&, TokensContext const& context);
};
struct PartitionTreeContext {
bool has_rows;
bool has_columns;
BlockSubsize block_subsize;
u8 num_8x8;
Vector<u8> const& above_partition_context;
Vector<u8> const& left_partition_context;
u32 row;
u32 column;
bool frame_is_intra;
};
}

View file

@ -1,133 +0,0 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <LibGfx/Size.h>
#include "LookupTables.h"
namespace Media::Video::VP9 {
// FIXME: Once everything is working, replace this with plain clamp
// since parameter order is different
template<typename T>
T clip_3(T x, T y, T z)
{
return clamp(z, x, y);
}
template<typename T>
u16 clip_1(u8 bit_depth, T x)
{
if (x < 0) {
return 0u;
}
T const max = (1u << bit_depth) - 1u;
if (x > max)
return max;
return x;
}
template<u8 bits>
inline u8 brev(u8 value)
{
static_assert(bits <= 8, "brev() expects an 8-bit value.");
static constexpr auto lookup_table = [] {
constexpr size_t value_count = 1 << bits;
Array<u8, value_count> the_table;
for (u8 lookup_value = 0; lookup_value < value_count; lookup_value++) {
u8 reversed = 0;
for (u8 bit_index = 0; bit_index < bits; bit_index++) {
auto bit = (lookup_value >> bit_index) & 1;
reversed |= bit << (bits - 1 - bit_index);
}
the_table[lookup_value] = reversed;
}
return the_table;
}();
return lookup_table[value];
}
inline BlockSubsize get_subsampled_block_size(BlockSubsize size, bool subsampling_x, bool subsampling_y)
{
return ss_size_lookup[size < Block_8x8 ? Block_8x8 : size][subsampling_x][subsampling_y];
}
inline Gfx::Size<u8> block_size_to_blocks(BlockSubsize size)
{
return Gfx::Size<u8>(num_8x8_blocks_wide_lookup[size], num_8x8_blocks_high_lookup[size]);
}
inline Gfx::Size<u8> block_size_to_sub_blocks(BlockSubsize size)
{
return Gfx::Size<u8>(num_4x4_blocks_wide_lookup[size], num_4x4_blocks_high_lookup[size]);
}
template<Integral T>
inline T blocks_to_superblocks(T blocks)
{
return blocks >> 3;
}
template<Integral T>
inline T superblocks_to_blocks(T superblocks)
{
return superblocks << 3;
}
template<Integral T>
inline T blocks_ceiled_to_superblocks(T blocks)
{
return blocks_to_superblocks(blocks + 7);
}
template<Integral T>
inline T blocks_to_sub_blocks(T blocks)
{
return blocks << 1;
}
template<Integral T>
inline T sub_blocks_to_blocks(T sub_blocks)
{
return sub_blocks >> 1;
}
template<Integral T>
inline T sub_blocks_to_pixels(T sub_blocks)
{
return sub_blocks << 2;
}
template<Integral T>
inline T pixels_to_sub_blocks(T pixels)
{
return pixels >> 2;
}
template<Integral T>
inline T blocks_to_pixels(T blocks)
{
return sub_blocks_to_pixels(blocks_to_sub_blocks(blocks));
}
template<Integral T>
inline T pixels_to_blocks(T pixels)
{
return sub_blocks_to_blocks(pixels_to_sub_blocks(pixels));
}
inline u8 transform_size_to_sub_blocks(TransformSize transform_size)
{
return 1 << transform_size;
}
}