Commit Graph

33 Commits

Author SHA1 Message Date
Zaggy1024
786bc63aaa LibMedia: Don't try to decode after destroying suspended data providers
If a data provider enters the suspended state, then is requested to
exit its thread, it could exit handle_suspension() without creating a
decoder. After this, we weren't checking if we should continue handling
seeks/data decoding. Thus, we got to push_data_and_decode_some_frames()
without a decoder and crashed.

Instead, always skip an iteration of the thread loop when the suspend
finishes, so that the loop has a change to exit.
2026-02-09 09:04:21 +01:00
Zaggy1024
a9591a1d5c LibMedia: Assert that we have a decoder where needed in data providers
This makes things a little clearer when we might crash due to the
decoder being null. Instead of a VERIFY being hit in OwnPtr.h with no
line numbers on macOS, we'll get `VERIFICATION FAILED: m_decoder`.
2026-02-09 09:04:21 +01:00
Zaggy1024
876f31dcef LibMedia: Check the data providers' exit state in wait loops
Without these, we may not exit the thread if we enter these loops while
exiting.
2026-01-30 17:27:44 -06:00
Zaggy1024
847edc405e LibMedia: Avoid a recursive lock to check exit state in data providers 2026-01-30 17:27:44 -06:00
Zaggy1024
75231e63b1 LibMedia: Only pass Demuxer to the data providers
...and abstract away the stream/cursor blocking/aborting functionality
so that demuxers can implement or ignore those methods as they see fit.

This is a step towards implementing a wrapper demuxer for MSE streams.
2026-01-30 10:02:00 -06:00
Zaggy1024
5687adffb3 LibMedia: Avoid reentrant locks in data providers' suspension methods
This was causing a freeze when IncrementallyPopulatedStream was
blocking on unavailable data.
2026-01-27 13:23:57 -06:00
Zaggy1024
e2635af2ed Everywhere: Move the thread name parameter for Thread constructors
The name parameter formats very poorly when a lambda is passed to
Thread, so let's instead put it first now that all Threads are named.
2026-01-26 15:51:46 -06:00
Zaggy1024
d2a1d727ac Everywhere: Give unnamed threads names 2026-01-26 15:51:46 -06:00
Zaggy1024
c4e95079e1 LibMedia: Destroy decoders while data providers are suspended
This doesn't appear to result in much of a delay in resumption,
creating the decoder takes about 1ms.
2026-01-26 15:49:07 -06:00
Zaggy1024
f9081fbde6 LibMedia: Dispose future media data and flush decoders when idle
In order to free up memory when a video is paused for an extended
period, we add a new Suspended state to PlaybackManager which tells the
data providers to suspend. The data providers will handle this signal
by disposing of their entire decoded data queue and flushing their
decoder.

When initially creating a PlaybackManager, and when resuming to a
paused state, the delay before suspension will be much lower than when
pausing from any other state. This is intended to prevent media
elements from consuming memory for long when decoding the first frame
for display, as well as to allow the data providers to suspend much
more quickly after a seek while paused.

Currently, resuming playback doesn't display much of a delay on my
MacBook, though that may change once we completely tear down the
decoder in the suspended state. It may also be exacerbated by using
hardware decoders due more complex decoder initialization.
2026-01-26 15:49:07 -06:00
Zaggy1024
e6dbcccb99 LibGfx+LibMedia: Send video frames to Skia as subsampled YUV
This saves us from having our own color conversion code, which was
taking up a fair amount of time in VideoDataProvider. With this change,
we should be able to play high resolution videos without interruptions
on machines where the CPU can keep up with decoding.

In order to make this change, ImmutableBitmap is now able to be
constructed with YUV data instead of an RBG bitmap. It holds onto a
YUVData instance that stores the buffers of image data, since Skia
itself doesn't take ownership of them.

In order to support greater than 8 bits of color depth, we normalize
the 10- or 12-bit color values into a 16-bit range.
2026-01-22 19:44:36 +01:00
Zaggy1024
9a421ffe9f LibMedia: Make demuxers thread-safe and remove MutexedDemuxer 2026-01-07 00:13:32 +01:00
Gingeh
451177f1f4 LibMedia: Propagate errors from create_context_for_track 2026-01-02 16:19:44 +01:00
Aliaksandr Kalenik
3a56e9580a LibMedia: Add "buffering" playback state
`IncrementallyPopulatedStream::Cursor` now tracks whether it's currently
blocked inside a wait for more bytes, allowing higher layers to
distinguish "no frames yet" from "decoder is idle".

Enter buffering when `DisplayingVideoSink` runs out of frames and the
associated `VideoDataProvider` is blocked waiting for data to arrive.
Exit buffering once decoding refills the frame queue.

For now, buffering behaves like paused, but it gives us an explicit
state to hook UI into.
2025-12-16 02:42:48 -06:00
Aliaksandr Kalenik
21e8ece013 LibMedia: Abort blocking reads for ongoing seek if it's replaced
When a seek is requested while a previous seek is still blocked waiting
for not yet available bytes, we want to abandon the old request
immediately and start processing the new one.
2025-12-16 02:42:48 -06:00
Aliaksandr Kalenik
c5d8cb5c47 LibMedia: Change demuxers to use IncrementallyPopulatedStream as input
Refactor the FFmpeg and Matroska demuxers to consume data through
`IncrementallyPopulatedStream::Cursor` instead of a pointer to fully
buffered.

This change establishes a new rule: each track must be initialized with
its own cursor. Data providers now explicitly create a per-track context
via `Demuxer::create_context_for_track(track, cursor)`, and own pointer
to that cursor. In the upcoming changes, holding the cursor in the
provider would allow to signal "cancel blocking reads" so an
in-flight seek can fail immediately when a newer seek request arrives.
2025-12-16 02:42:48 -06:00
Zaggy1024
c187315a20 LibMedia: Fix a typo in a comment in VideoDataProvider.cpp 2025-12-13 08:58:26 +01:00
Zaggy1024
d3374655cc LibMedia: Extend the playback duration based on demuxed/decoded frames 2025-12-10 16:02:40 -06:00
Zaggy1024
81a473314d LibMedia: Store the duration of VideoFrames 2025-12-10 16:02:40 -06:00
Zaggy1024
b48c5b4b27 LibMedia+Tests: Avoid deferred-invoking on dead event loops
Posting callbacks to the main thread is now predicated on whether the
event loop reference is alive, preventing a stack-use-after-return.

The data providers will also check if they've been requested to exit
before calling deferred_invoke, though this is not going to be the case
unless the media element gets GCed while the media is playing.
2025-12-10 16:02:40 -06:00
Zaggy1024
c2267e9dfe LibMedia: Don't crash upon calling start on data providers twice 2025-12-10 16:02:40 -06:00
Aliaksandr Kalenik
4249b14907 LibMedia: Delete unused VideoDataProvider::try_create() overload 2025-12-10 11:41:40 -06:00
Aliaksandr Kalenik
9f60828a57 LibMedia+LibWeb: Create demuxer and extract tracks on a separate thread
Demuxer creation and track+duration extraction are moved to a separate
thread so that the media data byte buffer is no longer accessed from the
main thread. This will be important once the buffer is populated
incrementally, as having the main thread both populate and read from the
same buffer could easily lead to deadlocks. Aside from that, moving
demuxer creation off the main thread helps to be more responsive.

`VideoDataProvider` and `AudioDataProvider` now accept the main thread
event loop pointer as they are constructed from the thread responsible
for demuxer creation.
2025-12-09 17:36:18 -06:00
Zaggy1024
bd1c9aedbc LibMedia: Only set data provider error state from the decoding thread
...and use that assumption to avoid locking when checking if we're in
that state.
2025-12-03 12:20:49 -06:00
Zaggy1024
2bd541c70c LibMedia: Keep data providers suspended until they are active
This allows us to avoid waiting on the condition variable for the error
handler to be set when an error occurs.
2025-12-03 12:20:49 -06:00
Zaggy1024
8bfaae473d LibMedia: Use only the ThreadData::wake() function in data providers 2025-12-03 12:20:49 -06:00
Zaggy1024
40d1f42418 LibMedia: Keep data providers' ThreadDatas alive in deferred_invoke
We need strong references to the thread data in order to prevent a UAF
when, for example, a seek starts as GC is destroying a media element.
2025-11-21 10:28:01 -06:00
Zaggy1024
9037f4e832 LibMedia: Signal EOF to decoders in the data providers
This lets the FFmpeg H.264 decoder know when we've reached the end of
the available data, so that it will output the remaining buffered video
frames.

Not sure why it needs this signal to output the buffered frames, but
surely there's a good reason. :^)
2025-11-05 18:40:02 +01:00
Zaggy1024
634e5ff491 LibMedia: Move CICP values from CodedFrame to Track
All our current demuxers have these constant for an entire track, so we
don't need to get them for every frame we output.
2025-11-05 18:40:02 +01:00
Zaggy1024
6f0d7f1caf LibMedia: Don't lock while calling data providers' seeking handlers
We already take locks for the important parts of the data providers'
handle_seek methods, but holding the lock for the entire duration of
the method call prevents cancelling a seek. With this change, locks are
only only held in the thread's loops when updating the loop conditions
and waiting, allowing the seek ID to increase during a seek.
2025-11-03 16:39:24 -06:00
Zaggy1024
ccf4b3f6e9 LibMedia: Implement media seeking
This implementation allows:
- Accurate seeking to an exact timestamp
- Seeking to the keyframe before a timestamp
- Seeking to the keyframe after a timestamp
These three options will be used to satisfy the playback position
selection in the media element's seeking steps.
2025-10-27 17:28:49 -07:00
Zaggy1024
9117f661a8 LibMedia: Split some repeated code in VideoDataProvider to functions 2025-10-27 17:28:49 -07:00
Zaggy1024
7e238cd724 LibMedia: Add separate classes managing decoding and displaying video
These are unused in this commit, but will later be used to output video
via PlaybackManager, or to decode video directly to some consumer.
2025-10-27 17:28:49 -07:00