However, the playback rate is still limited to 1.0 at the element, because audio stretching is not yet implemented.
275 lines
8 KiB
C++
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;
|
|
};
|
|
|
|
}
|