Commit Graph

10 Commits

Author SHA1 Message Date
Zaggy1024
4778c6a53b LibMedia: Deal with aborts in FFmpegDemuxer instead of the IO context
libavcodec apparently holds onto any error that is not AVERROR_EOF when
a read fails. This means that reading until EOF after an aborted read
results in us receiving an AVERROR_EXIT in FFmpegDemuxer instead of
AVERROR_EOF, which causes the playback system to enter an error state
without decoding all frames in the file.

Instead, just always return AVERROR_EOF, and check if the read was
aborted in FFmpegDemuxer instead to return the correct error category
from there.
2026-02-24 16:55:40 -06:00
Zaggy1024
af45418fbf Everywhere: Rename IncrementallyPopulatedStream::reached_end_of_body
This needs to be called even if we haven't reached the end of the body,
so let's call it close() instead.
2026-02-18 13:13:32 -06:00
Ben Wiederhake
dab83d35d1 AK: Remove unused include from ByteString 2026-02-17 12:38:51 +00:00
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
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