/* * Copyright (c) 2022, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "VideoDecoder.h" namespace Video { class FrameQueueItem { public: static constexpr Time no_timestamp = Time::min(); enum class Type { Frame, Error, }; static FrameQueueItem frame(RefPtr bitmap, Time timestamp) { return FrameQueueItem(move(bitmap), timestamp); } static FrameQueueItem error_marker(DecoderError&& error, Time timestamp) { return FrameQueueItem(move(error), timestamp); } bool is_frame() const { return m_data.has>(); } RefPtr bitmap() const { return m_data.get>(); } Time timestamp() const { return m_timestamp; } bool is_error() const { return m_data.has(); } DecoderError const& error() const { return m_data.get(); } DecoderError release_error() { auto error = move(m_data.get()); m_data.set(Empty()); return error; } DeprecatedString debug_string() const { if (is_error()) return DeprecatedString::formatted("{} at {}ms", error().string_literal(), timestamp().to_milliseconds()); return DeprecatedString::formatted("frame at {}ms", timestamp().to_milliseconds()); } private: FrameQueueItem(RefPtr bitmap, Time timestamp) : m_data(move(bitmap)) , m_timestamp(timestamp) { VERIFY(m_timestamp != no_timestamp); } FrameQueueItem(DecoderError&& error, Time timestamp) : m_data(move(error)) , m_timestamp(timestamp) { } Variant, DecoderError> m_data; Time m_timestamp; }; static constexpr size_t FRAME_BUFFER_COUNT = 4; using VideoFrameQueue = Queue; class PlaybackTimer { public: virtual ~PlaybackTimer() = default; virtual void start() = 0; virtual void start(int interval_ms) = 0; }; enum class PlaybackState { Playing, Paused, Buffering, Seeking, Stopped, }; class PlaybackManager { public: enum class SeekMode { Accurate, Fast, }; static constexpr SeekMode DEFAULT_SEEK_MODE = SeekMode::Accurate; using PlaybackTimerCreator = Function>(int, Function)>; static DecoderErrorOr> from_file(StringView file, PlaybackTimerCreator = nullptr); static DecoderErrorOr> from_data(ReadonlyBytes data, PlaybackTimerCreator = nullptr); PlaybackManager(NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr&& decoder, PlaybackTimerCreator); void resume_playback(); void pause_playback(); void restart_playback(); void seek_to_timestamp(Time, SeekMode = DEFAULT_SEEK_MODE); bool is_playing() const { return m_playback_handler->is_playing(); } PlaybackState get_state() const { return m_playback_handler->get_state(); } u64 number_of_skipped_frames() const { return m_skipped_frames; } Time current_playback_time(); Time duration(); Function)> on_video_frame; Function on_playback_state_change; Function on_decoder_error; Function on_fatal_playback_error; Track const& selected_video_track() const { return m_selected_video_track; } private: class PlaybackStateHandler; // Abstract class to allow resuming play/pause after the state is completed. class ResumingStateHandler; class PlayingStateHandler; class PausedStateHandler; class BufferingStateHandler; class SeekingStateHandler; class StoppedStateHandler; static DecoderErrorOr> create_with_demuxer(NonnullOwnPtr demuxer, PlaybackTimerCreator playback_timer_creator); void start_timer(int milliseconds); void timer_callback(); Optional