LibMedia: Don't display video frames that are late

Frames are considered late if the time is ahead by half their duration.
The fudging is necessary because Matroska (and perhaps other formats)
store their frame durations in different time units than their
timestamps.

Skipping these should make it clear when decoding is running behind,
instead of displaying the video in slow motion while audio runs at a
normal rate.

To give an accurate counting for video playback quality when it is
implemented, we'll most likely want to count all pulled frames in an
update as dropped if the last frame is dropped. Otherwise the frame
drop count will only increase at the display rate when decoding is
continually running behind.
This commit is contained in:
Zaggy1024 2026-06-09 17:11:46 -05:00 committed by Jelle Raaijmakers
parent c58b9be7d5
commit ae04222247
2 changed files with 17 additions and 0 deletions

View file

@ -85,6 +85,8 @@ void DisplayingVideoSink::seek(AK::Duration timestamp)
auto can_resolve_seek_within_cached_frames = [&] {
if (m_seek_status != SeekStatus::None)
return false;
if (m_cached_frames_are_discontinuous)
return false;
auto available_start = AK::Duration::max();
auto available_end = AK::Duration::min();
auto include_frame = [&](RefPtr<VideoFrame> const& frame) {
@ -150,7 +152,21 @@ DisplayingVideoSinkUpdateResult DisplayingVideoSink::update()
break;
if (m_next_frame->timestamp() > current_time)
break;
if (current_time > conservative_frame_end(*m_next_frame)) {
consume_moved_position_signals(last_status);
if (m_next_frame == nullptr)
continue;
if (!is_terminal(last_status)) {
if (last_status == PipelineStatus::HaveData) {
m_next_frame.clear();
m_cached_frames_are_discontinuous = true;
continue;
}
break;
}
}
m_current_frame = m_next_frame.release_nonnull();
m_cached_frames_are_discontinuous = false;
result = DisplayingVideoSinkUpdateResult::NewFrameAvailable;
}

View file

@ -49,6 +49,7 @@ private:
RefPtr<VideoFrame> m_next_frame;
RefPtr<VideoFrame> m_current_frame;
bool m_cached_frames_are_discontinuous { false };
enum class SeekStatus : u8 {
None,