From 71d70df34f781e628e29ae8023d2f4366a1106df Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Wed, 19 Apr 2023 17:11:51 -0500 Subject: [PATCH] LibVideo/PlaybackManager: Decode frames off the main thread Running decoding on the main thread can cause frames to be delayed due to the presentation timer waiting for a decode to finish. If a frame takes too long to decode, this delay can be quite visible. Therefore, decoding has been moved to a separate thread, with frames sent to the main thread through a thread-safe queue. This results in frame times going from being late by up to 16ms to a very consistent ~1-2ms. --- .../Libraries/LibVideo/PlaybackManager.cpp | 289 +++++++++++------- Userland/Libraries/LibVideo/PlaybackManager.h | 40 ++- 2 files changed, 203 insertions(+), 126 deletions(-) diff --git a/Userland/Libraries/LibVideo/PlaybackManager.cpp b/Userland/Libraries/LibVideo/PlaybackManager.cpp index d8973e155e..875ac95d7e 100644 --- a/Userland/Libraries/LibVideo/PlaybackManager.cpp +++ b/Userland/Libraries/LibVideo/PlaybackManager.cpp @@ -53,27 +53,45 @@ DecoderErrorOr> PlaybackManager::create(NonnullOw dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier()); auto decoder = DECODER_TRY_ALLOC(try_make()); - auto frame_queue = DECODER_TRY_ALLOC(try_make()); + auto frame_queue = DECODER_TRY_ALLOC(VideoFrameQueue::create()); auto playback_manager = DECODER_TRY_ALLOC(try_make(demuxer, track, move(decoder), move(frame_queue))); playback_manager->m_present_timer = DECODER_TRY_ALLOC(Core::Timer::create_single_shot(0, [&self = *playback_manager] { self.timer_callback(); })); - playback_manager->m_decode_timer = DECODER_TRY_ALLOC(Core::Timer::create_single_shot(0, [&self = *playback_manager] { self.on_decode_timer(); })); + + playback_manager->m_decode_thread = DECODER_TRY_ALLOC(Threading::Thread::try_create([&self = *playback_manager] { + while (!self.m_stop_decoding.load()) + self.decode_and_queue_one_sample(); + + dbgln_if(PLAYBACK_MANAGER_DEBUG, "Media Decoder thread ended."); + return 0; + }, + "Media Decoder"sv)); playback_manager->m_playback_handler = make(*playback_manager, false, Time::zero(), SeekMode::Fast); DECODER_TRY_ALLOC(playback_manager->m_playback_handler->on_enter()); + playback_manager->m_decode_thread->start(); + return playback_manager; } -PlaybackManager::PlaybackManager(NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr&& decoder, NonnullOwnPtr&& frame_queue) +PlaybackManager::PlaybackManager(NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr&& decoder, VideoFrameQueue&& frame_queue) : m_demuxer(move(demuxer)) , m_selected_video_track(video_track) - , m_decoder(move(decoder)) , m_frame_queue(move(frame_queue)) + , m_decoder(move(decoder)) + , m_decode_wait_condition(m_decode_wait_mutex) { } -PlaybackManager::~PlaybackManager() = default; +PlaybackManager::~PlaybackManager() +{ + m_stop_decoding.exchange(true); + m_decode_wait_condition.broadcast(); + dbgln_if(PLAYBACK_MANAGER_DEBUG, "Waiting for decode thread to end..."); + (void)m_decode_thread->join(); + dbgln_if(PLAYBACK_MANAGER_DEBUG, "Successfully destroyed PlaybackManager."); +} void PlaybackManager::resume_playback() { @@ -96,7 +114,10 @@ Time PlaybackManager::current_playback_time() Time PlaybackManager::duration() { - auto duration_result = m_demuxer->duration(); + auto duration_result = ({ + auto demuxer_locker = Threading::MutexLocker(m_demuxer_mutex); + m_demuxer->duration(); + }); if (duration_result.is_error()) dispatch_decoder_error(duration_result.release_error()); return duration_result.release_value(); @@ -142,7 +163,7 @@ bool PlaybackManager::dispatch_frame_queue_item(FrameQueueItem&& item) return true; } - dbgln_if(PLAYBACK_MANAGER_DEBUG, "Sent frame for presentation with timestamp {}ms", item.timestamp().to_milliseconds()); + dbgln_if(PLAYBACK_MANAGER_DEBUG, "Sent frame for presentation with timestamp {}ms, late by {}ms", item.timestamp().to_milliseconds(), (current_playback_time() - item.timestamp()).to_milliseconds()); dispatch_new_frame(item.bitmap()); return false; } @@ -165,112 +186,139 @@ void PlaybackManager::seek_to_timestamp(Time target_timestamp, SeekMode seek_mod Optional