Serenity Operating System
1/*
2 * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Format.h>
8#include <LibCore/Timer.h>
9#include <LibVideo/Containers/Matroska/MatroskaDemuxer.h>
10#include <LibVideo/VP9/Decoder.h>
11
12#include "PlaybackManager.h"
13
14namespace Video {
15
16#define TRY_OR_FATAL_ERROR(expression) \
17 ({ \
18 auto&& _fatal_expression = (expression); \
19 if (_fatal_expression.is_error()) { \
20 dispatch_fatal_error(_fatal_expression.release_error()); \
21 return; \
22 } \
23 static_assert(!::AK::Detail::IsLvalueReference<decltype(_fatal_expression.release_value())>, \
24 "Do not return a reference from a fallible expression"); \
25 _fatal_expression.release_value(); \
26 })
27
28DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::from_file(Core::Object& event_handler, StringView filename)
29{
30 NonnullOwnPtr<Demuxer> demuxer = TRY(Matroska::MatroskaDemuxer::from_file(filename));
31 auto video_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Video));
32 if (video_tracks.is_empty())
33 return DecoderError::with_description(DecoderErrorCategory::Invalid, "No video track is present"sv);
34 auto track = video_tracks[0];
35
36 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
37
38 return make<PlaybackManager>(event_handler, demuxer, track, make<VP9::Decoder>());
39}
40
41PlaybackManager::PlaybackManager(Core::Object& event_handler, NonnullOwnPtr<Demuxer>& demuxer, Track video_track, NonnullOwnPtr<VideoDecoder>&& decoder)
42 : m_event_handler(event_handler)
43 , m_main_loop(Core::EventLoop::current())
44 , m_demuxer(move(demuxer))
45 , m_selected_video_track(video_track)
46 , m_decoder(move(decoder))
47 , m_frame_queue(make<VideoFrameQueue>())
48 , m_playback_handler(make<StartingStateHandler>(*this, false))
49{
50 m_present_timer = Core::Timer::create_single_shot(0, [&] { timer_callback(); }).release_value_but_fixme_should_propagate_errors();
51 m_decode_timer = Core::Timer::create_single_shot(0, [&] { on_decode_timer(); }).release_value_but_fixme_should_propagate_errors();
52 TRY_OR_FATAL_ERROR(m_playback_handler->on_enter());
53}
54
55void PlaybackManager::resume_playback()
56{
57 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Resuming playback.");
58 TRY_OR_FATAL_ERROR(m_playback_handler->play());
59}
60
61void PlaybackManager::pause_playback()
62{
63 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Pausing playback.");
64 if (!m_playback_handler->is_playing())
65 warnln("Cannot pause.");
66 TRY_OR_FATAL_ERROR(m_playback_handler->pause());
67}
68
69Time PlaybackManager::current_playback_time()
70{
71 return m_playback_handler->current_time();
72}
73
74Time PlaybackManager::duration()
75{
76 auto duration_result = m_demuxer->duration();
77 if (duration_result.is_error())
78 dispatch_decoder_error(duration_result.release_error());
79 return duration_result.release_value();
80}
81
82void PlaybackManager::dispatch_fatal_error(Error error)
83{
84 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Encountered fatal error: {}", error.string_literal());
85 // FIXME: For threading, this will have to use a pre-allocated event to send to the main loop
86 // to be able to gracefully handle OOM.
87 VERIFY(&m_main_loop == &Core::EventLoop::current());
88 FatalPlaybackErrorEvent event { move(error) };
89 m_event_handler.dispatch_event(event);
90}
91
92void PlaybackManager::dispatch_decoder_error(DecoderError error)
93{
94 switch (error.category()) {
95 case DecoderErrorCategory::EndOfStream:
96 dbgln_if(PLAYBACK_MANAGER_DEBUG, "{}", error.string_literal());
97 TRY_OR_FATAL_ERROR(m_playback_handler->stop());
98 break;
99 default:
100 dbgln("Playback error encountered: {}", error.string_literal());
101 TRY_OR_FATAL_ERROR(m_playback_handler->stop());
102 m_main_loop.post_event(m_event_handler, make<DecoderErrorEvent>(move(error)));
103 break;
104 }
105}
106
107void PlaybackManager::dispatch_new_frame(RefPtr<Gfx::Bitmap> frame)
108{
109 m_main_loop.post_event(m_event_handler, make<VideoFramePresentEvent>(frame));
110}
111
112bool PlaybackManager::dispatch_frame_queue_item(FrameQueueItem&& item)
113{
114 if (item.is_error()) {
115 dispatch_decoder_error(item.release_error());
116 return true;
117 }
118
119 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Sent frame for presentation");
120 dispatch_new_frame(item.bitmap());
121 return false;
122}
123
124void PlaybackManager::dispatch_state_change()
125{
126 m_main_loop.post_event(m_event_handler, TRY_OR_FATAL_ERROR(try_make<PlaybackStateChangeEvent>()));
127}
128
129void PlaybackManager::timer_callback()
130{
131 TRY_OR_FATAL_ERROR(m_playback_handler->on_timer_callback());
132}
133
134void PlaybackManager::seek_to_timestamp(Time target_timestamp, SeekMode seek_mode)
135{
136 TRY_OR_FATAL_ERROR(m_playback_handler->seek(target_timestamp, seek_mode));
137}
138
139Optional<Time> PlaybackManager::seek_demuxer_to_most_recent_keyframe(Time timestamp, Optional<Time> earliest_available_sample)
140{
141 // FIXME: When the demuxer is getting samples off the main thread in the future, this needs to
142 // mutex so that seeking can't happen while that thread is getting a sample.
143 auto result = m_demuxer->seek_to_most_recent_keyframe(m_selected_video_track, timestamp, move(earliest_available_sample));
144 if (result.is_error())
145 dispatch_decoder_error(result.release_error());
146 return result.release_value();
147}
148
149void PlaybackManager::restart_playback()
150{
151 seek_to_timestamp(Time::zero());
152}
153
154void PlaybackManager::start_timer(int milliseconds)
155{
156 m_present_timer->start(milliseconds);
157}
158
159bool PlaybackManager::decode_and_queue_one_sample()
160{
161 if (m_frame_queue->size() >= FRAME_BUFFER_COUNT) {
162 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Frame queue is full, stopping");
163 return false;
164 }
165#if PLAYBACK_MANAGER_DEBUG
166 auto start_time = Time::now_monotonic();
167#endif
168
169 auto enqueue_error = [&](DecoderError&& error, Time timestamp) {
170 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Enqueued decoder error: {}", error.string_literal());
171 m_frame_queue->enqueue(FrameQueueItem::error_marker(move(error), timestamp));
172 };
173
174#define TRY_OR_ENQUEUE_ERROR(expression, timestamp) \
175 ({ \
176 auto&& _temporary_result = (expression); \
177 if (_temporary_result.is_error()) { \
178 enqueue_error(_temporary_result.release_error(), (timestamp)); \
179 return false; \
180 } \
181 static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
182 "Do not return a reference from a fallible expression"); \
183 _temporary_result.release_value(); \
184 })
185
186 auto frame_sample = TRY_OR_ENQUEUE_ERROR(m_demuxer->get_next_video_sample_for_track(m_selected_video_track), Time::min());
187 OwnPtr<VideoFrame> decoded_frame = nullptr;
188 while (!decoded_frame) {
189 TRY_OR_ENQUEUE_ERROR(m_decoder->receive_sample(frame_sample->data()), frame_sample->timestamp());
190
191 while (true) {
192 auto frame_result = m_decoder->get_decoded_frame();
193
194 if (frame_result.is_error()) {
195 if (frame_result.error().category() == DecoderErrorCategory::NeedsMoreInput)
196 break;
197
198 enqueue_error(frame_result.release_error(), frame_sample->timestamp());
199 return false;
200 }
201
202 decoded_frame = frame_result.release_value();
203 VERIFY(decoded_frame);
204 }
205 }
206
207 auto& cicp = decoded_frame->cicp();
208 cicp.adopt_specified_values(frame_sample->container_cicp());
209 cicp.default_code_points_if_unspecified({ ColorPrimaries::BT709, TransferCharacteristics::BT709, MatrixCoefficients::BT709, VideoFullRangeFlag::Studio });
210
211 // BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, so other applications
212 // (Chromium, VLC) forgo transfer characteristics conversion. We will emulate that behavior by
213 // handling those as sRGB instead, which causes no transfer function change in the output,
214 // unless display color management is later implemented.
215 switch (cicp.transfer_characteristics()) {
216 case TransferCharacteristics::BT601:
217 case TransferCharacteristics::BT709:
218 case TransferCharacteristics::BT2020BitDepth10:
219 case TransferCharacteristics::BT2020BitDepth12:
220 cicp.set_transfer_characteristics(TransferCharacteristics::SRGB);
221 break;
222 default:
223 break;
224 }
225
226 auto bitmap = TRY_OR_ENQUEUE_ERROR(decoded_frame->to_bitmap(), frame_sample->timestamp());
227 m_frame_queue->enqueue(FrameQueueItem::frame(bitmap, frame_sample->timestamp()));
228
229#if PLAYBACK_MANAGER_DEBUG
230 auto end_time = Time::now_monotonic();
231 dbgln("Decoding sample at {}ms took {}ms, queue contains {} items", frame_sample->timestamp().to_milliseconds(), (end_time - start_time).to_milliseconds(), m_frame_queue->size());
232#endif
233
234 return true;
235}
236
237void PlaybackManager::on_decode_timer()
238{
239 if (!decode_and_queue_one_sample()) {
240 // Note: When threading is implemented, this must be dispatched via an event loop.
241 TRY_OR_FATAL_ERROR(m_playback_handler->on_buffer_filled());
242 return;
243 }
244
245 // Continually decode until buffering is complete
246 m_decode_timer->start(0);
247}
248
249Time PlaybackManager::PlaybackStateHandler::current_time() const
250{
251 return m_manager.m_last_present_in_media_time;
252}
253
254ErrorOr<void> PlaybackManager::PlaybackStateHandler::seek(Time target_timestamp, SeekMode seek_mode)
255{
256 return replace_handler_and_delete_this<SeekingStateHandler>(is_playing(), target_timestamp, seek_mode);
257}
258
259ErrorOr<void> PlaybackManager::PlaybackStateHandler::stop()
260{
261 return replace_handler_and_delete_this<StoppedStateHandler>();
262}
263
264template<class T, class... Args>
265ErrorOr<void> PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this(Args... args)
266{
267 auto temp_handler = TRY(adopt_nonnull_own_or_enomem<PlaybackStateHandler>(new (nothrow) T(m_manager, args...)));
268 m_manager.m_playback_handler.swap(temp_handler);
269#if PLAYBACK_MANAGER_DEBUG
270 m_has_exited = true;
271 dbgln("Changing state from {} to {}", temp_handler->name(), m_manager.m_playback_handler->name());
272#endif
273 m_manager.dispatch_state_change();
274 TRY(m_manager.m_playback_handler->on_enter());
275 return {};
276}
277
278PlaybackManager& PlaybackManager::PlaybackStateHandler::manager() const
279{
280#if PLAYBACK_MANAGER_DEBUG
281 VERIFY(!m_has_exited);
282#endif
283 return m_manager;
284}
285
286class PlaybackManager::ResumingStateHandler : public PlaybackManager::PlaybackStateHandler {
287public:
288 ResumingStateHandler(PlaybackManager& manager, bool playing)
289 : PlaybackStateHandler(manager)
290 , m_playing(playing)
291 {
292 }
293 ~ResumingStateHandler() override = default;
294
295protected:
296 ErrorOr<void> assume_next_state()
297 {
298 if (!m_playing)
299 return replace_handler_and_delete_this<PausedStateHandler>();
300 return replace_handler_and_delete_this<PlayingStateHandler>();
301 }
302
303 ErrorOr<void> play() override
304 {
305 m_playing = true;
306 manager().dispatch_state_change();
307 return {};
308 }
309 bool is_playing() override { return m_playing; }
310 ErrorOr<void> pause() override
311 {
312 m_playing = false;
313 manager().dispatch_state_change();
314 return {};
315 }
316
317 bool m_playing;
318};
319
320class PlaybackManager::StartingStateHandler : public PlaybackManager::ResumingStateHandler {
321 using PlaybackManager::ResumingStateHandler::ResumingStateHandler;
322
323 ErrorOr<void> on_enter() override
324 {
325 return on_timer_callback();
326 }
327
328 StringView name() override { return "Starting"sv; }
329
330 ErrorOr<void> on_timer_callback() override
331 {
332 // Once we're threaded, instead of checking for the count here we can just mutex
333 // in the queue until we display the first and then again for the second to store it.
334 if (manager().m_frame_queue->size() < 3) {
335 manager().m_decode_timer->start(0);
336 manager().start_timer(0);
337 return {};
338 }
339
340 auto frame_to_display = manager().m_frame_queue->dequeue();
341 manager().m_last_present_in_media_time = frame_to_display.timestamp();
342 if (manager().dispatch_frame_queue_item(move(frame_to_display)))
343 return {};
344
345 manager().m_next_frame.emplace(manager().m_frame_queue->dequeue());
346 manager().m_decode_timer->start(0);
347 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Displayed frame at {}ms, emplaced second frame at {}ms, finishing start now", manager().m_last_present_in_media_time.to_milliseconds(), manager().m_next_frame->timestamp().to_milliseconds());
348 if (!m_playing)
349 return replace_handler_and_delete_this<PausedStateHandler>();
350 return replace_handler_and_delete_this<PlayingStateHandler>();
351 }
352
353 ErrorOr<void> play() override
354 {
355 m_playing = true;
356 return {};
357 }
358 bool is_playing() override { return m_playing; };
359 ErrorOr<void> pause() override
360 {
361 m_playing = false;
362 return {};
363 }
364
365 bool m_playing;
366};
367
368class PlaybackManager::PlayingStateHandler : public PlaybackManager::PlaybackStateHandler {
369public:
370 PlayingStateHandler(PlaybackManager& manager)
371 : PlaybackStateHandler(manager)
372 {
373 }
374 ~PlayingStateHandler() override = default;
375
376private:
377 ErrorOr<void> on_enter() override
378 {
379 m_last_present_in_real_time = Time::now_monotonic();
380 return on_timer_callback();
381 }
382
383 StringView name() override { return "Playing"sv; }
384
385 bool is_playing() override { return true; };
386 ErrorOr<void> pause() override
387 {
388 manager().m_last_present_in_media_time = current_time();
389 return replace_handler_and_delete_this<PausedStateHandler>();
390 }
391 ErrorOr<void> buffer() override
392 {
393 manager().m_last_present_in_media_time = current_time();
394 return replace_handler_and_delete_this<BufferingStateHandler>(true);
395 }
396
397 Time current_time() const override
398 {
399 return manager().m_last_present_in_media_time + (Time::now_monotonic() - m_last_present_in_real_time);
400 }
401
402 ErrorOr<void> on_timer_callback() override
403 {
404 auto set_presentation_timer = [&]() {
405 auto frame_time_ms = (manager().m_next_frame->timestamp() - current_time()).to_milliseconds();
406 VERIFY(frame_time_ms <= NumericLimits<int>::max());
407 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Time until next frame is {}ms", frame_time_ms);
408 manager().start_timer(max(static_cast<int>(frame_time_ms), 0));
409 };
410
411 if (manager().m_next_frame.has_value() && current_time() < manager().m_next_frame->timestamp()) {
412 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Current time {}ms is too early to present the next frame at {}ms, delaying", current_time().to_milliseconds(), manager().m_next_frame->timestamp().to_milliseconds());
413 set_presentation_timer();
414 return {};
415 }
416
417 Optional<FrameQueueItem> future_frame_item;
418 bool should_present_frame = false;
419
420 // Skip frames until we find a frame past the current playback time, and keep the one that precedes it to display.
421 while (!manager().m_frame_queue->is_empty()) {
422 future_frame_item.emplace(manager().m_frame_queue->dequeue());
423 manager().m_decode_timer->start(0);
424
425 if (future_frame_item->timestamp() >= current_time() || future_frame_item->timestamp() == FrameQueueItem::no_timestamp) {
426 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Should present frame, future {} is error or after {}ms", future_frame_item->debug_string(), current_time().to_milliseconds());
427 should_present_frame = true;
428 break;
429 }
430
431 if (manager().m_next_frame.has_value()) {
432 dbgln_if(PLAYBACK_MANAGER_DEBUG, "At {}ms: Dropped {} in favor of {}", current_time().to_milliseconds(), manager().m_next_frame->debug_string(), future_frame_item->debug_string());
433 manager().m_skipped_frames++;
434 }
435 manager().m_next_frame.emplace(future_frame_item.release_value());
436 }
437
438 // If we don't have both of these items, we can't present, since we need to set a timer for
439 // the next frame. Check if we need to buffer based on the current state.
440 if (!manager().m_next_frame.has_value() || !future_frame_item.has_value()) {
441#if PLAYBACK_MANAGER_DEBUG
442 StringBuilder debug_string_builder;
443 debug_string_builder.append("We don't have "sv);
444 if (!manager().m_next_frame.has_value()) {
445 debug_string_builder.append("a frame to present"sv);
446 if (!future_frame_item.has_value())
447 debug_string_builder.append(" or a future frame"sv);
448 } else {
449 debug_string_builder.append("a future frame"sv);
450 }
451 debug_string_builder.append(", checking for error and buffering"sv);
452 dbgln_if(PLAYBACK_MANAGER_DEBUG, debug_string_builder.to_deprecated_string());
453#endif
454 if (future_frame_item.has_value()) {
455 if (future_frame_item->is_error()) {
456 manager().dispatch_decoder_error(future_frame_item.release_value().release_error());
457 return {};
458 }
459 manager().m_next_frame.emplace(future_frame_item.release_value());
460 }
461 TRY(buffer());
462 return {};
463 }
464
465 // If we have a frame, send it for presentation.
466 if (should_present_frame) {
467 auto now = Time::now_monotonic();
468 manager().m_last_present_in_media_time += now - m_last_present_in_real_time;
469 m_last_present_in_real_time = now;
470
471 if (manager().dispatch_frame_queue_item(manager().m_next_frame.release_value()))
472 return {};
473 }
474
475 // Now that we've presented the current frame, we can throw whatever error is next in queue.
476 // This way, we always display a frame before the stream ends, and should also show any frames
477 // we already had when a real error occurs.
478 if (future_frame_item->is_error()) {
479 manager().dispatch_decoder_error(future_frame_item.release_value().release_error());
480 return {};
481 }
482
483 // The future frame item becomes the next one to present.
484 manager().m_next_frame.emplace(future_frame_item.release_value());
485 set_presentation_timer();
486 return {};
487 }
488
489 Time m_last_present_in_real_time = Time::zero();
490};
491
492class PlaybackManager::PausedStateHandler : public PlaybackManager::PlaybackStateHandler {
493public:
494 PausedStateHandler(PlaybackManager& manager)
495 : PlaybackStateHandler(manager)
496 {
497 }
498 ~PausedStateHandler() override = default;
499
500private:
501 StringView name() override { return "Paused"sv; }
502
503 ErrorOr<void> play() override
504 {
505 return replace_handler_and_delete_this<PlayingStateHandler>();
506 }
507 bool is_playing() override { return false; };
508};
509
510class PlaybackManager::BufferingStateHandler : public PlaybackManager::ResumingStateHandler {
511 using PlaybackManager::ResumingStateHandler::ResumingStateHandler;
512
513 ErrorOr<void> on_enter() override
514 {
515 manager().m_decode_timer->start(0);
516 return {};
517 }
518
519 StringView name() override { return "Buffering"sv; }
520
521 ErrorOr<void> on_buffer_filled() override
522 {
523 return assume_next_state();
524 }
525};
526
527class PlaybackManager::SeekingStateHandler : public PlaybackManager::ResumingStateHandler {
528public:
529 SeekingStateHandler(PlaybackManager& manager, bool playing, Time target_timestamp, SeekMode seek_mode)
530 : ResumingStateHandler(manager, playing)
531 , m_target_timestamp(target_timestamp)
532 , m_seek_mode(seek_mode)
533 {
534 }
535 ~SeekingStateHandler() override = default;
536
537private:
538 ErrorOr<void> on_enter() override
539 {
540 auto earliest_available_sample = manager().m_last_present_in_media_time;
541 if (manager().m_next_frame.has_value() && manager().m_next_frame->timestamp() != FrameQueueItem::no_timestamp) {
542 earliest_available_sample = min(earliest_available_sample, manager().m_next_frame->timestamp());
543 }
544 auto keyframe_timestamp = manager().seek_demuxer_to_most_recent_keyframe(m_target_timestamp, earliest_available_sample);
545
546#if PLAYBACK_MANAGER_DEBUG
547 auto seek_mode_name = m_seek_mode == SeekMode::Accurate ? "Accurate"sv : "Fast"sv;
548 if (keyframe_timestamp.has_value()) {
549 dbgln("{} seeking to timestamp target {}ms, selected keyframe at {}ms", seek_mode_name, m_target_timestamp.to_milliseconds(), keyframe_timestamp->to_milliseconds());
550 } else {
551 dbgln("{} seeking to timestamp target {}ms, demuxer kept its iterator position", seek_mode_name, m_target_timestamp.to_milliseconds());
552 }
553#endif
554
555 if (m_seek_mode == SeekMode::Fast) {
556 m_target_timestamp = keyframe_timestamp.value_or(earliest_available_sample);
557 }
558
559 if (keyframe_timestamp.has_value()) {
560 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Timestamp is earlier than current media time, clearing queue");
561 manager().m_frame_queue->clear();
562 manager().m_next_frame.clear();
563 } else if (m_target_timestamp >= manager().m_last_present_in_media_time && manager().m_next_frame.has_value() && manager().m_next_frame.value().timestamp() > m_target_timestamp) {
564 manager().m_last_present_in_media_time = m_target_timestamp;
565 return assume_next_state();
566 }
567
568 return skip_samples_until_timestamp();
569 }
570
571 ErrorOr<void> skip_samples_until_timestamp()
572 {
573 while (!manager().m_frame_queue->is_empty()) {
574 auto item = manager().m_frame_queue->dequeue();
575 manager().m_decode_timer->start(0);
576
577 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Dequeuing frame at {}ms and comparing to seek target {}ms", item.timestamp().to_milliseconds(), m_target_timestamp.to_milliseconds());
578 if (item.timestamp() > m_target_timestamp || item.timestamp() == FrameQueueItem::no_timestamp) {
579 // Fast seeking will result in an equal timestamp, so we can exit as soon as we see the next frame.
580 if (manager().m_next_frame.has_value()) {
581 manager().m_last_present_in_media_time = m_target_timestamp;
582
583 if (manager().dispatch_frame_queue_item(manager().m_next_frame.release_value()))
584 return {};
585 }
586
587 manager().m_next_frame.emplace(item);
588
589 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Exiting seek to {} state at {}ms", m_playing ? "Playing" : "Paused", manager().m_last_present_in_media_time.to_milliseconds());
590 return assume_next_state();
591 }
592 manager().m_next_frame.emplace(item);
593 }
594
595 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Frame queue is empty while seeking, waiting for buffer fill.");
596 manager().m_decode_timer->start(0);
597 return {};
598 }
599
600 StringView name() override { return "Seeking"sv; }
601
602 ErrorOr<void> seek(Time target_timestamp, SeekMode seek_mode) override
603 {
604 m_target_timestamp = target_timestamp;
605 m_seek_mode = seek_mode;
606 return on_enter();
607 }
608
609 Time current_time() const override
610 {
611 return m_target_timestamp;
612 }
613
614 // We won't need this override when threaded, the queue can pause us in on_enter().
615 ErrorOr<void> on_buffer_filled() override
616 {
617 dbgln_if(PLAYBACK_MANAGER_DEBUG, "Buffer filled while seeking, dequeuing until timestamp.");
618 return skip_samples_until_timestamp();
619 }
620
621 bool m_playing { false };
622 Time m_target_timestamp { Time::zero() };
623 SeekMode m_seek_mode { SeekMode::Accurate };
624};
625
626class PlaybackManager::StoppedStateHandler : public PlaybackManager::PlaybackStateHandler {
627public:
628 StoppedStateHandler(PlaybackManager& manager)
629 : PlaybackStateHandler(manager)
630 {
631 }
632 ~StoppedStateHandler() override = default;
633
634private:
635 ErrorOr<void> on_enter() override
636 {
637 return {};
638 }
639
640 StringView name() override { return "Stopped"sv; }
641
642 ErrorOr<void> play() override
643 {
644 manager().m_next_frame.clear();
645 manager().m_frame_queue->clear();
646 auto start_timestamp = manager().seek_demuxer_to_most_recent_keyframe(Time::zero());
647 VERIFY(start_timestamp.has_value());
648 manager().m_last_present_in_media_time = start_timestamp.release_value();
649 return replace_handler_and_delete_this<StartingStateHandler>(true);
650 }
651 bool is_playing() override { return false; };
652};
653
654}