Skip to content

Commit 6caa2f9

Browse files
Zaggy1024gmta
authored andcommitted
LibMedia+LibWeb: Rewrite PlaybackManager using the provider/sink model
With this commit, all PlaybackManager can do is autoplay a file from start to finish, with no pausing or seeking functionality. All audio playback functionality has been removed from HTMLMediaElement and HTMLAudioElement in anticipation of PlaybackManager taking that over, for both audio-only and audio/video.
1 parent 0f9fa47 commit 6caa2f9

File tree

15 files changed

+362
-1225
lines changed

15 files changed

+362
-1225
lines changed

Libraries/LibMedia/PlaybackManager.cpp

Lines changed: 78 additions & 678 deletions
Large diffs are not rendered by default.
Lines changed: 70 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,239 +1,113 @@
11
/*
2-
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
2+
* Copyright (c) 2022-2025, Gregory Bertilson <gregory@ladybird.org>
33
*
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

77
#pragma once
88

9-
#include <AK/Atomic.h>
10-
#include <AK/Function.h>
11-
#include <AK/NonnullOwnPtr.h>
12-
#include <AK/Queue.h>
9+
#include <AK/AtomicRefCounted.h>
10+
#include <AK/Forward.h>
11+
#include <AK/NonnullRefPtr.h>
12+
#include <AK/Stream.h>
1313
#include <AK/Time.h>
14-
#include <LibCore/SharedCircularQueue.h>
15-
#include <LibGfx/Bitmap.h>
16-
#include <LibMedia/Demuxer.h>
14+
#include <AK/Vector.h>
15+
#include <LibMedia/DecoderError.h>
1716
#include <LibMedia/Export.h>
18-
#include <LibThreading/ConditionVariable.h>
17+
#include <LibMedia/Forward.h>
18+
#include <LibMedia/Providers/MediaTimeProvider.h>
19+
#include <LibMedia/Track.h>
1920
#include <LibThreading/Mutex.h>
20-
#include <LibThreading/Thread.h>
21-
22-
#include "VideoDecoder.h"
2321

2422
namespace Media {
2523

26-
class FrameQueueItem {
27-
public:
28-
FrameQueueItem()
29-
: m_data(Empty())
30-
, m_timestamp(AK::Duration::zero())
31-
{
32-
}
33-
34-
static constexpr AK::Duration no_timestamp = AK::Duration::min();
35-
36-
enum class Type {
37-
Frame,
38-
Error,
39-
};
40-
41-
static FrameQueueItem frame(RefPtr<Gfx::Bitmap> bitmap, AK::Duration timestamp)
42-
{
43-
return FrameQueueItem(move(bitmap), timestamp);
44-
}
45-
46-
static FrameQueueItem error_marker(DecoderError&& error, AK::Duration timestamp)
47-
{
48-
return FrameQueueItem(move(error), timestamp);
49-
}
50-
51-
bool is_frame() const { return m_data.has<RefPtr<Gfx::Bitmap>>(); }
52-
RefPtr<Gfx::Bitmap> bitmap() const { return m_data.get<RefPtr<Gfx::Bitmap>>(); }
53-
AK::Duration timestamp() const { return m_timestamp; }
54-
55-
bool is_error() const { return m_data.has<DecoderError>(); }
56-
DecoderError const& error() const { return m_data.get<DecoderError>(); }
57-
DecoderError release_error()
58-
{
59-
auto error = move(m_data.get<DecoderError>());
60-
m_data.set(Empty());
61-
return error;
62-
}
63-
64-
bool is_empty() const { return m_data.has<Empty>(); }
65-
66-
ByteString debug_string() const
67-
{
68-
if (is_error())
69-
return ByteString::formatted("{} at {}ms", error().string_literal(), timestamp().to_milliseconds());
70-
return ByteString::formatted("frame at {}ms", timestamp().to_milliseconds());
71-
}
72-
73-
private:
74-
FrameQueueItem(RefPtr<Gfx::Bitmap> bitmap, AK::Duration timestamp)
75-
: m_data(move(bitmap))
76-
, m_timestamp(timestamp)
77-
{
78-
VERIFY(m_timestamp != no_timestamp);
79-
}
80-
81-
FrameQueueItem(DecoderError&& error, AK::Duration timestamp)
82-
: m_data(move(error))
83-
, m_timestamp(timestamp)
84-
{
85-
}
86-
87-
Variant<Empty, RefPtr<Gfx::Bitmap>, DecoderError> m_data { Empty() };
88-
AK::Duration m_timestamp { no_timestamp };
89-
};
90-
91-
static constexpr size_t frame_buffer_count = 4;
92-
using VideoFrameQueue = Core::SharedSingleProducerCircularQueue<FrameQueueItem, frame_buffer_count>;
93-
94-
enum class PlaybackState {
95-
Playing,
96-
Paused,
97-
Buffering,
98-
Seeking,
99-
Stopped,
100-
};
101-
102-
class MEDIA_API PlaybackManager {
24+
class MEDIA_API PlaybackManager final : public AtomicRefCounted<PlaybackManager> {
10325
AK_MAKE_NONCOPYABLE(PlaybackManager);
10426
AK_MAKE_NONMOVABLE(PlaybackManager);
10527

106-
public:
107-
enum class SeekMode {
108-
Accurate,
109-
Fast,
110-
};
28+
class WeakPlaybackManager;
11129

112-
static constexpr SeekMode DEFAULT_SEEK_MODE = SeekMode::Accurate;
30+
public:
31+
static constexpr size_t EXPECTED_VIDEO_TRACK_COUNT = 1;
11332

114-
static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> from_data(ReadonlyBytes data);
115-
static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> from_stream(NonnullOwnPtr<SeekableStream> stream);
33+
using VideoTracks = Vector<Track, EXPECTED_VIDEO_TRACK_COUNT>;
11634

117-
PlaybackManager(NonnullRefPtr<Demuxer> const& demuxer, Track video_track, NonnullOwnPtr<VideoDecoder>&& decoder, VideoFrameQueue&& frame_queue);
35+
static DecoderErrorOr<NonnullRefPtr<PlaybackManager>> try_create(NonnullOwnPtr<SeekableStream>&& stream);
11836
~PlaybackManager();
11937

120-
void resume_playback();
121-
void pause_playback();
122-
void restart_playback();
123-
void terminate_playback();
124-
void seek_to_timestamp(AK::Duration, SeekMode = DEFAULT_SEEK_MODE);
125-
bool is_playing() const
126-
{
127-
return m_playback_handler->is_playing();
128-
}
129-
PlaybackState get_state() const
130-
{
131-
return m_playback_handler->get_state();
132-
}
38+
AK::Duration current_time() const;
39+
AK::Duration duration() const;
13340

134-
u64 number_of_skipped_frames() const { return m_skipped_frames; }
41+
VideoTracks const& video_tracks() const { return m_video_tracks; }
42+
Optional<Track> preferred_video_track();
13543

136-
AK::Duration current_playback_time();
137-
AK::Duration duration();
44+
// Creates a DisplayingVideoSink for the specified track.
45+
//
46+
// Note that in order for the current frame to change based on the media time, users must call
47+
// DisplayingVideoSink::update(). It is recommended to drive this off of vertical sync.
48+
NonnullRefPtr<DisplayingVideoSink> get_or_create_the_displaying_video_sink_for_track(Track const& track);
49+
// Removes the DisplayingVideoSink for the specified track. This will prevent the sink from
50+
// retrieving any subsequent frames from the decoder.
51+
void remove_the_displaying_video_sink_for_track(Track const& track);
13852

139-
Function<void(RefPtr<Gfx::Bitmap>)> on_video_frame;
140-
Function<void()> on_playback_state_change;
141-
Function<void(DecoderError)> on_decoder_error;
142-
Function<void(Error)> on_fatal_playback_error;
143-
144-
Track const& selected_video_track() const { return m_selected_video_track; }
53+
Function<void(DecoderError&&)> on_error;
14554

14655
private:
147-
class PlaybackStateHandler;
148-
// Abstract class to allow resuming play/pause after the state is completed.
149-
class ResumingStateHandler;
150-
class PlayingStateHandler;
151-
class PausedStateHandler;
152-
class BufferingStateHandler;
153-
class SeekingStateHandler;
154-
class StoppedStateHandler;
155-
156-
static DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> create(NonnullRefPtr<Demuxer> const& demuxer);
157-
158-
void timer_callback();
159-
// This must be called with m_demuxer_mutex locked!
160-
DecoderErrorOr<Optional<AK::Duration>> seek_demuxer_to_most_recent_keyframe(AK::Duration timestamp, Optional<AK::Duration> earliest_available_sample = OptionalNone());
161-
162-
Optional<FrameQueueItem> dequeue_one_frame();
163-
void set_state_update_timer(int delay_ms);
164-
165-
void decode_and_queue_one_sample();
56+
class WeakPlaybackManager final : public MediaTimeProvider {
57+
friend class PlaybackManager;
16658

167-
void dispatch_decoder_error(DecoderError error);
168-
void dispatch_new_frame(RefPtr<Gfx::Bitmap> frame);
169-
// Returns whether we changed playback states. If so, any PlaybackStateHandler processing must cease.
170-
[[nodiscard]] bool dispatch_frame_queue_item(FrameQueueItem&&);
171-
void dispatch_state_change();
172-
void dispatch_fatal_error(Error);
173-
174-
AK::Duration m_last_present_in_media_time = AK::Duration::zero();
175-
176-
NonnullRefPtr<Demuxer> m_demuxer;
177-
Threading::Mutex m_decoder_mutex;
178-
Track m_selected_video_track;
179-
180-
VideoFrameQueue m_frame_queue;
181-
182-
RefPtr<Core::Timer> m_state_update_timer;
183-
184-
RefPtr<Threading::Thread> m_decode_thread;
185-
NonnullOwnPtr<VideoDecoder> m_decoder;
186-
Atomic<bool> m_stop_decoding { false };
187-
Threading::Mutex m_decode_wait_mutex;
188-
Threading::ConditionVariable m_decode_wait_condition;
189-
Atomic<bool> m_buffer_is_full { false };
59+
public:
60+
WeakPlaybackManager() = default;
19061

191-
OwnPtr<PlaybackStateHandler> m_playback_handler;
192-
Optional<FrameQueueItem> m_next_frame;
62+
RefPtr<PlaybackManager> take_strong() const
63+
{
64+
Threading::MutexLocker locker { m_mutex };
65+
return m_manager;
66+
}
19367

194-
u64 m_skipped_frames { 0 };
68+
virtual AK::Duration current_time() const override
69+
{
70+
Threading::MutexLocker locker { m_mutex };
71+
if (m_manager)
72+
return m_manager->current_time();
73+
return AK::Duration::zero();
74+
}
19575

196-
// This is a nested class to allow private access.
197-
class PlaybackStateHandler {
198-
public:
199-
PlaybackStateHandler(PlaybackManager& manager)
200-
: m_manager(manager)
76+
private:
77+
void revoke()
20178
{
79+
Threading::MutexLocker locker { m_mutex };
80+
m_manager = nullptr;
20281
}
203-
virtual ~PlaybackStateHandler() = default;
204-
virtual StringView name() = 0;
20582

206-
virtual ErrorOr<void> on_enter() { return {}; }
83+
mutable Threading::Mutex m_mutex;
84+
PlaybackManager* m_manager { nullptr };
85+
};
20786

208-
virtual ErrorOr<void> play() { return {}; }
209-
virtual bool is_playing() const = 0;
210-
virtual PlaybackState get_state() const = 0;
211-
virtual ErrorOr<void> pause() { return {}; }
212-
virtual ErrorOr<void> buffer() { return {}; }
213-
virtual ErrorOr<void> seek(AK::Duration target_timestamp, SeekMode);
214-
virtual ErrorOr<void> stop();
87+
struct VideoTrackData {
88+
Track track;
89+
NonnullRefPtr<VideoDataProvider> provider;
90+
RefPtr<DisplayingVideoSink> display;
91+
};
92+
using VideoTrackDatas = Vector<VideoTrackData, EXPECTED_VIDEO_TRACK_COUNT>;
21593

216-
virtual AK::Duration current_time() const;
94+
PlaybackManager(NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<WeakPlaybackManager> const&, VideoTracks&&, VideoTrackDatas&&);
21795

218-
virtual ErrorOr<void> do_timed_state_update() { return {}; }
96+
void set_up_error_handlers();
97+
void dispatch_error(DecoderError&&);
21998

220-
protected:
221-
template<class T, class... Args>
222-
ErrorOr<void> replace_handler_and_delete_this(Args... args);
99+
VideoTrackData& get_video_data_for_track(Track const& track);
223100

224-
PlaybackManager& manager() const;
101+
NonnullRefPtr<MutexedDemuxer> m_demuxer;
225102

226-
PlaybackManager& manager()
227-
{
228-
return const_cast<PlaybackManager&>(const_cast<PlaybackStateHandler const*>(this)->manager());
229-
}
103+
NonnullRefPtr<WeakPlaybackManager> m_weak_wrapper;
230104

231-
private:
232-
PlaybackManager& m_manager;
233-
#if PLAYBACK_MANAGER_DEBUG
234-
bool m_has_exited { false };
235-
#endif
236-
};
105+
VideoTracks m_video_tracks;
106+
VideoTrackDatas m_video_track_datas;
107+
108+
MonotonicTime m_real_time_base;
109+
110+
bool m_is_in_error_state { false };
237111
};
238112

239113
}

Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <LibWeb/DOM/Element.h>
1515
#include <LibWeb/HTML/BrowsingContext.h>
1616
#include <LibWeb/HTML/EventLoop/EventLoop.h>
17+
#include <LibWeb/HTML/HTMLMediaElement.h>
1718
#include <LibWeb/HTML/Scripting/Agent.h>
1819
#include <LibWeb/HTML/Scripting/Environments.h>
1920
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
@@ -362,6 +363,10 @@ void EventLoop::update_the_rendering()
362363
return true;
363364
});
364365

366+
// AD-HOC: Update all the displayed video frames on HTMLMediaElements in documents' pages.
367+
for (auto& document : docs)
368+
document->page().update_all_media_element_video_sinks();
369+
365370
// FIXME: 4. Unnecessary rendering: Remove from docs any Document object doc for which all of the following are true:
366371

367372
// FIXME: 5. Remove from docs all Document objects for which the user agent believes that it's preferable to skip updating the rendering for other reasons.

Libraries/LibWeb/HTML/HTMLAudioElement.cpp

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
#include <LibWeb/Bindings/HTMLAudioElementPrototype.h>
88
#include <LibWeb/CSS/ComputedProperties.h>
99
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
10-
#include <LibWeb/HTML/AudioTrack.h>
11-
#include <LibWeb/HTML/AudioTrackList.h>
1210
#include <LibWeb/HTML/HTMLAudioElement.h>
1311
#include <LibWeb/HTML/Window.h>
1412
#include <LibWeb/Layout/AudioBox.h>
@@ -57,32 +55,4 @@ Layout::AudioBox const* HTMLAudioElement::layout_node() const
5755
return static_cast<Layout::AudioBox const*>(Node::layout_node());
5856
}
5957

60-
void HTMLAudioElement::on_playing()
61-
{
62-
audio_tracks()->for_each_enabled_track([](auto& audio_track) {
63-
audio_track.play();
64-
});
65-
}
66-
67-
void HTMLAudioElement::on_paused()
68-
{
69-
audio_tracks()->for_each_enabled_track([](auto& audio_track) {
70-
audio_track.pause();
71-
});
72-
}
73-
74-
void HTMLAudioElement::on_seek(double position, MediaSeekMode seek_mode)
75-
{
76-
audio_tracks()->for_each_enabled_track([&](auto& audio_track) {
77-
audio_track.seek(position, seek_mode);
78-
});
79-
}
80-
81-
void HTMLAudioElement::on_volume_change()
82-
{
83-
audio_tracks()->for_each_enabled_track([&](auto& audio_track) {
84-
audio_track.update_volume();
85-
});
86-
}
87-
8858
}

Libraries/LibWeb/HTML/HTMLAudioElement.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ class HTMLAudioElement final : public HTMLMediaElement {
2929

3030
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;
3131
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
32-
33-
virtual void on_playing() override;
34-
virtual void on_paused() override;
35-
virtual void on_seek(double, MediaSeekMode) override;
36-
virtual void on_volume_change() override;
3732
};
3833

3934
}

0 commit comments

Comments
 (0)