LibMedia: Change demuxers to use IncrementallyPopulatedStream as input

Refactor the FFmpeg and Matroska demuxers to consume data through
`IncrementallyPopulatedStream::Cursor` instead of a pointer to fully
buffered.

This change establishes a new rule: each track must be initialized with
its own cursor. Data providers now explicitly create a per-track context
via `Demuxer::create_context_for_track(track, cursor)`, and own pointer
to that cursor. In the upcoming changes, holding the cursor in the
provider would allow to signal "cancel blocking reads" so an
in-flight seek can fail immediately when a newer seek request arrives.
This commit is contained in:
Aliaksandr Kalenik 2025-12-12 13:15:14 +01:00 committed by Gregory Bertilson
parent b9db157cea
commit c5d8cb5c47
22 changed files with 304 additions and 294 deletions

View file

@ -188,10 +188,10 @@ public:
bool discardable() const { return m_discardable; }
void set_discardable(bool discardable) { m_discardable = discardable; }
void set_frames(Vector<ReadonlyBytes>&& frames) { m_frames = move(frames); }
ReadonlyBytes const& frame(size_t index) const { return frames()[index]; }
void set_frames(Vector<ByteBuffer>&& frames) { m_frames = move(frames); }
ByteBuffer const& frame(size_t index) const { return frames()[index]; }
u64 frame_count() const { return m_frames.size(); }
Vector<ReadonlyBytes> const& frames() const { return m_frames; }
Vector<ByteBuffer> const& frames() const { return m_frames; }
private:
u64 m_track_number { 0 };
@ -201,7 +201,7 @@ private:
bool m_invisible { false };
Lacing m_lacing { None };
bool m_discardable { true };
Vector<ReadonlyBytes> m_frames;
Vector<ByteBuffer> m_frames;
};
class Cluster {

View file

@ -10,14 +10,15 @@
#include <LibMedia/CodedFrame.h>
#include <LibMedia/Containers/Matroska/Utilities.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include "MatroskaDemuxer.h"
namespace Media::Matroska {
DecoderErrorOr<NonnullRefPtr<MatroskaDemuxer>> MatroskaDemuxer::from_data(ReadonlyBytes data)
DecoderErrorOr<NonnullRefPtr<MatroskaDemuxer>> MatroskaDemuxer::from_stream(IncrementallyPopulatedStream::Cursor& stream_cursor)
{
return make_ref_counted<MatroskaDemuxer>(TRY(Reader::from_data(data)));
return make_ref_counted<MatroskaDemuxer>(TRY(Reader::from_stream(stream_cursor)));
}
static TrackEntry::TrackType matroska_track_type_from_track_type(TrackType type)
@ -82,6 +83,12 @@ static Track track_from_track_entry(TrackEntry const& track_entry)
return track;
}
void MatroskaDemuxer::create_context_for_track(Track const& track, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor)
{
auto iterator = MUST(m_reader.create_sample_iterator(stream_cursor, track.identifier()));
VERIFY(m_track_statuses.set(track, TrackStatus(move(iterator))) == HashSetResult::InsertedNewEntry);
}
DecoderErrorOr<Vector<Track>> MatroskaDemuxer::get_tracks_for_type(TrackType type)
{
auto matroska_track_type = matroska_track_type_from_track_type(type);
@ -108,11 +115,6 @@ DecoderErrorOr<Optional<Track>> MatroskaDemuxer::get_preferred_track_for_type(Tr
DecoderErrorOr<MatroskaDemuxer::TrackStatus*> MatroskaDemuxer::get_track_status(Track const& track)
{
if (!m_track_statuses.contains(track)) {
auto iterator = TRY(m_reader.create_sample_iterator(track.identifier()));
DECODER_TRY_ALLOC(m_track_statuses.try_set(track, TrackStatus(move(iterator))));
}
return &m_track_statuses.get(track).release_value();
}

View file

@ -9,6 +9,7 @@
#include <AK/HashMap.h>
#include <LibMedia/Demuxer.h>
#include <LibMedia/Export.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include "Reader.h"
@ -16,13 +17,15 @@ namespace Media::Matroska {
class MEDIA_API MatroskaDemuxer final : public Demuxer {
public:
static DecoderErrorOr<NonnullRefPtr<MatroskaDemuxer>> from_data(ReadonlyBytes data);
static DecoderErrorOr<NonnullRefPtr<MatroskaDemuxer>> from_stream(IncrementallyPopulatedStream::Cursor&);
MatroskaDemuxer(Reader&& reader)
: m_reader(move(reader))
{
}
virtual void create_context_for_track(Track const&, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&) override;
DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType) override;
DecoderErrorOr<Optional<Track>> get_preferred_track_for_type(TrackType) override;

View file

@ -12,7 +12,6 @@
#include <AK/Optional.h>
#include <AK/Time.h>
#include <AK/Utf8View.h>
#include <LibCore/MappedFile.h>
#include <LibMedia/CodecID.h>
#include <LibMedia/Containers/Matroska/Utilities.h>
@ -20,8 +19,6 @@
namespace Media::Matroska {
#define TRY_READ(expression) DECODER_TRY(DecoderErrorCategory::Corrupted, expression)
// RFC 8794 - Extensible Binary Meta Language
// https://datatracker.ietf.org/doc/html/rfc8794
constexpr u32 EBML_MASTER_ELEMENT_ID = 0x1A45DFA3;
@ -98,9 +95,9 @@ constexpr u32 CUE_RELATIVE_POSITION_ID = 0xF0;
constexpr u32 CUE_CODEC_STATE_ID = 0xEA;
constexpr u32 CUE_REFERENCE_ID = 0xDB;
DecoderErrorOr<Reader> Reader::from_data(ReadonlyBytes data)
DecoderErrorOr<Reader> Reader::from_stream(IncrementallyPopulatedStream::Cursor& stream_consumer)
{
Reader reader(data);
Reader reader(stream_consumer);
TRY(reader.parse_initial_data());
return reader;
}
@ -108,7 +105,7 @@ DecoderErrorOr<Reader> Reader::from_data(ReadonlyBytes data)
// Returns the position of the first element that is read from this master element.
static DecoderErrorOr<size_t> parse_master_element(Streamer& streamer, [[maybe_unused]] StringView element_name, Function<DecoderErrorOr<IterationDecision>(u64)> element_consumer)
{
auto element_data_size = TRY_READ(streamer.read_variable_size_integer());
auto element_data_size = TRY(streamer.read_variable_size_integer());
dbgln_if(MATROSKA_DEBUG, "{} has {} octets of data.", element_name, element_data_size);
bool first_element = true;
@ -117,7 +114,7 @@ static DecoderErrorOr<size_t> parse_master_element(Streamer& streamer, [[maybe_u
streamer.push_octets_read();
while (streamer.octets_read() < element_data_size) {
dbgln_if(MATROSKA_TRACE_DEBUG, "====== Reading element ======");
auto element_id = TRY_READ(streamer.read_variable_size_integer(false));
auto element_id = TRY(streamer.read_variable_size_integer(false));
dbgln_if(MATROSKA_TRACE_DEBUG, "{:s} element ID is {:#010x}", element_name, element_id);
if (element_id == EBML_CRC32_ELEMENT_ID) {
@ -140,13 +137,13 @@ static DecoderErrorOr<size_t> parse_master_element(Streamer& streamer, [[maybe_u
// will result in longer buffering times in streamed contexts, so it may not be
// worth the effort checking those. It would also prevent error correction in
// video codecs from taking effect.
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
continue;
}
if (element_id == EBML_VOID_ELEMENT_ID) {
// Used to void data or to avoid unexpected behaviors when using damaged data.
// The content is discarded. Also used to reserve space in a subelement for later use.
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
continue;
}
@ -170,15 +167,15 @@ static DecoderErrorOr<EBMLHeader> parse_ebml_header(Streamer& streamer)
TRY(parse_master_element(streamer, "Header"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case DOCTYPE_ELEMENT_ID:
header.doc_type = TRY_READ(streamer.read_string());
header.doc_type = TRY(streamer.read_string());
dbgln_if(MATROSKA_DEBUG, "Read DocType attribute: {}", header.doc_type);
break;
case DOCTYPE_VERSION_ELEMENT_ID:
header.doc_type_version = TRY_READ(streamer.read_u64());
header.doc_type_version = TRY(streamer.read_u64());
dbgln_if(MATROSKA_DEBUG, "Read DocTypeVersion attribute: {}", header.doc_type_version);
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -189,8 +186,8 @@ static DecoderErrorOr<EBMLHeader> parse_ebml_header(Streamer& streamer)
DecoderErrorOr<void> Reader::parse_initial_data()
{
Streamer streamer { m_data };
auto first_element_id = TRY_READ(streamer.read_variable_size_integer(false));
Streamer streamer { m_stream_cursor };
auto first_element_id = TRY(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);
@ -198,14 +195,13 @@ DecoderErrorOr<void> Reader::parse_initial_data()
m_header = TRY(parse_ebml_header(streamer));
dbgln_if(MATROSKA_DEBUG, "Parsed EBML header");
auto root_element_id = TRY_READ(streamer.read_variable_size_integer(false));
auto root_element_id = TRY(streamer.read_variable_size_integer(false));
if (root_element_id != SEGMENT_ELEMENT_ID)
return DecoderError::corrupted("Second element was not a segment element"sv);
m_segment_contents_size = TRY_READ(streamer.read_variable_size_integer());
m_segment_contents_size = TRY(streamer.read_variable_size_integer());
m_segment_contents_position = streamer.position();
dbgln_if(MATROSKA_TRACE_DEBUG, "Segment is at {} with size {}, available size is {}", m_segment_contents_position, m_segment_contents_size, m_data.size() - m_segment_contents_position);
m_segment_contents_size = min(m_segment_contents_size, m_data.size() - m_segment_contents_position);
dbgln_if(MATROSKA_TRACE_DEBUG, "Segment is at {} with size {}, available size is {}", m_segment_contents_position, m_segment_contents_size, m_stream_cursor->size() - m_segment_contents_position);
return {};
}
@ -218,15 +214,15 @@ static DecoderErrorOr<void> parse_seek_head(Streamer& streamer, size_t base_posi
TRY(parse_master_element(streamer, "Seek"sv, [&](u64 seek_entry_child_id) -> DecoderErrorOr<IterationDecision> {
switch (seek_entry_child_id) {
case SEEK_ID_ELEMENT_ID:
seek_id = TRY_READ(streamer.read_u64());
seek_id = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Seek Element ID value {:#010x}", seek_id.value());
break;
case SEEK_POSITION_ELEMENT_ID:
seek_position = TRY_READ(streamer.read_u64());
seek_position = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Seek Position value {}", seek_position.value());
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -266,16 +262,16 @@ DecoderErrorOr<Optional<size_t>> Reader::find_first_top_level_element_with_id([[
return m_seek_entries.get(element_id).release_value();
}
Streamer streamer { m_data };
Streamer streamer { m_stream_cursor };
if (m_last_top_level_element_position != 0)
TRY_READ(streamer.seek_to_position(m_last_top_level_element_position));
TRY(streamer.seek_to_position(m_last_top_level_element_position));
else
TRY_READ(streamer.seek_to_position(m_segment_contents_position));
TRY(streamer.seek_to_position(m_segment_contents_position));
Optional<size_t> position;
while (streamer.position() < m_segment_contents_position + m_segment_contents_size) {
auto found_element_id = TRY_READ(streamer.read_variable_size_integer(false));
auto found_element_id = TRY(streamer.read_variable_size_integer(false));
auto found_element_position = streamer.position();
dbgln_if(MATROSKA_TRACE_DEBUG, "Found element ID {:#010x} with position {}.", found_element_id, found_element_position);
@ -292,9 +288,7 @@ DecoderErrorOr<Optional<size_t>> Reader::find_first_top_level_element_with_id([[
continue;
}
auto result = streamer.read_unknown_element();
if (result.is_error())
return DecoderError::format(DecoderErrorCategory::Corrupted, "While seeking to {}: {}", element_name, result.release_error().string_literal());
TRY(streamer.read_unknown_element());
m_last_top_level_element_position = streamer.position();
@ -317,23 +311,23 @@ static DecoderErrorOr<SegmentInformation> parse_information(Streamer& streamer)
TRY(parse_master_element(streamer, "Segment Information"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case TIMESTAMP_SCALE_ID:
segment_information.set_timestamp_scale(TRY_READ(streamer.read_u64()));
segment_information.set_timestamp_scale(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_DEBUG, "Read TimestampScale attribute: {}", segment_information.timestamp_scale());
break;
case MUXING_APP_ID:
segment_information.set_muxing_app(TRY_READ(streamer.read_string()));
segment_information.set_muxing_app(TRY(streamer.read_string()));
dbgln_if(MATROSKA_DEBUG, "Read MuxingApp attribute: {}", segment_information.muxing_app());
break;
case WRITING_APP_ID:
segment_information.set_writing_app(TRY_READ(streamer.read_string()));
segment_information.set_writing_app(TRY(streamer.read_string()));
dbgln_if(MATROSKA_DEBUG, "Read WritingApp attribute: {}", segment_information.writing_app());
break;
case DURATION_ID:
segment_information.set_duration_unscaled(TRY_READ(streamer.read_float()));
segment_information.set_duration_unscaled(TRY(streamer.read_float()));
dbgln_if(MATROSKA_DEBUG, "Read Duration attribute: {}", segment_information.duration_unscaled().value());
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -350,8 +344,8 @@ DecoderErrorOr<SegmentInformation> Reader::segment_information()
auto position = TRY(find_first_top_level_element_with_id("Segment Information"sv, SEGMENT_INFORMATION_ELEMENT_ID));
if (!position.has_value())
return DecoderError::corrupted("No Segment Information element found"sv);
Streamer streamer { m_data };
TRY_READ(streamer.seek_to_position(position.release_value()));
Streamer streamer { m_stream_cursor };
TRY(streamer.seek_to_position(position.release_value()));
m_segment_information = TRY(parse_information(streamer));
return m_segment_information.value();
}
@ -363,8 +357,8 @@ DecoderErrorOr<void> Reader::ensure_tracks_are_parsed()
auto position = TRY(find_first_top_level_element_with_id("Tracks"sv, TRACK_ELEMENT_ID));
if (!position.has_value())
return DecoderError::corrupted("No Tracks element found"sv);
Streamer streamer { m_data };
TRY_READ(streamer.seek_to_position(position.release_value()));
Streamer streamer { m_stream_cursor };
TRY(streamer.seek_to_position(position.release_value()));
TRY(parse_tracks(streamer));
return {};
}
@ -376,27 +370,27 @@ static DecoderErrorOr<TrackEntry::ColorFormat> parse_video_color_information(Str
TRY(parse_master_element(streamer, "Colour"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case PRIMARIES_ID:
color_format.color_primaries = static_cast<ColorPrimaries>(TRY_READ(streamer.read_u64()));
color_format.color_primaries = static_cast<ColorPrimaries>(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's Primaries attribute: {}", color_primaries_to_string(color_format.color_primaries));
break;
case TRANSFER_CHARACTERISTICS_ID:
color_format.transfer_characteristics = static_cast<TransferCharacteristics>(TRY_READ(streamer.read_u64()));
color_format.transfer_characteristics = static_cast<TransferCharacteristics>(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's TransferCharacteristics attribute: {}", transfer_characteristics_to_string(color_format.transfer_characteristics));
break;
case MATRIX_COEFFICIENTS_ID:
color_format.matrix_coefficients = static_cast<MatrixCoefficients>(TRY_READ(streamer.read_u64()));
color_format.matrix_coefficients = static_cast<MatrixCoefficients>(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's MatrixCoefficients attribute: {}", matrix_coefficients_to_string(color_format.matrix_coefficients));
break;
case RANGE_ID:
color_format.range = static_cast<TrackEntry::ColorRange>(TRY_READ(streamer.read_u64()));
color_format.range = static_cast<TrackEntry::ColorRange>(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's Range attribute: {}", to_underlying(color_format.range));
break;
case BITS_PER_CHANNEL_ID:
color_format.bits_per_channel = TRY_READ(streamer.read_u64());
color_format.bits_per_channel = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's BitsPerChannel attribute: {}", color_format.bits_per_channel);
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -412,18 +406,18 @@ static DecoderErrorOr<TrackEntry::VideoTrack> parse_video_track_information(Stre
TRY(parse_master_element(streamer, "VideoTrack"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case PIXEL_WIDTH_ID:
video_track.pixel_width = TRY_READ(streamer.read_u64());
video_track.pixel_width = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelWidth attribute: {}", video_track.pixel_width);
break;
case PIXEL_HEIGHT_ID:
video_track.pixel_height = TRY_READ(streamer.read_u64());
video_track.pixel_height = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelHeight attribute: {}", video_track.pixel_height);
break;
case COLOR_ENTRY_ID:
video_track.color_format = TRY(parse_video_color_information(streamer));
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -439,19 +433,19 @@ static DecoderErrorOr<TrackEntry::AudioTrack> parse_audio_track_information(Stre
TRY(parse_master_element(streamer, "AudioTrack"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case CHANNELS_ID:
audio_track.channels = TRY_READ(streamer.read_u64());
audio_track.channels = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's Channels attribute: {}", audio_track.channels);
break;
case SAMPLING_FREQUENCY_ID:
audio_track.sampling_frequency = TRY_READ(streamer.read_float());
audio_track.sampling_frequency = TRY(streamer.read_float());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's SamplingFrequency attribute: {}", audio_track.channels);
break;
case BIT_DEPTH_ID:
audio_track.bit_depth = TRY_READ(streamer.read_u64());
audio_track.bit_depth = TRY(streamer.read_u64());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read AudioTrack's BitDepth attribute: {}", audio_track.bit_depth);
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -466,57 +460,57 @@ static DecoderErrorOr<NonnullRefPtr<TrackEntry>> parse_track_entry(Streamer& str
TRY(parse_master_element(streamer, "Track"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case TRACK_NUMBER_ID:
track_entry->set_track_number(TRY_READ(streamer.read_u64()));
track_entry->set_track_number(TRY(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()));
track_entry->set_track_uid(TRY(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())));
track_entry->set_track_type(static_cast<TrackEntry::TrackType>(TRY(streamer.read_u64())));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read TrackType attribute: {}", to_underlying(track_entry->track_type()));
break;
case TRACK_NAME_ID:
track_entry->set_name(TRY_READ(streamer.read_string()));
track_entry->set_name(TRY(streamer.read_string()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's Name attribute: {}", track_entry->name());
break;
case TRACK_LANGUAGE_ID:
track_entry->set_language(TRY_READ(streamer.read_string()));
track_entry->set_language(TRY(streamer.read_string()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's Language attribute: {}", track_entry->language());
break;
case TRACK_LANGUAGE_BCP_47_ID:
track_entry->set_language_bcp_47(TRY_READ(streamer.read_string()));
track_entry->set_language_bcp_47(TRY(streamer.read_string()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's LanguageBCP47 attribute: {}", track_entry->language());
break;
case TRACK_CODEC_ID:
track_entry->set_codec_id(TRY_READ(streamer.read_string()));
track_entry->set_codec_id(TRY(streamer.read_string()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecID attribute: {}", track_entry->codec_id());
break;
case TRACK_CODEC_PRIVATE_ID: {
auto codec_private_data = TRY_READ(streamer.read_raw_octets(TRY_READ(streamer.read_variable_size_integer())));
auto codec_private_data = TRY(streamer.read_raw_octets(TRY(streamer.read_variable_size_integer())));
DECODER_TRY_ALLOC(track_entry->set_codec_private_data(codec_private_data));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecPrivateData element");
break;
}
case TRACK_CODEC_DELAY_ID:
track_entry->set_codec_delay(TRY_READ(streamer.read_u64()));
track_entry->set_codec_delay(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecDelay attribute: {}", track_entry->codec_delay());
break;
case TRACK_SEEK_PRE_ROLL_ID:
track_entry->set_seek_pre_roll(TRY_READ(streamer.read_u64()));
track_entry->set_seek_pre_roll(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's SeekPreRoll attribute: {}", track_entry->seek_pre_roll());
break;
case TRACK_TIMESTAMP_SCALE_ID:
track_entry->set_timestamp_scale(TRY_READ(streamer.read_float()));
track_entry->set_timestamp_scale(TRY(streamer.read_float()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's TrackTimestampScale attribute: {}", track_entry->timestamp_scale());
break;
case TRACK_OFFSET_ID:
track_entry->set_timestamp_offset(TRY_READ(streamer.read_variable_size_signed_integer()));
track_entry->set_timestamp_offset(TRY(streamer.read_variable_size_signed_integer()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's TrackOffset attribute: {}", track_entry->timestamp_offset());
break;
case TRACK_DEFAULT_DURATION_ID:
track_entry->set_default_duration(TRY_READ(streamer.read_u64()));
track_entry->set_default_duration(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's DefaultDuration attribute: {}", track_entry->default_duration());
break;
case TRACK_VIDEO_ID:
@ -526,7 +520,7 @@ static DecoderErrorOr<NonnullRefPtr<TrackEntry>> parse_track_entry(Streamer& str
track_entry->set_audio_track(TRY(parse_audio_track_information(streamer)));
break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -561,7 +555,7 @@ DecoderErrorOr<void> Reader::parse_tracks(Streamer& streamer)
dbgln_if(MATROSKA_DEBUG, "Parsed track {}", track_entry->track_number());
DECODER_TRY_ALLOC(m_tracks.try_set(track_entry->track_number(), track_entry));
} else {
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -703,10 +697,10 @@ static DecoderErrorOr<Cluster> parse_cluster(Streamer& streamer, u64 timestamp_s
auto first_element_position = TRY(parse_master_element(streamer, "Cluster"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case TIMESTAMP_ID:
timestamp = TRY_READ(streamer.read_u64());
timestamp = TRY(streamer.read_u64());
return IterationDecision::Break;
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
return IterationDecision::Continue;
@ -718,7 +712,7 @@ static DecoderErrorOr<Cluster> parse_cluster(Streamer& streamer, u64 timestamp_s
return DecoderError::corrupted("Cluster had no children"sv);
dbgln_if(MATROSKA_TRACE_DEBUG, "Seeking back to position {}", first_element_position);
TRY_READ(streamer.seek_to_position(first_element_position));
TRY(streamer.seek_to_position(first_element_position));
Cluster cluster;
cluster.set_timestamp(AK::Duration::from_nanoseconds(AK::clamp_to<i64>(timestamp.release_value() * timestamp_scale)));
@ -746,25 +740,25 @@ static AK::Duration block_timestamp_to_duration(AK::Duration cluster_timestamp,
return cluster_timestamp + AK::Duration::from_nanoseconds(timestamp_offset_in_cluster_offset.value());
}
static DecoderErrorOr<Vector<ReadonlyBytes>> parse_frames(Streamer& streamer, Block::Lacing lacing, size_t content_size)
static DecoderErrorOr<Vector<ByteBuffer>> parse_frames(Streamer& streamer, Block::Lacing lacing, size_t content_size)
{
Vector<ReadonlyBytes> frames;
Vector<ByteBuffer> frames;
if (lacing == Block::Lacing::EBML) {
auto octets_read_before_frame_sizes = streamer.octets_read();
auto frame_count = TRY_READ(streamer.read_octet()) + 1;
auto frame_count = TRY(streamer.read_octet()) + 1;
Vector<u64> frame_sizes;
frame_sizes.ensure_capacity(frame_count);
u64 frame_size_sum = 0;
u64 previous_frame_size;
auto first_frame_size = TRY_READ(streamer.read_variable_size_integer());
auto first_frame_size = TRY(streamer.read_variable_size_integer());
frame_sizes.append(first_frame_size);
frame_size_sum += first_frame_size;
previous_frame_size = first_frame_size;
for (int i = 0; i < frame_count - 2; i++) {
auto frame_size_difference = TRY_READ(streamer.read_variable_size_signed_integer());
auto frame_size_difference = TRY(streamer.read_variable_size_signed_integer());
u64 frame_size;
// FIXME: x - (-y) == x + y?
if (frame_size_difference < 0)
@ -780,17 +774,17 @@ static DecoderErrorOr<Vector<ReadonlyBytes>> parse_frames(Streamer& streamer, Bl
for (int i = 0; i < frame_count; i++) {
// FIXME: ReadonlyBytes instead of copying the frame data?
auto current_frame_size = frame_sizes.at(i);
frames.append(TRY_READ(streamer.read_raw_octets(current_frame_size)));
frames.append(TRY(streamer.read_raw_octets(current_frame_size)));
}
} else if (lacing == Block::Lacing::FixedSize) {
auto frame_count = TRY_READ(streamer.read_octet()) + 1;
auto frame_count = TRY(streamer.read_octet()) + 1;
auto individual_frame_size = content_size / frame_count;
for (int i = 0; i < frame_count; i++)
frames.append(TRY_READ(streamer.read_raw_octets(individual_frame_size)));
frames.append(TRY(streamer.read_raw_octets(individual_frame_size)));
} else if (lacing == Block::Lacing::XIPH) {
auto frames_start_position = streamer.octets_read();
auto frame_count_minus_one = TRY_READ(streamer.read_octet());
auto frame_count_minus_one = TRY(streamer.read_octet());
frames.ensure_capacity(frame_count_minus_one + 1);
auto frame_sizes = Vector<size_t>();
@ -798,7 +792,7 @@ static DecoderErrorOr<Vector<ReadonlyBytes>> parse_frames(Streamer& streamer, Bl
for (auto i = 0; i < frame_count_minus_one; i++) {
auto frame_size = 0;
while (true) {
auto octet = TRY_READ(streamer.read_octet());
auto octet = TRY(streamer.read_octet());
frame_size += octet;
if (octet < 255)
break;
@ -807,10 +801,10 @@ static DecoderErrorOr<Vector<ReadonlyBytes>> parse_frames(Streamer& streamer, Bl
}
for (auto i = 0; i < frame_count_minus_one; i++)
frames.append(TRY_READ(streamer.read_raw_octets(frame_sizes[i])));
frames.append(TRY_READ(streamer.read_raw_octets(content_size - (streamer.octets_read() - frames_start_position))));
frames.append(TRY(streamer.read_raw_octets(frame_sizes[i])));
frames.append(TRY(streamer.read_raw_octets(content_size - (streamer.octets_read() - frames_start_position))));
} else {
frames.append(TRY_READ(streamer.read_raw_octets(content_size)));
frames.append(TRY(streamer.read_raw_octets(content_size)));
}
return frames;
@ -827,15 +821,15 @@ static DecoderErrorOr<Block> parse_simple_block(Streamer& streamer, AK::Duration
Block block;
set_block_duration_to_default(block, track);
auto content_size = TRY_READ(streamer.read_variable_size_integer());
auto content_size = TRY(streamer.read_variable_size_integer());
auto position_before_track_number = streamer.position();
block.set_track_number(TRY_READ(streamer.read_variable_size_integer()));
block.set_track_number(TRY(streamer.read_variable_size_integer()));
auto timestamp_offset = TRY_READ(streamer.read_i16());
auto timestamp_offset = TRY(streamer.read_i16());
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, track, timestamp_offset));
auto flags = TRY_READ(streamer.read_octet());
auto flags = TRY(streamer.read_octet());
block.set_only_keyframes((flags & (1u << 7u)) != 0);
block.set_invisible((flags & (1u << 3u)) != 0);
block.set_lacing(static_cast<Block::Lacing>((flags & 0b110u) >> 1u));
@ -858,15 +852,15 @@ static DecoderErrorOr<Block> parse_block_group(Streamer& streamer, AK::Duration
if (parsed_a_block)
return DecoderError::with_description(DecoderErrorCategory::Corrupted, "Block group contained multiple blocks"sv);
auto content_size = TRY_READ(streamer.read_variable_size_integer());
auto content_size = TRY(streamer.read_variable_size_integer());
auto position_before_track_number = streamer.position();
block.set_track_number(TRY_READ(streamer.read_variable_size_integer()));
block.set_track_number(TRY(streamer.read_variable_size_integer()));
auto timestamp_offset = TRY_READ(streamer.read_i16());
auto timestamp_offset = TRY(streamer.read_i16());
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, track, timestamp_offset));
auto flags = TRY_READ(streamer.read_octet());
auto flags = TRY(streamer.read_octet());
block.set_invisible((flags & (1u << 3)) != 0);
block.set_lacing(static_cast<Block::Lacing>((flags & 0b110) >> 1u));
@ -875,7 +869,7 @@ static DecoderErrorOr<Block> parse_block_group(Streamer& streamer, AK::Duration
break;
}
case BLOCK_DURATION_ID: {
auto duration = TRY_READ(streamer.read_u64());
auto duration = TRY(streamer.read_u64());
auto duration_nanoseconds = Checked<i64>::saturating_mul(duration, segment_timestamp_scale);
if (track.timestamp_scale() != 1)
duration_nanoseconds = AK::clamp_to<i64>(static_cast<double>(duration_nanoseconds) * track.timestamp_scale());
@ -883,7 +877,7 @@ static DecoderErrorOr<Block> parse_block_group(Streamer& streamer, AK::Duration
break;
}
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
break;
}
@ -893,18 +887,17 @@ static DecoderErrorOr<Block> parse_block_group(Streamer& streamer, AK::Duration
return block;
}
DecoderErrorOr<SampleIterator> Reader::create_sample_iterator(u64 track_number)
DecoderErrorOr<SampleIterator> Reader::create_sample_iterator(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_consumer, u64 track_number)
{
auto optional_position = TRY(find_first_top_level_element_with_id("Cluster"sv, CLUSTER_ELEMENT_ID));
if (!optional_position.has_value())
return DecoderError::corrupted("No clusters are present in the segment"sv);
ReadonlyBytes segment_view = m_data.slice(m_segment_contents_position, m_segment_contents_size);
// We need to have the element ID included so that the iterator knows where it is.
auto position = optional_position.value() - get_element_id_size(CLUSTER_ELEMENT_ID) - m_segment_contents_position;
auto position = optional_position.value() - get_element_id_size(CLUSTER_ELEMENT_ID);
dbgln_if(MATROSKA_DEBUG, "Creating sample iterator starting at {} relative to segment at {}", position, m_segment_contents_position);
return SampleIterator(segment_view, TRY(track_for_track_number(track_number)), TRY(segment_information()).timestamp_scale(), position);
return SampleIterator(stream_consumer, TRY(track_for_track_number(track_number)), TRY(segment_information()).timestamp_scale(), m_segment_contents_position, position);
}
static DecoderErrorOr<CueTrackPosition> parse_cue_track_position(Streamer& streamer)
@ -913,31 +906,31 @@ static DecoderErrorOr<CueTrackPosition> parse_cue_track_position(Streamer& strea
bool had_cluster_position = false;
TRY_READ(parse_master_element(streamer, "CueTrackPositions"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
TRY(parse_master_element(streamer, "CueTrackPositions"sv, [&](u64 element_id) -> DecoderErrorOr<IterationDecision> {
switch (element_id) {
case CUE_TRACK_ID:
track_position.set_track_number(TRY_READ(streamer.read_u64()));
track_position.set_track_number(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read CueTrackPositions track number {}", track_position.track_number());
break;
case CUE_CLUSTER_POSITION_ID:
track_position.set_cluster_position(TRY_READ(streamer.read_u64()));
track_position.set_cluster_position(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read CueTrackPositions cluster position {}", track_position.cluster_position());
had_cluster_position = true;
break;
case CUE_RELATIVE_POSITION_ID:
track_position.set_block_offset(TRY_READ(streamer.read_u64()));
track_position.set_block_offset(TRY(streamer.read_u64()));
dbgln_if(MATROSKA_TRACE_DEBUG, "Read CueTrackPositions relative position {}", track_position.block_offset());
break;
case CUE_CODEC_STATE_ID:
// Mandatory in spec, but not present in files? 0 means use TrackEntry's codec state.
// FIXME: Do something with this value.
dbgln_if(MATROSKA_DEBUG, "Found CodecState, skipping");
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
break;
case CUE_REFERENCE_ID:
return DecoderError::not_implemented();
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
break;
}
@ -969,18 +962,18 @@ static DecoderErrorOr<CuePoint> parse_cue_point(Streamer& streamer, u64 timestam
// https://github.com/mozilla/nestegg/tree/ec6adfbbf979678e3058cc4695257366f39e290b/src/nestegg.c#L2411-L2416
// https://github.com/mozilla/nestegg/tree/ec6adfbbf979678e3058cc4695257366f39e290b/src/nestegg.c#L1383-L1392
// Other fields that specify Matroska Ticks may also use Segment Ticks instead, who knows :^(
auto timestamp = AK::Duration::from_nanoseconds(static_cast<i64>(TRY_READ(streamer.read_u64()) * timestamp_scale));
auto timestamp = AK::Duration::from_nanoseconds(static_cast<i64>(TRY(streamer.read_u64()) * timestamp_scale));
cue_point.set_timestamp(timestamp);
dbgln_if(MATROSKA_DEBUG, "Read CuePoint timestamp {}ms", cue_point.timestamp().to_milliseconds());
break;
}
case CUE_TRACK_POSITIONS_ID: {
auto track_position = TRY_READ(parse_cue_track_position(streamer));
auto track_position = TRY(parse_cue_track_position(streamer));
DECODER_TRY_ALLOC(cue_point.track_positions().try_set(track_position.track_number(), track_position));
break;
}
default:
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
break;
}
@ -1033,8 +1026,8 @@ DecoderErrorOr<void> Reader::ensure_cues_are_parsed()
auto position = TRY(find_first_top_level_element_with_id("Cues"sv, CUES_ID));
if (!position.has_value())
return DecoderError::corrupted("No Tracks element found"sv);
Streamer streamer { m_data };
TRY_READ(streamer.seek_to_position(position.release_value()));
Streamer streamer { m_stream_cursor };
TRY(streamer.seek_to_position(position.release_value()));
TRY(parse_cues(streamer));
m_cues_have_been_parsed = true;
return {};
@ -1133,7 +1126,7 @@ DecoderErrorOr<SampleIterator> Reader::seek_to_random_access_point(SampleIterato
if (!iterator.last_timestamp().has_value() || timestamp < iterator.last_timestamp().value()) {
// If the timestamp is before the iterator's current position, then we need to start from the beginning of the Segment.
iterator = TRY(create_sample_iterator(iterator.m_track->track_number()));
iterator = TRY(create_sample_iterator(iterator.m_stream_cursor, iterator.m_track->track_number()));
TRY(search_clusters_for_keyframe_before_timestamp(iterator, timestamp));
return iterator;
}
@ -1150,19 +1143,16 @@ DecoderErrorOr<Optional<Vector<CuePoint> const&>> Reader::cue_points_for_track(u
DecoderErrorOr<Block> SampleIterator::next_block()
{
if (m_position >= m_data.size())
return DecoderError::with_description(DecoderErrorCategory::EndOfStream, "Still at end of stream :^)"sv);
Streamer streamer { m_data };
TRY_READ(streamer.seek_to_position(m_position));
Streamer streamer { m_stream_cursor };
TRY(streamer.seek_to_position(m_position));
Optional<Block> block;
while (streamer.has_octet()) {
while (true) {
#if MATROSKA_TRACE_DEBUG
auto element_position = streamer.position();
#endif
auto element_id = TRY_READ(streamer.read_variable_size_integer(false));
auto element_id = TRY(streamer.read_variable_size_integer(false));
#if MATROSKA_TRACE_DEBUG
dbgln("Iterator found element with ID {:#010x} at offset {} within the segment.", element_id, element_position);
#endif
@ -1182,7 +1172,7 @@ DecoderErrorOr<Block> SampleIterator::next_block()
block = move(candidate_block);
} else {
dbgln_if(MATROSKA_TRACE_DEBUG, " Iterator is skipping unknown element with ID {:#010x}.", element_id);
TRY_READ(streamer.read_unknown_element());
TRY(streamer.read_unknown_element());
}
m_position = streamer.position();
@ -1192,19 +1182,17 @@ DecoderErrorOr<Block> SampleIterator::next_block()
}
}
m_last_timestamp.clear();
m_current_cluster.clear();
return DecoderError::with_description(DecoderErrorCategory::EndOfStream, "End of stream"sv);
VERIFY_NOT_REACHED();
}
DecoderErrorOr<void> SampleIterator::seek_to_cue_point(CuePoint const& cue_point)
{
// This is a private function. The position getter can return optional, but the caller should already know that this track has a position.
auto const& cue_position = cue_point.position_for_track(m_track->track_number()).release_value();
Streamer streamer { m_data };
TRY_READ(streamer.seek_to_position(cue_position.cluster_position()));
Streamer streamer { m_stream_cursor };
TRY(streamer.seek_to_position(m_segment_contents_position + cue_position.cluster_position()));
auto element_id = TRY_READ(streamer.read_variable_size_integer(false));
auto element_id = TRY(streamer.read_variable_size_integer(false));
if (element_id != CLUSTER_ELEMENT_ID)
return DecoderError::corrupted("Cue point's cluster position didn't point to a cluster"sv);
@ -1216,41 +1204,38 @@ DecoderErrorOr<void> SampleIterator::seek_to_cue_point(CuePoint const& cue_point
return {};
}
ErrorOr<String> Streamer::read_string()
DecoderErrorOr<String> Streamer::read_string()
{
auto string_length = TRY(read_variable_size_integer());
if (remaining() < string_length)
return Error::from_string_literal("String length extends past the end of the stream");
auto const* string_data = data_as_chars();
auto string_value = String::from_utf8(ReadonlyBytes(string_data, strnlen(string_data, string_length)));
TRY(read_raw_octets(string_length));
return string_value;
auto string_data = TRY(read_raw_octets(string_length));
auto const* string_data_raw = reinterpret_cast<char const*>(string_data.data());
auto string_value = String::from_utf8(ReadonlyBytes(string_data.data(), strnlen(string_data_raw, string_length)));
if (string_value.is_error())
return DecoderError::format(DecoderErrorCategory::Invalid, "String is not valid UTF-8");
return string_value.release_value();
}
ErrorOr<u8> Streamer::read_octet()
DecoderErrorOr<u8> Streamer::read_octet()
{
if (!has_octet()) {
dbgln_if(MATROSKA_TRACE_DEBUG, "Ran out of stream data");
return Error::from_string_literal("Stream is out of data");
}
u8 byte = *data();
u8 result;
Bytes bytes { &result, 1 };
TRY(m_stream_cursor->read_into(bytes));
m_octets_read.last()++;
m_position++;
return byte;
return bytes[0];
}
ErrorOr<i16> Streamer::read_i16()
DecoderErrorOr<i16> Streamer::read_i16()
{
return (TRY(read_octet()) << 8) | TRY(read_octet());
}
ErrorOr<u64> Streamer::read_variable_size_integer(bool mask_length)
DecoderErrorOr<u64> Streamer::read_variable_size_integer(bool mask_length)
{
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading VINT from offset {:p}", position());
auto length_descriptor = TRY(read_octet());
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading VINT, first byte is {:#02x}", length_descriptor);
if (length_descriptor == 0)
return Error::from_string_literal("read_variable_size_integer: Length descriptor has no terminating set bit");
return DecoderError::format(DecoderErrorCategory::Invalid, "read_variable_size_integer: Length descriptor has no terminating set bit");
size_t length = 0;
while (length < 8) {
if (((length_descriptor >> (8 - length)) & 1) == 1)
@ -1259,7 +1244,7 @@ ErrorOr<u64> Streamer::read_variable_size_integer(bool mask_length)
}
dbgln_if(MATROSKA_TRACE_DEBUG, "Reading VINT of total length {}", length);
if (length > 8)
return Error::from_string_literal("read_variable_size_integer: Length is too large");
return DecoderError::format(DecoderErrorCategory::Invalid, "read_variable_size_integer: Length is too large");
u64 result;
if (mask_length)
@ -1276,11 +1261,11 @@ ErrorOr<u64> Streamer::read_variable_size_integer(bool mask_length)
return result;
}
ErrorOr<i64> Streamer::read_variable_size_signed_integer()
DecoderErrorOr<i64> Streamer::read_variable_size_signed_integer()
{
auto length_descriptor = TRY(read_octet());
if (length_descriptor == 0)
return Error::from_string_literal("read_variable_sized_signed_integer: Length descriptor has no terminating set bit");
return DecoderError::format(DecoderErrorCategory::Invalid, "read_variable_sized_signed_integer: Length descriptor has no terminating set bit");
i64 length = 0;
while (length < 8) {
if (((length_descriptor >> (8 - length)) & 1) == 1)
@ -1288,7 +1273,7 @@ ErrorOr<i64> Streamer::read_variable_size_signed_integer()
length++;
}
if (length > 8)
return Error::from_string_literal("read_variable_size_integer: Length is too large");
return DecoderError::format(DecoderErrorCategory::Invalid, "read_variable_size_integer: Length is too large");
i64 result = length_descriptor & ~(1u << (8 - length));
for (i64 i = 1; i < length; i++) {
@ -1299,17 +1284,16 @@ ErrorOr<i64> Streamer::read_variable_size_signed_integer()
return result;
}
ErrorOr<ReadonlyBytes> Streamer::read_raw_octets(size_t num_octets)
DecoderErrorOr<ByteBuffer> Streamer::read_raw_octets(size_t num_octets)
{
if (remaining() < num_octets)
return Error::from_string_literal("Tried to drop octets past the end of the stream");
ReadonlyBytes result = { data(), num_octets };
m_position += num_octets;
auto result = MUST(ByteBuffer::create_uninitialized(num_octets));
auto bytes = result.bytes();
TRY(m_stream_cursor->read_into(bytes));
m_octets_read.last() += num_octets;
return result;
}
ErrorOr<u64> Streamer::read_u64()
DecoderErrorOr<u64> Streamer::read_u64()
{
auto integer_length = TRY(read_variable_size_integer());
u64 result = 0;
@ -1319,11 +1303,11 @@ ErrorOr<u64> Streamer::read_u64()
return result;
}
ErrorOr<double> Streamer::read_float()
DecoderErrorOr<double> Streamer::read_float()
{
auto length = TRY(read_variable_size_integer());
if (length != 4u && length != 8u)
return Error::from_string_literal("Float size must be 4 or 8 bytes");
return DecoderError::format(DecoderErrorCategory::Invalid, "Float size must be 4 or 8 bytes");
union {
u64 value;
@ -1339,20 +1323,18 @@ ErrorOr<double> Streamer::read_float()
return read_data.double_value;
}
ErrorOr<void> Streamer::read_unknown_element()
DecoderErrorOr<void> Streamer::read_unknown_element()
{
auto element_length = TRY(read_variable_size_integer());
dbgln_if(MATROSKA_TRACE_DEBUG, "Skipping unknown element of size {}.", element_length);
TRY(read_raw_octets(element_length));
TRY(m_stream_cursor->seek(element_length, IncrementallyPopulatedStream::Cursor::SeekMode::FromCurrentPosition));
m_octets_read.last() += element_length;
return {};
}
ErrorOr<void> Streamer::seek_to_position(size_t position)
DecoderErrorOr<void> Streamer::seek_to_position(size_t position)
{
if (position >= m_data.size())
return Error::from_string_literal("Attempted to seek past the end of the stream");
m_position = position;
return {};
return m_stream_cursor->seek(position, IncrementallyPopulatedStream::Cursor::SeekMode::SetPosition);
}
}

View file

@ -10,9 +10,9 @@
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <LibCore/MappedFile.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/Export.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include "Document.h"
@ -25,7 +25,7 @@ class MEDIA_API Reader {
public:
typedef Function<DecoderErrorOr<IterationDecision>(TrackEntry const&)> TrackEntryCallback;
static DecoderErrorOr<Reader> from_data(ReadonlyBytes data);
static DecoderErrorOr<Reader> from_stream(IncrementallyPopulatedStream::Cursor&);
EBMLHeader const& header() const { return m_header.value(); }
@ -36,14 +36,14 @@ public:
DecoderErrorOr<NonnullRefPtr<TrackEntry>> track_for_track_number(u64);
DecoderErrorOr<size_t> track_count();
DecoderErrorOr<SampleIterator> create_sample_iterator(u64 track_number);
DecoderErrorOr<SampleIterator> create_sample_iterator(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_consumer, u64 track_number);
DecoderErrorOr<SampleIterator> seek_to_random_access_point(SampleIterator, AK::Duration);
DecoderErrorOr<Optional<Vector<CuePoint> const&>> cue_points_for_track(u64 track_number);
DecoderErrorOr<bool> has_cues_for_track(u64 track_number);
private:
Reader(ReadonlyBytes data)
: m_data(data)
Reader(IncrementallyPopulatedStream::Cursor& stream_cursor)
: m_stream_cursor(stream_cursor)
{
}
@ -60,7 +60,7 @@ private:
DecoderErrorOr<void> ensure_cues_are_parsed();
DecoderErrorOr<void> seek_to_cue_for_timestamp(SampleIterator&, AK::Duration const&);
ReadonlyBytes m_data;
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
Optional<EBMLHeader> m_header;
@ -89,19 +89,21 @@ public:
private:
friend class Reader;
SampleIterator(ReadonlyBytes data, TrackEntry& track, u64 timestamp_scale, size_t position)
: m_data(data)
SampleIterator(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor, TrackEntry& track, u64 timestamp_scale, size_t segment_contents_position, size_t position)
: m_stream_cursor(stream_cursor)
, m_track(track)
, m_segment_timestamp_scale(timestamp_scale)
, m_segment_contents_position(segment_contents_position)
, m_position(position)
{
}
DecoderErrorOr<void> seek_to_cue_point(CuePoint const& cue_point);
ReadonlyBytes m_data;
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
NonnullRefPtr<TrackEntry> m_track;
u64 m_segment_timestamp_scale { 0 };
size_t m_segment_contents_position { 0 };
// Must always point to an element ID or the end of the stream.
size_t m_position { 0 };
@ -113,15 +115,11 @@ private:
class Streamer {
public:
Streamer(ReadonlyBytes data)
: m_data(data)
Streamer(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor)
: m_stream_cursor(stream_cursor)
{
}
u8 const* data() { return m_data.data() + m_position; }
char const* data_as_chars() { return reinterpret_cast<char const*>(data()); }
size_t octets_read() { return m_octets_read.last(); }
void push_octets_read() { m_octets_read.append(0); }
@ -133,33 +131,28 @@ public:
m_octets_read.last() += popped;
}
ErrorOr<u8> read_octet();
DecoderErrorOr<u8> read_octet();
ErrorOr<i16> read_i16();
DecoderErrorOr<i16> read_i16();
ErrorOr<u64> read_variable_size_integer(bool mask_length = true);
ErrorOr<i64> read_variable_size_signed_integer();
DecoderErrorOr<u64> read_variable_size_integer(bool mask_length = true);
DecoderErrorOr<i64> read_variable_size_signed_integer();
ErrorOr<u64> read_u64();
ErrorOr<double> read_float();
DecoderErrorOr<u64> read_u64();
DecoderErrorOr<double> read_float();
ErrorOr<String> read_string();
DecoderErrorOr<String> read_string();
ErrorOr<void> read_unknown_element();
DecoderErrorOr<void> read_unknown_element();
ErrorOr<ReadonlyBytes> read_raw_octets(size_t num_octets);
DecoderErrorOr<ByteBuffer> read_raw_octets(size_t num_octets);
size_t position() const { return m_position; }
size_t remaining() const { return m_data.size() - position(); }
size_t position() const { return m_stream_cursor->position(); }
bool at_end() const { return remaining() == 0; }
bool has_octet() const { return remaining() >= 1; }
ErrorOr<void> seek_to_position(size_t position);
DecoderErrorOr<void> seek_to_position(size_t position);
private:
ReadonlyBytes m_data;
size_t m_position { 0 };
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
Vector<size_t> m_octets_read { 0 };
};

View file

@ -10,6 +10,7 @@
#include <AK/EnumBits.h>
#include <AK/NonnullOwnPtr.h>
#include <LibCore/EventReceiver.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include "CodecID.h"
#include "CodedFrame.h"
@ -34,6 +35,8 @@ class Demuxer : public AtomicRefCounted<Demuxer> {
public:
virtual ~Demuxer() = default;
virtual void create_context_for_track(Track const&, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&) = 0;
virtual DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType) = 0;
// Returns the container's preferred track for a given track type. This must return a value if any track of the
// given type is present.

View file

@ -18,10 +18,8 @@ extern "C" {
namespace Media::FFmpeg {
FFmpegDemuxer::FFmpegDemuxer(ReadonlyBytes data, NonnullOwnPtr<SeekableStream>&& stream, NonnullOwnPtr<Media::FFmpeg::FFmpegIOContext>&& io_context)
: m_data(data)
, m_stream(move(stream))
, m_io_context(move(io_context))
FFmpegDemuxer::FFmpegDemuxer(NonnullOwnPtr<Media::FFmpeg::FFmpegIOContext>&& io_context)
: m_io_context(move(io_context))
{
}
@ -47,32 +45,34 @@ static DecoderErrorOr<void> initialize_format_context(AVFormatContext*& format_c
return {};
}
DecoderErrorOr<NonnullRefPtr<FFmpegDemuxer>> FFmpegDemuxer::from_data(ReadonlyBytes data)
DecoderErrorOr<NonnullRefPtr<FFmpegDemuxer>> FFmpegDemuxer::from_stream(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_consumer)
{
auto stream = DECODER_TRY_ALLOC(try_make<FixedMemoryStream>(data));
auto io_context = DECODER_TRY_ALLOC(Media::FFmpeg::FFmpegIOContext::create(*stream));
auto demuxer = DECODER_TRY_ALLOC(adopt_nonnull_ref_or_enomem(new (nothrow) FFmpegDemuxer(data, move(stream), move(io_context))));
auto io_context = DECODER_TRY_ALLOC(Media::FFmpeg::FFmpegIOContext::create(stream_consumer));
auto demuxer = DECODER_TRY_ALLOC(adopt_nonnull_ref_or_enomem(new (nothrow) FFmpegDemuxer(move(io_context))));
TRY(initialize_format_context(demuxer->m_format_context, *demuxer->m_io_context->avio_context()));
return demuxer;
}
void FFmpegDemuxer::create_context_for_track(Track const& track, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor)
{
auto io_context = MUST(Media::FFmpeg::FFmpegIOContext::create(stream_cursor));
auto track_context = make<TrackContext>(move(io_context));
// We've already initialized a format context, so the only way this can fail is OOM.
MUST(initialize_format_context(track_context->format_context, *track_context->io_context->avio_context()));
track_context->packet = av_packet_alloc();
VERIFY(track_context->packet != nullptr);
VERIFY(m_track_contexts.set(track, move(track_context)) == HashSetResult::InsertedNewEntry);
}
FFmpegDemuxer::TrackContext& FFmpegDemuxer::get_track_context(Track const& track)
{
return *m_track_contexts.ensure(track, [&] {
auto stream = MUST(try_make<FixedMemoryStream>(m_data));
auto io_context = MUST(Media::FFmpeg::FFmpegIOContext::create(*stream));
auto track_context = make<TrackContext>(move(stream), move(io_context));
// We've already initialized a format context, so the only way this can fail is OOM.
MUST(initialize_format_context(track_context->format_context, *track_context->io_context->avio_context()));
track_context->packet = av_packet_alloc();
VERIFY(track_context->packet != nullptr);
return track_context;
});
return *m_track_contexts.get(track).release_value();
}
static inline AK::Duration time_units_to_duration(i64 time_units, AVRational const& time_base)

View file

@ -19,9 +19,12 @@ namespace Media::FFmpeg {
class MEDIA_API FFmpegDemuxer : public Demuxer {
public:
static DecoderErrorOr<NonnullRefPtr<FFmpegDemuxer>> from_data(ReadonlyBytes data);
static DecoderErrorOr<NonnullRefPtr<FFmpegDemuxer>> from_stream(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&);
virtual ~FFmpegDemuxer() override;
virtual void create_context_for_track(Track const&, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&) override;
virtual DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType) override;
virtual DecoderErrorOr<Optional<Track>> get_preferred_track_for_type(TrackType) override;
@ -38,15 +41,13 @@ public:
private:
struct TrackContext {
TrackContext(NonnullOwnPtr<SeekableStream>&& stream, NonnullOwnPtr<FFmpegIOContext>&& io_context)
: stream(move(stream))
, io_context(move(io_context))
TrackContext(NonnullOwnPtr<FFmpegIOContext>&& io_context)
: io_context(move(io_context))
{
}
~TrackContext();
TrackContext(TrackContext&&) = default;
NonnullOwnPtr<SeekableStream> stream;
NonnullOwnPtr<FFmpegIOContext> io_context;
AVFormatContext* format_context { nullptr };
AVPacket* packet { nullptr };
@ -54,13 +55,11 @@ private:
bool peeked_packet_already { false };
};
FFmpegDemuxer(ReadonlyBytes, NonnullOwnPtr<SeekableStream>&&, NonnullOwnPtr<Media::FFmpeg::FFmpegIOContext>&&);
FFmpegDemuxer(NonnullOwnPtr<Media::FFmpeg::FFmpegIOContext>&&);
TrackContext& get_track_context(Track const&);
DecoderErrorOr<Track> get_track_for_stream_index(u32 stream_index);
ReadonlyBytes m_data;
NonnullOwnPtr<SeekableStream> m_stream;
NonnullOwnPtr<FFmpegIOContext> m_io_context;
AVFormatContext* m_format_context;

View file

@ -13,8 +13,9 @@ extern "C" {
namespace Media::FFmpeg {
FFmpegIOContext::FFmpegIOContext(AVIOContext* avio_context)
: m_avio_context(avio_context)
FFmpegIOContext::FFmpegIOContext(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> stream_cursor, AVIOContext* avio_context)
: m_stream_cursor(move(stream_cursor))
, m_avio_context(avio_context)
{
}
@ -25,7 +26,7 @@ FFmpegIOContext::~FFmpegIOContext()
avio_context_free(&m_avio_context);
}
ErrorOr<NonnullOwnPtr<FFmpegIOContext>> FFmpegIOContext::create(AK::SeekableStream& stream)
ErrorOr<NonnullOwnPtr<FFmpegIOContext>> FFmpegIOContext::create(NonnullRefPtr<IncrementallyPopulatedStream::Cursor> stream_cursor)
{
auto* avio_buffer = av_malloc(PAGE_SIZE);
if (avio_buffer == nullptr)
@ -36,47 +37,50 @@ ErrorOr<NonnullOwnPtr<FFmpegIOContext>> FFmpegIOContext::create(AK::SeekableStre
static_cast<unsigned char*>(avio_buffer),
PAGE_SIZE,
0,
&stream,
stream_cursor.ptr(),
[](void* opaque, u8* buffer, int size) -> int {
auto& stream = *static_cast<SeekableStream*>(opaque);
AK::Bytes buffer_bytes { buffer, AK::min<size_t>(size, PAGE_SIZE) };
auto read_bytes_or_error = stream.read_some(buffer_bytes);
if (read_bytes_or_error.is_error()) {
if (read_bytes_or_error.error().code() == EOF)
auto& stream_cursor = *static_cast<IncrementallyPopulatedStream::Cursor*>(opaque);
Bytes buffer_bytes { buffer, AK::min<size_t>(size, PAGE_SIZE) };
auto buffer_bytes_or_error = stream_cursor.read_into(buffer_bytes);
if (buffer_bytes_or_error.is_error()) {
if (buffer_bytes_or_error.error().category() == DecoderErrorCategory::EndOfStream)
return AVERROR_EOF;
return AVERROR_UNKNOWN;
}
int number_of_bytes_read = read_bytes_or_error.value().size();
if (number_of_bytes_read == 0)
if (buffer_bytes_or_error.value() == 0)
return AVERROR_EOF;
return number_of_bytes_read;
return static_cast<int>(buffer_bytes_or_error.value());
},
nullptr,
[](void* opaque, int64_t offset, int whence) -> int64_t {
whence &= ~AVSEEK_FORCE;
auto& stream = *static_cast<SeekableStream*>(opaque);
auto& stream_cursor = *static_cast<IncrementallyPopulatedStream::Cursor*>(opaque);
if (whence == AVSEEK_SIZE)
return static_cast<int64_t>(stream.size().value());
return stream_cursor.size();
auto seek_mode_from_whence = [](int origin) -> SeekMode {
auto seek_mode_from_whence = [](int origin) -> IncrementallyPopulatedStream::Cursor::SeekMode {
if (origin == SEEK_CUR)
return SeekMode::FromCurrentPosition;
return IncrementallyPopulatedStream::Cursor::SeekMode::FromCurrentPosition;
if (origin == SEEK_END)
return SeekMode::FromEndPosition;
return SeekMode::SetPosition;
return IncrementallyPopulatedStream::Cursor::SeekMode::FromEndPosition;
return IncrementallyPopulatedStream::Cursor::SeekMode::SetPosition;
};
auto offset_or_error = stream.seek(offset, seek_mode_from_whence(whence));
if (offset_or_error.is_error())
return -EIO;
return 0;
auto maybe_seek_error = stream_cursor.seek(offset, seek_mode_from_whence(whence));
if (maybe_seek_error.is_error()) {
if (maybe_seek_error.error().category() == DecoderErrorCategory::EndOfStream)
return AVERROR_EOF;
return AVERROR_UNKNOWN;
}
return stream_cursor.position();
});
if (avio_context == nullptr) {
av_free(avio_buffer);
return Error::from_string_literal("Failed to allocate AVIO context");
}
return make<FFmpegIOContext>(avio_context);
return make<FFmpegIOContext>(move(stream_cursor), avio_context);
}
}

View file

@ -9,19 +9,21 @@
#include <AK/Error.h>
#include <AK/NonnullOwnPtr.h>
#include <LibMedia/FFmpeg/FFmpegForward.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
namespace Media::FFmpeg {
class FFmpegIOContext {
public:
explicit FFmpegIOContext(AVIOContext*);
explicit FFmpegIOContext(NonnullRefPtr<IncrementallyPopulatedStream::Cursor>, AVIOContext*);
~FFmpegIOContext();
static ErrorOr<NonnullOwnPtr<FFmpegIOContext>> create(AK::SeekableStream& stream);
static ErrorOr<NonnullOwnPtr<FFmpegIOContext>> create(NonnullRefPtr<IncrementallyPopulatedStream::Cursor>);
AVIOContext* avio_context() const { return m_avio_context; }
private:
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
AVIOContext* m_avio_context { nullptr };
};

View file

@ -28,6 +28,13 @@ public:
});
}
virtual void create_context_for_track(Track const& track, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor) override
{
m_demuxer.with_locked([&](auto& demuxer) {
demuxer->create_context_for_track(track, stream_cursor);
});
}
virtual DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType type) override
{
return m_demuxer.with_locked([&](auto& demuxer) {

View file

@ -22,13 +22,13 @@
namespace Media {
DecoderErrorOr<void> PlaybackManager::prepare_playback_from_media_data(ReadonlyBytes media_data, NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop_reference)
DecoderErrorOr<void> PlaybackManager::prepare_playback_from_media_data(NonnullRefPtr<IncrementallyPopulatedStream> stream, NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop_reference)
{
auto inner_demuxer = TRY([&] -> DecoderErrorOr<NonnullRefPtr<Demuxer>> {
auto matroska_result = Matroska::MatroskaDemuxer::from_data(media_data);
auto matroska_result = Matroska::MatroskaDemuxer::from_stream(stream->create_cursor());
if (!matroska_result.is_error())
return matroska_result.release_value();
return TRY(FFmpeg::FFmpegDemuxer::from_data(media_data));
return TRY(FFmpeg::FFmpegDemuxer::from_stream(stream->create_cursor()));
}());
auto demuxer = DECODER_TRY_ALLOC(try_make_ref_counted<MutexedDemuxer>(inner_demuxer));
@ -40,7 +40,7 @@ DecoderErrorOr<void> PlaybackManager::prepare_playback_from_media_data(ReadonlyB
supported_video_tracks.ensure_capacity(all_video_tracks.size());
supported_video_track_datas.ensure_capacity(all_video_tracks.size());
for (auto const& track : all_video_tracks) {
auto video_data_provider_result = VideoDataProvider::try_create(main_thread_event_loop_reference, demuxer, track);
auto video_data_provider_result = VideoDataProvider::try_create(main_thread_event_loop_reference, demuxer, stream, track);
if (video_data_provider_result.is_error())
continue;
supported_video_tracks.append(track);
@ -57,7 +57,7 @@ DecoderErrorOr<void> PlaybackManager::prepare_playback_from_media_data(ReadonlyB
supported_audio_tracks.ensure_capacity(all_audio_tracks.size());
supported_audio_track_datas.ensure_capacity(all_audio_tracks.size());
for (auto const& track : all_audio_tracks) {
auto audio_data_provider_result = AudioDataProvider::try_create(main_thread_event_loop_reference, demuxer, track);
auto audio_data_provider_result = AudioDataProvider::try_create(main_thread_event_loop_reference, demuxer, stream, track);
if (audio_data_provider_result.is_error())
continue;
auto audio_data_provider = audio_data_provider_result.release_value();
@ -134,11 +134,11 @@ PlaybackManager::~PlaybackManager()
m_weak_wrapper->revoke();
}
void PlaybackManager::add_media_source(ReadonlyBytes media_data)
void PlaybackManager::add_media_source(NonnullRefPtr<IncrementallyPopulatedStream> stream)
{
auto thread = Threading::Thread::construct([playback_manager = NonnullRefPtr { *this }, media_data, main_thread_event_loop_reference = Core::EventLoop::current_weak()] -> int {
auto thread = Threading::Thread::construct([playback_manager = NonnullRefPtr { *this }, stream, main_thread_event_loop_reference = Core::EventLoop::current_weak()] -> int {
auto main_thread_event_loop = main_thread_event_loop_reference->take();
auto maybe_error = playback_manager->prepare_playback_from_media_data(media_data, main_thread_event_loop_reference);
auto maybe_error = playback_manager->prepare_playback_from_media_data(stream, main_thread_event_loop_reference);
if (maybe_error.is_error()) {
main_thread_event_loop->deferred_invoke([playback_manager, error = maybe_error.release_error()] mutable {
if (playback_manager->on_unsupported_format_error)

View file

@ -83,7 +83,7 @@ public:
Function<void(AK::Duration)> on_duration_change;
Function<void(DecoderError&&)> on_error;
void add_media_source(ReadonlyBytes media_data);
void add_media_source(NonnullRefPtr<IncrementallyPopulatedStream>);
private:
class WeakPlaybackManager : public AtomicRefCounted<WeakPlaybackManager> {
@ -131,7 +131,7 @@ private:
VideoTrackData& get_video_data_for_track(Track const& track);
AudioTrackData& get_audio_data_for_track(Track const& track);
DecoderErrorOr<void> prepare_playback_from_media_data(ReadonlyBytes media_data, NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop_reference);
DecoderErrorOr<void> prepare_playback_from_media_data(NonnullRefPtr<IncrementallyPopulatedStream>, NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop_reference);
template<typename T, typename... Args>
void replace_state_handler(Args&&... args);

View file

@ -18,7 +18,7 @@
namespace Media {
DecoderErrorOr<NonnullRefPtr<AudioDataProvider>> AudioDataProvider::try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, Track const& track)
DecoderErrorOr<NonnullRefPtr<AudioDataProvider>> AudioDataProvider::try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<IncrementallyPopulatedStream> const& stream, Track const& track)
{
auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
auto const& sample_specification = track.audio_data().sample_specification;
@ -26,7 +26,9 @@ DecoderErrorOr<NonnullRefPtr<AudioDataProvider>> AudioDataProvider::try_create(N
auto decoder = DECODER_TRY_ALLOC(FFmpeg::FFmpegAudioDecoder::try_create(codec_id, sample_specification, codec_initialization_data));
auto converter = DECODER_TRY_ALLOC(FFmpeg::FFmpegAudioConverter::try_create());
auto thread_data = DECODER_TRY_ALLOC(try_make_ref_counted<AudioDataProvider::ThreadData>(main_thread_event_loop, demuxer, track, move(decoder), move(converter)));
auto stream_cursor = stream->create_cursor();
demuxer->create_context_for_track(track, stream_cursor);
auto thread_data = DECODER_TRY_ALLOC(try_make_ref_counted<AudioDataProvider::ThreadData>(main_thread_event_loop, demuxer, stream_cursor, track, move(decoder), move(converter)));
auto provider = DECODER_TRY_ALLOC(try_make_ref_counted<AudioDataProvider>(thread_data));
auto thread = DECODER_TRY_ALLOC(Threading::Thread::try_create([thread_data]() -> int {
@ -78,9 +80,10 @@ void AudioDataProvider::seek(AK::Duration timestamp, SeekCompletionHandler&& com
m_thread_data->seek(timestamp, move(completion_handler));
}
AudioDataProvider::ThreadData::ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, Track const& track, NonnullOwnPtr<AudioDecoder>&& decoder, NonnullOwnPtr<Audio::AudioConverter>&& converter)
AudioDataProvider::ThreadData::ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor, Track const& track, NonnullOwnPtr<AudioDecoder>&& decoder, NonnullOwnPtr<Audio::AudioConverter>&& converter)
: m_main_thread_event_loop(main_thread_event_loop)
, m_demuxer(demuxer)
, m_stream_cursor(stream_cursor)
, m_track(track)
, m_decoder(move(decoder))
, m_converter(move(converter))

View file

@ -17,6 +17,7 @@
#include <LibMedia/DecoderError.h>
#include <LibMedia/Export.h>
#include <LibMedia/Forward.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include <LibMedia/Track.h>
#include <LibThreading/ConditionVariable.h>
#include <LibThreading/Forward.h>
@ -36,7 +37,7 @@ public:
using BlockEndTimeHandler = Function<void(AK::Duration)>;
using SeekCompletionHandler = Function<void()>;
static DecoderErrorOr<NonnullRefPtr<AudioDataProvider>> try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, Track const& track);
static DecoderErrorOr<NonnullRefPtr<AudioDataProvider>> try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<IncrementallyPopulatedStream> const&, Track const& track);
AudioDataProvider(NonnullRefPtr<ThreadData> const&);
~AudioDataProvider();
@ -53,7 +54,7 @@ public:
private:
class ThreadData final : public AtomicRefCounted<ThreadData> {
public:
ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, Track const&, NonnullOwnPtr<AudioDecoder>&&, NonnullOwnPtr<Audio::AudioConverter>&&);
ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&, Track const&, NonnullOwnPtr<AudioDecoder>&&, NonnullOwnPtr<Audio::AudioConverter>&&);
~ThreadData();
void set_error_handler(ErrorHandler&&);
@ -101,6 +102,7 @@ private:
RequestedState m_requested_state { RequestedState::None };
NonnullRefPtr<MutexedDemuxer> m_demuxer;
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
Track m_track;
NonnullOwnPtr<AudioDecoder> m_decoder;
NonnullOwnPtr<Audio::AudioConverter> m_converter;

View file

@ -17,13 +17,16 @@
namespace Media {
DecoderErrorOr<NonnullRefPtr<VideoDataProvider>> VideoDataProvider::try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, Track const& track, RefPtr<MediaTimeProvider> const& time_provider)
DecoderErrorOr<NonnullRefPtr<VideoDataProvider>> VideoDataProvider::try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<IncrementallyPopulatedStream> const& stream, Track const& track, RefPtr<MediaTimeProvider> const& time_provider)
{
auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
auto codec_initialization_data = TRY(demuxer->get_codec_initialization_data_for_track(track));
auto decoder = DECODER_TRY_ALLOC(FFmpeg::FFmpegVideoDecoder::try_create(codec_id, codec_initialization_data));
auto thread_data = DECODER_TRY_ALLOC(try_make_ref_counted<VideoDataProvider::ThreadData>(main_thread_event_loop, demuxer, track, move(decoder), time_provider));
auto stream_cursor = stream->create_cursor();
demuxer->create_context_for_track(track, stream_cursor);
auto thread_data = DECODER_TRY_ALLOC(try_make_ref_counted<VideoDataProvider::ThreadData>(main_thread_event_loop, demuxer, stream_cursor, track, move(decoder), time_provider));
auto provider = DECODER_TRY_ALLOC(try_make_ref_counted<VideoDataProvider>(thread_data));
auto thread = DECODER_TRY_ALLOC(Threading::Thread::try_create([thread_data]() -> int {
@ -80,9 +83,10 @@ void VideoDataProvider::seek(AK::Duration timestamp, SeekMode seek_mode, SeekCom
m_thread_data->seek(timestamp, seek_mode, move(completion_handler));
}
VideoDataProvider::ThreadData::ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, Track const& track, NonnullOwnPtr<VideoDecoder>&& decoder, RefPtr<MediaTimeProvider> const& time_provider)
VideoDataProvider::ThreadData::ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const& stream_cursor, Track const& track, NonnullOwnPtr<VideoDecoder>&& decoder, RefPtr<MediaTimeProvider> const& time_provider)
: m_main_thread_event_loop(main_thread_event_loop)
, m_demuxer(demuxer)
, m_stream_cursor(stream_cursor)
, m_track(track)
, m_decoder(move(decoder))
, m_time_provider(time_provider)

View file

@ -16,6 +16,7 @@
#include <LibMedia/DecoderError.h>
#include <LibMedia/Export.h>
#include <LibMedia/Forward.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include <LibMedia/SeekMode.h>
#include <LibMedia/TimedImage.h>
#include <LibMedia/Track.h>
@ -36,7 +37,7 @@ public:
using FrameEndTimeHandler = Function<void(AK::Duration)>;
using SeekCompletionHandler = Function<void(AK::Duration)>;
static DecoderErrorOr<NonnullRefPtr<VideoDataProvider>> try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, Track const&, RefPtr<MediaTimeProvider> const& = nullptr);
static DecoderErrorOr<NonnullRefPtr<VideoDataProvider>> try_create(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<IncrementallyPopulatedStream> const&, Track const&, RefPtr<MediaTimeProvider> const& = nullptr);
VideoDataProvider(NonnullRefPtr<ThreadData> const&);
~VideoDataProvider();
@ -53,7 +54,7 @@ public:
private:
class ThreadData final : public AtomicRefCounted<ThreadData> {
public:
ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, Track const&, NonnullOwnPtr<VideoDecoder>&&, RefPtr<MediaTimeProvider> const&);
ThreadData(NonnullRefPtr<Core::WeakEventLoopReference> const& main_thread_event_loop, NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<IncrementallyPopulatedStream::Cursor> const&, Track const&, NonnullOwnPtr<VideoDecoder>&&, RefPtr<MediaTimeProvider> const&);
~ThreadData();
void set_error_handler(ErrorHandler&&);
@ -99,6 +100,7 @@ private:
RequestedState m_requested_state { RequestedState::None };
NonnullRefPtr<MutexedDemuxer> m_demuxer;
NonnullRefPtr<IncrementallyPopulatedStream::Cursor> m_stream_cursor;
Track m_track;
NonnullOwnPtr<VideoDecoder> m_decoder;

View file

@ -7,6 +7,7 @@
*/
#include <LibJS/Runtime/Promise.h>
#include <LibMedia/IncrementallyPopulatedStream.h>
#include <LibMedia/PlaybackManager.h>
#include <LibMedia/Sinks/DisplayingVideoSink.h>
#include <LibMedia/Track.h>
@ -1065,7 +1066,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(URL::URL const& url_r
// 6. Update the media data with the contents of response's unsafe response obtained in this fashion. response can be CORS-same-origin or
// CORS-cross-origin; this affects whether subtitles referenced in the media data are exposed in the API and, for video elements, whether
// a canvas gets tainted when the video is drawn on it.
m_media_data = move(media_data);
m_media_data = Media::IncrementallyPopulatedStream::create_from_buffer(move(media_data));
queue_a_media_element_task([this, failure_callback = move(failure_callback)]() mutable {
process_media_data(move(failure_callback)).release_value_but_fixme_should_propagate_errors();
@ -1411,7 +1412,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::setup_playback_manager(Function<void
failure_callback(MUST(String::from_utf8(error.description())));
};
m_playback_manager->add_media_source(m_media_data);
m_playback_manager->add_media_source(*m_media_data);
m_playback_manager->on_playback_state_change = [weak_self = GC::Weak(*this)] {
if (weak_self)

View file

@ -323,7 +323,7 @@ private:
GC::Ptr<TextTrackList> m_text_tracks;
// https://html.spec.whatwg.org/multipage/media.html#media-data
ByteBuffer m_media_data;
RefPtr<Media::IncrementallyPopulatedStream> m_media_data;
// https://html.spec.whatwg.org/multipage/media.html#can-autoplay-flag
bool m_can_autoplay { true };

View file

@ -10,7 +10,8 @@
extern "C" int LLVMFuzzerTestOneInput(u8 const* data, size_t size)
{
AK::set_debug_enabled(false);
auto matroska_reader_result = Media::Matroska::Reader::from_data({ data, size });
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(ByteBuffer::copy(data, size)));
auto matroska_reader_result = Media::Matroska::Reader::from_stream(stream->create_cursor());
if (matroska_reader_result.is_error())
return 0;
(void)matroska_reader_result.value().segment_information();

View file

@ -23,8 +23,8 @@ template<typename T>
static inline void decode_video(StringView path, size_t expected_frame_count, T create_decoder)
{
auto file = MUST(Core::File::open(path, Core::File::OpenMode::Read));
auto file_data = MUST(file->read_until_eof());
auto matroska_reader = MUST(Media::Matroska::Reader::from_data(file_data));
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(file->read_until_eof()));
auto matroska_reader = MUST(Media::Matroska::Reader::from_stream(stream->create_cursor()));
u64 video_track = 0;
MUST(matroska_reader.for_each_track_of_type(Media::Matroska::TrackEntry::TrackType::Video, [&](Media::Matroska::TrackEntry const& track_entry) -> Media::DecoderErrorOr<IterationDecision> {
video_track = track_entry.track_number();
@ -32,7 +32,7 @@ static inline void decode_video(StringView path, size_t expected_frame_count, T
}));
VERIFY(video_track != 0);
auto iterator = MUST(matroska_reader.create_sample_iterator(video_track));
auto iterator = MUST(matroska_reader.create_sample_iterator(stream->create_cursor(), video_track));
size_t frame_count = 0;
NonnullOwnPtr<Media::VideoDecoder> decoder = create_decoder(iterator);
@ -69,17 +69,18 @@ static inline void decode_audio(StringView path, u32 sample_rate, u8 channel_cou
{
Core::EventLoop loop;
auto mapped_file = TRY_OR_FAIL(Core::MappedFile::map(path));
auto demuxer = MUST([&] -> Media::DecoderErrorOr<NonnullRefPtr<Media::Demuxer>> {
auto matroska_result = Media::Matroska::MatroskaDemuxer::from_data(mapped_file->bytes());
auto file = MUST(Core::File::open(path, Core::File::OpenMode::Read));
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(file->read_until_eof()));
auto inner_demuxer = MUST([&] -> Media::DecoderErrorOr<NonnullRefPtr<Media::Demuxer>> {
auto matroska_result = Media::Matroska::MatroskaDemuxer::from_stream(stream->create_cursor());
if (!matroska_result.is_error())
return matroska_result.release_value();
return Media::FFmpeg::FFmpegDemuxer::from_data(mapped_file->bytes());
return Media::FFmpeg::FFmpegDemuxer::from_stream(stream->create_cursor());
}());
auto mutexed_demuxer = make_ref_counted<Media::MutexedDemuxer>(demuxer);
auto demuxer = make_ref_counted<Media::MutexedDemuxer>(inner_demuxer);
auto track = TRY_OR_FAIL(demuxer->get_preferred_track_for_type(Media::TrackType::Audio));
VERIFY(track.has_value());
auto provider = TRY_OR_FAIL(Media::AudioDataProvider::try_create(Core::EventLoop::current_weak(), mutexed_demuxer, track.release_value()));
auto provider = TRY_OR_FAIL(Media::AudioDataProvider::try_create(Core::EventLoop::current_weak(), demuxer, stream, track.release_value()));
auto reached_end = false;
provider->set_error_handler([&](Media::DecoderError&& error) {

View file

@ -13,8 +13,8 @@
TEST_CASE(master_elements_containing_crc32)
{
auto file = MUST(Core::File::open("./master_elements_containing_crc32.mkv"sv, Core::File::OpenMode::Read));
auto file_data = MUST(file->read_until_eof());
auto matroska_reader = MUST(Media::Matroska::Reader::from_data(file_data));
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(file->read_until_eof()));
auto matroska_reader = MUST(Media::Matroska::Reader::from_stream(stream->create_cursor()));
u64 video_track = 0;
MUST(matroska_reader.for_each_track_of_type(Media::Matroska::TrackEntry::TrackType::Video, [&](Media::Matroska::TrackEntry const& track_entry) -> Media::DecoderErrorOr<IterationDecision> {
video_track = track_entry.track_number();
@ -22,7 +22,7 @@ TEST_CASE(master_elements_containing_crc32)
}));
VERIFY(video_track == 1);
auto iterator = MUST(matroska_reader.create_sample_iterator(video_track));
auto iterator = MUST(matroska_reader.create_sample_iterator(stream->create_cursor(), video_track));
MUST(iterator.next_block());
MUST(matroska_reader.seek_to_random_access_point(iterator, AK::Duration::from_seconds(7)));
MUST(iterator.next_block());
@ -31,11 +31,12 @@ TEST_CASE(master_elements_containing_crc32)
TEST_CASE(seek_in_multi_frame_blocks)
{
auto file = MUST(Core::File::open("./test-webm-xiph-lacing.mka"sv, Core::File::OpenMode::Read));
auto file_data = MUST(file->read_until_eof());
auto demuxer = MUST(Media::Matroska::MatroskaDemuxer::from_data(file_data));
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(file->read_until_eof()));
auto demuxer = MUST(Media::Matroska::MatroskaDemuxer::from_stream(stream->create_cursor()));
auto optional_track = MUST(demuxer->get_preferred_track_for_type(Media::TrackType::Audio));
EXPECT(optional_track.has_value());
auto track = optional_track.release_value();
demuxer->create_context_for_track(track, stream->create_cursor());
auto initial_coded_frame = MUST(demuxer->get_next_sample_for_track(track));
EXPECT(initial_coded_frame.timestamp() <= AK::Duration::zero());