1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 22:27:35 +00:00

LibVideo: Pass the current sample to demuxers to lazily seek better

In cases where the PlaybackManager's earliest buffered or displayed
sample is closer to the seek target than the demuxer's chosen keyframe,
we don't want to seek at all. To enable this, demuxers now receive an
optional parameter with the earliest timestamp that the caller can
still access.

The demuxer in turn returns an optional to indicate when a seek was not
needed, which allows PlaybackManager to avoid clearing its queue and
re-decoding frames.
This commit is contained in:
Zaggy1024 2023-02-06 01:25:02 -06:00 committed by Andreas Kling
parent 275d57eb6f
commit 3bfef8bfe0
7 changed files with 61 additions and 42 deletions

View file

@ -806,7 +806,7 @@ DecoderErrorOr<void> Reader::seek_to_cue_for_timestamp(SampleIterator& iterator,
return {};
}
static DecoderErrorOr<bool> find_keyframe_before_timestamp(SampleIterator& iterator, Time const& timestamp)
static DecoderErrorOr<void> search_clusters_for_keyframe_before_timestamp(SampleIterator& iterator, Time const& timestamp)
{
#if MATROSKA_DEBUG
size_t inter_frames_count;
@ -837,10 +837,9 @@ static DecoderErrorOr<bool> find_keyframe_before_timestamp(SampleIterator& itera
dbgln("Seeked to a keyframe with {} inter frames to skip", inter_frames_count);
#endif
iterator = last_keyframe.release_value();
return true;
}
return false;
return {};
}
DecoderErrorOr<bool> Reader::has_cues_for_track(u64 track_number)
@ -849,37 +848,23 @@ DecoderErrorOr<bool> Reader::has_cues_for_track(u64 track_number)
return m_cues.contains(track_number);
}
DecoderErrorOr<void> Reader::seek_to_random_access_point(SampleIterator& iterator, Time timestamp)
DecoderErrorOr<SampleIterator> Reader::seek_to_random_access_point(SampleIterator iterator, Time timestamp)
{
if (iterator.m_last_timestamp == timestamp)
return {};
if (TRY(has_cues_for_track(iterator.m_track.track_number()))) {
auto seeked_iterator = iterator;
TRY(seek_to_cue_for_timestamp(seeked_iterator, timestamp));
VERIFY(seeked_iterator.m_last_timestamp <= timestamp);
// We only need to seek to a keyframe if it's not faster to continue from the current position.
if (timestamp < iterator.m_last_timestamp || seeked_iterator.m_last_timestamp > iterator.m_last_timestamp)
iterator = seeked_iterator;
return {};
TRY(seek_to_cue_for_timestamp(iterator, timestamp));
VERIFY(iterator.last_timestamp().has_value() && iterator.last_timestamp().value() <= timestamp);
return iterator;
}
// FIXME: This could cache the keyframes it finds. Is it worth doing? Probably not, most files will have Cues :^)
if (timestamp < iterator.last_timestamp() || iterator.last_timestamp().is_negative()) {
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()));
if (!TRY(find_keyframe_before_timestamp(iterator, timestamp)))
return DecoderError::corrupted("No random access points found"sv);
return {};
TRY(search_clusters_for_keyframe_before_timestamp(iterator, timestamp));
return iterator;
}
auto seeked_iterator = iterator;
if (TRY(find_keyframe_before_timestamp(seeked_iterator, timestamp)))
iterator = seeked_iterator;
VERIFY(iterator.last_timestamp() <= timestamp);
return {};
TRY(search_clusters_for_keyframe_before_timestamp(iterator, timestamp));
return iterator;
}
DecoderErrorOr<Optional<Vector<CuePoint> const&>> Reader::cue_points_for_track(u64 track_number)