/* * Copyright (c) 2022, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include "PlaybackManager.h" namespace Video { #define TRY_OR_FATAL_ERROR(expression) \ ({ \ auto&& _fatal_expression = (expression); \ if (_fatal_expression.is_error()) { \ dispatch_fatal_error(_fatal_expression.release_error()); \ return; \ } \ static_assert(!::AK::Detail::IsLvalueReference, \ "Do not return a reference from a fallible expression"); \ _fatal_expression.release_value(); \ }) DecoderErrorOr> PlaybackManager::from_file(StringView filename) { auto demuxer = TRY(Matroska::MatroskaDemuxer::from_file(filename)); return create(move(demuxer)); } DecoderErrorOr> PlaybackManager::from_mapped_file(NonnullRefPtr mapped_file) { auto demuxer = TRY(Matroska::MatroskaDemuxer::from_mapped_file(move(mapped_file))); return create(move(demuxer)); } DecoderErrorOr> PlaybackManager::from_data(ReadonlyBytes data) { auto demuxer = TRY(Matroska::MatroskaDemuxer::from_data(data)); return create(move(demuxer)); } DecoderErrorOr> PlaybackManager::create(NonnullOwnPtr demuxer) { auto video_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Video)); if (video_tracks.is_empty()) return DecoderError::with_description(DecoderErrorCategory::Invalid, "No video track is present"sv); auto track = video_tracks[0]; dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier()); auto decoder = 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_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, VideoFrameQueue&& frame_queue) : m_demuxer(move(demuxer)) , m_selected_video_track(video_track) , m_frame_queue(move(frame_queue)) , m_decoder(move(decoder)) , m_decode_wait_condition(m_decode_wait_mutex) { } 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() { dbgln_if(PLAYBACK_MANAGER_DEBUG, "Resuming playback."); TRY_OR_FATAL_ERROR(m_playback_handler->play()); } void PlaybackManager::pause_playback() { dbgln_if(PLAYBACK_MANAGER_DEBUG, "Pausing playback."); if (!m_playback_handler->is_playing()) warnln("Cannot pause."); TRY_OR_FATAL_ERROR(m_playback_handler->pause()); } Time PlaybackManager::current_playback_time() { return m_playback_handler->current_time(); } Time PlaybackManager::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(); } void PlaybackManager::dispatch_fatal_error(Error error) { dbgln_if(PLAYBACK_MANAGER_DEBUG, "Encountered fatal error: {}", error.string_literal()); // FIXME: For threading, this will have to use a pre-allocated event to send to the main loop // to be able to gracefully handle OOM. if (on_fatal_playback_error) on_fatal_playback_error(move(error)); } void PlaybackManager::dispatch_decoder_error(DecoderError error) { switch (error.category()) { case DecoderErrorCategory::EndOfStream: dbgln_if(PLAYBACK_MANAGER_DEBUG, "{}", error.string_literal()); TRY_OR_FATAL_ERROR(m_playback_handler->stop()); break; default: dbgln("Playback error encountered: {}", error.string_literal()); TRY_OR_FATAL_ERROR(m_playback_handler->stop()); if (on_decoder_error) on_decoder_error(move(error)); break; } } void PlaybackManager::dispatch_new_frame(RefPtr frame) { if (on_video_frame) on_video_frame(move(frame)); } bool PlaybackManager::dispatch_frame_queue_item(FrameQueueItem&& item) { if (item.is_error()) { dispatch_decoder_error(item.release_error()); return true; } 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; } void PlaybackManager::dispatch_state_change() { if (on_playback_state_change) on_playback_state_change(); } void PlaybackManager::timer_callback() { TRY_OR_FATAL_ERROR(m_playback_handler->on_timer_callback()); } void PlaybackManager::seek_to_timestamp(Time target_timestamp, SeekMode seek_mode) { TRY_OR_FATAL_ERROR(m_playback_handler->seek(target_timestamp, seek_mode)); } Optional