mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 06:07:34 +00:00
LibVideo/VP9: Implement a bit stream to decode VP9 data
The VP9 specification requires a special decoding process to parse a lot of the data read from a frame, so this BitStream wrapper implements that behavior. These processes are defined in section 9 of the VP9 spec.
This commit is contained in:
parent
18759ff56d
commit
2e31b9cf7c
3 changed files with 193 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
MatroskaDocument.h
|
MatroskaDocument.h
|
||||||
MatroskaReader.cpp
|
MatroskaReader.cpp
|
||||||
|
VP9/BitStream.cpp
|
||||||
VP9/Enums.h
|
VP9/Enums.h
|
||||||
VP9/ProbabilityTables.cpp
|
VP9/ProbabilityTables.cpp
|
||||||
VP9/Symbols.h
|
VP9/Symbols.h
|
||||||
|
|
142
Userland/Libraries/LibVideo/VP9/BitStream.cpp
Normal file
142
Userland/Libraries/LibVideo/VP9/BitStream.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
Userland/Libraries/LibVideo/VP9/BitStream.h
Normal file
50
Userland/Libraries/LibVideo/VP9/BitStream.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
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<u8> 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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue