diff --git a/Userland/Libraries/LibVideo/CMakeLists.txt b/Userland/Libraries/LibVideo/CMakeLists.txt index ac59a71380..743fbc693b 100644 --- a/Userland/Libraries/LibVideo/CMakeLists.txt +++ b/Userland/Libraries/LibVideo/CMakeLists.txt @@ -1,6 +1,7 @@ set(SOURCES MatroskaDocument.h MatroskaReader.cpp + VP9/BitStream.cpp VP9/Enums.h VP9/ProbabilityTables.cpp VP9/Symbols.h diff --git a/Userland/Libraries/LibVideo/VP9/BitStream.cpp b/Userland/Libraries/LibVideo/VP9/BitStream.cpp new file mode 100644 index 0000000000..f51cdbf25a --- /dev/null +++ b/Userland/Libraries/LibVideo/VP9/BitStream.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021, Hunter Salyer + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "BitStream.h" + +namespace Video::VP9 { + +u8 BitStream::read_byte() +{ + VERIFY(m_bytes_remaining >= 1); + m_bytes_remaining--; + return *(m_data_ptr++); +} + +bool BitStream::read_bit() +{ + if (!m_current_byte.has_value()) { + m_current_byte = read_byte(); + m_current_bit_position = 7; + } + + bool bit_value = m_current_byte.value() & (1u << m_current_bit_position); + if (--m_current_bit_position < 0) + m_current_byte.clear(); + return bit_value; +} + +u8 BitStream::read_f(size_t n) +{ + u8 result = 0; + for (size_t i = 0; i < n; i++) { + result = (2 * result) + read_bit(); + } + return result; +} + +i8 BitStream::read_s(size_t n) +{ + auto value = read_f(n); + auto sign = read_bit(); + return sign ? -value : value; +} + +u8 BitStream::read_f8() +{ + if (!m_current_byte.has_value()) + return read_byte(); + + auto high_bits = m_current_byte.value() & ((1u << m_current_bit_position) - 1); + u8 remaining_bits = 7 - m_current_bit_position; + m_current_byte = read_byte(); + m_current_bit_position = 7; + auto low_bits = (m_current_byte.value() >> (8u - remaining_bits)) & ((1u << remaining_bits) - 1); + m_current_bit_position -= remaining_bits; + return (high_bits << remaining_bits) | low_bits; +} + +u16 BitStream::read_f16() +{ + return (read_f8() << 8u) | read_f8(); +} + +u8 BitStream::read_literal(size_t n) +{ + u8 return_value = 0; + for (size_t i = 0; i < n; i++) { + return_value = (2 * return_value) + read_bool(128); + } + return return_value; +} + +u64 BitStream::get_position() +{ + return (m_bytes_read * 8) + (7 - m_current_bit_position); +} + +size_t BitStream::bytes_remaining() +{ + return m_bytes_remaining; +} + +size_t BitStream::bits_remaining() +{ + return (bytes_remaining() * 8) + m_current_bit_position + 1; +} + +/* 9.2.1 */ +bool BitStream::init_bool(size_t bytes) +{ + if (bytes < 1) + return false; + m_bool_value = read_f8(); + m_bool_range = 255; + m_bool_max_bits = (8 * bytes) - 8; + return !read_bool(128); +} + +/* 9.2.2 */ +bool BitStream::read_bool(u8 probability) +{ + auto split = 1u + (((m_bool_range - 1u) * probability) >> 8u); + bool return_bool; + + if (m_bool_value < split) { + m_bool_range = split; + return_bool = false; + } else { + m_bool_range -= split; + m_bool_value -= split; + return_bool = true; + } + + while (m_bool_range < 128) { + bool new_bit; + if (m_bool_max_bits) { + new_bit = read_bit(); + m_bool_max_bits--; + } else { + new_bit = false; + } + m_bool_range *= 2; + m_bool_value = (m_bool_value << 1u) + new_bit; + } + + return return_bool; +} + +/* 9.2.3 */ +bool BitStream::exit_bool() +{ + // FIXME: I'm not sure if this call to min is spec compliant, or if there is an issue elsewhere earlier in the parser. + auto padding_element = read_f(min(m_bool_max_bits, (u64)bits_remaining())); + + // FIXME: It is a requirement of bitstream conformance that enough padding bits are inserted to ensure that the final coded byte of a frame is not equal to a superframe marker. + // A byte b is equal to a superframe marker if and only if (b & 0xe0)is equal to 0xc0, i.e. if the most significant 3 bits are equal to 0b110. + return padding_element == 0; +} + +} diff --git a/Userland/Libraries/LibVideo/VP9/BitStream.h b/Userland/Libraries/LibVideo/VP9/BitStream.h new file mode 100644 index 0000000000..2cfc1e52d8 --- /dev/null +++ b/Userland/Libraries/LibVideo/VP9/BitStream.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Hunter Salyer + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Video::VP9 { + +class BitStream { +public: + BitStream(const u8* data, size_t size) + : m_data_ptr(data) + , m_bytes_remaining(size) + { + } + + u8 read_byte(); + bool read_bit(); + u8 read_f(size_t n); + i8 read_s(size_t n); + u8 read_f8(); + u16 read_f16(); + u8 read_literal(size_t n); + + u64 get_position(); + size_t bytes_remaining(); + size_t bits_remaining(); + + bool init_bool(size_t bytes); + bool read_bool(u8 probability); + bool exit_bool(); + +private: + const u8* m_data_ptr { nullptr }; + size_t m_bytes_remaining { 0 }; + Optional m_current_byte; + i8 m_current_bit_position { 0 }; + u64 m_bytes_read { 0 }; + + u8 m_bool_value { 0 }; + u8 m_bool_range { 0 }; + u64 m_bool_max_bits { 0 }; +}; + +}