ladybird/Libraries/LibMedia/PlaybackManager.h
Zaggy1024 7ddbc2d7dd LibWeb+LibMedia: Wire up setting the pipeline's playback rate
However, the playback rate is still limited to 1.0 at the element,
because audio stretching is not yet implemented.
2026-06-06 19:58:17 -05:00

275 lines
8 KiB
C++

/*
* Copyright (c) 2022-2025, Gregory Bertilson <gregory@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/AtomicRefCounted.h>
#include <AK/Forward.h>
#include <AK/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Time.h>
#include <AK/Vector.h>
#include <LibCore/EventLoop.h>
#include <LibMedia/DecoderError.h>
#include <LibMedia/Export.h>
#include <LibMedia/Forward.h>
#include <LibMedia/MediaTimeProvider.h>
#include <LibMedia/PipelineStatus.h>
#include <LibMedia/PlaybackStates/Forward.h>
#include <LibMedia/PlaybackStates/PlaybackState.h>
#include <LibMedia/TimeRanges.h>
#include <LibMedia/Track.h>
#include <LibSync/Mutex.h>
namespace Media {
class WeakPlaybackManagerLink;
class WeakPlaybackManager;
class MEDIA_API PlaybackManager final {
AK_MAKE_NONCOPYABLE(PlaybackManager);
AK_MAKE_NONMOVABLE(PlaybackManager);
#define __MAKE_PLAYBACK_STATE_HANDLER_FRIEND(clazz) \
friend class clazz;
ENUMERATE_PLAYBACK_STATE_HANDLERS(__MAKE_PLAYBACK_STATE_HANDLER_FRIEND)
#undef __MAKE_PLAYBACK_STATE_HANDLER_FRIEND
public:
static constexpr size_t EXPECTED_VIDEO_TRACK_COUNT = 1;
using VideoTracks = Vector<Track, EXPECTED_VIDEO_TRACK_COUNT>;
static constexpr size_t EXPECTED_AUDIO_TRACK_COUNT = 1;
using AudioTracks = Vector<Track, EXPECTED_AUDIO_TRACK_COUNT>;
static NonnullOwnPtr<PlaybackManager> create();
~PlaybackManager();
void set_audio_output_disabled(bool disabled) { m_audio_output_disabled = disabled; }
AK::Duration duration() const { return m_duration; }
void set_duration(AK::Duration duration) { m_duration = duration; }
AK::Duration current_time() const { return min(m_time_provider->current_time(), duration()); }
Optional<AK::UnixDateTime> start_time_realtime() const { return m_start_time_realtime; }
auto const& video_tracks() const { return m_video_tracks; }
auto const& audio_tracks() const { return m_audio_tracks; }
Optional<Track> preferred_video_track() { return m_preferred_video_track; }
Optional<Track> preferred_audio_track() { return m_preferred_audio_track; }
// Creates a DisplayingVideoSink for the specified track.
//
// Note that in order for the current frame to change based on the media time, users must call
// DisplayingVideoSink::update(). It is recommended to drive this off of vertical sync.
NonnullRefPtr<DisplayingVideoSink> get_or_create_the_displaying_video_sink_for_track(Track const&);
// Removes the DisplayingVideoSink for the specified track. This will prevent the sink from
// retrieving any subsequent frames from the decoder.
void remove_the_displaying_video_sink_for_track(Track const&);
void enable_an_audio_track(Track const&);
void disable_an_audio_track(Track const&);
bool track_is_enabled(Track const&) const;
void start();
void play();
void pause();
void seek(AK::Duration timestamp, SeekMode);
bool is_playing();
PlaybackState state();
AvailableData available_data();
TimeRanges buffered_time_ranges() const;
void set_volume(double);
void set_playback_rate(float);
Function<void()> on_metadata_parsed;
Function<void(DecoderError&&)> on_unsupported_format_error;
Function<void(Track const&)> on_track_added;
Function<void()> on_playback_state_change;
Function<void(AK::Duration)> on_duration_change;
Function<void(DecoderError&&)> on_error;
void add_media_source(NonnullRefPtr<MediaStream> const&);
void add_media_source(NonnullRefPtr<Demuxer> const&);
private:
struct VideoTrackData {
Track track;
NonnullRefPtr<DecodedVideoProducer> producer;
RefPtr<DisplayingVideoSink> display;
};
using VideoTrackDatas = Vector<VideoTrackData, EXPECTED_VIDEO_TRACK_COUNT>;
struct AudioTrackData {
Track track;
NonnullRefPtr<DecodedAudioProducer> producer;
bool enabled { false };
};
using AudioTrackDatas = Vector<AudioTrackData, EXPECTED_AUDIO_TRACK_COUNT>;
PlaybackManager();
WeakPlaybackManager weak();
void set_time_provider(NonnullRefPtr<MediaTimeProvider> const&);
void disable_audio();
void set_up_producers();
void on_audio_sink_state_changed(PipelineStatus);
void on_video_sink_state_changed(Track const&, PipelineStatus);
void update_buffering_state();
void check_for_duration_change(AK::Duration);
void dispatch_error(DecoderError&&);
template<typename Self>
decltype(auto) get_video_data_for_track(this Self&& self, Track const& track)
{
for (auto& track_data : self.m_video_track_datas) {
if (track_data.track == track)
return track_data;
}
VERIFY_NOT_REACHED();
}
template<typename Self>
decltype(auto) get_audio_data_for_track(this Self&& self, Track const& track)
{
for (auto& track_data : self.m_audio_track_datas) {
if (track_data.track == track)
return track_data;
}
VERIFY_NOT_REACHED();
}
static DecoderErrorOr<NonnullRefPtr<Demuxer>> create_demuxer_for_stream(NonnullRefPtr<MediaStream> const&);
static DecoderErrorOr<void> prepare_playback_from_demuxer(WeakPlaybackManager const&, NonnullRefPtr<Demuxer> const&, Core::EventLoop&);
template<typename T, typename... Args>
void replace_state_handler(Args&&... args);
inline void dispatch_state_change() const;
OwnPtr<PlaybackStateHandler> m_handler;
NonnullRefPtr<WeakPlaybackManagerLink> m_weak_link;
NonnullRefPtr<MediaTimeProvider> m_time_provider;
float m_playback_rate { 1.0f };
bool m_audio_output_disabled { false };
VideoTracks m_video_tracks;
VideoTrackDatas m_video_track_datas;
RefPtr<AudioMixer> m_audio_mixer;
RefPtr<AudioPlaybackSink> m_audio_sink;
AudioTracks m_audio_tracks;
AudioTrackDatas m_audio_track_datas;
Optional<Track> m_preferred_video_track;
Optional<Track> m_preferred_audio_track;
AK::Duration m_duration;
Optional<AK::UnixDateTime> m_start_time_realtime;
bool m_audio_buffering { false };
HashTable<Track> m_video_tracks_buffering;
bool m_was_buffering { false };
bool m_is_in_error_state { false };
};
template<typename T, typename... Args>
void PlaybackManager::replace_state_handler(Args&&... args)
{
m_handler->on_exit();
OwnPtr<PlaybackStateHandler> new_handler = make<T>(*this, args...);
m_handler.swap(new_handler);
m_handler->on_enter();
dispatch_state_change();
}
void PlaybackManager::dispatch_state_change() const
{
if (on_playback_state_change)
on_playback_state_change();
}
class WeakPlaybackManagerLink : public AtomicRefCounted<WeakPlaybackManagerLink> {
public:
WeakPlaybackManagerLink(PlaybackManager& manager)
: m_manager(&manager)
, m_originating_event_loop(Core::EventLoop::current())
{
}
bool is_alive() const
{
verify_thread_is_originating_thread();
return m_manager != nullptr;
}
PlaybackManager& get() const
{
VERIFY(is_alive());
return *m_manager;
}
void revoke(Badge<PlaybackManager>)
{
Sync::MutexLocker locker { m_mutex };
m_manager = nullptr;
}
private:
void verify_thread_is_originating_thread() const
{
VERIFY(Core::EventLoop::is_running());
VERIFY(&Core::EventLoop::current() == &m_originating_event_loop);
}
mutable Sync::Mutex m_mutex;
PlaybackManager* m_manager { nullptr };
Core::EventLoop& m_originating_event_loop;
};
class WeakPlaybackManager {
AK_MAKE_DEFAULT_COPYABLE(WeakPlaybackManager);
AK_MAKE_DEFAULT_MOVABLE(WeakPlaybackManager);
public:
WeakPlaybackManager(WeakPlaybackManagerLink& link)
: m_link(link)
{
}
operator bool() const
{
return m_link->is_alive();
}
PlaybackManager& operator*() const
{
return m_link->get();
}
PlaybackManager* operator->() const
{
return &m_link->get();
}
private:
NonnullRefPtr<WeakPlaybackManagerLink> m_link;
};
}