/* * 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_with_demuxer(move(demuxer)); } DecoderErrorOr> PlaybackManager::from_mapped_file(NonnullRefPtr mapped_file) { auto demuxer = TRY(Matroska::MatroskaDemuxer::from_mapped_file(move(mapped_file))); return create_with_demuxer(move(demuxer)); } DecoderErrorOr> PlaybackManager::from_data(ReadonlyBytes data) { auto demuxer = TRY(Matroska::MatroskaDemuxer::from_data(data)); return create_with_demuxer(move(demuxer)); } DecoderErrorOr> PlaybackManager::create_with_demuxer(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()); return make(demuxer, track, make()); } PlaybackManager::PlaybackManager(NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr&& decoder) : m_demuxer(move(demuxer)) , m_selected_video_track(video_track) , m_decoder(move(decoder)) , m_frame_queue(make()) , m_playback_handler(make(*this, false, Time::zero(), SeekMode::Fast)) { m_present_timer = Core::Timer::create_single_shot(0, [&] { timer_callback(); }).release_value_but_fixme_should_propagate_errors(); m_decode_timer = Core::Timer::create_single_shot(0, [&] { on_decode_timer(); }).release_value_but_fixme_should_propagate_errors(); TRY_OR_FATAL_ERROR(m_playback_handler->on_enter()); } PlaybackManager::~PlaybackManager() = default; 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 = 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", 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