Commit Graph

282 Commits

Author SHA1 Message Date
Zaggy1024
08faa83340 LibMedia+LibWeb: Add an initial Starting state to PlaybackManager
This state will indicate to the media element that it's not guaranteed
to have a frame yet, for the purposes of determining the ready state.
JavaScript should be sure that video elements with a ready state of
HAVE_CURRENT_DATA or greater represent the current video frame already.

To allow the state to be exited if audio is disabled, audio tracks are
now only added to the buffering set on enable if the audio sink exists,
since without the sink starting the data provider, it will never be
removed.

This is a step towards making video ref tests.
2026-04-21 19:11:24 -05:00
Zaggy1024
e1e752cc28 LibMedia+LibWeb: Indicate playback states' available data with an enum
This allows us to differentiate between having no data available yet,
having current data, and having future data. The main purpose of this
is to allow a new starting state to explicitly force HAVE_METADATA
instead of >= HAVE_CURRENT_DATA.

Note that the SeekingStateHandler returns Current instead of None. This
is deliberate, since the buffered ranges from the demuxer(s) can be
used to inform whether the possibly-current data is actually available
at the seek target.
2026-04-21 19:11:24 -05:00
Zaggy1024
bedcccbdb9 LibMedia: Simplify starting/stopping buffering
The check for the previous state isn't really necessary, states that
don't care about buffering can just make this a no-op.
2026-04-21 19:11:24 -05:00
Jonathan Gamble
baefb51902 LibWeb: Add Media Capture and Stream APIs 2026-04-21 16:40:46 -05:00
Zaggy1024
102ce69c8d LibGfx/LibMedia: Override transfer characteristics to sRGB in LibGfx
Doing this in FFmpegVideoDecoder meant that the fallback to BT.709 with
unspecified transfer characteristics was taking precedence. Instead of
overriding unspecified to sRGB there, change the switch statement in
ColorSpace::from_cicp() so that the unspecified fallback and override
both live in the same place.

This should fix the contrast on a lot of YouTube videos.

Note that this now also affects images, which is consistent with
Chromium.
2026-04-18 01:25:00 -05:00
Zaggy1024
e42d9efcb6 LibMedia: Remove reserved values from CICP enums
These served no purpose, instead we should determine validity with a
full switch statement.
2026-04-18 01:25:00 -05:00
Zaggy1024
c47971e563 LibGfx: Stop storing SkYUVAPixmaps in YUVData
Storing these was pointless, since they're only used briefly to provide
the info needed to upload the buffers to the GPU.

For BT.2020 coefficients, we can just say that the bit depth is 16 bits
since we're always bit replicating to that for Skia's shaders.
2026-04-18 01:25:00 -05:00
Zaggy1024
acd4ea9129 LibMedia: Ensure that Matroska seeks after a get_frames() call fails
This fixes a rare flake in HTMLMediaElement-load-after-decode-error,
where if the demuxer managed to get a block successfully but have its
reads in get_frames() aborted by the initial seek, it would skip the
single bad frame and never fire the error event.
2026-04-10 15:21:07 -05:00
Zaggy1024
ab9776955f LibMedia: Allow audio tracks to be enabled without a mixing sink
This allows audio elements in headless mode to advance their ready
states past HAVE_CURRENT_DATA.
2026-04-10 15:21:07 -05:00
Zaggy1024
c8382bb465 LibMedia+Tests: Start PlaybackManager in the Paused state again
Having PlaybackManager start in Buffering was causing us to report
a media element readyState of HAVE_CURRENT_DATA. HAVE_CURRENT_DATA
doesn't make a whole lot of sense for local files, since we should have
all the data immediately when we process the metadata. This is
reflected in the buffered attribute, so let's not limit the ready state
unecessarily.
2026-04-10 15:21:07 -05:00
Zaggy1024
d8cff1838a LibMedia: Don't suspend data providers if the manager isn't suspended
This could cause audio or video to get stuck if the suspension ended
while the seek of an enabled track was still ongoing.
2026-04-10 15:21:07 -05:00
Zaggy1024
e96759859e LibMedia: Simplify SeekingStateHandler's logic
One behavior change: seeking with no enabled tracks now properly
resumes.
2026-04-10 15:21:07 -05:00
Zaggy1024
8208d6f3c5 LibMedia: Ignore unknown track types in FFmpegDemuxer
Fixes #8804.
2026-04-08 13:03:39 +02:00
Andreas Kling
b23aa38546 AK: Adopt mimalloc v2 as main allocator
Use mimalloc for Ladybird-owned allocations without overriding malloc().
Route kmalloc(), kcalloc(), krealloc(), and kfree() through mimalloc,
and put the embedded Rust crates on the same allocator via a shared
shim in AK/kmalloc.cpp.

This also lets us drop kfree_sized(), since it no longer used its size
argument. StringData, Utf16StringData, JS object storage, Rust error
strings, and the CoreAudio playback helpers can all free their AK-backed
storage with plain kfree().

Sanitizer builds still use the system allocator. LeakSanitizer does not
reliably trace references stored in mimalloc-managed AK containers, so
static caches and other long-lived roots can look leaked. Pass the old
size into the Rust realloc shim so aligned fallback reallocations can
move posix_memalign-backed blocks safely.

Static builds still need a little linker help. macOS app binaries need
the Rust allocator entry points forced in from liblagom-ak.a, while
static ELF links can pull in identical allocator shim definitions from
multiple Rust staticlibs. Keep the Apple -u flags and allow those
duplicate shim symbols for LibJS and LibRegex links on Linux and BSD.
2026-04-08 09:57:53 +02:00
Zaggy1024
666ce0f854 LibMedia: Deal with tracks being toggled during seeks properly
This fixes a crash when a track is enabled and then disabled while a
seek is in progress.

The logic in SeekingStateHandler is reworked to keep track of the
tracks that are currently being seeked, and when a track is disabled,
it is no longer counted against the seek completion. Any seek
completion callback that was instated is cleared by calling seek with
a null callback.

It may be worth making a separate function on the data providers to
clear the current seek instead, to avoid the extra work of seeking, but
this scenario is a very rare one unless someone intentionally triggers
it, and the cost is minimal unless the toggles are spammed.

A crash test is included, which both tests for the crash, and would
also time out if the failing VERIFY in on_track_enabled() was avoided
with the previous seeking implementation, due to the originally-enabled
video track's seek callback being clobbered by on_track_enabled()'s
seek.
2026-04-03 20:13:15 -05:00
Zaggy1024
1467127d35 LibMedia+LibWeb: Disable audio output in headless mode
Audio output on macOS was consuming Core Audio resources until the
PlaybackStream creation took well over the timeout for some tests.
This was observed in media-source-buffered.html, where it would time
out due to the long-running callback on the main thread to create the
PlaybackStream for AudioMixingSink.

However, the AudioUnit init should definitely not be blocking the main
thread, so I've added a FIXME there.
2026-04-01 02:54:22 -05:00
Zaggy1024
c4612110f5 LibMedia: Add a setter for PlaybackManager's duration
This will be needed for MediaSource's duration setter.
2026-04-01 02:54:22 -05:00
Zaggy1024
d40ad922ba LibMedia: Add a method to determine whether there is future media data
This is delegated to the state handlers, but it essentially amounts to
`state() != Buffering && state() != Seeking`. If the PlaybackManager is
in either state, we know that there is no future data yet, as it should
exit those states as soon as the data is ready.
2026-04-01 02:54:22 -05:00
Zaggy1024
9d26a274e4 LibMedia: Allow demuxers to report buffered times to PlaybackManager
PlaybackManager then intersects all enabled tracks' buffered time
ranges. This will be used by the media element for the buffered
attribute and to update the ready state.
2026-04-01 02:54:22 -05:00
Zaggy1024
1b488f9595 LibMedia: Add PlaybackManager::track_is_enabled()
And in order to get the track data in a const context, use deducing
this on get_video_data_for_track() and get_audio_data_for_track().
2026-04-01 02:54:22 -05:00
Zaggy1024
2a75c0db62 LibMedia: Add a TimeRanges class to be used for buffered attributes
This will be used by PlaybackManager and Demuxer implementations to
communicate the buffered time up to HTMLMediaElement and SourceBuffer.
2026-04-01 02:54:22 -05:00
Zaggy1024
5083a96c90 LibMedia: Add a declaration for the Matroska Chapters element
We'll need to check for this element in the MSE WebM byte stream parser
in order to skip it.
2026-04-01 02:54:22 -05:00
Zaggy1024
b5eddc4ff0 LibMedia: Stop using av_find_best_stream() to select preferred tracks
Apparently this function uses a bitrate heuristic to determine which
track is best. We don't want or need that, so just select the first
track with default disposition (e.g. FlagDefault=1 in Matroska).
2026-04-01 02:54:22 -05:00
Zaggy1024
a3309fe5f6 LibMedia: Set track kinds in FFmpegDemuxer according to spec
For tracks that aren't ISOBMFF (and therefore have no specified kind),
just always set "main".
2026-04-01 02:54:22 -05:00
Zaggy1024
09f3b8015d LibMedia: Specify WebM track kinds based on the track sourcing spec
This should bring is completely in line with the spec. We specify all
non-WebM text tracks as kind "subtitles", per the spec note.
2026-04-01 02:54:22 -05:00
Zaggy1024
498e571772 LibMedia: Move Matroska::track_from_track_entry to Document.h
This will need to be used by the MSE WebM byte stream parser.
2026-04-01 02:54:22 -05:00
Zaggy1024
b4db8f11c5 LibMedia+LibWeb: Align Media::Track more to the web spec
...giving tracks a kind attribute, and renaming name to label.

Demuxers will need to determine the kind attribute, since the spec for
sourcing tracks requires us to select based on info we don't expose.
2026-04-01 02:54:22 -05:00
Zaggy1024
9664c11c15 LibMedia+LibWeb: Remove an unnecessary parameter from on_track_added
The TrackType parameter is redundant, since the actual Track object
already contains it.
2026-04-01 02:54:22 -05:00
Zaggy1024
d6f821f22d LibMedia: Parse Opus frame durations in Matroska::Reader
Most WebM files don't have their default duration defined, so we need
to parse the Opus frame header to determine the duration. This is
needed for buffered range calculation.
2026-04-01 02:54:22 -05:00
Zaggy1024
6b45a11716 LibMedia: Calculate Matroska block timestamps for their actual tracks
Instead of using a single track entry for all blocks in the file, use a
lookup to get the info needed to calculate the timestamp for the
specific track a block belongs to. No change in behavior for
SampleIterator, since that only returns blocks from the track that was
passed. This will be useful for MSE, since it demuxes all tracks at
once.
2026-04-01 02:54:22 -05:00
Zaggy1024
1834dfdb63 LibMedia: Prevent duplicate PlaybackManager tracks across media sources 2026-04-01 02:54:22 -05:00
Zaggy1024
ac6fa2f2f6 LibMedia: Support tracks from multiple sources at once 2026-04-01 02:54:22 -05:00
Zaggy1024
d7a1c66d4f LibMedia: Accept streams or demuxers for playback manager sources
This will allow us to pass in a class implementing Demuxer for each
track owned by a MediaSource.

We'll also use the new ThreadPool class instead of a dedicated media
initialization thread. We shouldn't spin up a new thread for such a
trivial operation.
2026-04-01 02:54:22 -05:00
Zaggy1024
ca811192ab LibMedia: Make Matroska::Reader::duration() const 2026-04-01 02:54:22 -05:00
Zaggy1024
b23abd4b11 LibMedia: Simplify reading of Matroska VINTs and handle unknown sizes
Unknown sizes are referenced in the MSE WebM spec, so we need to expose
them in the reader functions.
2026-04-01 02:54:22 -05:00
Zaggy1024
25754993ce LibMedia: Add a method to read a Matroska signed integer and use it
The TrackOffset element doesn't use a signed VINT, but rather a VINT
size followed by an integer of that size.
2026-04-01 02:54:22 -05:00
Zaggy1024
c867a5b4cd LibMedia: Ensure Matroska integer elements aren't too large
The EBML spec mandates that these don't exceed 8 bytes.

Also, add a check for a length of 0 for floats, this is allowed by the
spec.
2026-04-01 02:54:22 -05:00
Zaggy1024
227d78b16c LibMedia: Expose some Matroska element parsing functions for MSE
We'll want to reuse these for a byte stream parser, which has some more
restrictions over what we do in Matroska::Reader.
2026-04-01 02:54:22 -05:00
Zaggy1024
cd5daa5135 LibMedia: Move Matroska element IDs to their own file
These will be used to sniff for initialization segments in MSE.
2026-04-01 02:54:22 -05:00
Zaggy1024
2809af55f8 LibMedia: Add a MediaStreamCursor subclass for reading ReadonlyBytes
This is useful for testing, and will also be used to parse incoming MSE
data.
2026-04-01 02:54:22 -05:00
Zaggy1024
90e334c97a LibMedia: Remove a leftover log from PlaybackManager::set_time_provider 2026-04-01 02:54:22 -05:00
Zaggy1024
792771bfa0 LibMedia: Avoid unnecessary atomic refs in PulseAudio streams 2026-03-30 20:57:04 -05:00
Zaggy1024
f1c05aefde LibMedia: Don't keep PlaybackStreamPulseAudio alive in its thread
As soon as the promise is resolved, we need to unref it, so move it to
the deferred_invoke() callback. This allows PulseAudio streams to be
released properly and gives TestPlaybackStream a chance to pass instead
of timing out.
2026-03-30 20:57:04 -05:00
Zaggy1024
6e8f6df872 LibMedia: Skip PlaybackStream rejection if the main event loop is dead
This was causing a flake on CI due to the race between the teardown of
the event loop and the rejection of the promise from another thread.

Regressed in 39d865b.
2026-03-30 20:57:04 -05:00
Zaggy1024
5e770666a8 LibMedia: Detect buffering in audio tracks as well as video
Previously, we would just listen to the single video track for
buffering, so if for some reason the audio data runs ahead of the
video, we would drop some audio until the video buffered. Instead,
stop playing audio at the last available sample when any provider is
blocked.

Also, PlaybackManager now starts in the Buffering state, so that it can
wait for enough data to be ready to play without interruption. When the
end of the stream is reached, the buffering state is exited to ensure
that we don't get stuck buffering at the end of a media file.
2026-03-21 23:11:47 -05:00
Zaggy1024
512ed03e78 LibMedia: Gracefully fall back to video-only playback when audio fails
Hook up a callback in AudioMixingSink to notify PlaybackManager if the
output fails to be initialized. Then, when that happens, swap out the
time provider for GenericTimeProvider and continue without audio.

Fixes #8071
2026-03-21 23:11:47 -05:00
Zaggy1024
4c30d0209a LibMedia: Remove redundant parameter names in PlaybackManager.h 2026-03-21 23:11:47 -05:00
Zaggy1024
39d865b403 LibMedia: Provide new PlaybackStreams through promises
This allows us to avoid returning a PlaybackStream in cases where the
async initialization fails.

This is a step towards more graceful fallbacks when audio fails in
AudioMixingSink.
2026-03-21 23:11:47 -05:00
Andreas Kling
eb789e790e Everywhere: Use AK::SaturatingMath and remove Checked saturating APIs
Port all callers of Checked<T>::saturating_add/sub/mul to the new
standalone functions in AK/SaturatingMath.h, and remove the old
APIs from Checked.
2026-03-21 18:20:09 -05:00
Zaggy1024
f6ed54baf4 LibWeb: Keep the media element alive until fetches complete
d146adf made the fetch callbacks use the media element via weak
references. This caused the `error` event not to fire on media elements
that are detached from the document and go out of scope, if the GC got
to them before the fetch completed.

Instead of relying on weak references in the callbacks, we can stop the
ongoing fetch when the document becomes inactive to allow it to be GCed
after that point. By storing the FetchData on the media element, we're
able to resume the fetch where it left off if the document becomes
active again.

We could potentially figure out a way to make elements with no event
handlers and no parent stop their fetches in order to be GCed sooner,
but that is probably a bit fiddly, so may not be worth it for now.

Fixes a rare flake in WPT's `html/semantics/embedded-content/media-
elements/error-codes/error.html` test. A test to force the bug using
`Internals::gc()` has been added.
2026-03-01 23:13:22 -06:00