/* * 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 #include "VideoDecoder.h" namespace Video { enum class PlaybackStatus { Playing, Paused, Buffering, SeekingPlaying, SeekingPaused, Stopped, Corrupted, }; struct FrameQueueItem { enum class Type { Frame, Error, }; static FrameQueueItem frame(RefPtr bitmap, Time timestamp) { return FrameQueueItem(move(bitmap), timestamp); } static FrameQueueItem error_marker(DecoderError&& error) { return FrameQueueItem(move(error)); } bool is_frame() const { return m_data.has(); } RefPtr bitmap() const { return m_data.get().bitmap; } Time timestamp() const { return m_data.get().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 error().string_literal(); return DeprecatedString::formatted("frame at {}ms", timestamp().to_milliseconds()); } private: struct FrameData { RefPtr bitmap; Time timestamp; }; FrameQueueItem(RefPtr bitmap, Time timestamp) : m_data(FrameData { move(bitmap), timestamp }) { } FrameQueueItem(DecoderError&& error) : m_data(move(error)) { } Variant m_data; }; static constexpr size_t FRAME_BUFFER_COUNT = 4; using VideoFrameQueue = Queue; class PlaybackManager { public: enum class SeekMode { Accurate, Fast, }; static constexpr SeekMode DEFAULT_SEEK_MODE = SeekMode::Accurate; static DecoderErrorOr> from_file(Core::Object& event_handler, StringView file); static DecoderErrorOr> from_data(Core::Object& event_handler, Span data); PlaybackManager(Core::Object& event_handler, NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr&& decoder); void resume_playback(); void pause_playback(); void restart_playback(); void seek_to_timestamp(Time); bool is_playing() const { return m_status == PlaybackStatus::Playing || m_status == PlaybackStatus::SeekingPlaying || m_status == PlaybackStatus::Buffering; } bool is_seeking() const { return m_status == PlaybackStatus::SeekingPlaying || m_status == PlaybackStatus::SeekingPaused; } bool is_buffering() const { return m_status == PlaybackStatus::Buffering; } bool is_stopped() const { return m_status == PlaybackStatus::Stopped || m_status == PlaybackStatus::Corrupted; } SeekMode seek_mode() { return m_seek_mode; } void set_seek_mode(SeekMode mode) { m_seek_mode = mode; } u64 number_of_skipped_frames() const { return m_skipped_frames; } void on_decoder_error(DecoderError error); Time current_playback_time(); Time duration(); Function, Time)> on_frame_present; private: void set_playback_status(PlaybackStatus status); void end_seek(); void update_presented_frame(); // May run off the main thread void post_decoder_error(DecoderError error); bool decode_and_queue_one_sample(); void on_decode_timer(); Core::Object& m_event_handler; Core::EventLoop& m_main_loop; PlaybackStatus m_status { PlaybackStatus::Stopped }; Time m_last_present_in_media_time = Time::zero(); Time m_last_present_in_real_time = Time::zero(); Time m_seek_to_media_time = Time::min(); SeekMode m_seek_mode = DEFAULT_SEEK_MODE; NonnullOwnPtr m_demuxer; Track m_selected_video_track; NonnullOwnPtr m_decoder; NonnullOwnPtr m_frame_queue; Optional m_next_frame; NonnullRefPtr m_present_timer; unsigned m_decoding_buffer_time_ms = 16; NonnullRefPtr m_decode_timer; u64 m_skipped_frames; }; enum EventType : unsigned { DecoderErrorOccurred = (('v' << 2) | ('i' << 1) | 'd') << 4, VideoFramePresent, PlaybackStatusChange, }; class DecoderErrorEvent : public Core::Event { public: explicit DecoderErrorEvent(DecoderError error) : Core::Event(DecoderErrorOccurred) , m_error(move(error)) { } virtual ~DecoderErrorEvent() = default; DecoderError const& error() { return m_error; } private: DecoderError m_error; }; class VideoFramePresentEvent : public Core::Event { public: VideoFramePresentEvent() = default; explicit VideoFramePresentEvent(RefPtr frame) : Core::Event(VideoFramePresent) , m_frame(move(frame)) { } virtual ~VideoFramePresentEvent() = default; RefPtr frame() { return m_frame; } private: RefPtr m_frame; }; class PlaybackStatusChangeEvent : public Core::Event { public: PlaybackStatusChangeEvent() = default; explicit PlaybackStatusChangeEvent(PlaybackStatus status, PlaybackStatus previous_status) : Core::Event(PlaybackStatusChange) , m_status(status) , m_previous_status(previous_status) { } virtual ~PlaybackStatusChangeEvent() = default; PlaybackStatus status(); PlaybackStatus previous_status(); private: PlaybackStatus m_status; PlaybackStatus m_previous_status; }; inline StringView playback_status_to_string(PlaybackStatus status) { switch (status) { case PlaybackStatus::Playing: return "Playing"sv; case PlaybackStatus::Paused: return "Paused"sv; case PlaybackStatus::Buffering: return "Buffering"sv; case PlaybackStatus::SeekingPlaying: return "SeekingPlaying"sv; case PlaybackStatus::SeekingPaused: return "SeekingPaused"sv; case PlaybackStatus::Stopped: return "Stopped"sv; case PlaybackStatus::Corrupted: return "Corrupted"sv; } return "Unknown"sv; }; }