1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 04:27:45 +00:00

LibVideo: Scaffold LibVideo and implement simplistic Matroska parser

This commit initializes the LibVideo library and implements parsing
basic Matroska container files. Currently, it will only parse audio
and video tracks.
This commit is contained in:
FalseHonesty 2021-06-05 16:06:55 -04:00 committed by Andreas Kling
parent 6a15bd06cb
commit 403bb07443
7 changed files with 840 additions and 0 deletions

View file

@ -0,0 +1,169 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "MatroskaDocument.h"
#include <AK/Debug.h>
#include <AK/NonnullOwnPtrVector.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <math.h>
namespace Video {
class MatroskaReader {
public:
MatroskaReader(const u8* data, size_t size)
: m_streamer(data, size)
{
}
static OwnPtr<MatroskaDocument> parse_matroska_from_file(const StringView& path);
static OwnPtr<MatroskaDocument> parse_matroska_from_data(const u8*, size_t);
OwnPtr<MatroskaDocument> parse();
private:
class Streamer {
public:
Streamer(const u8* data, size_t size)
: m_data_ptr(data)
, m_size_remaining(size)
{
}
const u8* data() { return m_data_ptr; }
const char* data_as_chars() { return reinterpret_cast<const char*>(m_data_ptr); }
u8 read_octet()
{
VERIFY(m_size_remaining >= 1);
m_size_remaining--;
m_octets_read.last()++;
return *(m_data_ptr++);
}
i16 read_i16()
{
return (read_octet() << 8) | read_octet();
}
size_t octets_read() { return m_octets_read.last(); }
void push_octets_read() { m_octets_read.append(0); }
void pop_octets_read()
{
auto popped = m_octets_read.take_last();
if (!m_octets_read.is_empty())
m_octets_read.last() += popped;
}
Optional<u64> read_variable_size_integer(bool mask_length = true)
{
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading from offset {:p}", m_data_ptr);
auto length_descriptor = read_octet();
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading VINT, first byte is {:#02x}", length_descriptor);
if (length_descriptor == 0)
return {};
size_t length = 0;
while (length < 8) {
if (length_descriptor & (1u << (8 - length)))
break;
length++;
}
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading VINT of total length {}", length);
if (length > 8)
return {};
u64 result;
if (mask_length)
result = length_descriptor & ~(1u << (8 - length));
else
result = length_descriptor;
dbgln_if(MATROSKA_TRACE_DEBUG, "Beginning of VINT is {:#02x}", result);
for (size_t i = 1; i < length; i++) {
if (!has_octet()) {
dbgln_if(MATROSKA_TRACE_DEBUG, "Ran out of stream data");
return {};
}
u8 next_octet = read_octet();
dbgln_if(MATROSKA_TRACE_DEBUG, "Read octet of {:#02x}", next_octet);
result = (result << 8u) | next_octet;
dbgln_if(MATROSKA_TRACE_DEBUG, "New result is {:#010x}", result);
}
return result;
}
Optional<i64> read_variable_sized_signed_integer()
{
auto length_descriptor = read_octet();
if (length_descriptor == 0)
return {};
size_t length = 0;
while (length < 8) {
if (length_descriptor & (1u << (8 - length)))
break;
length++;
}
if (length > 8)
return {};
i64 result = length_descriptor & ~(1u << (8 - length));
for (size_t i = 1; i < length; i++) {
if (!has_octet()) {
return {};
}
u8 next_octet = read_octet();
result = (result << 8u) | next_octet;
}
result -= pow(2, length * 7 - 1) - 1;
return result;
}
void drop_octets(size_t num_octets)
{
VERIFY(m_size_remaining >= num_octets);
m_size_remaining -= num_octets;
m_octets_read.last() += num_octets;
m_data_ptr += num_octets;
}
bool at_end() const { return !m_size_remaining; }
bool has_octet() const { return m_size_remaining >= 1; }
size_t remaining() const { return m_size_remaining; }
void set_remaining(size_t remaining) { m_size_remaining = remaining; }
private:
const u8* m_data_ptr { nullptr };
size_t m_size_remaining { 0 };
Vector<size_t> m_octets_read { 0 };
};
bool parse_master_element(const StringView& element_name, Function<bool(u64 element_id)> element_consumer);
Optional<EBMLHeader> parse_ebml_header();
bool parse_segment_elements(MatroskaDocument&);
OwnPtr<SegmentInformation> parse_information();
bool parse_tracks(MatroskaDocument&);
OwnPtr<TrackEntry> parse_track_entry();
Optional<TrackEntry::VideoTrack> parse_video_track_information();
Optional<TrackEntry::AudioTrack> parse_audio_track_information();
OwnPtr<Cluster> parse_cluster();
OwnPtr<Block> parse_simple_block();
Optional<String> read_string_element();
Optional<u64> read_u64_element();
bool read_unknown_element();
Streamer m_streamer;
};
}