/* * Copyright (c) 2022, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #include "MatroskaDemuxer.h" #include "AK/Debug.h" namespace Video::Matroska { DecoderErrorOr> MatroskaDemuxer::from_file(StringView filename) { return make(TRY(Reader::from_file(filename))); } DecoderErrorOr> MatroskaDemuxer::from_mapped_file(NonnullRefPtr mapped_file) { return make(TRY(Reader::from_mapped_file(move(mapped_file)))); } DecoderErrorOr> MatroskaDemuxer::from_data(ReadonlyBytes data) { return make(TRY(Reader::from_data(data))); } DecoderErrorOr> MatroskaDemuxer::get_tracks_for_type(TrackType type) { TrackEntry::TrackType matroska_track_type; switch (type) { case TrackType::Video: matroska_track_type = TrackEntry::TrackType::Video; break; case TrackType::Audio: matroska_track_type = TrackEntry::TrackType::Audio; break; case TrackType::Subtitles: matroska_track_type = TrackEntry::TrackType::Subtitle; break; } Vector tracks; TRY(m_reader.for_each_track_of_type(matroska_track_type, [&](TrackEntry const& track_entry) -> DecoderErrorOr { VERIFY(track_entry.track_type() == matroska_track_type); Track track(type, track_entry.track_number()); switch (type) { case TrackType::Video: if (auto video_track = track_entry.video_track(); video_track.has_value()) track.set_video_data({ TRY(duration()), video_track->pixel_width, video_track->pixel_height }); break; default: break; } DECODER_TRY_ALLOC(tracks.try_append(track)); return IterationDecision::Continue; })); return tracks; } DecoderErrorOr MatroskaDemuxer::get_track_status(Track 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, { iterator })); } return &m_track_statuses.get(track).release_value(); } DecoderErrorOr> MatroskaDemuxer::seek_to_most_recent_keyframe(Track track, Duration timestamp, Optional earliest_available_sample) { // Removing the track status will cause us to start from the beginning. if (timestamp.is_zero()) { m_track_statuses.remove(track); return timestamp; } auto& track_status = *TRY(get_track_status(track)); auto seeked_iterator = TRY(m_reader.seek_to_random_access_point(track_status.iterator, timestamp)); VERIFY(seeked_iterator.last_timestamp().has_value()); auto last_sample = earliest_available_sample; if (!last_sample.has_value()) { last_sample = track_status.iterator.last_timestamp(); } if (last_sample.has_value()) { bool skip_seek = seeked_iterator.last_timestamp().value() <= last_sample.value() && last_sample.value() <= timestamp; dbgln_if(MATROSKA_DEBUG, "The last available sample at {}ms is {}closer to target timestamp {}ms than the keyframe at {}ms, {}", last_sample->to_milliseconds(), skip_seek ? ""sv : "not "sv, timestamp.to_milliseconds(), seeked_iterator.last_timestamp()->to_milliseconds(), skip_seek ? "skipping seek"sv : "seeking"sv); if (skip_seek) { return OptionalNone(); } } track_status.iterator = move(seeked_iterator); return track_status.iterator.last_timestamp(); } DecoderErrorOr> MatroskaDemuxer::get_next_sample_for_track(Track track) { // FIXME: This makes a copy of the sample, which shouldn't be necessary. // Matroska should make a RefPtr, probably. auto& status = *TRY(get_track_status(track)); if (!status.block.has_value() || status.frame_index >= status.block->frame_count()) { status.block = TRY(status.iterator.next_block()); status.frame_index = 0; } auto cicp = TRY(m_reader.track_for_track_number(track.identifier())).video_track()->color_format.to_cicp(); return make(status.block->frame(status.frame_index++), cicp, status.block->timestamp()); } DecoderErrorOr MatroskaDemuxer::duration() { auto duration = TRY(m_reader.segment_information()).duration(); return duration.value_or(Duration::zero()); } }