Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibMedia/PlaybackManager.cpp
Line
Count
Source
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 <LibMedia/Containers/Matroska/MatroskaDemuxer.h>
10
#include <LibMedia/Video/VP8/Decoder.h>
11
#include <LibMedia/Video/VP9/Decoder.h>
12
13
#include "PlaybackManager.h"
14
15
namespace Media {
16
17
#define TRY_OR_FATAL_ERROR(expression)                                                               \
18
0
    ({                                                                                               \
19
0
        auto&& _fatal_expression = (expression);                                                     \
20
0
        if (_fatal_expression.is_error()) {                                                          \
21
0
            dispatch_fatal_error(_fatal_expression.release_error());                                 \
22
0
            return;                                                                                  \
23
0
        }                                                                                            \
24
0
        static_assert(!::AK::Detail::IsLvalueReference<decltype(_fatal_expression.release_value())>, \
25
0
            "Do not return a reference from a fallible expression");                                 \
26
0
        _fatal_expression.release_value();                                                           \
27
0
    })
28
29
DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::from_file(StringView filename)
30
0
{
31
0
    auto demuxer = TRY(Matroska::MatroskaDemuxer::from_file(filename));
32
0
    return create(move(demuxer));
33
0
}
34
35
DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::from_mapped_file(NonnullOwnPtr<Core::MappedFile> mapped_file)
36
0
{
37
0
    auto demuxer = TRY(Matroska::MatroskaDemuxer::from_mapped_file(move(mapped_file)));
38
0
    return create(move(demuxer));
39
0
}
40
41
DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::from_data(ReadonlyBytes data)
42
0
{
43
0
    auto demuxer = TRY(Matroska::MatroskaDemuxer::from_data(data));
44
0
    return create(move(demuxer));
45
0
}
46
47
PlaybackManager::PlaybackManager(NonnullOwnPtr<Demuxer>& demuxer, Track video_track, NonnullOwnPtr<VideoDecoder>&& decoder, VideoFrameQueue&& frame_queue)
48
0
    : m_demuxer(move(demuxer))
49
0
    , m_selected_video_track(video_track)
50
0
    , m_frame_queue(move(frame_queue))
51
0
    , m_decoder(move(decoder))
52
0
    , m_decode_wait_condition(m_decode_wait_mutex)
53
0
{
54
0
}
55
56
PlaybackManager::~PlaybackManager()
57
0
{
58
0
    terminate_playback();
59
0
}
60
61
void PlaybackManager::resume_playback()
62
0
{
63
0
    dbgln_if(PLAYBACK_MANAGER_DEBUG, "Resuming playback.");
64
0
    TRY_OR_FATAL_ERROR(m_playback_handler->play());
65
0
}
66
67
void PlaybackManager::pause_playback()
68
0
{
69
0
    dbgln_if(PLAYBACK_MANAGER_DEBUG, "Pausing playback.");
70
0
    if (!m_playback_handler->is_playing())
71
0
        warnln("Cannot pause.");
72
0
    TRY_OR_FATAL_ERROR(m_playback_handler->pause());
73
0
}
74
75
void PlaybackManager::terminate_playback()
76
0
{
77
0
    m_stop_decoding.exchange(true);
78
0
    m_decode_wait_condition.broadcast();
79
80
0
    if (m_decode_thread->needs_to_be_joined()) {
81
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Waiting for decode thread to end...");
82
0
        (void)m_decode_thread->join();
83
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Successfully destroyed PlaybackManager.");
84
0
    }
85
0
}
86
87
Duration PlaybackManager::current_playback_time()
88
0
{
89
0
    return m_playback_handler->current_time();
90
0
}
91
92
Duration PlaybackManager::duration()
93
0
{
94
0
    auto duration_result = ({
95
0
        auto demuxer_locker = Threading::MutexLocker(m_decoder_mutex);
96
0
        m_demuxer->duration();
97
0
    });
98
0
    if (duration_result.is_error()) {
99
0
        dispatch_decoder_error(duration_result.release_error());
100
        // FIXME: We should determine the last sample that the demuxer knows is available and
101
        //        use that as the current duration. The duration may change if the demuxer doesn't
102
        //        know there is a fixed duration.
103
0
        return Duration::zero();
104
0
    }
105
0
    return duration_result.release_value();
106
0
}
107
108
void PlaybackManager::dispatch_fatal_error(Error error)
109
0
{
110
0
    dbgln_if(PLAYBACK_MANAGER_DEBUG, "Encountered fatal error: {}", error.string_literal());
111
    // FIXME: For threading, this will have to use a pre-allocated event to send to the main loop
112
    //        to be able to gracefully handle OOM.
113
0
    if (on_fatal_playback_error)
114
0
        on_fatal_playback_error(move(error));
115
0
}
116
117
void PlaybackManager::dispatch_decoder_error(DecoderError error)
118
0
{
119
0
    switch (error.category()) {
120
0
    case DecoderErrorCategory::EndOfStream:
121
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "{}", error.string_literal());
122
0
        TRY_OR_FATAL_ERROR(m_playback_handler->stop());
123
0
        break;
124
0
    default:
125
0
        dbgln("Playback error encountered: {}", error.string_literal());
126
0
        TRY_OR_FATAL_ERROR(m_playback_handler->stop());
127
128
0
        if (on_decoder_error)
129
0
            on_decoder_error(move(error));
130
131
0
        break;
132
0
    }
133
0
}
134
135
void PlaybackManager::dispatch_new_frame(RefPtr<Gfx::Bitmap> frame)
136
0
{
137
0
    if (on_video_frame)
138
0
        on_video_frame(move(frame));
139
0
}
140
141
bool PlaybackManager::dispatch_frame_queue_item(FrameQueueItem&& item)
142
0
{
143
0
    if (item.is_error()) {
144
0
        dispatch_decoder_error(item.release_error());
145
0
        return true;
146
0
    }
147
148
0
    dbgln_if(PLAYBACK_MANAGER_DEBUG, "Sent frame for presentation with timestamp {}ms, late by {}ms", item.timestamp().to_milliseconds(), (current_playback_time() - item.timestamp()).to_milliseconds());
149
0
    dispatch_new_frame(item.bitmap());
150
0
    return false;
151
0
}
152
153
void PlaybackManager::dispatch_state_change()
154
0
{
155
0
    if (on_playback_state_change)
156
0
        on_playback_state_change();
157
0
}
158
159
void PlaybackManager::timer_callback()
160
0
{
161
0
    TRY_OR_FATAL_ERROR(m_playback_handler->do_timed_state_update());
162
0
}
163
164
void PlaybackManager::seek_to_timestamp(Duration target_timestamp, SeekMode seek_mode)
165
0
{
166
0
    TRY_OR_FATAL_ERROR(m_playback_handler->seek(target_timestamp, seek_mode));
167
0
}
168
169
DecoderErrorOr<Optional<Duration>> PlaybackManager::seek_demuxer_to_most_recent_keyframe(Duration timestamp, Optional<Duration> earliest_available_sample)
170
0
{
171
0
    auto seeked_timestamp = TRY(m_demuxer->seek_to_most_recent_keyframe(m_selected_video_track, timestamp, move(earliest_available_sample)));
172
0
    if (seeked_timestamp.has_value())
173
0
        m_decoder->flush();
174
0
    return seeked_timestamp;
175
0
}
176
177
Optional<FrameQueueItem> PlaybackManager::dequeue_one_frame()
178
0
{
179
0
    auto result = m_frame_queue.dequeue();
180
0
    m_decode_wait_condition.broadcast();
181
0
    if (result.is_error()) {
182
0
        if (result.error() != VideoFrameQueue::QueueStatus::Empty)
183
0
            dispatch_fatal_error(Error::from_string_literal("Dequeue failed with an unexpected error"));
184
0
        return {};
185
0
    }
186
0
    return result.release_value();
187
0
}
188
189
void PlaybackManager::set_state_update_timer(int delay_ms)
190
0
{
191
0
    m_state_update_timer->start(delay_ms);
192
0
}
193
194
void PlaybackManager::restart_playback()
195
0
{
196
0
    seek_to_timestamp(Duration::zero());
197
0
}
198
199
void PlaybackManager::decode_and_queue_one_sample()
200
0
{
201
#if PLAYBACK_MANAGER_DEBUG
202
    auto start_time = MonotonicTime::now();
203
#endif
204
205
0
    FrameQueueItem item_to_enqueue;
206
207
0
    while (item_to_enqueue.is_empty()) {
208
0
        OwnPtr<VideoFrame> decoded_frame = nullptr;
209
0
        CodingIndependentCodePoints container_cicp;
210
211
0
        {
212
0
            Threading::MutexLocker decoder_locker(m_decoder_mutex);
213
214
            // Get a sample to decode.
215
0
            auto sample_result = m_demuxer->get_next_sample_for_track(m_selected_video_track);
216
0
            if (sample_result.is_error()) {
217
0
                item_to_enqueue = FrameQueueItem::error_marker(sample_result.release_error(), FrameQueueItem::no_timestamp);
218
0
                break;
219
0
            }
220
0
            auto sample = sample_result.release_value();
221
0
            container_cicp = sample.auxiliary_data().get<VideoSampleData>().container_cicp();
222
223
            // Submit the sample to the decoder.
224
0
            auto decode_result = m_decoder->receive_sample(sample.timestamp(), sample.data());
225
0
            if (decode_result.is_error()) {
226
0
                item_to_enqueue = FrameQueueItem::error_marker(decode_result.release_error(), sample.timestamp());
227
0
                break;
228
0
            }
229
230
            // Retrieve the last available frame to present.
231
0
            while (true) {
232
0
                auto frame_result = m_decoder->get_decoded_frame();
233
234
0
                if (frame_result.is_error()) {
235
0
                    if (frame_result.error().category() == DecoderErrorCategory::NeedsMoreInput) {
236
0
                        break;
237
0
                    }
238
239
0
                    item_to_enqueue = FrameQueueItem::error_marker(frame_result.release_error(), sample.timestamp());
240
0
                    break;
241
0
                }
242
243
0
                decoded_frame = frame_result.release_value();
244
0
            }
245
0
        }
246
247
        // Convert the frame for display.
248
0
        if (decoded_frame != nullptr) {
249
0
            auto& cicp = decoded_frame->cicp();
250
0
            cicp.adopt_specified_values(container_cicp);
251
0
            cicp.default_code_points_if_unspecified({ ColorPrimaries::BT709, TransferCharacteristics::BT709, MatrixCoefficients::BT709, VideoFullRangeFlag::Studio });
252
253
            // BT.470 M, B/G, BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, so other applications
254
            // (Chromium, VLC) forgo transfer characteristics conversion. We will emulate that behavior by
255
            // handling those as sRGB instead, which causes no transfer function change in the output,
256
            // unless display color management is later implemented.
257
0
            switch (cicp.transfer_characteristics()) {
258
0
            case TransferCharacteristics::BT470BG:
259
0
            case TransferCharacteristics::BT470M:
260
0
            case TransferCharacteristics::BT601:
261
0
            case TransferCharacteristics::BT709:
262
0
            case TransferCharacteristics::BT2020BitDepth10:
263
0
            case TransferCharacteristics::BT2020BitDepth12:
264
0
                cicp.set_transfer_characteristics(TransferCharacteristics::SRGB);
265
0
                break;
266
0
            default:
267
0
                break;
268
0
            }
269
270
0
            auto bitmap_result = decoded_frame->to_bitmap();
271
272
0
            if (bitmap_result.is_error())
273
0
                item_to_enqueue = FrameQueueItem::error_marker(bitmap_result.release_error(), decoded_frame->timestamp());
274
0
            else
275
0
                item_to_enqueue = FrameQueueItem::frame(bitmap_result.release_value(), decoded_frame->timestamp());
276
0
            break;
277
0
        }
278
0
    }
279
280
0
    VERIFY(!item_to_enqueue.is_empty());
281
#if PLAYBACK_MANAGER_DEBUG
282
    dbgln("Media Decoder: Sample at {}ms took {}ms to decode, queue contains ~{} items", item_to_enqueue.timestamp().to_milliseconds(), (MonotonicTime::now() - start_time).to_milliseconds(), m_frame_queue.weak_used());
283
#endif
284
285
0
    auto wait = [&] {
286
0
        auto wait_locker = Threading::MutexLocker(m_decode_wait_mutex);
287
0
        m_decode_wait_condition.wait();
288
0
    };
289
290
0
    bool had_error = item_to_enqueue.is_error();
291
0
    while (true) {
292
0
        if (m_frame_queue.can_enqueue()) {
293
0
            MUST(m_frame_queue.enqueue(move(item_to_enqueue)));
294
0
            break;
295
0
        }
296
297
0
        if (m_stop_decoding.load()) {
298
0
            dbgln_if(PLAYBACK_MANAGER_DEBUG, "Media Decoder: Received signal to stop, exiting decode function...");
299
0
            return;
300
0
        }
301
302
0
        m_buffer_is_full.exchange(true);
303
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Media Decoder: Waiting for a frame to be dequeued...");
304
0
        wait();
305
0
    }
306
307
0
    if (had_error) {
308
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Media Decoder: Encountered {}, waiting...", item_to_enqueue.error().category() == DecoderErrorCategory::EndOfStream ? "end of stream"sv : "error"sv);
309
0
        m_buffer_is_full.exchange(true);
310
0
        wait();
311
0
    }
312
313
0
    m_buffer_is_full.exchange(false);
314
0
}
315
316
Duration PlaybackManager::PlaybackStateHandler::current_time() const
317
0
{
318
0
    return m_manager.m_last_present_in_media_time;
319
0
}
320
321
ErrorOr<void> PlaybackManager::PlaybackStateHandler::seek(Duration target_timestamp, SeekMode seek_mode)
322
0
{
323
0
    return replace_handler_and_delete_this<SeekingStateHandler>(is_playing(), target_timestamp, seek_mode);
324
0
}
325
326
ErrorOr<void> PlaybackManager::PlaybackStateHandler::stop()
327
0
{
328
0
    return replace_handler_and_delete_this<StoppedStateHandler>();
329
0
}
330
331
template<class T, class... Args>
332
ErrorOr<void> PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this(Args... args)
333
0
{
334
0
    OwnPtr<PlaybackStateHandler> temp_handler = TRY(try_make<T>(m_manager, args...));
335
0
    m_manager.m_playback_handler.swap(temp_handler);
336
#if PLAYBACK_MANAGER_DEBUG
337
    m_has_exited = true;
338
    dbgln("Changing state from {} to {}", temp_handler->name(), m_manager.m_playback_handler->name());
339
#endif
340
0
    TRY(m_manager.m_playback_handler->on_enter());
341
0
    m_manager.dispatch_state_change();
342
0
    return {};
343
0
}
Unexecuted instantiation: AK::ErrorOr<void, AK::Error> Media::PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this<Media::PlaybackManager::SeekingStateHandler, bool, AK::Duration, Media::PlaybackManager::SeekMode>(bool, AK::Duration, Media::PlaybackManager::SeekMode)
Unexecuted instantiation: AK::ErrorOr<void, AK::Error> Media::PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this<Media::PlaybackManager::PausedStateHandler>()
Unexecuted instantiation: AK::ErrorOr<void, AK::Error> Media::PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this<Media::PlaybackManager::PlayingStateHandler>()
Unexecuted instantiation: AK::ErrorOr<void, AK::Error> Media::PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this<Media::PlaybackManager::BufferingStateHandler, bool>(bool)
Unexecuted instantiation: AK::ErrorOr<void, AK::Error> Media::PlaybackManager::PlaybackStateHandler::replace_handler_and_delete_this<Media::PlaybackManager::StoppedStateHandler>()
344
345
PlaybackManager& PlaybackManager::PlaybackStateHandler::manager() const
346
0
{
347
#if PLAYBACK_MANAGER_DEBUG
348
    VERIFY(!m_has_exited);
349
#endif
350
0
    return m_manager;
351
0
}
352
353
class PlaybackManager::ResumingStateHandler : public PlaybackManager::PlaybackStateHandler {
354
public:
355
    ResumingStateHandler(PlaybackManager& manager, bool playing)
356
0
        : PlaybackStateHandler(manager)
357
0
        , m_playing(playing)
358
0
    {
359
0
    }
360
    ~ResumingStateHandler() override = default;
361
362
protected:
363
    ErrorOr<void> assume_next_state()
364
0
    {
365
0
        if (!m_playing)
366
0
            return replace_handler_and_delete_this<PausedStateHandler>();
367
0
        return replace_handler_and_delete_this<PlayingStateHandler>();
368
0
    }
369
370
    ErrorOr<void> play() override
371
0
    {
372
0
        m_playing = true;
373
0
        manager().dispatch_state_change();
374
0
        return {};
375
0
    }
376
0
    bool is_playing() const override { return m_playing; }
377
    ErrorOr<void> pause() override
378
0
    {
379
0
        m_playing = false;
380
0
        manager().dispatch_state_change();
381
0
        return {};
382
0
    }
383
384
    bool m_playing { false };
385
};
386
387
class PlaybackManager::PlayingStateHandler : public PlaybackManager::PlaybackStateHandler {
388
public:
389
    PlayingStateHandler(PlaybackManager& manager)
390
0
        : PlaybackStateHandler(manager)
391
0
    {
392
0
    }
393
    ~PlayingStateHandler() override = default;
394
395
private:
396
    ErrorOr<void> on_enter() override
397
0
    {
398
0
        m_last_present_in_real_time = MonotonicTime::now();
399
0
        return do_timed_state_update();
400
0
    }
401
402
0
    StringView name() override { return "Playing"sv; }
403
404
0
    bool is_playing() const override { return true; }
405
0
    PlaybackState get_state() const override { return PlaybackState::Playing; }
406
    ErrorOr<void> pause() override
407
0
    {
408
0
        manager().m_last_present_in_media_time = current_time();
409
0
        return replace_handler_and_delete_this<PausedStateHandler>();
410
0
    }
411
    ErrorOr<void> buffer() override
412
0
    {
413
0
        manager().m_last_present_in_media_time = current_time();
414
0
        return replace_handler_and_delete_this<BufferingStateHandler>(true);
415
0
    }
416
417
    Duration current_time() const override
418
0
    {
419
0
        return manager().m_last_present_in_media_time + (MonotonicTime::now() - m_last_present_in_real_time);
420
0
    }
421
422
    ErrorOr<void> do_timed_state_update() override
423
0
    {
424
0
        auto set_presentation_timer = [&]() {
425
0
            auto frame_time_ms = (manager().m_next_frame->timestamp() - current_time()).to_milliseconds();
426
0
            VERIFY(frame_time_ms <= NumericLimits<int>::max());
427
0
            dbgln_if(PLAYBACK_MANAGER_DEBUG, "Time until next frame is {}ms", frame_time_ms);
428
0
            manager().set_state_update_timer(max(static_cast<int>(frame_time_ms), 0));
429
0
        };
430
431
0
        if (manager().m_next_frame.has_value() && current_time() < manager().m_next_frame->timestamp()) {
432
0
            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());
433
0
            set_presentation_timer();
434
0
            return {};
435
0
        }
436
437
0
        Optional<FrameQueueItem> future_frame_item;
438
0
        bool should_present_frame = false;
439
440
        // Skip frames until we find a frame past the current playback time, and keep the one that precedes it to display.
441
0
        while (true) {
442
0
            future_frame_item = manager().dequeue_one_frame();
443
0
            if (!future_frame_item.has_value())
444
0
                break;
445
446
0
            if (future_frame_item->timestamp() >= current_time() || future_frame_item->timestamp() == FrameQueueItem::no_timestamp) {
447
0
                dbgln_if(PLAYBACK_MANAGER_DEBUG, "Should present frame, future {} is error or after {}ms", future_frame_item->debug_string(), current_time().to_milliseconds());
448
0
                should_present_frame = true;
449
0
                break;
450
0
            }
451
452
0
            if (manager().m_next_frame.has_value()) {
453
0
                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());
454
0
                manager().m_skipped_frames++;
455
0
            }
456
0
            manager().m_next_frame.emplace(future_frame_item.release_value());
457
0
        }
458
459
        // If we don't have both of these items, we can't present, since we need to set a timer for
460
        // the next frame. Check if we need to buffer based on the current state.
461
0
        if (!manager().m_next_frame.has_value() || !future_frame_item.has_value()) {
462
#if PLAYBACK_MANAGER_DEBUG
463
            StringBuilder debug_string_builder;
464
            debug_string_builder.append("We don't have "sv);
465
            if (!manager().m_next_frame.has_value()) {
466
                debug_string_builder.append("a frame to present"sv);
467
                if (!future_frame_item.has_value())
468
                    debug_string_builder.append(" or a future frame"sv);
469
            } else {
470
                debug_string_builder.append("a future frame"sv);
471
            }
472
            debug_string_builder.append(", checking for error and buffering"sv);
473
            dbgln_if(PLAYBACK_MANAGER_DEBUG, debug_string_builder.to_byte_string());
474
#endif
475
0
            if (future_frame_item.has_value()) {
476
0
                if (future_frame_item->is_error()) {
477
0
                    manager().dispatch_decoder_error(future_frame_item.release_value().release_error());
478
0
                    return {};
479
0
                }
480
0
                manager().m_next_frame.emplace(future_frame_item.release_value());
481
0
            }
482
0
            TRY(buffer());
483
0
            return {};
484
0
        }
485
486
        // If we have a frame, send it for presentation.
487
0
        if (should_present_frame) {
488
0
            auto now = MonotonicTime::now();
489
0
            manager().m_last_present_in_media_time += now - m_last_present_in_real_time;
490
0
            m_last_present_in_real_time = now;
491
492
0
            if (manager().dispatch_frame_queue_item(manager().m_next_frame.release_value()))
493
0
                return {};
494
0
        }
495
496
        // Now that we've presented the current frame, we can throw whatever error is next in queue.
497
        // This way, we always display a frame before the stream ends, and should also show any frames
498
        // we already had when a real error occurs.
499
0
        if (future_frame_item->is_error()) {
500
0
            manager().dispatch_decoder_error(future_frame_item.release_value().release_error());
501
0
            return {};
502
0
        }
503
504
        // The future frame item becomes the next one to present.
505
0
        manager().m_next_frame.emplace(future_frame_item.release_value());
506
0
        set_presentation_timer();
507
0
        return {};
508
0
    }
509
510
    MonotonicTime m_last_present_in_real_time = MonotonicTime::now_coarse();
511
};
512
513
class PlaybackManager::PausedStateHandler : public PlaybackManager::PlaybackStateHandler {
514
public:
515
    PausedStateHandler(PlaybackManager& manager)
516
0
        : PlaybackStateHandler(manager)
517
0
    {
518
0
    }
519
    ~PausedStateHandler() override = default;
520
521
private:
522
0
    StringView name() override { return "Paused"sv; }
523
524
    ErrorOr<void> play() override
525
0
    {
526
0
        return replace_handler_and_delete_this<PlayingStateHandler>();
527
0
    }
528
0
    bool is_playing() const override { return false; }
529
0
    PlaybackState get_state() const override { return PlaybackState::Paused; }
530
};
531
532
// FIXME: This is a placeholder variable that could be scaled based on how long each frame decode takes to
533
//        avoid triggering the timer to check the queue constantly. However, doing so may reduce the speed
534
//        of seeking due to the decode thread having to wait for a signal to continue decoding.
535
constexpr int buffering_or_seeking_decode_wait_time = 1;
536
537
class PlaybackManager::BufferingStateHandler : public PlaybackManager::ResumingStateHandler {
538
    using PlaybackManager::ResumingStateHandler::ResumingStateHandler;
539
540
    ErrorOr<void> on_enter() override
541
0
    {
542
0
        manager().set_state_update_timer(buffering_or_seeking_decode_wait_time);
543
0
        return {};
544
0
    }
545
546
0
    StringView name() override { return "Buffering"sv; }
547
548
    ErrorOr<void> do_timed_state_update() override
549
0
    {
550
0
        auto buffer_is_full = manager().m_buffer_is_full.load();
551
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Buffering timer callback has been called. Buffer is {}.", buffer_is_full ? "full, exiting"sv : "not full, waiting"sv);
552
0
        if (buffer_is_full)
553
0
            return assume_next_state();
554
555
0
        manager().set_state_update_timer(buffering_or_seeking_decode_wait_time);
556
0
        return {};
557
0
    }
558
559
0
    PlaybackState get_state() const override { return PlaybackState::Buffering; }
560
};
561
562
class PlaybackManager::SeekingStateHandler : public PlaybackManager::ResumingStateHandler {
563
public:
564
    SeekingStateHandler(PlaybackManager& manager, bool playing, Duration target_timestamp, SeekMode seek_mode)
565
0
        : ResumingStateHandler(manager, playing)
566
0
        , m_target_timestamp(target_timestamp)
567
0
        , m_seek_mode(seek_mode)
568
0
    {
569
0
    }
570
    ~SeekingStateHandler() override = default;
571
572
private:
573
    ErrorOr<void> on_enter() override
574
0
    {
575
0
        auto earliest_available_sample = manager().m_last_present_in_media_time;
576
0
        if (manager().m_next_frame.has_value() && manager().m_next_frame->timestamp() != FrameQueueItem::no_timestamp) {
577
0
            earliest_available_sample = min(earliest_available_sample, manager().m_next_frame->timestamp());
578
0
        }
579
580
0
        {
581
0
            Threading::MutexLocker demuxer_locker(manager().m_decoder_mutex);
582
583
0
            auto demuxer_seek_result = manager().seek_demuxer_to_most_recent_keyframe(m_target_timestamp, earliest_available_sample);
584
0
            if (demuxer_seek_result.is_error()) {
585
0
                manager().dispatch_decoder_error(demuxer_seek_result.release_error());
586
0
                return {};
587
0
            }
588
0
            auto keyframe_timestamp = demuxer_seek_result.release_value();
589
590
#if PLAYBACK_MANAGER_DEBUG
591
            auto seek_mode_name = m_seek_mode == SeekMode::Accurate ? "Accurate"sv : "Fast"sv;
592
            if (keyframe_timestamp.has_value())
593
                dbgln("{} seeking to timestamp target {}ms, selected keyframe at {}ms", seek_mode_name, m_target_timestamp.to_milliseconds(), keyframe_timestamp->to_milliseconds());
594
            else
595
                dbgln("{} seeking to timestamp target {}ms, demuxer kept its iterator position after {}ms", seek_mode_name, m_target_timestamp.to_milliseconds(), earliest_available_sample.to_milliseconds());
596
#endif
597
598
0
            if (m_seek_mode == SeekMode::Fast)
599
0
                m_target_timestamp = keyframe_timestamp.value_or(manager().m_last_present_in_media_time);
600
601
0
            if (keyframe_timestamp.has_value()) {
602
0
                dbgln_if(PLAYBACK_MANAGER_DEBUG, "Keyframe is nearer to the target than the current frames, emptying queue");
603
0
                while (manager().dequeue_one_frame().has_value()) { }
604
0
                manager().m_next_frame.clear();
605
0
                manager().m_last_present_in_media_time = keyframe_timestamp.value();
606
0
            } 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) {
607
0
                dbgln_if(PLAYBACK_MANAGER_DEBUG, "Target timestamp is between the last presented frame and the next frame, exiting seek at {}ms", m_target_timestamp.to_milliseconds());
608
0
                manager().m_last_present_in_media_time = m_target_timestamp;
609
0
                return assume_next_state();
610
0
            }
611
0
        }
612
613
0
        return skip_samples_until_timestamp();
614
0
    }
615
616
    ErrorOr<void> skip_samples_until_timestamp()
617
0
    {
618
0
        while (true) {
619
0
            auto optional_item = manager().dequeue_one_frame();
620
0
            if (!optional_item.has_value())
621
0
                break;
622
0
            auto item = optional_item.release_value();
623
624
0
            dbgln_if(PLAYBACK_MANAGER_DEBUG, "Dequeuing frame at {}ms and comparing to seek target {}ms", item.timestamp().to_milliseconds(), m_target_timestamp.to_milliseconds());
625
0
            if (manager().m_next_frame.has_value() && (item.timestamp() > m_target_timestamp || item.timestamp() == FrameQueueItem::no_timestamp)) {
626
                // If the frame we're presenting is later than the target timestamp, skip the timestamp forward to it.
627
0
                if (manager().m_next_frame->timestamp() > m_target_timestamp) {
628
0
                    manager().m_last_present_in_media_time = manager().m_next_frame->timestamp();
629
0
                } else {
630
0
                    manager().m_last_present_in_media_time = m_target_timestamp;
631
0
                }
632
633
0
                if (manager().dispatch_frame_queue_item(manager().m_next_frame.release_value()))
634
0
                    return {};
635
636
0
                manager().m_next_frame.emplace(item);
637
638
0
                dbgln_if(PLAYBACK_MANAGER_DEBUG, "Exiting seek to {} state at {}ms", m_playing ? "Playing" : "Paused", manager().m_last_present_in_media_time.to_milliseconds());
639
0
                return assume_next_state();
640
0
            }
641
0
            manager().m_next_frame.emplace(item);
642
0
        }
643
644
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Frame queue is empty while seeking, waiting for buffer to fill.");
645
0
        manager().set_state_update_timer(buffering_or_seeking_decode_wait_time);
646
0
        return {};
647
0
    }
648
649
0
    StringView name() override { return "Seeking"sv; }
650
651
    ErrorOr<void> seek(Duration target_timestamp, SeekMode seek_mode) override
652
0
    {
653
0
        m_target_timestamp = target_timestamp;
654
0
        m_seek_mode = seek_mode;
655
0
        return on_enter();
656
0
    }
657
658
    Duration current_time() const override
659
0
    {
660
0
        return m_target_timestamp;
661
0
    }
662
663
    // We won't need this override when threaded, the queue can pause us in on_enter().
664
    ErrorOr<void> do_timed_state_update() override
665
0
    {
666
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Seeking wait finished, attempting to dequeue until timestamp.");
667
0
        return skip_samples_until_timestamp();
668
0
    }
669
670
0
    PlaybackState get_state() const override { return PlaybackState::Seeking; }
671
672
    Duration m_target_timestamp { Duration::zero() };
673
    SeekMode m_seek_mode { SeekMode::Accurate };
674
};
675
676
class PlaybackManager::StoppedStateHandler : public PlaybackManager::PlaybackStateHandler {
677
public:
678
    StoppedStateHandler(PlaybackManager& manager)
679
0
        : PlaybackStateHandler(manager)
680
0
    {
681
0
    }
682
    ~StoppedStateHandler() override = default;
683
684
private:
685
    ErrorOr<void> on_enter() override
686
0
    {
687
0
        return {};
688
0
    }
689
690
0
    StringView name() override { return "Stopped"sv; }
691
692
    ErrorOr<void> play() override
693
0
    {
694
        // When Stopped, the decoder thread will be waiting for a signal to start its loop going again.
695
0
        manager().m_decode_wait_condition.broadcast();
696
0
        return replace_handler_and_delete_this<SeekingStateHandler>(true, Duration::zero(), SeekMode::Fast);
697
0
    }
698
0
    bool is_playing() const override { return false; }
699
0
    PlaybackState get_state() const override { return PlaybackState::Stopped; }
700
};
701
702
DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::create(NonnullOwnPtr<Demuxer> demuxer)
703
0
{
704
0
    auto video_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Video));
705
0
    if (video_tracks.is_empty())
706
0
        return DecoderError::with_description(DecoderErrorCategory::Invalid, "No video track is present"sv);
707
0
    auto track = video_tracks[0];
708
709
0
    dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
710
711
0
    auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
712
0
    OwnPtr<VideoDecoder> decoder;
713
0
    switch (codec_id) {
714
0
    case CodecID::VP8:
715
0
        decoder = DECODER_TRY_ALLOC(try_make<Video::VP8::Decoder>());
716
0
        break;
717
718
0
    case CodecID::VP9:
719
0
        decoder = DECODER_TRY_ALLOC(try_make<Video::VP9::Decoder>());
720
0
        break;
721
722
0
    default:
723
0
        return DecoderError::format(DecoderErrorCategory::Invalid, "Unsupported codec: {}", codec_id);
724
0
    }
725
0
    auto decoder_non_null = decoder.release_nonnull();
726
0
    auto frame_queue = DECODER_TRY_ALLOC(VideoFrameQueue::create());
727
0
    auto playback_manager = DECODER_TRY_ALLOC(try_make<PlaybackManager>(demuxer, track, move(decoder_non_null), move(frame_queue)));
728
729
0
    playback_manager->m_state_update_timer = Core::Timer::create_single_shot(0, [&self = *playback_manager] { self.timer_callback(); });
730
731
0
    playback_manager->m_decode_thread = DECODER_TRY_ALLOC(Threading::Thread::try_create([&self = *playback_manager] {
732
0
        while (!self.m_stop_decoding.load())
733
0
            self.decode_and_queue_one_sample();
734
735
0
        dbgln_if(PLAYBACK_MANAGER_DEBUG, "Media Decoder thread ended.");
736
0
        return 0;
737
0
    },
738
0
        "Media Decoder"sv));
739
740
0
    playback_manager->m_playback_handler = make<SeekingStateHandler>(*playback_manager, false, Duration::zero(), SeekMode::Fast);
741
0
    DECODER_TRY_ALLOC(playback_manager->m_playback_handler->on_enter());
742
743
0
    playback_manager->m_decode_thread->start();
744
745
0
    return playback_manager;
746
0
}
747
748
}