1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 20:28:11 +00:00

LibVideo: Add StartingStateHandler to prepare the first frames of video

This new state handler will retrieve and display the first frame, while
ensuring that playback can start as soon as possible by buffering two
frames on top of the first frame for the PlayingStateHandler to set its
next frame timer by.
This commit is contained in:
Zaggy1024 2023-02-12 00:48:27 -06:00 committed by Andreas Kling
parent f9a65c1502
commit 789bc99b8d
2 changed files with 58 additions and 2 deletions

View file

@ -277,6 +277,61 @@ PlaybackManager& PlaybackManager::PlaybackStateHandler::manager() const
return m_manager;
}
class PlaybackManager::StartingStateHandler : public PlaybackManager::PlaybackStateHandler {
public:
StartingStateHandler(PlaybackManager& manager, bool playing)
: PlaybackStateHandler(manager)
, m_playing(playing)
{
}
~StartingStateHandler() override = default;
private:
ErrorOr<void> on_enter() override
{
return on_timer_callback();
}
StringView name() override { return "Starting"sv; }
ErrorOr<void> on_timer_callback() override
{
// Once we're threaded, instead of checking for the count here we can just mutex
// in the queue until we display the first and then again for the second to store it.
if (manager().m_frame_queue->size() < 3) {
manager().m_decode_timer->start(0);
manager().start_timer(0);
return {};
}
auto frame_to_display = manager().m_frame_queue->dequeue();
manager().m_last_present_in_media_time = frame_to_display.timestamp();
if (manager().dispatch_frame_queue_item(move(frame_to_display)))
return {};
manager().m_next_frame.emplace(manager().m_frame_queue->dequeue());
manager().m_decode_timer->start(0);
dbgln_if(PLAYBACK_MANAGER_DEBUG, "Displayed frame at {}ms, emplaced second frame at {}ms, finishing start now", manager().m_last_present_in_media_time.to_milliseconds(), manager().m_next_frame->timestamp().to_milliseconds());
if (!m_playing)
return replace_handler_and_delete_this<PausedStateHandler>();
return replace_handler_and_delete_this<PlayingStateHandler>();
}
ErrorOr<void> play() override
{
m_playing = true;
return {};
}
bool is_playing() override { return m_playing; };
ErrorOr<void> pause() override
{
m_playing = false;
return {};
}
bool m_playing;
};
class PlaybackManager::PlayingStateHandler : public PlaybackManager::PlaybackStateHandler {
public:
PlayingStateHandler(PlaybackManager& manager)
@ -320,7 +375,7 @@ private:
manager().start_timer(max(static_cast<int>(frame_time_ms), 0));
};
if (manager().m_next_frame.has_value() && manager().m_next_frame->timestamp() < current_time()) {
if (manager().m_next_frame.has_value() && current_time() < manager().m_next_frame->timestamp()) {
dbgln_if(PLAYBACK_MANAGER_DEBUG, "Current time {}ms is too early to present the next frame at {}ms, delaying", current_time().to_milliseconds(), manager().m_next_frame->timestamp().to_milliseconds());
set_presentation_timer();
return {};
@ -600,7 +655,7 @@ private:
auto start_timestamp = manager().seek_demuxer_to_most_recent_keyframe(Time::zero());
VERIFY(start_timestamp.has_value());
manager().m_last_present_in_media_time = start_timestamp.release_value();
return replace_handler_and_delete_this<PlayingStateHandler>();
return replace_handler_and_delete_this<StartingStateHandler>(true);
}
bool is_playing() override { return false; };
};