/* * 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 "Demuxer.h" #include "MatroskaDocument.h" #include "VideoDecoder.h" namespace Video { enum class PlaybackStatus { Playing, Paused, Buffering, Seeking, Stopped, }; struct FrameQueueItem { static FrameQueueItem eos_marker() { return { nullptr, Time::max() }; } RefPtr bitmap; Time timestamp; bool is_eos_marker() const { return !bitmap; } }; static constexpr size_t FRAME_BUFFER_COUNT = 4; using VideoFrameQueue = Queue; class PlaybackManager : public Core::Object { C_OBJECT(PlaybackManager) public: static DecoderErrorOr> from_file(Object* event_handler, StringView file); static DecoderErrorOr> from_data(Object* event_handler, Span data); PlaybackManager(Object* event_handler, NonnullOwnPtr& demuxer, Track video_track, NonnullOwnPtr& decoder); ~PlaybackManager() override = default; void resume_playback(); void pause_playback(); void restart_playback(); bool is_playing() const { return m_status == PlaybackStatus::Playing; } bool is_buffering() const { return m_status == PlaybackStatus::Buffering; } bool is_stopped() const { return m_status == PlaybackStatus::Stopped; } u64 number_of_skipped_frames() const { return m_skipped_frames; } void event(Core::Event& event) override; Time current_playback_time(); Time duration(); Function, Time)> on_frame_present; private: void set_playback_status(PlaybackStatus status); bool prepare_next_frame(); void update_presented_frame(); // Runs off the main thread bool decode_and_queue_one_sample(); void on_decode_timer(); 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(); 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 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::Seeking: return "Seeking"sv; case PlaybackStatus::Stopped: return "Stopped"sv; } return "Unknown"sv; }; }