/src/serenity/Userland/Libraries/LibMedia/PlaybackManager.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Atomic.h> |
10 | | #include <AK/Function.h> |
11 | | #include <AK/NonnullOwnPtr.h> |
12 | | #include <AK/Queue.h> |
13 | | #include <AK/Time.h> |
14 | | #include <LibCore/SharedCircularQueue.h> |
15 | | #include <LibGfx/Bitmap.h> |
16 | | #include <LibMedia/Containers/Matroska/Document.h> |
17 | | #include <LibMedia/Demuxer.h> |
18 | | #include <LibThreading/ConditionVariable.h> |
19 | | #include <LibThreading/Mutex.h> |
20 | | #include <LibThreading/Thread.h> |
21 | | |
22 | | #include "VideoDecoder.h" |
23 | | |
24 | | namespace Media { |
25 | | |
26 | | class FrameQueueItem { |
27 | | public: |
28 | | FrameQueueItem() |
29 | 0 | : m_data(Empty()) |
30 | 0 | , m_timestamp(Duration::zero()) |
31 | 0 | { |
32 | 0 | } |
33 | | |
34 | | static constexpr Duration no_timestamp = Duration::min(); |
35 | | |
36 | | enum class Type { |
37 | | Frame, |
38 | | Error, |
39 | | }; |
40 | | |
41 | | static FrameQueueItem frame(RefPtr<Gfx::Bitmap> bitmap, Duration timestamp) |
42 | 0 | { |
43 | 0 | return FrameQueueItem(move(bitmap), timestamp); |
44 | 0 | } |
45 | | |
46 | | static FrameQueueItem error_marker(DecoderError&& error, Duration timestamp) |
47 | 0 | { |
48 | 0 | return FrameQueueItem(move(error), timestamp); |
49 | 0 | } |
50 | | |
51 | 0 | bool is_frame() const { return m_data.has<RefPtr<Gfx::Bitmap>>(); } |
52 | 0 | RefPtr<Gfx::Bitmap> bitmap() const { return m_data.get<RefPtr<Gfx::Bitmap>>(); } |
53 | 0 | Duration timestamp() const { return m_timestamp; } |
54 | | |
55 | 0 | bool is_error() const { return m_data.has<DecoderError>(); } |
56 | 0 | DecoderError const& error() const { return m_data.get<DecoderError>(); } |
57 | | DecoderError release_error() |
58 | 0 | { |
59 | 0 | auto error = move(m_data.get<DecoderError>()); |
60 | 0 | m_data.set(Empty()); |
61 | 0 | return error; |
62 | 0 | } |
63 | | |
64 | 0 | bool is_empty() const { return m_data.has<Empty>(); } |
65 | | |
66 | | ByteString debug_string() const |
67 | 0 | { |
68 | 0 | if (is_error()) |
69 | 0 | return ByteString::formatted("{} at {}ms", error().string_literal(), timestamp().to_milliseconds()); |
70 | 0 | return ByteString::formatted("frame at {}ms", timestamp().to_milliseconds()); |
71 | 0 | } |
72 | | |
73 | | private: |
74 | | FrameQueueItem(RefPtr<Gfx::Bitmap> bitmap, Duration timestamp) |
75 | 0 | : m_data(move(bitmap)) |
76 | 0 | , m_timestamp(timestamp) |
77 | 0 | { |
78 | 0 | VERIFY(m_timestamp != no_timestamp); |
79 | 0 | } |
80 | | |
81 | | FrameQueueItem(DecoderError&& error, Duration timestamp) |
82 | 0 | : m_data(move(error)) |
83 | 0 | , m_timestamp(timestamp) |
84 | 0 | { |
85 | 0 | } |
86 | | |
87 | | Variant<Empty, RefPtr<Gfx::Bitmap>, DecoderError> m_data { Empty() }; |
88 | | Duration m_timestamp { no_timestamp }; |
89 | | }; |
90 | | |
91 | | static constexpr size_t frame_buffer_count = 4; |
92 | | using VideoFrameQueue = Core::SharedSingleProducerCircularQueue<FrameQueueItem, frame_buffer_count>; |
93 | | |
94 | | enum class PlaybackState { |
95 | | Playing, |
96 | | Paused, |
97 | | Buffering, |
98 | | Seeking, |
99 | | Stopped, |
100 | | }; |
101 | | |
102 | | class PlaybackManager { |
103 | | AK_MAKE_NONCOPYABLE(PlaybackManager); |
104 | | AK_MAKE_NONMOVABLE(PlaybackManager); |
105 | | |
106 | | public: |
107 | | enum class SeekMode { |
108 | | Accurate, |
109 | | Fast, |
110 | | }; |
111 | | |
112 | | static constexpr SeekMode DEFAULT_SEEK_MODE = SeekMode::Accurate; |
113 | | |
114 | | static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> from_file(StringView file); |
115 | | static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> from_mapped_file(NonnullOwnPtr<Core::MappedFile> file); |
116 | | |
117 | | static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> from_data(ReadonlyBytes data); |
118 | | |
119 | | PlaybackManager(NonnullOwnPtr<Demuxer>& demuxer, Track video_track, NonnullOwnPtr<VideoDecoder>&& decoder, VideoFrameQueue&& frame_queue); |
120 | | ~PlaybackManager(); |
121 | | |
122 | | void resume_playback(); |
123 | | void pause_playback(); |
124 | | void restart_playback(); |
125 | | void terminate_playback(); |
126 | | void seek_to_timestamp(Duration, SeekMode = DEFAULT_SEEK_MODE); |
127 | | bool is_playing() const |
128 | 0 | { |
129 | 0 | return m_playback_handler->is_playing(); |
130 | 0 | } |
131 | | PlaybackState get_state() const |
132 | 0 | { |
133 | 0 | return m_playback_handler->get_state(); |
134 | 0 | } |
135 | | |
136 | 0 | u64 number_of_skipped_frames() const { return m_skipped_frames; } |
137 | | |
138 | | Duration current_playback_time(); |
139 | | Duration duration(); |
140 | | |
141 | | Function<void(RefPtr<Gfx::Bitmap>)> on_video_frame; |
142 | | Function<void()> on_playback_state_change; |
143 | | Function<void(DecoderError)> on_decoder_error; |
144 | | Function<void(Error)> on_fatal_playback_error; |
145 | | |
146 | 0 | Track const& selected_video_track() const { return m_selected_video_track; } |
147 | | |
148 | | private: |
149 | | class PlaybackStateHandler; |
150 | | // Abstract class to allow resuming play/pause after the state is completed. |
151 | | class ResumingStateHandler; |
152 | | class PlayingStateHandler; |
153 | | class PausedStateHandler; |
154 | | class BufferingStateHandler; |
155 | | class SeekingStateHandler; |
156 | | class StoppedStateHandler; |
157 | | |
158 | | static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> create(NonnullOwnPtr<Demuxer> demuxer); |
159 | | |
160 | | void timer_callback(); |
161 | | // This must be called with m_demuxer_mutex locked! |
162 | | DecoderErrorOr<Optional<Duration>> seek_demuxer_to_most_recent_keyframe(Duration timestamp, Optional<Duration> earliest_available_sample = OptionalNone()); |
163 | | |
164 | | Optional<FrameQueueItem> dequeue_one_frame(); |
165 | | void set_state_update_timer(int delay_ms); |
166 | | |
167 | | void decode_and_queue_one_sample(); |
168 | | |
169 | | void dispatch_decoder_error(DecoderError error); |
170 | | void dispatch_new_frame(RefPtr<Gfx::Bitmap> frame); |
171 | | // Returns whether we changed playback states. If so, any PlaybackStateHandler processing must cease. |
172 | | [[nodiscard]] bool dispatch_frame_queue_item(FrameQueueItem&&); |
173 | | void dispatch_state_change(); |
174 | | void dispatch_fatal_error(Error); |
175 | | |
176 | | Duration m_last_present_in_media_time = Duration::zero(); |
177 | | |
178 | | NonnullOwnPtr<Demuxer> m_demuxer; |
179 | | Threading::Mutex m_decoder_mutex; |
180 | | Track m_selected_video_track; |
181 | | |
182 | | VideoFrameQueue m_frame_queue; |
183 | | |
184 | | RefPtr<Core::Timer> m_state_update_timer; |
185 | | unsigned m_decoding_buffer_time_ms = 16; |
186 | | |
187 | | RefPtr<Threading::Thread> m_decode_thread; |
188 | | NonnullOwnPtr<VideoDecoder> m_decoder; |
189 | | Atomic<bool> m_stop_decoding { false }; |
190 | | Threading::Mutex m_decode_wait_mutex; |
191 | | Threading::ConditionVariable m_decode_wait_condition; |
192 | | Atomic<bool> m_buffer_is_full { false }; |
193 | | |
194 | | OwnPtr<PlaybackStateHandler> m_playback_handler; |
195 | | Optional<FrameQueueItem> m_next_frame; |
196 | | |
197 | | u64 m_skipped_frames { 0 }; |
198 | | |
199 | | // This is a nested class to allow private access. |
200 | | class PlaybackStateHandler { |
201 | | public: |
202 | | PlaybackStateHandler(PlaybackManager& manager) |
203 | 0 | : m_manager(manager) |
204 | 0 | { |
205 | 0 | } |
206 | 0 | virtual ~PlaybackStateHandler() = default; |
207 | | virtual StringView name() = 0; |
208 | | |
209 | 0 | virtual ErrorOr<void> on_enter() { return {}; } |
210 | | |
211 | 0 | virtual ErrorOr<void> play() { return {}; } |
212 | | virtual bool is_playing() const = 0; |
213 | | virtual PlaybackState get_state() const = 0; |
214 | 0 | virtual ErrorOr<void> pause() { return {}; } |
215 | 0 | virtual ErrorOr<void> buffer() { return {}; } |
216 | | virtual ErrorOr<void> seek(Duration target_timestamp, SeekMode); |
217 | | virtual ErrorOr<void> stop(); |
218 | | |
219 | | virtual Duration current_time() const; |
220 | | |
221 | 0 | virtual ErrorOr<void> do_timed_state_update() { return {}; } |
222 | | |
223 | | protected: |
224 | | template<class T, class... Args> |
225 | | ErrorOr<void> replace_handler_and_delete_this(Args... args); |
226 | | |
227 | | PlaybackManager& manager() const; |
228 | | |
229 | | PlaybackManager& manager() |
230 | 0 | { |
231 | 0 | return const_cast<PlaybackManager&>(const_cast<PlaybackStateHandler const*>(this)->manager()); |
232 | 0 | } |
233 | | |
234 | | private: |
235 | | PlaybackManager& m_manager; |
236 | | #if PLAYBACK_MANAGER_DEBUG |
237 | | bool m_has_exited { false }; |
238 | | #endif |
239 | | }; |
240 | | }; |
241 | | |
242 | | } |