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:
parent
b9db157cea
commit
c5d8cb5c47
22 changed files with 304 additions and 294 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in a new issue