2022-10-29 20:16:14 -03:00
|
|
|
/*
|
2026-04-18 07:00:05 -03:00
|
|
|
* Copyright (c) 2022-2026, Gregory Bertilson <gregory@ladybird.org>
|
2022-10-29 20:16:14 -03:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-27 17:11:06 -03:00
|
|
|
#include <LibMedia/Containers/Matroska/MatroskaDemuxer.h>
|
2025-12-07 18:33:08 -03:00
|
|
|
#include <LibMedia/Demuxer.h>
|
2025-03-10 11:54:02 -03:00
|
|
|
#include <LibMedia/FFmpeg/FFmpegDemuxer.h>
|
2026-04-25 22:51:09 -03:00
|
|
|
#include <LibMedia/GenericTimeProvider.h>
|
2026-04-18 07:33:02 -03:00
|
|
|
#include <LibMedia/PlaybackStates/StartingStateHandler.h>
|
2026-04-25 21:50:24 -03:00
|
|
|
#include <LibMedia/Processors/AudioMixer.h>
|
2026-06-03 09:58:52 -03:00
|
|
|
#include <LibMedia/Processors/AudioTimeStretchProcessor.h>
|
2026-04-25 22:51:09 -03:00
|
|
|
#include <LibMedia/Producers/DecodedAudioProducer.h>
|
|
|
|
|
#include <LibMedia/Producers/DecodedVideoProducer.h>
|
2026-04-25 20:31:38 -03:00
|
|
|
#include <LibMedia/Sinks/AudioPlaybackSink.h>
|
2025-10-03 02:39:39 -03:00
|
|
|
#include <LibMedia/Sinks/DisplayingVideoSink.h>
|
|
|
|
|
#include <LibMedia/Track.h>
|
2025-12-07 18:33:08 -03:00
|
|
|
#include <LibThreading/Thread.h>
|
2026-03-19 09:07:02 -03:00
|
|
|
#include <LibThreading/ThreadPool.h>
|
2022-10-29 20:16:14 -03:00
|
|
|
|
|
|
|
|
#include "PlaybackManager.h"
|
|
|
|
|
|
2024-06-10 17:04:03 -03:00
|
|
|
namespace Media {
|
2022-10-29 20:16:14 -03:00
|
|
|
|
2026-03-19 09:07:02 -03:00
|
|
|
DecoderErrorOr<NonnullRefPtr<Demuxer>> PlaybackManager::create_demuxer_for_stream(NonnullRefPtr<MediaStream> const& stream)
|
2023-04-19 19:11:51 -03:00
|
|
|
{
|
2026-03-19 09:07:02 -03:00
|
|
|
if (Matroska::Reader::is_matroska_or_webm(stream->create_cursor()))
|
|
|
|
|
return Matroska::MatroskaDemuxer::from_stream(stream);
|
|
|
|
|
return FFmpeg::FFmpegDemuxer::from_stream(stream);
|
|
|
|
|
}
|
2023-04-25 10:02:24 -03:00
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
DecoderErrorOr<void> PlaybackManager::prepare_playback_from_demuxer(WeakPlaybackManager const& self, NonnullRefPtr<Demuxer> const& demuxer, Core::EventLoop& main_thread_event_loop)
|
2026-03-19 09:07:02 -03:00
|
|
|
{
|
2026-04-25 22:51:09 -03:00
|
|
|
// Create the video tracks and their producers.
|
2025-10-03 02:39:39 -03:00
|
|
|
auto all_video_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Video));
|
2022-10-29 20:16:14 -03:00
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
auto supported_video_tracks = VideoTracks();
|
|
|
|
|
auto supported_video_track_datas = VideoTrackDatas();
|
|
|
|
|
supported_video_tracks.ensure_capacity(all_video_tracks.size());
|
|
|
|
|
supported_video_track_datas.ensure_capacity(all_video_tracks.size());
|
|
|
|
|
for (auto const& track : all_video_tracks) {
|
2026-06-04 15:00:52 -03:00
|
|
|
auto video_producer_result = DecodedVideoProducer::try_create(main_thread_event_loop, demuxer, track);
|
2026-04-25 22:51:09 -03:00
|
|
|
if (video_producer_result.is_error())
|
2025-10-03 02:39:39 -03:00
|
|
|
continue;
|
|
|
|
|
supported_video_tracks.append(track);
|
2026-04-25 22:51:09 -03:00
|
|
|
supported_video_track_datas.empend(VideoTrackData(track, video_producer_result.release_value(), nullptr));
|
2024-04-25 09:35:56 -03:00
|
|
|
}
|
2025-10-03 02:39:39 -03:00
|
|
|
supported_video_tracks.shrink_to_fit();
|
|
|
|
|
supported_video_track_datas.shrink_to_fit();
|
2024-04-25 09:35:56 -03:00
|
|
|
|
2026-04-25 22:51:09 -03:00
|
|
|
// Create all the audio tracks, their producers, and the audio output.
|
2025-09-24 15:40:17 -03:00
|
|
|
auto all_audio_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Audio));
|
2022-10-29 20:16:14 -03:00
|
|
|
|
2025-09-24 15:40:17 -03:00
|
|
|
auto supported_audio_tracks = AudioTracks();
|
|
|
|
|
auto supported_audio_track_datas = AudioTrackDatas();
|
|
|
|
|
supported_audio_tracks.ensure_capacity(all_audio_tracks.size());
|
|
|
|
|
supported_audio_track_datas.ensure_capacity(all_audio_tracks.size());
|
|
|
|
|
for (auto const& track : all_audio_tracks) {
|
2026-06-04 15:00:52 -03:00
|
|
|
auto audio_producer_result = DecodedAudioProducer::try_create(main_thread_event_loop, demuxer, track);
|
2026-04-25 22:51:09 -03:00
|
|
|
if (audio_producer_result.is_error())
|
2025-09-24 15:40:17 -03:00
|
|
|
continue;
|
2026-04-25 22:51:09 -03:00
|
|
|
auto audio_producer = audio_producer_result.release_value();
|
2025-09-24 15:40:17 -03:00
|
|
|
supported_audio_tracks.append(track);
|
2026-04-25 22:51:09 -03:00
|
|
|
supported_audio_track_datas.empend(AudioTrackData(track, move(audio_producer)));
|
2025-09-24 15:40:17 -03:00
|
|
|
}
|
|
|
|
|
supported_audio_tracks.shrink_to_fit();
|
|
|
|
|
supported_audio_track_datas.shrink_to_fit();
|
|
|
|
|
|
|
|
|
|
if (supported_video_tracks.is_empty() && supported_audio_tracks.is_empty())
|
|
|
|
|
return DecoderError::with_description(DecoderErrorCategory::NotImplemented, "No supported video or audio tracks found"sv);
|
|
|
|
|
|
2025-12-07 18:33:08 -03:00
|
|
|
auto preferred_video_track = demuxer->get_preferred_track_for_type(TrackType::Video).value_or({});
|
|
|
|
|
if (preferred_video_track.has_value() && !supported_video_tracks.contains_slow(*preferred_video_track))
|
|
|
|
|
preferred_video_track = {};
|
|
|
|
|
auto preferred_audio_track = demuxer->get_preferred_track_for_type(TrackType::Audio).value_or({});
|
|
|
|
|
if (preferred_audio_track.has_value() && !supported_audio_tracks.contains_slow(*preferred_audio_track))
|
|
|
|
|
preferred_audio_track = {};
|
|
|
|
|
|
|
|
|
|
auto duration = demuxer->total_duration().value_or(AK::Duration::zero());
|
2026-05-02 22:53:18 -03:00
|
|
|
auto start_time_realtime = demuxer->start_time_realtime();
|
2025-12-07 18:33:08 -03:00
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
main_thread_event_loop.deferred_invoke([self, video_tracks = move(supported_video_tracks), video_track_datas = move(supported_video_track_datas), preferred_video_track, audio_tracks = move(supported_audio_tracks), audio_track_datas = move(supported_audio_track_datas), preferred_audio_track, duration, start_time_realtime] mutable {
|
2026-02-22 20:59:10 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
2025-12-07 18:33:08 -03:00
|
|
|
|
2026-03-19 18:58:27 -03:00
|
|
|
for (auto const& existing_track : self->m_video_tracks) {
|
|
|
|
|
if (video_tracks.contains_slow(existing_track)) {
|
|
|
|
|
self->on_unsupported_format_error(DecoderError::with_description(DecoderErrorCategory::Invalid, "Duplicate video track found"sv));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (auto const& existing_track : self->m_audio_tracks) {
|
|
|
|
|
if (audio_tracks.contains_slow(existing_track)) {
|
|
|
|
|
self->on_unsupported_format_error(DecoderError::with_description(DecoderErrorCategory::Invalid, "Duplicate audio track found"sv));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 09:12:13 -03:00
|
|
|
auto first_new_video_index = self->m_video_tracks.size();
|
|
|
|
|
auto first_new_audio_index = self->m_audio_tracks.size();
|
|
|
|
|
|
2026-02-22 20:59:10 -03:00
|
|
|
self->m_video_tracks.extend(move(video_tracks));
|
|
|
|
|
self->m_video_track_datas.extend(move(video_track_datas));
|
|
|
|
|
self->m_audio_tracks.extend(move(audio_tracks));
|
|
|
|
|
self->m_audio_track_datas.extend(move(audio_track_datas));
|
2026-03-19 09:12:13 -03:00
|
|
|
|
|
|
|
|
if (!self->m_preferred_video_track.has_value())
|
|
|
|
|
self->m_preferred_video_track = preferred_video_track;
|
|
|
|
|
if (!self->m_preferred_audio_track.has_value())
|
|
|
|
|
self->m_preferred_audio_track = preferred_audio_track;
|
2025-11-26 20:32:39 -03:00
|
|
|
|
2026-05-02 22:53:18 -03:00
|
|
|
self->m_start_time_realtime = start_time_realtime;
|
2026-02-22 20:59:10 -03:00
|
|
|
self->check_for_duration_change(duration);
|
2025-12-07 18:33:08 -03:00
|
|
|
|
2026-04-25 22:51:09 -03:00
|
|
|
self->set_up_producers();
|
2026-02-22 20:59:10 -03:00
|
|
|
|
2026-04-01 03:54:56 -03:00
|
|
|
if (!self->m_audio_output_disabled && !self->m_audio_sink && !self->m_audio_tracks.is_empty()) {
|
2026-04-25 21:50:24 -03:00
|
|
|
self->m_audio_mixer = MUST(AudioMixer::try_create());
|
2026-06-03 09:58:52 -03:00
|
|
|
self->m_audio_time_stretch_processor = MUST(AudioTimeStretchProcessor::try_create());
|
2026-04-27 00:23:17 -03:00
|
|
|
self->m_audio_sink = MUST(AudioPlaybackSink::try_create(
|
2026-04-26 07:49:40 -03:00
|
|
|
[self](PipelineStatus status) {
|
|
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->on_audio_sink_state_changed(status);
|
|
|
|
|
}));
|
2026-06-03 09:58:52 -03:00
|
|
|
MUST(self->m_audio_time_stretch_processor->connect_input(*self->m_audio_mixer));
|
|
|
|
|
MUST(self->m_audio_sink->connect_input(*self->m_audio_time_stretch_processor));
|
2026-04-25 20:21:16 -03:00
|
|
|
self->set_time_provider(*self->m_audio_sink);
|
2026-03-21 08:00:58 -03:00
|
|
|
self->m_audio_sink->on_audio_output_error = [self](Error&& error) {
|
|
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
dbgln("Audio output initialization failed with error: {}", error);
|
|
|
|
|
self->disable_audio();
|
|
|
|
|
};
|
2025-12-07 18:33:08 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 20:59:10 -03:00
|
|
|
if (self->on_track_added) {
|
2026-03-19 09:12:13 -03:00
|
|
|
for (size_t i = first_new_audio_index; i < self->m_audio_tracks.size(); i++)
|
2026-03-25 16:31:32 -03:00
|
|
|
self->on_track_added(self->m_audio_tracks[i]);
|
2026-03-19 09:12:13 -03:00
|
|
|
for (size_t i = first_new_video_index; i < self->m_video_tracks.size(); i++)
|
2026-03-25 16:31:32 -03:00
|
|
|
self->on_track_added(self->m_video_tracks[i]);
|
2025-12-07 18:33:08 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 20:59:10 -03:00
|
|
|
if (self->on_metadata_parsed)
|
|
|
|
|
self->on_metadata_parsed();
|
2025-12-07 18:33:08 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2025-09-25 17:15:13 -03:00
|
|
|
|
2026-02-22 20:59:10 -03:00
|
|
|
NonnullOwnPtr<PlaybackManager> PlaybackManager::create()
|
2025-12-07 18:33:08 -03:00
|
|
|
{
|
2026-02-22 20:59:10 -03:00
|
|
|
auto playback_manager = adopt_own(*new (nothrow) PlaybackManager());
|
2026-04-18 07:33:02 -03:00
|
|
|
playback_manager->m_handler = make<StartingStateHandler>(*playback_manager);
|
2026-01-21 21:20:29 -03:00
|
|
|
playback_manager->m_handler->on_enter();
|
2025-10-03 02:39:39 -03:00
|
|
|
return playback_manager;
|
2023-02-06 04:24:33 -03:00
|
|
|
}
|
2022-11-27 03:45:14 -03:00
|
|
|
|
2025-12-07 18:33:08 -03:00
|
|
|
PlaybackManager::PlaybackManager()
|
2026-02-22 20:59:10 -03:00
|
|
|
: m_weak_link(make_ref_counted<WeakPlaybackManagerLink>(*this))
|
2025-12-07 18:33:08 -03:00
|
|
|
, m_time_provider(make_ref_counted<GenericTimeProvider>())
|
2023-02-06 04:24:33 -03:00
|
|
|
{
|
2022-11-09 09:52:37 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
PlaybackManager::~PlaybackManager()
|
2023-02-12 03:44:16 -03:00
|
|
|
{
|
2026-05-26 20:46:15 -03:00
|
|
|
m_time_provider->pause();
|
2026-02-22 20:59:10 -03:00
|
|
|
m_weak_link->revoke({});
|
2023-02-12 03:44:16 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
static void handle_media_init_error(WeakPlaybackManager self, Core::EventLoop& main_thread_event_loop, DecoderError error)
|
2025-12-07 18:33:08 -03:00
|
|
|
{
|
2026-06-04 15:00:52 -03:00
|
|
|
main_thread_event_loop.deferred_invoke([self = move(self), error = move(error)] mutable {
|
2026-03-19 09:07:02 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
if (self->on_unsupported_format_error)
|
|
|
|
|
self->on_unsupported_format_error(move(error));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::add_media_source(NonnullRefPtr<MediaStream> const& stream)
|
|
|
|
|
{
|
|
|
|
|
auto self = weak();
|
2026-06-04 15:00:52 -03:00
|
|
|
auto& main_thread_event_loop = Core::EventLoop::current();
|
2026-03-19 09:07:02 -03:00
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
Threading::ThreadPool::the().submit([self = move(self), stream, &main_thread_event_loop] mutable {
|
2026-03-19 09:07:02 -03:00
|
|
|
auto demuxer_or_error = create_demuxer_for_stream(stream);
|
|
|
|
|
if (demuxer_or_error.is_error()) {
|
2026-06-04 15:00:52 -03:00
|
|
|
handle_media_init_error(move(self), main_thread_event_loop, demuxer_or_error.release_error());
|
2026-03-19 09:07:02 -03:00
|
|
|
return;
|
2025-12-07 18:33:08 -03:00
|
|
|
}
|
2026-03-19 09:07:02 -03:00
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
auto maybe_error = prepare_playback_from_demuxer(self, demuxer_or_error.release_value(), main_thread_event_loop);
|
2026-03-19 09:07:02 -03:00
|
|
|
if (maybe_error.is_error())
|
2026-06-04 15:00:52 -03:00
|
|
|
handle_media_init_error(move(self), main_thread_event_loop, maybe_error.release_error());
|
2026-01-23 18:14:34 -03:00
|
|
|
});
|
2026-03-19 09:07:02 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::add_media_source(NonnullRefPtr<Demuxer> const& demuxer)
|
|
|
|
|
{
|
|
|
|
|
auto self = weak();
|
2026-06-04 15:00:52 -03:00
|
|
|
auto& main_thread_event_loop = Core::EventLoop::current();
|
2025-12-07 18:33:08 -03:00
|
|
|
|
2026-06-04 15:00:52 -03:00
|
|
|
Threading::ThreadPool::the().submit([self = move(self), demuxer, &main_thread_event_loop] mutable {
|
|
|
|
|
auto maybe_error = prepare_playback_from_demuxer(self, demuxer, main_thread_event_loop);
|
2026-03-19 09:07:02 -03:00
|
|
|
if (maybe_error.is_error())
|
2026-06-04 15:00:52 -03:00
|
|
|
handle_media_init_error(move(self), main_thread_event_loop, maybe_error.release_error());
|
2026-03-19 09:07:02 -03:00
|
|
|
});
|
2025-12-07 18:33:08 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 20:59:10 -03:00
|
|
|
WeakPlaybackManager PlaybackManager::weak()
|
|
|
|
|
{
|
|
|
|
|
return WeakPlaybackManager(m_weak_link);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 22:51:09 -03:00
|
|
|
void PlaybackManager::set_up_producers()
|
2023-02-12 03:44:16 -03:00
|
|
|
{
|
2025-10-03 02:39:39 -03:00
|
|
|
for (auto const& video_track_data : m_video_track_datas) {
|
2026-04-26 07:49:40 -03:00
|
|
|
video_track_data.producer->set_error_handler([self = weak()](DecoderError&& error) {
|
2025-10-03 02:39:39 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->dispatch_error(move(error));
|
|
|
|
|
});
|
2026-04-25 22:51:09 -03:00
|
|
|
video_track_data.producer->set_duration_change_handler([self = weak()](AK::Duration time) {
|
2025-11-26 20:32:39 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->check_for_duration_change(time);
|
|
|
|
|
});
|
2023-02-12 03:44:16 -03:00
|
|
|
}
|
2025-09-24 15:40:17 -03:00
|
|
|
|
|
|
|
|
for (auto const& audio_track_data : m_audio_track_datas) {
|
2026-04-26 07:49:40 -03:00
|
|
|
audio_track_data.producer->set_error_handler([self = weak()](DecoderError&& error) {
|
2025-09-24 15:40:17 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->dispatch_error(move(error));
|
|
|
|
|
});
|
2026-04-25 22:51:09 -03:00
|
|
|
audio_track_data.producer->set_duration_change_handler([self = weak()](AK::Duration time) {
|
2025-11-26 20:32:39 -03:00
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->check_for_duration_change(time);
|
|
|
|
|
});
|
2025-09-24 15:40:17 -03:00
|
|
|
}
|
2023-02-12 03:44:16 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 19:25:24 -03:00
|
|
|
AK::Duration PlaybackManager::current_time() const
|
|
|
|
|
{
|
|
|
|
|
auto time = m_handler->current_time();
|
|
|
|
|
return min(time, duration());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 07:49:40 -03:00
|
|
|
void PlaybackManager::on_audio_sink_state_changed(PipelineStatus status)
|
|
|
|
|
{
|
2026-06-09 19:39:16 -03:00
|
|
|
m_audio_sink_status = status;
|
|
|
|
|
update_pipeline_state();
|
2026-04-26 07:49:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::on_video_sink_state_changed(Track const& track, PipelineStatus status)
|
2026-03-21 00:45:30 -03:00
|
|
|
{
|
2026-06-09 19:39:16 -03:00
|
|
|
auto& track_data = get_video_data_for_track(track);
|
|
|
|
|
track_data.sink_status = status;
|
|
|
|
|
update_pipeline_state();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PipelineStatus PlaybackManager::combined_pipeline_status() const
|
|
|
|
|
{
|
|
|
|
|
auto status = PipelineStatus::EndOfStream;
|
|
|
|
|
|
|
|
|
|
if (m_audio_sink != nullptr)
|
|
|
|
|
status = select_combined_pipeline_status(status, m_audio_sink_status);
|
|
|
|
|
|
|
|
|
|
for (auto const& track_data : m_video_track_datas) {
|
|
|
|
|
if (track_data.display == nullptr)
|
|
|
|
|
continue;
|
|
|
|
|
status = select_combined_pipeline_status(status, track_data.sink_status);
|
2026-04-26 07:49:40 -03:00
|
|
|
}
|
2026-06-09 19:39:16 -03:00
|
|
|
|
|
|
|
|
return status;
|
2026-03-21 00:45:30 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 19:39:16 -03:00
|
|
|
void PlaybackManager::update_pipeline_state()
|
2026-03-21 00:45:30 -03:00
|
|
|
{
|
2026-06-09 19:39:16 -03:00
|
|
|
m_handler->on_pipeline_status_changed(combined_pipeline_status());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::reset_pipeline_state()
|
|
|
|
|
{
|
|
|
|
|
for (auto& track_data : m_video_track_datas) {
|
|
|
|
|
if (track_data.display == nullptr)
|
|
|
|
|
continue;
|
|
|
|
|
track_data.sink_status = PipelineStatus::Pending;
|
|
|
|
|
}
|
|
|
|
|
m_audio_sink_status = m_audio_sink != nullptr ? PipelineStatus::Pending : PipelineStatus::HaveData;
|
2026-03-21 00:45:30 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-26 20:32:39 -03:00
|
|
|
void PlaybackManager::check_for_duration_change(AK::Duration duration)
|
|
|
|
|
{
|
|
|
|
|
if (m_duration >= duration)
|
|
|
|
|
return;
|
|
|
|
|
m_duration = duration;
|
|
|
|
|
if (on_duration_change)
|
|
|
|
|
on_duration_change(m_duration);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
void PlaybackManager::dispatch_error(DecoderError&& error)
|
2023-02-12 05:25:25 -03:00
|
|
|
{
|
2026-03-21 00:45:30 -03:00
|
|
|
VERIFY(error.category() != DecoderErrorCategory::EndOfStream);
|
2026-02-26 02:16:57 -03:00
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
if (m_is_in_error_state)
|
|
|
|
|
return;
|
|
|
|
|
m_is_in_error_state = true;
|
|
|
|
|
if (on_error)
|
|
|
|
|
on_error(move(error));
|
2023-02-12 05:25:25 -03:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 08:00:58 -03:00
|
|
|
void PlaybackManager::set_time_provider(NonnullRefPtr<MediaTimeProvider> const& provider)
|
|
|
|
|
{
|
|
|
|
|
auto time = current_time();
|
2026-04-26 16:59:19 -03:00
|
|
|
provider->seek(time);
|
2026-03-21 08:00:58 -03:00
|
|
|
m_time_provider = provider;
|
|
|
|
|
for (auto& track_data : m_video_track_datas) {
|
|
|
|
|
if (!track_data.display)
|
|
|
|
|
continue;
|
|
|
|
|
track_data.display->set_time_provider(provider);
|
|
|
|
|
}
|
2026-06-03 08:37:23 -03:00
|
|
|
provider->set_playback_rate(m_playback_rate);
|
2026-03-21 08:00:58 -03:00
|
|
|
if (is_playing())
|
|
|
|
|
provider->resume();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::disable_audio()
|
|
|
|
|
{
|
2026-04-25 21:50:24 -03:00
|
|
|
m_audio_mixer = nullptr;
|
2026-06-03 09:58:52 -03:00
|
|
|
m_audio_time_stretch_processor = nullptr;
|
2026-03-21 08:00:58 -03:00
|
|
|
m_audio_sink = nullptr;
|
|
|
|
|
set_time_provider(make_ref_counted<GenericTimeProvider>());
|
2026-04-26 07:49:40 -03:00
|
|
|
on_audio_sink_state_changed(PipelineStatus::EndOfStream);
|
2026-03-21 08:00:58 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
NonnullRefPtr<DisplayingVideoSink> PlaybackManager::get_or_create_the_displaying_video_sink_for_track(Track const& track)
|
2023-02-06 04:24:33 -03:00
|
|
|
{
|
2025-10-03 02:39:39 -03:00
|
|
|
auto& track_data = get_video_data_for_track(track);
|
|
|
|
|
if (track_data.display == nullptr) {
|
2026-06-09 19:39:16 -03:00
|
|
|
track_data.sink_status = PipelineStatus::HaveData;
|
2026-04-26 19:24:29 -03:00
|
|
|
auto display = MUST(Media::DisplayingVideoSink::try_create(m_time_provider,
|
2026-04-26 07:49:40 -03:00
|
|
|
[self = weak(), track](PipelineStatus status) {
|
|
|
|
|
if (!self)
|
|
|
|
|
return;
|
|
|
|
|
self->on_video_sink_state_changed(track, status);
|
|
|
|
|
}));
|
2026-04-26 19:24:29 -03:00
|
|
|
MUST(display->connect_input(track_data.producer));
|
|
|
|
|
track_data.display = move(display);
|
2026-06-09 19:39:16 -03:00
|
|
|
update_pipeline_state();
|
2023-02-12 05:36:06 -03:00
|
|
|
}
|
2025-10-03 02:39:39 -03:00
|
|
|
return *track_data.display;
|
|
|
|
|
}
|
2023-02-06 04:24:33 -03:00
|
|
|
|
2025-10-03 02:39:39 -03:00
|
|
|
void PlaybackManager::remove_the_displaying_video_sink_for_track(Track const& track)
|
2024-05-17 13:39:29 -03:00
|
|
|
{
|
2025-10-03 02:39:39 -03:00
|
|
|
auto& track_data = get_video_data_for_track(track);
|
2026-04-01 21:13:31 -03:00
|
|
|
VERIFY(track_data.display);
|
2026-04-26 19:24:29 -03:00
|
|
|
track_data.display->disconnect_input(track_data.producer);
|
2025-10-03 02:39:39 -03:00
|
|
|
track_data.display = nullptr;
|
2026-06-09 19:39:16 -03:00
|
|
|
track_data.sink_status = PipelineStatus::HaveData;
|
|
|
|
|
update_pipeline_state();
|
2024-05-17 13:39:29 -03:00
|
|
|
}
|
|
|
|
|
|
2025-09-24 15:40:17 -03:00
|
|
|
void PlaybackManager::enable_an_audio_track(Track const& track)
|
|
|
|
|
{
|
|
|
|
|
auto& track_data = get_audio_data_for_track(track);
|
2026-04-03 18:48:25 -03:00
|
|
|
VERIFY(!track_data.enabled);
|
2026-06-09 19:39:16 -03:00
|
|
|
m_audio_sink_status = PipelineStatus::HaveData;
|
2026-06-05 13:33:14 -03:00
|
|
|
if (m_audio_mixer) {
|
|
|
|
|
m_audio_mixer->seek(current_time());
|
2026-04-26 19:24:29 -03:00
|
|
|
MUST(m_audio_mixer->connect_input(track_data.producer));
|
2026-06-05 13:33:14 -03:00
|
|
|
}
|
2026-04-18 07:33:02 -03:00
|
|
|
track_data.enabled = true;
|
2026-06-09 19:39:16 -03:00
|
|
|
update_pipeline_state();
|
2025-09-24 15:40:17 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::disable_an_audio_track(Track const& track)
|
|
|
|
|
{
|
|
|
|
|
auto& track_data = get_audio_data_for_track(track);
|
2026-04-03 18:48:25 -03:00
|
|
|
VERIFY(track_data.enabled);
|
2026-06-09 19:39:16 -03:00
|
|
|
m_audio_sink_status = PipelineStatus::HaveData;
|
2026-06-05 13:33:14 -03:00
|
|
|
if (m_audio_mixer) {
|
|
|
|
|
m_audio_mixer->seek(current_time());
|
2026-04-26 19:24:29 -03:00
|
|
|
m_audio_mixer->disconnect_input(track_data.producer);
|
2026-06-05 13:33:14 -03:00
|
|
|
}
|
2026-04-03 18:48:25 -03:00
|
|
|
track_data.enabled = false;
|
2026-06-09 19:39:16 -03:00
|
|
|
update_pipeline_state();
|
2025-09-24 15:40:17 -03:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:21:59 -03:00
|
|
|
bool PlaybackManager::track_is_enabled(Track const& track) const
|
|
|
|
|
{
|
|
|
|
|
if (track.type() == TrackType::Video) {
|
|
|
|
|
auto const& track_data = get_video_data_for_track(track);
|
|
|
|
|
return track_data.display != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VERIFY(track.type() == TrackType::Audio);
|
|
|
|
|
auto const& track_data = get_audio_data_for_track(track);
|
2026-04-26 19:24:29 -03:00
|
|
|
return track_data.enabled;
|
2026-03-31 19:21:59 -03:00
|
|
|
}
|
|
|
|
|
|
2026-04-18 07:33:02 -03:00
|
|
|
void PlaybackManager::start()
|
|
|
|
|
{
|
|
|
|
|
m_handler->start();
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-27 17:13:45 -03:00
|
|
|
void PlaybackManager::play()
|
|
|
|
|
{
|
2025-09-29 18:08:47 -03:00
|
|
|
m_handler->play();
|
2025-09-27 17:13:45 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PlaybackManager::pause()
|
|
|
|
|
{
|
2025-09-29 18:08:47 -03:00
|
|
|
m_handler->pause();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 20:58:11 -03:00
|
|
|
void PlaybackManager::seek(AK::Duration timestamp, SeekMode mode)
|
|
|
|
|
{
|
2026-06-09 19:39:16 -03:00
|
|
|
reset_pipeline_state();
|
2025-10-02 20:58:11 -03:00
|
|
|
m_handler->seek(timestamp, mode);
|
|
|
|
|
m_is_in_error_state = false;
|
2026-06-09 19:39:16 -03:00
|
|
|
update_pipeline_state();
|
2025-10-02 20:58:11 -03:00
|
|
|
}
|
|
|
|
|
|
2025-09-29 18:08:47 -03:00
|
|
|
bool PlaybackManager::is_playing()
|
|
|
|
|
{
|
|
|
|
|
return m_handler->is_playing();
|
2025-09-27 17:13:45 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-02 20:52:47 -03:00
|
|
|
PlaybackState PlaybackManager::state()
|
|
|
|
|
{
|
|
|
|
|
return m_handler->state();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 07:23:13 -03:00
|
|
|
AvailableData PlaybackManager::available_data()
|
2026-03-25 18:38:40 -03:00
|
|
|
{
|
2026-04-18 07:23:13 -03:00
|
|
|
return m_handler->available_data();
|
2026-03-25 18:38:40 -03:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:24:33 -03:00
|
|
|
TimeRanges PlaybackManager::buffered_time_ranges() const
|
|
|
|
|
{
|
2026-05-17 18:06:24 -03:00
|
|
|
TimeRanges intersection { { AK::Duration::zero(), m_duration } };
|
2026-03-31 19:24:33 -03:00
|
|
|
|
|
|
|
|
auto intersect_ranges = [&](auto const& track_datas) {
|
|
|
|
|
for (auto const& track_data : track_datas) {
|
|
|
|
|
if (!track_is_enabled(track_data.track))
|
|
|
|
|
continue;
|
|
|
|
|
|
2026-04-25 22:51:09 -03:00
|
|
|
auto range = track_data.producer->buffered_time_ranges();
|
2026-03-31 19:24:33 -03:00
|
|
|
intersection = intersection.intersection(range);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
intersect_ranges(m_video_track_datas);
|
|
|
|
|
intersect_ranges(m_audio_track_datas);
|
|
|
|
|
|
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-27 18:02:48 -03:00
|
|
|
void PlaybackManager::set_volume(double volume)
|
|
|
|
|
{
|
|
|
|
|
if (m_audio_sink)
|
|
|
|
|
m_audio_sink->set_volume(volume);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 08:37:23 -03:00
|
|
|
void PlaybackManager::set_playback_rate(float rate)
|
|
|
|
|
{
|
|
|
|
|
m_playback_rate = rate;
|
|
|
|
|
m_time_provider->set_playback_rate(rate);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-29 20:16:14 -03:00
|
|
|
}
|