mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 05:37:34 +00:00
LibVideo: Make Matroska element parsing functions static
Making these functions static makes it easier to implement lazy-loading since the parsing functions can now be called at any time. The functions were reorganized because they were not defined in the order they are called. However, instead of moving every function to that order, I've declared some but defined them further into the file, which allows the next commit's diff to be more readable.
This commit is contained in:
parent
be9de58932
commit
f4c476b26f
2 changed files with 211 additions and 219 deletions
|
@ -67,59 +67,40 @@ DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> Reader::parse_matroska_from_data
|
||||||
return reader.parse();
|
return reader.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> Reader::parse()
|
static DecoderErrorOr<void> parse_master_element(Streamer& streamer, [[maybe_unused]] StringView element_name, Function<DecoderErrorOr<void>(u64)> element_consumer)
|
||||||
{
|
{
|
||||||
auto first_element_id = TRY_READ(m_streamer.read_variable_size_integer(false));
|
auto element_data_size = TRY_READ(streamer.read_variable_size_integer());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "First element ID is {:#010x}\n", first_element_id);
|
|
||||||
if (first_element_id != EBML_MASTER_ELEMENT_ID)
|
|
||||||
return DecoderError::corrupted("First element was not an EBML header"sv);
|
|
||||||
|
|
||||||
auto header = TRY(parse_ebml_header());
|
|
||||||
dbgln_if(MATROSKA_DEBUG, "Parsed EBML header");
|
|
||||||
|
|
||||||
auto root_element_id = TRY_READ(m_streamer.read_variable_size_integer(false));
|
|
||||||
if (root_element_id != SEGMENT_ELEMENT_ID)
|
|
||||||
return DecoderError::corrupted("Second element was not a segment element"sv);
|
|
||||||
|
|
||||||
auto matroska_document = make<MatroskaDocument>(header);
|
|
||||||
TRY(parse_segment_elements(*matroska_document));
|
|
||||||
return matroska_document;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecoderErrorOr<void> Reader::parse_master_element([[maybe_unused]] StringView element_name, Function<DecoderErrorOr<void>(u64)> element_consumer)
|
|
||||||
{
|
|
||||||
auto element_data_size = TRY_READ(m_streamer.read_variable_size_integer());
|
|
||||||
dbgln_if(MATROSKA_DEBUG, "{} has {} octets of data.", element_name, element_data_size);
|
dbgln_if(MATROSKA_DEBUG, "{} has {} octets of data.", element_name, element_data_size);
|
||||||
|
|
||||||
m_streamer.push_octets_read();
|
streamer.push_octets_read();
|
||||||
while (m_streamer.octets_read() < element_data_size) {
|
while (streamer.octets_read() < element_data_size) {
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "====== Reading element ======");
|
dbgln_if(MATROSKA_TRACE_DEBUG, "====== Reading element ======");
|
||||||
auto element_id = TRY_READ(m_streamer.read_variable_size_integer(false));
|
auto element_id = TRY_READ(streamer.read_variable_size_integer(false));
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "{:s} element ID is {:#010x}\n", element_name, element_id);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "{:s} element ID is {:#010x}\n", element_name, element_id);
|
||||||
|
|
||||||
TRY(element_consumer(element_id));
|
TRY(element_consumer(element_id));
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read {} octets of the {} so far.", m_streamer.octets_read(), element_name);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read {} octets of the {} so far.", streamer.octets_read(), element_name);
|
||||||
}
|
}
|
||||||
m_streamer.pop_octets_read();
|
streamer.pop_octets_read();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<EBMLHeader> Reader::parse_ebml_header()
|
static DecoderErrorOr<EBMLHeader> parse_ebml_header(Streamer& streamer)
|
||||||
{
|
{
|
||||||
EBMLHeader header;
|
EBMLHeader header;
|
||||||
TRY(parse_master_element("Header"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "Header"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case DOCTYPE_ELEMENT_ID:
|
case DOCTYPE_ELEMENT_ID:
|
||||||
header.doc_type = TRY_READ(m_streamer.read_string());
|
header.doc_type = TRY_READ(streamer.read_string());
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read DocType attribute: {}", header.doc_type);
|
dbgln_if(MATROSKA_DEBUG, "Read DocType attribute: {}", header.doc_type);
|
||||||
break;
|
break;
|
||||||
case DOCTYPE_VERSION_ELEMENT_ID:
|
case DOCTYPE_VERSION_ELEMENT_ID:
|
||||||
header.doc_type_version = TRY_READ(m_streamer.read_u64());
|
header.doc_type_version = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read DocTypeVersion attribute: {}", header.doc_type_version);
|
dbgln_if(MATROSKA_DEBUG, "Read DocTypeVersion attribute: {}", header.doc_type_version);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -128,51 +109,74 @@ DecoderErrorOr<EBMLHeader> Reader::parse_ebml_header()
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<void> Reader::parse_segment_elements(MatroskaDocument& matroska_document)
|
static DecoderErrorOr<NonnullOwnPtr<SegmentInformation>> parse_information(Streamer& streamer);
|
||||||
|
static DecoderErrorOr<void> parse_tracks(Streamer& streamer, MatroskaDocument& matroska_document);
|
||||||
|
static DecoderErrorOr<NonnullOwnPtr<Cluster>> parse_cluster(Streamer& streamer);
|
||||||
|
|
||||||
|
static DecoderErrorOr<void> parse_segment_elements(Streamer& streamer, MatroskaDocument& matroska_document)
|
||||||
{
|
{
|
||||||
dbgln_if(MATROSKA_DEBUG, "Parsing segment elements");
|
dbgln_if(MATROSKA_DEBUG, "Parsing segment elements");
|
||||||
return parse_master_element("Segment"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
return parse_master_element(streamer, "Segment"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case SEGMENT_INFORMATION_ELEMENT_ID:
|
case SEGMENT_INFORMATION_ELEMENT_ID:
|
||||||
matroska_document.set_segment_information(TRY(parse_information()));
|
matroska_document.set_segment_information(TRY(parse_information(streamer)));
|
||||||
break;
|
break;
|
||||||
case TRACK_ELEMENT_ID:
|
case TRACK_ELEMENT_ID:
|
||||||
TRY(parse_tracks(matroska_document));
|
TRY(parse_tracks(streamer, matroska_document));
|
||||||
break;
|
break;
|
||||||
case CLUSTER_ELEMENT_ID:
|
case CLUSTER_ELEMENT_ID:
|
||||||
matroska_document.clusters().append(TRY(parse_cluster()));
|
matroska_document.clusters().append(TRY(parse_cluster(streamer)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<SegmentInformation>> Reader::parse_information()
|
DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> Reader::parse()
|
||||||
|
{
|
||||||
|
auto first_element_id = TRY_READ(m_streamer.read_variable_size_integer(false));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "First element ID is {:#010x}\n", first_element_id);
|
||||||
|
if (first_element_id != EBML_MASTER_ELEMENT_ID)
|
||||||
|
return DecoderError::corrupted("First element was not an EBML header"sv);
|
||||||
|
|
||||||
|
auto header = TRY(parse_ebml_header(m_streamer));
|
||||||
|
dbgln_if(MATROSKA_DEBUG, "Parsed EBML header");
|
||||||
|
|
||||||
|
auto root_element_id = TRY_READ(m_streamer.read_variable_size_integer(false));
|
||||||
|
if (root_element_id != SEGMENT_ELEMENT_ID)
|
||||||
|
return DecoderError::corrupted("Second element was not a segment element"sv);
|
||||||
|
|
||||||
|
auto matroska_document = make<MatroskaDocument>(header);
|
||||||
|
TRY(parse_segment_elements(m_streamer, *matroska_document));
|
||||||
|
return matroska_document;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DecoderErrorOr<NonnullOwnPtr<SegmentInformation>> parse_information(Streamer& streamer)
|
||||||
{
|
{
|
||||||
auto segment_information = make<SegmentInformation>();
|
auto segment_information = make<SegmentInformation>();
|
||||||
TRY(parse_master_element("Segment Information"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "Segment Information"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case TIMESTAMP_SCALE_ID:
|
case TIMESTAMP_SCALE_ID:
|
||||||
segment_information->set_timestamp_scale(TRY_READ(m_streamer.read_u64()));
|
segment_information->set_timestamp_scale(TRY_READ(streamer.read_u64()));
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read TimestampScale attribute: {}", segment_information->timestamp_scale());
|
dbgln_if(MATROSKA_DEBUG, "Read TimestampScale attribute: {}", segment_information->timestamp_scale());
|
||||||
break;
|
break;
|
||||||
case MUXING_APP_ID:
|
case MUXING_APP_ID:
|
||||||
segment_information->set_muxing_app(TRY_READ(m_streamer.read_string()));
|
segment_information->set_muxing_app(TRY_READ(streamer.read_string()));
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read MuxingApp attribute: {}", segment_information->muxing_app().as_string());
|
dbgln_if(MATROSKA_DEBUG, "Read MuxingApp attribute: {}", segment_information->muxing_app().as_string());
|
||||||
break;
|
break;
|
||||||
case WRITING_APP_ID:
|
case WRITING_APP_ID:
|
||||||
segment_information->set_writing_app(TRY_READ(m_streamer.read_string()));
|
segment_information->set_writing_app(TRY_READ(streamer.read_string()));
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read WritingApp attribute: {}", segment_information->writing_app().as_string());
|
dbgln_if(MATROSKA_DEBUG, "Read WritingApp attribute: {}", segment_information->writing_app().as_string());
|
||||||
break;
|
break;
|
||||||
case DURATION_ID:
|
case DURATION_ID:
|
||||||
segment_information->set_duration(TRY_READ(m_streamer.read_float()));
|
segment_information->set_duration(TRY_READ(streamer.read_float()));
|
||||||
dbgln_if(MATROSKA_DEBUG, "Read Duration attribute: {}", segment_information->duration().value());
|
dbgln_if(MATROSKA_DEBUG, "Read Duration attribute: {}", segment_information->duration().value());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -181,88 +185,30 @@ DecoderErrorOr<NonnullOwnPtr<SegmentInformation>> Reader::parse_information()
|
||||||
return segment_information;
|
return segment_information;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<void> Reader::parse_tracks(MatroskaDocument& matroska_document)
|
static DecoderErrorOr<TrackEntry::ColorFormat> parse_video_color_information(Streamer& streamer)
|
||||||
{
|
|
||||||
return parse_master_element("Tracks"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
|
||||||
if (element_id == TRACK_ENTRY_ID) {
|
|
||||||
dbgln_if(MATROSKA_DEBUG, "Parsing track");
|
|
||||||
auto track_entry = TRY(parse_track_entry());
|
|
||||||
auto track_number = track_entry->track_number();
|
|
||||||
dbgln_if(MATROSKA_DEBUG, "Adding track {} to document", track_number);
|
|
||||||
matroska_document.add_track(track_number, track_entry.release_nonnull<TrackEntry>());
|
|
||||||
} else {
|
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<TrackEntry>> Reader::parse_track_entry()
|
|
||||||
{
|
|
||||||
auto track_entry = make<TrackEntry>();
|
|
||||||
TRY(parse_master_element("Track"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
|
||||||
switch (element_id) {
|
|
||||||
case TRACK_NUMBER_ID:
|
|
||||||
track_entry->set_track_number(TRY_READ(m_streamer.read_u64()));
|
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackNumber attribute: {}", track_entry->track_number());
|
|
||||||
break;
|
|
||||||
case TRACK_UID_ID:
|
|
||||||
track_entry->set_track_uid(TRY_READ(m_streamer.read_u64()));
|
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackUID attribute: {}", track_entry->track_uid());
|
|
||||||
break;
|
|
||||||
case TRACK_TYPE_ID:
|
|
||||||
track_entry->set_track_type(static_cast<TrackEntry::TrackType>(TRY_READ(m_streamer.read_u64())));
|
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackType attribute: {}", track_entry->track_type());
|
|
||||||
break;
|
|
||||||
case TRACK_LANGUAGE_ID:
|
|
||||||
track_entry->set_language(TRY_READ(m_streamer.read_string()));
|
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's Language attribute: {}", track_entry->language());
|
|
||||||
break;
|
|
||||||
case TRACK_CODEC_ID:
|
|
||||||
track_entry->set_codec_id(TRY_READ(m_streamer.read_string()));
|
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecID attribute: {}", track_entry->codec_id());
|
|
||||||
break;
|
|
||||||
case TRACK_VIDEO_ID:
|
|
||||||
track_entry->set_video_track(TRY(parse_video_track_information()));
|
|
||||||
break;
|
|
||||||
case TRACK_AUDIO_ID:
|
|
||||||
track_entry->set_audio_track(TRY(parse_audio_track_information()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}));
|
|
||||||
|
|
||||||
return track_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecoderErrorOr<TrackEntry::ColorFormat> Reader::parse_video_color_information()
|
|
||||||
{
|
{
|
||||||
TrackEntry::ColorFormat color_format {};
|
TrackEntry::ColorFormat color_format {};
|
||||||
|
|
||||||
TRY(parse_master_element("Colour"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "Colour"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case PRIMARIES_ID:
|
case PRIMARIES_ID:
|
||||||
color_format.color_primaries = static_cast<ColorPrimaries>(TRY_READ(m_streamer.read_u64()));
|
color_format.color_primaries = static_cast<ColorPrimaries>(TRY_READ(streamer.read_u64()));
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's Primaries attribute: {}", color_primaries_to_string(color_format.color_primaries));
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's Primaries attribute: {}", color_primaries_to_string(color_format.color_primaries));
|
||||||
break;
|
break;
|
||||||
case TRANSFER_CHARACTERISTICS_ID:
|
case TRANSFER_CHARACTERISTICS_ID:
|
||||||
color_format.transfer_characteristics = static_cast<TransferCharacteristics>(TRY_READ(m_streamer.read_u64()));
|
color_format.transfer_characteristics = static_cast<TransferCharacteristics>(TRY_READ(streamer.read_u64()));
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's TransferCharacteristics attribute: {}", transfer_characteristics_to_string(color_format.transfer_characteristics));
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's TransferCharacteristics attribute: {}", transfer_characteristics_to_string(color_format.transfer_characteristics));
|
||||||
break;
|
break;
|
||||||
case MATRIX_COEFFICIENTS_ID:
|
case MATRIX_COEFFICIENTS_ID:
|
||||||
color_format.matrix_coefficients = static_cast<MatrixCoefficients>(TRY_READ(m_streamer.read_u64()));
|
color_format.matrix_coefficients = static_cast<MatrixCoefficients>(TRY_READ(streamer.read_u64()));
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's MatrixCoefficients attribute: {}", matrix_coefficients_to_string(color_format.matrix_coefficients));
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's MatrixCoefficients attribute: {}", matrix_coefficients_to_string(color_format.matrix_coefficients));
|
||||||
break;
|
break;
|
||||||
case BITS_PER_CHANNEL_ID:
|
case BITS_PER_CHANNEL_ID:
|
||||||
color_format.bits_per_channel = TRY_READ(m_streamer.read_u64());
|
color_format.bits_per_channel = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's BitsPerChannel attribute: {}", color_format.bits_per_channel);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's BitsPerChannel attribute: {}", color_format.bits_per_channel);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -271,25 +217,25 @@ DecoderErrorOr<TrackEntry::ColorFormat> Reader::parse_video_color_information()
|
||||||
return color_format;
|
return color_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<TrackEntry::VideoTrack> Reader::parse_video_track_information()
|
static DecoderErrorOr<TrackEntry::VideoTrack> parse_video_track_information(Streamer& streamer)
|
||||||
{
|
{
|
||||||
TrackEntry::VideoTrack video_track {};
|
TrackEntry::VideoTrack video_track {};
|
||||||
|
|
||||||
TRY(parse_master_element("VideoTrack"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "VideoTrack"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case PIXEL_WIDTH_ID:
|
case PIXEL_WIDTH_ID:
|
||||||
video_track.pixel_width = TRY_READ(m_streamer.read_u64());
|
video_track.pixel_width = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelWidth attribute: {}", video_track.pixel_width);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelWidth attribute: {}", video_track.pixel_width);
|
||||||
break;
|
break;
|
||||||
case PIXEL_HEIGHT_ID:
|
case PIXEL_HEIGHT_ID:
|
||||||
video_track.pixel_height = TRY_READ(m_streamer.read_u64());
|
video_track.pixel_height = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelHeight attribute: {}", video_track.pixel_height);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelHeight attribute: {}", video_track.pixel_height);
|
||||||
break;
|
break;
|
||||||
case COLOR_ENTRY_ID:
|
case COLOR_ENTRY_ID:
|
||||||
video_track.color_format = TRY(parse_video_color_information());
|
video_track.color_format = TRY(parse_video_color_information(streamer));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -298,22 +244,22 @@ DecoderErrorOr<TrackEntry::VideoTrack> Reader::parse_video_track_information()
|
||||||
return video_track;
|
return video_track;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<TrackEntry::AudioTrack> Reader::parse_audio_track_information()
|
static DecoderErrorOr<TrackEntry::AudioTrack> parse_audio_track_information(Streamer& streamer)
|
||||||
{
|
{
|
||||||
TrackEntry::AudioTrack audio_track {};
|
TrackEntry::AudioTrack audio_track {};
|
||||||
|
|
||||||
TRY(parse_master_element("AudioTrack"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "AudioTrack"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case CHANNELS_ID:
|
case CHANNELS_ID:
|
||||||
audio_track.channels = TRY_READ(m_streamer.read_u64());
|
audio_track.channels = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's Channels attribute: {}", audio_track.channels);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's Channels attribute: {}", audio_track.channels);
|
||||||
break;
|
break;
|
||||||
case BIT_DEPTH_ID:
|
case BIT_DEPTH_ID:
|
||||||
audio_track.bit_depth = TRY_READ(m_streamer.read_u64());
|
audio_track.bit_depth = TRY_READ(streamer.read_u64());
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's BitDepth attribute: {}", audio_track.bit_depth);
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's BitDepth attribute: {}", audio_track.bit_depth);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -322,20 +268,80 @@ DecoderErrorOr<TrackEntry::AudioTrack> Reader::parse_audio_track_information()
|
||||||
return audio_track;
|
return audio_track;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<Cluster>> Reader::parse_cluster()
|
static DecoderErrorOr<NonnullOwnPtr<TrackEntry>> parse_track_entry(Streamer& streamer)
|
||||||
|
{
|
||||||
|
auto track_entry = make<TrackEntry>();
|
||||||
|
TRY(parse_master_element(streamer, "Track"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
|
switch (element_id) {
|
||||||
|
case TRACK_NUMBER_ID:
|
||||||
|
track_entry->set_track_number(TRY_READ(streamer.read_u64()));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackNumber attribute: {}", track_entry->track_number());
|
||||||
|
break;
|
||||||
|
case TRACK_UID_ID:
|
||||||
|
track_entry->set_track_uid(TRY_READ(streamer.read_u64()));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackUID attribute: {}", track_entry->track_uid());
|
||||||
|
break;
|
||||||
|
case TRACK_TYPE_ID:
|
||||||
|
track_entry->set_track_type(static_cast<TrackEntry::TrackType>(TRY_READ(streamer.read_u64())));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackType attribute: {}", track_entry->track_type());
|
||||||
|
break;
|
||||||
|
case TRACK_LANGUAGE_ID:
|
||||||
|
track_entry->set_language(TRY_READ(streamer.read_string()));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's Language attribute: {}", track_entry->language());
|
||||||
|
break;
|
||||||
|
case TRACK_CODEC_ID:
|
||||||
|
track_entry->set_codec_id(TRY_READ(streamer.read_string()));
|
||||||
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecID attribute: {}", track_entry->codec_id());
|
||||||
|
break;
|
||||||
|
case TRACK_VIDEO_ID:
|
||||||
|
track_entry->set_video_track(TRY(parse_video_track_information(streamer)));
|
||||||
|
break;
|
||||||
|
case TRACK_AUDIO_ID:
|
||||||
|
track_entry->set_audio_track(TRY(parse_audio_track_information(streamer)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TRY_READ(streamer.read_unknown_element());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}));
|
||||||
|
|
||||||
|
return track_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DecoderErrorOr<void> parse_tracks(Streamer& streamer, MatroskaDocument& matroska_document)
|
||||||
|
{
|
||||||
|
return parse_master_element(streamer, "Tracks"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
|
if (element_id == TRACK_ENTRY_ID) {
|
||||||
|
dbgln_if(MATROSKA_DEBUG, "Parsing track");
|
||||||
|
auto track_entry = TRY(parse_track_entry(streamer));
|
||||||
|
auto track_number = track_entry->track_number();
|
||||||
|
dbgln_if(MATROSKA_DEBUG, "Adding track {} to document", track_number);
|
||||||
|
matroska_document.add_track(track_number, track_entry.release_nonnull<TrackEntry>());
|
||||||
|
} else {
|
||||||
|
TRY_READ(streamer.read_unknown_element());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static DecoderErrorOr<NonnullOwnPtr<Block>> parse_simple_block(Streamer& streamer);
|
||||||
|
|
||||||
|
static DecoderErrorOr<NonnullOwnPtr<Cluster>> parse_cluster(Streamer& streamer)
|
||||||
{
|
{
|
||||||
auto cluster = make<Cluster>();
|
auto cluster = make<Cluster>();
|
||||||
|
|
||||||
TRY(parse_master_element("Cluster"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
TRY(parse_master_element(streamer, "Cluster"sv, [&](u64 element_id) -> DecoderErrorOr<void> {
|
||||||
switch (element_id) {
|
switch (element_id) {
|
||||||
case SIMPLE_BLOCK_ID:
|
case SIMPLE_BLOCK_ID:
|
||||||
cluster->blocks().append(TRY(parse_simple_block()));
|
cluster->blocks().append(TRY(parse_simple_block(streamer)));
|
||||||
break;
|
break;
|
||||||
case TIMESTAMP_ID:
|
case TIMESTAMP_ID:
|
||||||
cluster->set_timestamp(TRY_READ(m_streamer.read_u64()));
|
cluster->set_timestamp(TRY_READ(streamer.read_u64()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRY_READ(m_streamer.read_unknown_element());
|
TRY_READ(streamer.read_unknown_element());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -344,40 +350,40 @@ DecoderErrorOr<NonnullOwnPtr<Cluster>> Reader::parse_cluster()
|
||||||
return cluster;
|
return cluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<Block>> Reader::parse_simple_block()
|
static DecoderErrorOr<NonnullOwnPtr<Block>> parse_simple_block(Streamer& streamer)
|
||||||
{
|
{
|
||||||
auto block = make<Block>();
|
auto block = make<Block>();
|
||||||
|
|
||||||
auto content_size = TRY_READ(m_streamer.read_variable_size_integer());
|
auto content_size = TRY_READ(streamer.read_variable_size_integer());
|
||||||
|
|
||||||
auto octets_read_before_track_number = m_streamer.octets_read();
|
auto octets_read_before_track_number = streamer.octets_read();
|
||||||
auto track_number = TRY_READ(m_streamer.read_variable_size_integer());
|
auto track_number = TRY_READ(streamer.read_variable_size_integer());
|
||||||
block->set_track_number(track_number);
|
block->set_track_number(track_number);
|
||||||
|
|
||||||
block->set_timestamp(TRY_READ(m_streamer.read_i16()));
|
block->set_timestamp(TRY_READ(streamer.read_i16()));
|
||||||
|
|
||||||
auto flags = TRY_READ(m_streamer.read_octet());
|
auto flags = TRY_READ(streamer.read_octet());
|
||||||
block->set_only_keyframes(flags & (1u << 7u));
|
block->set_only_keyframes((flags & (1u << 7u)) != 0);
|
||||||
block->set_invisible(flags & (1u << 3u));
|
block->set_invisible((flags & (1u << 3u)) != 0);
|
||||||
block->set_lacing(static_cast<Block::Lacing>((flags & 0b110u) >> 1u));
|
block->set_lacing(static_cast<Block::Lacing>((flags & 0b110u) >> 1u));
|
||||||
block->set_discardable(flags & 1u);
|
block->set_discardable((flags & 1u) != 0);
|
||||||
|
|
||||||
auto total_frame_content_size = content_size - (m_streamer.octets_read() - octets_read_before_track_number);
|
auto total_frame_content_size = content_size - (streamer.octets_read() - octets_read_before_track_number);
|
||||||
if (block->lacing() == Block::Lacing::EBML) {
|
if (block->lacing() == Block::Lacing::EBML) {
|
||||||
auto octets_read_before_frame_sizes = m_streamer.octets_read();
|
auto octets_read_before_frame_sizes = streamer.octets_read();
|
||||||
auto frame_count = TRY_READ(m_streamer.read_octet()) + 1;
|
auto frame_count = TRY_READ(streamer.read_octet()) + 1;
|
||||||
Vector<u64> frame_sizes;
|
Vector<u64> frame_sizes;
|
||||||
frame_sizes.ensure_capacity(frame_count);
|
frame_sizes.ensure_capacity(frame_count);
|
||||||
|
|
||||||
u64 frame_size_sum = 0;
|
u64 frame_size_sum = 0;
|
||||||
u64 previous_frame_size;
|
u64 previous_frame_size;
|
||||||
auto first_frame_size = TRY_READ(m_streamer.read_variable_size_integer());
|
auto first_frame_size = TRY_READ(streamer.read_variable_size_integer());
|
||||||
frame_sizes.append(first_frame_size);
|
frame_sizes.append(first_frame_size);
|
||||||
frame_size_sum += first_frame_size;
|
frame_size_sum += first_frame_size;
|
||||||
previous_frame_size = first_frame_size;
|
previous_frame_size = first_frame_size;
|
||||||
|
|
||||||
for (int i = 0; i < frame_count - 2; i++) {
|
for (int i = 0; i < frame_count - 2; i++) {
|
||||||
auto frame_size_difference = TRY_READ(m_streamer.read_variable_size_signed_integer());
|
auto frame_size_difference = TRY_READ(streamer.read_variable_size_signed_integer());
|
||||||
u64 frame_size;
|
u64 frame_size;
|
||||||
// FIXME: x - (-y) == x + y??
|
// FIXME: x - (-y) == x + y??
|
||||||
if (frame_size_difference < 0)
|
if (frame_size_difference < 0)
|
||||||
|
@ -388,29 +394,29 @@ DecoderErrorOr<NonnullOwnPtr<Block>> Reader::parse_simple_block()
|
||||||
frame_size_sum += frame_size;
|
frame_size_sum += frame_size;
|
||||||
previous_frame_size = frame_size;
|
previous_frame_size = frame_size;
|
||||||
}
|
}
|
||||||
frame_sizes.append(total_frame_content_size - frame_size_sum - (m_streamer.octets_read() - octets_read_before_frame_sizes));
|
frame_sizes.append(total_frame_content_size - frame_size_sum - (streamer.octets_read() - octets_read_before_frame_sizes));
|
||||||
|
|
||||||
for (int i = 0; i < frame_count; i++) {
|
for (int i = 0; i < frame_count; i++) {
|
||||||
// FIXME: ReadonlyBytes instead of copying the frame data?
|
// FIXME: ReadonlyBytes instead of copying the frame data?
|
||||||
auto current_frame_size = frame_sizes.at(i);
|
auto current_frame_size = frame_sizes.at(i);
|
||||||
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(m_streamer.data(), current_frame_size)));
|
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(streamer.data(), current_frame_size)));
|
||||||
TRY_READ(m_streamer.drop_octets(current_frame_size));
|
TRY_READ(streamer.drop_octets(current_frame_size));
|
||||||
}
|
}
|
||||||
} else if (block->lacing() == Block::Lacing::FixedSize) {
|
} else if (block->lacing() == Block::Lacing::FixedSize) {
|
||||||
auto frame_count = TRY_READ(m_streamer.read_octet()) + 1;
|
auto frame_count = TRY_READ(streamer.read_octet()) + 1;
|
||||||
auto individual_frame_size = total_frame_content_size / frame_count;
|
auto individual_frame_size = total_frame_content_size / frame_count;
|
||||||
for (int i = 0; i < frame_count; i++) {
|
for (int i = 0; i < frame_count; i++) {
|
||||||
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(m_streamer.data(), individual_frame_size)));
|
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(streamer.data(), individual_frame_size)));
|
||||||
TRY_READ(m_streamer.drop_octets(individual_frame_size));
|
TRY_READ(streamer.drop_octets(individual_frame_size));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(m_streamer.data(), total_frame_content_size)));
|
block->add_frame(DECODER_TRY_ALLOC(ByteBuffer::copy(streamer.data(), total_frame_content_size)));
|
||||||
TRY_READ(m_streamer.drop_octets(total_frame_content_size));
|
TRY_READ(streamer.drop_octets(total_frame_content_size));
|
||||||
}
|
}
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<String> Reader::Streamer::read_string()
|
ErrorOr<String> Streamer::read_string()
|
||||||
{
|
{
|
||||||
auto string_length = TRY(read_variable_size_integer());
|
auto string_length = TRY(read_variable_size_integer());
|
||||||
if (remaining() < string_length)
|
if (remaining() < string_length)
|
||||||
|
@ -420,7 +426,7 @@ ErrorOr<String> Reader::Streamer::read_string()
|
||||||
return string_value;
|
return string_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u8> Reader::Streamer::read_octet()
|
ErrorOr<u8> Streamer::read_octet()
|
||||||
{
|
{
|
||||||
if (!has_octet()) {
|
if (!has_octet()) {
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Ran out of stream data");
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Ran out of stream data");
|
||||||
|
@ -432,12 +438,12 @@ ErrorOr<u8> Reader::Streamer::read_octet()
|
||||||
return byte;
|
return byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<i16> Reader::Streamer::read_i16()
|
ErrorOr<i16> Streamer::read_i16()
|
||||||
{
|
{
|
||||||
return (TRY(read_octet()) << 8) | TRY(read_octet());
|
return (TRY(read_octet()) << 8) | TRY(read_octet());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u64> Reader::Streamer::read_variable_size_integer(bool mask_length)
|
ErrorOr<u64> Streamer::read_variable_size_integer(bool mask_length)
|
||||||
{
|
{
|
||||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading from offset {:p}", data());
|
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading from offset {:p}", data());
|
||||||
auto length_descriptor = TRY(read_octet());
|
auto length_descriptor = TRY(read_octet());
|
||||||
|
@ -469,7 +475,7 @@ ErrorOr<u64> Reader::Streamer::read_variable_size_integer(bool mask_length)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<i64> Reader::Streamer::read_variable_size_signed_integer()
|
ErrorOr<i64> Streamer::read_variable_size_signed_integer()
|
||||||
{
|
{
|
||||||
auto length_descriptor = TRY(read_octet());
|
auto length_descriptor = TRY(read_octet());
|
||||||
if (length_descriptor == 0)
|
if (length_descriptor == 0)
|
||||||
|
@ -492,7 +498,7 @@ ErrorOr<i64> Reader::Streamer::read_variable_size_signed_integer()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Reader::Streamer::drop_octets(size_t num_octets)
|
ErrorOr<void> Streamer::drop_octets(size_t num_octets)
|
||||||
{
|
{
|
||||||
if (remaining() < num_octets)
|
if (remaining() < num_octets)
|
||||||
return Error::from_string_literal("Tried to drop octets past the end of the stream");
|
return Error::from_string_literal("Tried to drop octets past the end of the stream");
|
||||||
|
@ -501,7 +507,7 @@ ErrorOr<void> Reader::Streamer::drop_octets(size_t num_octets)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u64> Reader::Streamer::read_u64()
|
ErrorOr<u64> Streamer::read_u64()
|
||||||
{
|
{
|
||||||
auto integer_length = TRY(read_variable_size_integer());
|
auto integer_length = TRY(read_variable_size_integer());
|
||||||
u64 result = 0;
|
u64 result = 0;
|
||||||
|
@ -511,7 +517,7 @@ ErrorOr<u64> Reader::Streamer::read_u64()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<double> Reader::Streamer::read_float()
|
ErrorOr<double> Streamer::read_float()
|
||||||
{
|
{
|
||||||
auto length = TRY(read_variable_size_integer());
|
auto length = TRY(read_variable_size_integer());
|
||||||
if (length != 4u && length != 8u)
|
if (length != 4u && length != 8u)
|
||||||
|
@ -531,7 +537,7 @@ ErrorOr<double> Reader::Streamer::read_float()
|
||||||
return read_data.double_value;
|
return read_data.double_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Reader::Streamer::read_unknown_element()
|
ErrorOr<void> Streamer::read_unknown_element()
|
||||||
{
|
{
|
||||||
auto element_length = TRY(read_variable_size_integer());
|
auto element_length = TRY(read_variable_size_integer());
|
||||||
return drop_octets(element_length);
|
return drop_octets(element_length);
|
||||||
|
|
|
@ -18,21 +18,8 @@
|
||||||
|
|
||||||
namespace Video::Matroska {
|
namespace Video::Matroska {
|
||||||
|
|
||||||
class Reader {
|
class Streamer {
|
||||||
public:
|
public:
|
||||||
Reader(ReadonlyBytes data)
|
|
||||||
: m_streamer(data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse_matroska_from_file(StringView path);
|
|
||||||
static DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse_matroska_from_data(ReadonlyBytes data);
|
|
||||||
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse();
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Streamer {
|
|
||||||
public:
|
|
||||||
Streamer(ReadonlyBytes data)
|
Streamer(ReadonlyBytes data)
|
||||||
: m_data(data)
|
: m_data(data)
|
||||||
{
|
{
|
||||||
|
@ -74,26 +61,25 @@ private:
|
||||||
|
|
||||||
size_t remaining() const { return m_data.size() - m_position; }
|
size_t remaining() const { return m_data.size() - m_position; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ReadonlyBytes m_data;
|
ReadonlyBytes m_data;
|
||||||
size_t m_position { 0 };
|
size_t m_position { 0 };
|
||||||
Vector<size_t> m_octets_read { 0 };
|
Vector<size_t> m_octets_read { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
DecoderErrorOr<void> parse_master_element(StringView element_name, Function<DecoderErrorOr<void>(u64 element_id)> element_consumer);
|
class Reader {
|
||||||
DecoderErrorOr<EBMLHeader> parse_ebml_header();
|
public:
|
||||||
|
Reader(ReadonlyBytes data)
|
||||||
|
: m_streamer(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DecoderErrorOr<void> parse_segment_elements(MatroskaDocument&);
|
static DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse_matroska_from_file(StringView path);
|
||||||
DecoderErrorOr<NonnullOwnPtr<SegmentInformation>> parse_information();
|
static DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse_matroska_from_data(ReadonlyBytes data);
|
||||||
|
|
||||||
DecoderErrorOr<void> parse_tracks(MatroskaDocument&);
|
DecoderErrorOr<NonnullOwnPtr<MatroskaDocument>> parse();
|
||||||
DecoderErrorOr<NonnullOwnPtr<TrackEntry>> parse_track_entry();
|
|
||||||
DecoderErrorOr<TrackEntry::VideoTrack> parse_video_track_information();
|
|
||||||
DecoderErrorOr<TrackEntry::ColorFormat> parse_video_color_information();
|
|
||||||
DecoderErrorOr<TrackEntry::AudioTrack> parse_audio_track_information();
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<Cluster>> parse_cluster();
|
|
||||||
DecoderErrorOr<NonnullOwnPtr<Block>> parse_simple_block();
|
|
||||||
|
|
||||||
|
private:
|
||||||
Streamer m_streamer;
|
Streamer m_streamer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue