mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-18 09:02:53 +00:00
This copies the video data from the Matroska document into the Track structure that outside users have access to. Because Track can actually represent other media types, this is set up such that the Track can hold metadata for those other types when they are needed. This is needed for LibWeb's HTMLMediaElement implementation.
117 lines
4.2 KiB
C++
117 lines
4.2 KiB
C++
/*
|
|
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "MatroskaDemuxer.h"
|
|
#include "AK/Debug.h"
|
|
|
|
namespace Video::Matroska {
|
|
|
|
DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_file(StringView filename)
|
|
{
|
|
return make<MatroskaDemuxer>(TRY(Reader::from_file(filename)));
|
|
}
|
|
|
|
DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_data(ReadonlyBytes data)
|
|
{
|
|
return make<MatroskaDemuxer>(TRY(Reader::from_data(data)));
|
|
}
|
|
|
|
DecoderErrorOr<Vector<Track>> 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<Track> tracks;
|
|
TRY(m_reader.for_each_track_of_type(matroska_track_type, [&](TrackEntry const& track_entry) -> DecoderErrorOr<IterationDecision> {
|
|
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::TrackStatus*> 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<Optional<Time>> MatroskaDemuxer::seek_to_most_recent_keyframe(Track track, Time timestamp, Optional<Time> 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<NonnullOwnPtr<Sample>> 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<ByteBuffer>, 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<VideoSample>(status.block->frame(status.frame_index++), cicp, status.block->timestamp());
|
|
}
|
|
|
|
DecoderErrorOr<Time> MatroskaDemuxer::duration()
|
|
{
|
|
auto duration = TRY(m_reader.segment_information()).duration();
|
|
return duration.value_or(Time::zero());
|
|
}
|
|
|
|
}
|