LibWeb: Stop using VideoFrameSource for video frame updates

Future compositor-process work needs to push video frame updates without
going through VideoFrameSource. That object cannot be shared through
IPC, so video display-list resources now use stable VideoFrameResourceId
values and the current frame is sent through explicit resource/update
commands.
This commit is contained in:
Aliaksandr Kalenik 2026-05-18 22:35:31 +02:00 committed by Alexander Kalenik
parent 22fc50e606
commit 6063650261
21 changed files with 157 additions and 165 deletions

View file

@ -875,7 +875,6 @@ set(SOURCES
Painting/RadioButtonPaintable.cpp
Painting/ReplacedElementCommon.cpp
Painting/ResolvedCSSFilter.cpp
Painting/VideoFrameSource.cpp
Painting/ResizeHandle.cpp
Painting/Scrollbar.cpp
Painting/ScrollFrame.cpp

View file

@ -9,6 +9,7 @@
#include <LibGfx/PaintingSurface.h>
#include <LibGfx/SharedImage.h>
#include <LibGfx/SkiaBackendContext.h>
#include <LibMedia/VideoFrame.h>
#include <LibThreading/Thread.h>
#include <LibWeb/Compositor/AsyncScrollTree.h>
#include <LibWeb/Compositor/AsyncScrollingState.h>
@ -63,6 +64,15 @@ struct UpdateScrollStateCommand {
Painting::ScrollStateSnapshot scroll_state_snapshot;
};
struct UpdateVideoFrameCommand {
Painting::VideoFrameResourceId frame_id;
NonnullRefPtr<Media::VideoFrame const> frame;
};
struct ClearVideoFrameCommand {
Painting::VideoFrameResourceId frame_id;
};
struct UpdateCompositorSurfaceCommand {
Painting::CompositorSurfaceId surface_id;
Gfx::SharedImage shared_image;
@ -88,8 +98,8 @@ struct ScreenshotCommand {
};
using CompositorCommand = Variant<UpdateDisplayListCommand, AsyncScrollByCommand, ViewportScrollbarDragCommand,
UpdateScrollStateCommand, UpdateCompositorSurfaceCommand, ClearCompositorSurfaceCommand, ViewportSizeUpdatedCommand,
PresentFrameCommand, ScreenshotCommand>;
UpdateScrollStateCommand, UpdateVideoFrameCommand, ClearVideoFrameCommand, UpdateCompositorSurfaceCommand,
ClearCompositorSurfaceCommand, ViewportSizeUpdatedCommand, PresentFrameCommand, ScreenshotCommand>;
struct CompositorCommandEnvelope {
CompositorContextId context_id;
@ -610,6 +620,12 @@ public:
}
}
},
[&context](UpdateVideoFrameCommand& cmd) {
context.display_list_resource_storage.update_video_frame(cmd.frame_id, move(cmd.frame));
},
[&context](ClearVideoFrameCommand& cmd) {
context.display_list_resource_storage.clear_video_frame(cmd.frame_id);
},
[&context](UpdateCompositorSurfaceCommand& cmd) {
context.display_list_resource_storage.update_compositor_surface(cmd.surface_id, move(cmd.shared_image));
},
@ -1304,6 +1320,16 @@ void CompositorThread::Context::update_display_list(
m_thread_data->enqueue_command(m_context_id, UpdateDisplayListCommand { move(display_list), move(resource_transaction), move(scroll_state_snapshot) });
}
void CompositorThread::Context::update_video_frame(Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame)
{
m_thread_data->enqueue_command(m_context_id, UpdateVideoFrameCommand { frame_id, move(frame) });
}
void CompositorThread::Context::clear_video_frame(Painting::VideoFrameResourceId frame_id)
{
m_thread_data->enqueue_command(m_context_id, ClearVideoFrameCommand { frame_id });
}
void CompositorThread::Context::update_compositor_surface(Painting::CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image)
{
m_thread_data->enqueue_command(m_context_id, UpdateCompositorSurfaceCommand { surface_id, move(shared_image) });

View file

@ -18,6 +18,7 @@
#include <LibGfx/Rect.h>
#include <LibGfx/SharedImage.h>
#include <LibGfx/Size.h>
#include <LibMedia/Forward.h>
#include <LibSync/ConditionVariable.h>
#include <LibThreading/Forward.h>
#include <LibWeb/Compositor/AsyncScrollingState.h>
@ -81,6 +82,8 @@ public:
void set_presentation_mode(PresentationMode);
void update_display_list(NonnullRefPtr<Painting::DisplayList>, Painting::DisplayListResourceTransaction&&, Painting::ScrollStateSnapshot&&);
void update_video_frame(Painting::VideoFrameResourceId, NonnullRefPtr<Media::VideoFrame const>);
void clear_video_frame(Painting::VideoFrameResourceId);
void update_compositor_surface(Painting::CompositorSurfaceId, Gfx::SharedImage&&);
void clear_compositor_surface(Painting::CompositorSurfaceId);
void update_scroll_state(Painting::ScrollStateSnapshot&&);

View file

@ -50,7 +50,6 @@ class DisplayList;
class DisplayListPlayerSkia;
class DisplayListRecorder;
class DisplayListResourceStorage;
class VideoFrameSource;
struct GradientPaintStyle;
struct PatternPaintStyle;
class ScrollStateSnapshot;

View file

@ -131,6 +131,8 @@ void HTMLMediaElement::finalize()
// already cleared.
m_controls.clear();
clear_compositor_video_frame();
document().page().unregister_media_element({}, unique_id());
}
@ -1549,11 +1551,37 @@ void HTMLMediaElement::set_audio_track_enabled(Badge<AudioTrack>, GC::Ptr<HTML::
m_playback_manager->disable_an_audio_track(audio_track->track_in_playback_manager());
}
Painting::VideoFrameSource& HTMLMediaElement::ensure_video_frame_source()
Painting::VideoFrameResourceId HTMLMediaElement::ensure_video_frame_resource_id()
{
if (!m_video_frame_source)
m_video_frame_source = Painting::VideoFrameSource::create();
return *m_video_frame_source;
if (!m_video_frame_resource_id.has_value())
m_video_frame_resource_id = Painting::allocate_video_frame_resource_id();
return *m_video_frame_resource_id;
}
void HTMLMediaElement::update_compositor_video_frame(NonnullRefPtr<Media::VideoFrame const> frame)
{
auto frame_id = ensure_video_frame_resource_id();
if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
navigable->compositor_context().update_video_frame(frame_id, move(frame));
}
void HTMLMediaElement::clear_compositor_video_frame()
{
if (!m_video_frame_resource_id.has_value())
return;
if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
navigable->compositor_context().clear_video_frame(*m_video_frame_resource_id);
}
void HTMLMediaElement::update_current_video_frame()
{
if (auto current_frame = m_selected_video_track_sink->current_frame())
update_compositor_video_frame(NonnullRefPtr<Media::VideoFrame const> { *current_frame });
else
clear_compositor_video_frame();
auto intrinsic_dimensions_changed = update_intrinsic_video_dimensions();
set_needs_repaint(intrinsic_dimensions_changed ? InvalidateDisplayList::Yes : InvalidateDisplayList::No);
}
void HTMLMediaElement::set_selected_video_track(Badge<VideoTrack>, GC::Ptr<HTML::VideoTrack> video_track)
@ -1563,8 +1591,7 @@ void HTMLMediaElement::set_selected_video_track(Badge<VideoTrack>, GC::Ptr<HTML:
if (video_track && !m_playback_manager->video_tracks().contains_slow(video_track->track_in_playback_manager()))
return;
if (m_video_frame_source)
m_video_frame_source->clear();
clear_compositor_video_frame();
auto previous_track = m_selected_video_track;
@ -1573,12 +1600,7 @@ void HTMLMediaElement::set_selected_video_track(Badge<VideoTrack>, GC::Ptr<HTML:
m_selected_video_track_sink = m_playback_manager->get_or_create_the_displaying_video_sink_for_track(video_track->track_in_playback_manager());
auto sink_update_result = m_selected_video_track_sink->update();
if (sink_update_result == Media::DisplayingVideoSinkUpdateResult::NewFrameAvailable) {
if (auto current_frame = m_selected_video_track_sink->current_frame())
ensure_video_frame_source().update(move(current_frame));
else if (m_video_frame_source)
m_video_frame_source->clear();
update_intrinsic_video_dimensions();
set_needs_repaint();
update_current_video_frame();
} else if (auto* video_element = as_if<HTMLVideoElement>(this)) {
auto const& video_data = video_track->track_in_playback_manager().video_data();
video_element->set_intrinsic_video_dimensions(Gfx::Size<u32>(video_data.pixel_width, video_data.pixel_height));
@ -1598,14 +1620,8 @@ void HTMLMediaElement::update_video_frame_and_timeline()
if (m_selected_video_track_sink) {
auto sink_update_result = m_selected_video_track_sink->update();
if (sink_update_result == Media::DisplayingVideoSinkUpdateResult::NewFrameAvailable) {
if (auto current_frame = m_selected_video_track_sink->current_frame())
ensure_video_frame_source().update(move(current_frame));
else if (m_video_frame_source)
m_video_frame_source->clear();
update_intrinsic_video_dimensions();
set_needs_repaint();
}
if (sink_update_result == Media::DisplayingVideoSinkUpdateResult::NewFrameAvailable)
update_current_video_frame();
}
// Wait for the seek to complete before updating the timestamp, otherwise we'll display the timestamp from
@ -2008,6 +2024,7 @@ void HTMLMediaElement::forget_media_resource_specific_tracks()
m_audio_tracks->remove_all_tracks();
m_video_tracks->remove_all_tracks();
m_playback_manager.clear();
clear_compositor_video_frame();
// NB: At this point, we no longer have any selected tracks to derive the video dimensions from.
update_intrinsic_video_dimensions();

View file

@ -9,6 +9,7 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/Time.h>
#include <AK/Variant.h>
@ -21,7 +22,7 @@
#include <LibWeb/HTML/EventLoop/Task.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/HTML/MediaControls.h>
#include <LibWeb/Painting/VideoFrameSource.h>
#include <LibWeb/Painting/DisplayListResourceIds.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/WebIDL/DOMException.h>
@ -170,9 +171,10 @@ public:
RefPtr<Media::DisplayingVideoSink> const& selected_video_track_sink() const { return m_selected_video_track_sink; }
Painting::VideoFrameSource& ensure_video_frame_source();
Painting::VideoFrameResourceId ensure_video_frame_resource_id();
Optional<Painting::VideoFrameResourceId> video_frame_resource_id() const { return m_video_frame_resource_id; }
virtual void update_intrinsic_video_dimensions() { }
virtual bool update_intrinsic_video_dimensions() { return false; }
virtual void update_natural_dimensions() { }
protected:
@ -245,6 +247,9 @@ private:
void volume_or_muted_attribute_changed();
void update_volume();
void update_compositor_video_frame(NonnullRefPtr<Media::VideoFrame const>);
void clear_compositor_video_frame();
void update_current_video_frame();
bool is_eligible_for_autoplay() const;
@ -380,7 +385,7 @@ private:
bool m_has_enabled_preferred_audio_track { false };
bool m_has_selected_preferred_video_track { false };
RefPtr<Painting::VideoFrameSource> m_video_frame_source;
Optional<Painting::VideoFrameResourceId> m_video_frame_resource_id;
};
}

View file

@ -138,21 +138,23 @@ u32 HTMLVideoElement::video_height() const
return 0;
}
void HTMLVideoElement::update_intrinsic_video_dimensions()
bool HTMLVideoElement::update_intrinsic_video_dimensions()
{
if (selected_video_track_sink() == nullptr) {
auto had_intrinsic_video_dimensions = m_intrinsic_video_dimensions.has_value();
set_intrinsic_video_dimensions({});
return;
return had_intrinsic_video_dimensions;
}
auto current_frame = selected_video_track_sink()->current_frame();
if (current_frame == nullptr)
return;
return false;
auto current_frame_size = current_frame->size();
if (current_frame_size == m_intrinsic_video_dimensions)
return;
return false;
set_intrinsic_video_dimensions(current_frame_size);
return true;
}
void HTMLVideoElement::update_natural_dimensions()

View file

@ -38,7 +38,7 @@ public:
u32 video_width() const;
u32 video_height() const;
virtual void update_intrinsic_video_dimensions() override;
virtual bool update_intrinsic_video_dimensions() override;
virtual void update_natural_dimensions() override;
Optional<Gfx::Size<u32>> natural_media_size() const;
Optional<CSSPixelSize> natural_element_size() const;

View file

@ -23,7 +23,6 @@
#include <LibWeb/Painting/DisplayListCommand.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
#include <LibWeb/Painting/ScrollState.h>
#include <LibWeb/Painting/VideoFrameSource.h>
namespace Web::Painting {
@ -97,7 +96,7 @@ private:
virtual void draw_scaled_decoded_image_frame(DrawScaledDecodedImageFrame const&) = 0;
virtual void draw_repeated_decoded_image_frame(DrawRepeatedDecodedImageFrame const&) = 0;
virtual void draw_compositor_surface(DrawCompositorSurface const&) = 0;
virtual void draw_video_frame_source(DrawVideoFrameSource const&) = 0;
virtual void draw_video_frame(DrawVideoFrame const&) = 0;
virtual void save(Save const&) = 0;
virtual void save_layer(SaveLayer const&) = 0;
virtual void restore(Restore const&) = 0;

View file

@ -45,7 +45,7 @@ void DrawCompositorSurface::dump(StringBuilder& builder) const
builder.appendff(" dst_rect={}", dst_rect);
}
void DrawVideoFrameSource::dump(StringBuilder& builder) const
void DrawVideoFrame::dump(StringBuilder& builder) const
{
builder.appendff(" dst_rect={}", dst_rect);
}

View file

@ -39,7 +39,7 @@ class DisplayList;
V(DrawScaledDecodedImageFrame, draw_scaled_decoded_image_frame) \
V(DrawRepeatedDecodedImageFrame, draw_repeated_decoded_image_frame) \
V(DrawCompositorSurface, draw_compositor_surface) \
V(DrawVideoFrameSource, draw_video_frame_source) \
V(DrawVideoFrame, draw_video_frame) \
V(Save, save) \
V(SaveLayer, save_layer) \
V(Restore, restore) \
@ -177,12 +177,12 @@ struct DrawCompositorSurface {
void dump(StringBuilder&) const;
};
struct DrawVideoFrameSource {
static constexpr StringView command_name = "DrawVideoFrameSource"sv;
static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawVideoFrameSource;
struct DrawVideoFrame {
static constexpr StringView command_name = "DrawVideoFrame"sv;
static constexpr DisplayListCommandType command_type = DisplayListCommandType::DrawVideoFrame;
Gfx::IntRect dst_rect;
VideoFrameResourceId source_id;
VideoFrameResourceId video_frame_id;
Gfx::ScalingMode scaling_mode;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }

View file

@ -39,7 +39,6 @@
#include <LibGfx/YUVData.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
#include <LibWeb/Painting/VideoFrameSource.h>
namespace Web::Painting {
@ -183,9 +182,9 @@ void DisplayListPlayerSkia::draw_compositor_surface(DrawCompositorSurface const&
canvas.drawImageRect(image.get(), src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint);
}
void DisplayListPlayerSkia::draw_video_frame_source(DrawVideoFrameSource const& command)
void DisplayListPlayerSkia::draw_video_frame(DrawVideoFrame const& command)
{
auto frame = resource_storage().video_frame_source(command.source_id).current_frame();
auto frame = resource_storage().video_frame(command.video_frame_id);
if (!frame)
return;

View file

@ -29,7 +29,7 @@ private:
void draw_scaled_decoded_image_frame(DrawScaledDecodedImageFrame const&) override;
void draw_repeated_decoded_image_frame(DrawRepeatedDecodedImageFrame const&) override;
void draw_compositor_surface(DrawCompositorSurface const&) override;
void draw_video_frame_source(DrawVideoFrameSource const&) override;
void draw_video_frame(DrawVideoFrame const&) override;
void add_clip_rect(AddClipRect const&) override;
void save(Save const&) override;
void save_layer(SaveLayer const&) override;

View file

@ -510,13 +510,13 @@ void DisplayListRecorder::draw_compositor_surface(Gfx::IntRect const& dst_rect,
});
}
void DisplayListRecorder::draw_video_frame_source(Gfx::IntRect const& dst_rect, NonnullRefPtr<VideoFrameSource> source, Gfx::ScalingMode scaling_mode)
void DisplayListRecorder::draw_video_frame(Gfx::IntRect const& dst_rect, VideoFrameResourceId frame_id, RefPtr<Media::VideoFrame const> frame, Gfx::ScalingMode scaling_mode)
{
if (dst_rect.is_empty())
return;
append_command(DrawVideoFrameSource {
append_command(DrawVideoFrame {
.dst_rect = dst_rect,
.source_id = resource_storage().add_video_frame_source(move(source)),
.video_frame_id = resource_storage().add_video_frame(frame_id, move(frame)),
.scaling_mode = scaling_mode,
});
}

View file

@ -20,6 +20,7 @@
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibGfx/ScalingMode.h>
#include <LibMedia/Forward.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/AccumulatedVisualContext.h>
@ -73,7 +74,7 @@ public:
void draw_scaled_decoded_image_frame(Gfx::IntRect const& dst_rect, Gfx::IntRect const& clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_compositor_surface(Gfx::IntRect const& dst_rect, CompositorSurfaceId, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_video_frame_source(Gfx::IntRect const& dst_rect, NonnullRefPtr<VideoFrameSource>, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_video_frame(Gfx::IntRect const& dst_rect, VideoFrameResourceId, RefPtr<Media::VideoFrame const>, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
void draw_repeated_decoded_image_frame(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, Gfx::DecodedImageFrame frame, Gfx::ScalingMode scaling_mode, bool repeat_x, bool repeat_y);

View file

@ -18,6 +18,12 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, VideoFrameResourceId);
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, DisplayListResourceId);
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, CompositorSurfaceId);
inline VideoFrameResourceId allocate_video_frame_resource_id()
{
static Atomic<u64> s_next_id { 1 };
return VideoFrameResourceId { s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed) };
}
inline CompositorSurfaceId allocate_compositor_surface_id()
{
static Atomic<u64> s_next_id { 1 };

View file

@ -7,6 +7,7 @@
#include <LibGfx/Filter.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/SharedImageBuffer.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
@ -28,11 +29,10 @@ ImageFrameResourceId DisplayListResourceStorage::add_image_frame(Gfx::DecodedIma
return { id };
}
VideoFrameResourceId DisplayListResourceStorage::add_video_frame_source(NonnullRefPtr<VideoFrameSource const> source)
VideoFrameResourceId DisplayListResourceStorage::add_video_frame(VideoFrameResourceId id, RefPtr<Media::VideoFrame const> frame)
{
auto id = source->id();
m_video_frame_sources.ensure(id, [&] { return move(source); });
return { id };
m_video_frames.set(id.value(), move(frame), AK::HashSetExistingEntryBehavior::Keep);
return id;
}
DisplayListResourceId DisplayListResourceStorage::add_display_list(NonnullRefPtr<DisplayList const> display_list)
@ -57,8 +57,8 @@ void DisplayListResourceStorage::append_referenced_resources_from(
add_font(source.font(id));
for (auto id : referenced_resources.image_frames)
add_image_frame(source.image_frame(id));
for (auto id : referenced_resources.video_frame_sources)
add_video_frame_source(source.video_frame_source(id));
for (auto id : referenced_resources.video_frames)
add_video_frame(id, source.video_frame(id));
for (auto id : referenced_resources.display_lists)
add_display_list(source.display_list(id));
}
@ -80,8 +80,8 @@ void DisplayListResourceStorage::collect_referenced_resources(
referenced_resources.fonts.set(command.font_id, AK::HashSetExistingEntryBehavior::Keep);
if constexpr (requires { command.frame_id; })
referenced_resources.image_frames.set(command.frame_id, AK::HashSetExistingEntryBehavior::Keep);
if constexpr (requires { video_frame_source(command.source_id); })
referenced_resources.video_frame_sources.set(command.source_id, AK::HashSetExistingEntryBehavior::Keep);
if constexpr (requires { command.video_frame_id; })
referenced_resources.video_frames.set(command.video_frame_id, AK::HashSetExistingEntryBehavior::Keep);
if constexpr (requires { command.paint_style; command.paint_kind; }) {
if (command.paint_kind == decltype(command.paint_kind)::PaintStyle
&& command.paint_style.type == DisplayListPaintStyleType::Pattern)
@ -139,9 +139,9 @@ DisplayListResourceTransaction DisplayListResourceStorage::create_transaction(
if (!previous.image_frames.contains(id))
transaction.image_frames.append(image_frame(id));
}
for (auto id : current.video_frame_sources) {
if (!previous.video_frame_sources.contains(id))
transaction.video_frame_sources.append(video_frame_source(id));
for (auto id : current.video_frames) {
if (!previous.video_frames.contains(id))
transaction.video_frames.append({ id, video_frame(id) });
}
for (auto id : current.display_lists) {
if (!previous.display_lists.contains(id))
@ -156,9 +156,9 @@ DisplayListResourceTransaction DisplayListResourceStorage::create_transaction(
if (!current.image_frames.contains(id))
transaction.image_frame_ids_to_remove.append(id);
}
for (auto id : previous.video_frame_sources) {
if (!current.video_frame_sources.contains(id))
transaction.video_frame_source_ids_to_remove.append(id);
for (auto id : previous.video_frames) {
if (!current.video_frames.contains(id))
transaction.video_frame_ids_to_remove.append(id);
}
for (auto id : previous.display_lists) {
if (!current.display_lists.contains(id))
@ -173,8 +173,8 @@ void DisplayListResourceStorage::apply_transaction(DisplayListResourceTransactio
add_font(*font);
for (auto const& frame : transaction.image_frames)
add_image_frame(frame);
for (auto& source : transaction.video_frame_sources)
add_video_frame_source(move(source));
for (auto& video_frame : transaction.video_frames)
add_video_frame(video_frame.id, move(video_frame.frame));
for (auto& display_list : transaction.display_lists)
add_display_list(move(display_list));
@ -182,8 +182,8 @@ void DisplayListResourceStorage::apply_transaction(DisplayListResourceTransactio
m_fonts.remove(id.value());
for (auto id : transaction.image_frame_ids_to_remove)
m_image_frames.remove(id.value());
for (auto id : transaction.video_frame_source_ids_to_remove)
m_video_frame_sources.remove(id.value());
for (auto id : transaction.video_frame_ids_to_remove)
m_video_frames.remove(id.value());
for (auto id : transaction.display_list_ids_to_remove)
m_display_lists.remove(id.value());
}
@ -192,10 +192,21 @@ void DisplayListResourceStorage::retain_only(DisplayListResourceSet const& resou
{
m_fonts.remove_all_matching([&](auto id, auto const&) { return !resource_set.fonts.contains(FontResourceId { id }); });
m_image_frames.remove_all_matching([&](auto id, auto const&) { return !resource_set.image_frames.contains(ImageFrameResourceId { id }); });
m_video_frame_sources.remove_all_matching([&](auto id, auto const&) { return !resource_set.video_frame_sources.contains(VideoFrameResourceId { id }); });
m_video_frames.remove_all_matching([&](auto id, auto const&) { return !resource_set.video_frames.contains(VideoFrameResourceId { id }); });
m_display_lists.remove_all_matching([&](auto id, auto const&) { return !resource_set.display_lists.contains(DisplayListResourceId { id }); });
}
void DisplayListResourceStorage::update_video_frame(VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame)
{
m_video_frames.set(frame_id.value(), move(frame));
}
void DisplayListResourceStorage::clear_video_frame(VideoFrameResourceId frame_id)
{
if (m_video_frames.contains(frame_id.value()))
m_video_frames.set(frame_id.value(), nullptr);
}
void DisplayListResourceStorage::update_compositor_surface(CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image)
{
auto shared_image_buffer = Gfx::SharedImageBuffer::import_from_shared_image(move(shared_image));

View file

@ -12,32 +12,38 @@
#include <AK/Noncopyable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/RefPtr.h>
#include <AK/Span.h>
#include <AK/Vector.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Forward.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/DisplayListResourceIds.h>
#include <LibWeb/Painting/VideoFrameSource.h>
namespace Web::Painting {
struct DisplayListResourceSet {
HashTable<FontResourceId> fonts;
HashTable<ImageFrameResourceId> image_frames;
HashTable<VideoFrameResourceId> video_frame_sources;
HashTable<VideoFrameResourceId> video_frames;
HashTable<DisplayListResourceId> display_lists;
};
struct DisplayListVideoFrameResource {
VideoFrameResourceId id;
RefPtr<Media::VideoFrame const> frame;
};
struct DisplayListResourceTransaction {
Vector<NonnullRefPtr<Gfx::Font const>> fonts;
Vector<Gfx::DecodedImageFrame> image_frames;
Vector<NonnullRefPtr<VideoFrameSource const>> video_frame_sources;
Vector<DisplayListVideoFrameResource> video_frames;
Vector<NonnullRefPtr<DisplayList const>> display_lists;
Vector<FontResourceId> font_ids_to_remove;
Vector<ImageFrameResourceId> image_frame_ids_to_remove;
Vector<VideoFrameResourceId> video_frame_source_ids_to_remove;
Vector<VideoFrameResourceId> video_frame_ids_to_remove;
Vector<DisplayListResourceId> display_list_ids_to_remove;
};
@ -51,7 +57,7 @@ public:
FontResourceId add_font(Gfx::Font const&);
ImageFrameResourceId add_image_frame(Gfx::DecodedImageFrame const&);
VideoFrameResourceId add_video_frame_source(NonnullRefPtr<VideoFrameSource const>);
VideoFrameResourceId add_video_frame(VideoFrameResourceId, RefPtr<Media::VideoFrame const> = nullptr);
DisplayListResourceId add_display_list(NonnullRefPtr<DisplayList const>);
void append_referenced_resources_from(DisplayListResourceStorage const& source, ReadonlyBytes command_bytes);
void apply_transaction(DisplayListResourceTransaction&&);
@ -59,12 +65,14 @@ public:
DisplayListResourceSet collect_referenced_resources(DisplayList const&) const;
DisplayListResourceSet collect_referenced_resources(ReadonlyBytes command_bytes) const;
void retain_only(DisplayListResourceSet const&);
void update_video_frame(VideoFrameResourceId, NonnullRefPtr<Media::VideoFrame const>);
void clear_video_frame(VideoFrameResourceId);
void update_compositor_surface(CompositorSurfaceId, Gfx::SharedImage&&);
void clear_compositor_surface(CompositorSurfaceId);
Gfx::Font const& font(FontResourceId id) const { return *m_fonts.get(id.value()).value(); }
Gfx::DecodedImageFrame const& image_frame(ImageFrameResourceId id) const { return m_image_frames.get(id.value()).value(); }
VideoFrameSource const& video_frame_source(VideoFrameResourceId id) const { return *m_video_frame_sources.get(id.value()).value(); }
RefPtr<Media::VideoFrame const> video_frame(VideoFrameResourceId id) const { return m_video_frames.get(id.value()).value(); }
DisplayList const& display_list(DisplayListResourceId id) const { return *m_display_lists.get(id.value()).value(); }
Optional<Gfx::DecodedImageFrame const&> compositor_surface(CompositorSurfaceId id) const { return m_compositor_surfaces.get(id.value()); }
@ -73,7 +81,7 @@ private:
HashMap<u64, NonnullRefPtr<Gfx::Font const>> m_fonts;
HashMap<u64, Gfx::DecodedImageFrame> m_image_frames;
HashMap<u64, NonnullRefPtr<VideoFrameSource const>> m_video_frame_sources;
HashMap<u64, RefPtr<Media::VideoFrame const>> m_video_frames;
HashMap<u64, NonnullRefPtr<DisplayList const>> m_display_lists;
HashMap<u64, Gfx::DecodedImageFrame> m_compositor_surfaces;
};

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Atomic.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Painting/VideoFrameSource.h>
namespace Web::Painting {
static Atomic<u64> s_next_id { 1 };
NonnullRefPtr<VideoFrameSource> VideoFrameSource::create()
{
return adopt_ref(*new VideoFrameSource());
}
VideoFrameSource::VideoFrameSource()
: m_id(s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed))
{
}
VideoFrameSource::~VideoFrameSource() = default;
void VideoFrameSource::update(RefPtr<Media::VideoFrame> frame)
{
RefPtr<Media::VideoFrame> old;
{
Sync::MutexLocker const locker { m_mutex };
old = move(m_frame);
m_frame = move(frame);
}
}
void VideoFrameSource::clear()
{
RefPtr<Media::VideoFrame> old;
{
Sync::MutexLocker const locker { m_mutex };
old = move(m_frame);
}
}
RefPtr<Media::VideoFrame> VideoFrameSource::current_frame() const
{
Sync::MutexLocker const locker { m_mutex };
return m_frame;
}
}

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/AtomicRefCounted.h>
#include <AK/RefPtr.h>
#include <LibMedia/Forward.h>
#include <LibSync/Mutex.h>
namespace Web::Painting {
class VideoFrameSource final : public AtomicRefCounted<VideoFrameSource> {
public:
static NonnullRefPtr<VideoFrameSource> create();
~VideoFrameSource();
u64 id() const { return m_id; }
void update(RefPtr<Media::VideoFrame>);
void clear();
RefPtr<Media::VideoFrame> current_frame() const;
private:
VideoFrameSource();
u64 m_id { 0 };
mutable Sync::Mutex m_mutex;
RefPtr<Media::VideoFrame> m_frame;
};
}

View file

@ -7,6 +7,7 @@
#include <LibGfx/Bitmap.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibMedia/Sinks/DisplayingVideoSink.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/HTMLMediaElement.h>
@ -61,8 +62,9 @@ void VideoPaintable::paint(DisplayListRecordingContext& context, PaintPhase phas
};
auto paint_video_frame = [&]() {
auto& source = const_cast<HTML::HTMLVideoElement&>(video_element).ensure_video_frame_source();
auto current = source.current_frame();
RefPtr<Media::VideoFrame> current;
if (auto const& sink = video_element.selected_video_track_sink())
current = sink->current_frame();
Gfx::IntSize src_size;
if (current)
@ -76,7 +78,9 @@ void VideoPaintable::paint(DisplayListRecordingContext& context, PaintPhase phas
if (dst_rect.is_empty())
return;
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), src_size, dst_rect.size());
context.display_list_recorder().draw_video_frame_source(dst_rect, source, scaling_mode);
RefPtr<Media::VideoFrame const> frame = current;
auto frame_id = const_cast<HTML::HTMLVideoElement&>(video_element).ensure_video_frame_resource_id();
context.display_list_recorder().draw_video_frame(dst_rect, frame_id, move(frame), scaling_mode);
};
auto paint_transparent_black = [&]() {