1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:57:45 +00:00

LibVideo: Implement Matroska keyframe search for when there are no Cues

This just searches sequentially through each block in a SampleIterator
until it finds a block after the specified seek timestamp. Once it
finds one, it will try to set the input/output iterator to the most
recent keyframe. If the iterator's original position is closer to the
target, however, it leaves it at that original position, allowing
callers to continue decoding from that position until they reach the
target timestamp.
This commit is contained in:
Zaggy1024 2022-11-12 04:04:13 -06:00 committed by Andreas Kling
parent 9040194d54
commit eef8867d9e
3 changed files with 67 additions and 9 deletions

View file

@ -599,11 +599,62 @@ DecoderErrorOr<SampleIterator> Reader::create_sample_iterator(u64 track_number)
return SampleIterator(this->m_mapped_file, segment_view, track_number, position, TRY(segment_information()).timestamp_scale());
}
static DecoderErrorOr<bool> find_keyframe_before_timestamp(SampleIterator& iterator, Time const& timestamp)
{
#if MATROSKA_DEBUG
size_t inter_frames_count;
#endif
Optional<SampleIterator> last_keyframe;
while (true) {
SampleIterator rewind_iterator = iterator;
auto block = TRY(iterator.next_block());
if (block.only_keyframes()) {
last_keyframe.emplace(rewind_iterator);
#if MATROSKA_DEBUG
inter_frames_count = 0;
#endif
}
if (block.timestamp() > timestamp)
break;
#if MATROSKA_DEBUG
inter_frames_count++;
#endif
}
if (last_keyframe.has_value()) {
#if MATROSKA_DEBUG
dbgln("Seeked to a keyframe with {} inter frames to skip", inter_frames_count);
#endif
iterator = last_keyframe.release_value();
return true;
}
return false;
}
DecoderErrorOr<void> Reader::seek_to_random_access_point(SampleIterator& iterator, Time timestamp)
{
(void)iterator;
(void)timestamp;
return DecoderError::not_implemented();
// FIXME: Use Cues to look these up if the element is present.
// 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 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_id));
if (!TRY(find_keyframe_before_timestamp(iterator, timestamp)))
return DecoderError::corrupted("No random access points found"sv);
return {};
}
auto seeked_iterator = iterator;
if (TRY(find_keyframe_before_timestamp(seeked_iterator, timestamp)))
iterator = seeked_iterator;
VERIFY(iterator.last_timestamp() <= timestamp);
return {};
}
DecoderErrorOr<Block> SampleIterator::next_block()
@ -639,8 +690,10 @@ DecoderErrorOr<Block> SampleIterator::next_block()
}
m_position = streamer.position();
if (block.has_value())
if (block.has_value()) {
m_last_timestamp = block->timestamp();
return block.release_value();
}
}
m_current_cluster.clear();