Commit graph

8 commits

Author SHA1 Message Date
Zaggy1024
972438c4d7 LibMedia: Abstract the interface of IncrementallyPopulatedStream
The way that other classes interact with IncrementallyPopulatedStream
is now through a virtual interface MediaStream and MediaStreamCursor.
This way, we can have simpler implementations of reading media data
that will not require an RB tree and synchronization.
2026-01-30 10:02:00 -06:00
Zaggy1024
4482f91827 LibMedia: Wake IncrementallyPopulatedStream upon adding a new chunk
In the early exit for adding a new chunk to m_chunks when no chunk
exists before this one, we were not previously waking any cursors that
may be waiting for this data. This would not generally be an issue when
data is coming in in small chunks, since a second chunk will cause it
to wake, but a test case which only appended one chunk exposed this
bug.
2026-01-29 18:06:02 -06:00
Zaggy1024
1b06792e8f LibMedia+LibWeb: Use range requests to fulfill media data
This makes media playback able to start without having to wait for data
to sequentially download, especially when seeking the media to a
timestamp residing in data that hasn't loaded yet.

Initially, the HTMLMediaElement will request the file without range a
range request. Then, if the IncrementallyPopulatedStream finds that it
needs data that is not yet available, it will decide whether to wait
for that data to be received through the current request, or start a
new request that is closer to the required data.

In this commit, it assumes that the server will support range requests.
2026-01-29 05:22:27 -06:00
Zaggy1024
f06105bf3d LibMedia+Meta: Add IncrementallyPopulatedStream::create_from_data
Data will be copied when added to the stream anyway, so callers should
be able to pass ReadonlyBytes instead of ByteBuffer&&.
2026-01-29 05:22:27 -06:00
Aliaksandr Kalenik
7bc5016868 LibMedia: Add IncrementallyPopulatedStream::set_expected_size()
Store the response's `Content-Length` (when available) as an "expected
size" on `IncrementallyPopulatedStream`.

This allows `IncrementallyPopulatedStream::size()` to return a
meaningful total length early, instead of blocking until EOF. That's
important for the FFmpeg MP4 demuxer, which queries the stream size
immediately after initialization.

Additionally, use the expected size to pre-reserve the underlying
`ByteBuffer` capacity, avoiding repeated reallocations as chunks are
appended.
2025-12-16 02:42:48 -06: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
c659cba240 LibMedia: Introduce IncrementallyPopulatedStream
...a shared byte stream that can be appended to as network data arrives.

The stream supports creating multiple independent `Cursor`s, each with
its own read position. This matches our demuxing needs, where different
audio/video tracks may read from the same underlying data concurrently.

`Cursor::read_into()` and `Cursor::seek()` block until the requested
range is available (or the stream is closed). This is intentional: the
FFmpeg `avio_alloc_context()` read callback cannot reliably surface
`EAGAIN` without putting the demuxer into an error state that
requires recovery via seeking, so we instead wait until we can satisfy
the read.
2025-12-16 02:42:48 -06:00