Commit Graph
1696 Commits
Author SHA1 Message Date
Kacper Michajłow c318236b88 player: drop the enhancement-layer pairing when the media has no EL 2026-08-26 01:47:41 +02:00
Kacper Michajłow 93451ef64f demux_disc: mark streams the current program does not carry
The outer streams live for as long as the disc does, but each program
brings its own set. Playing a BD playlist without the Dolby Vision
enhancement layer left the outer EL stream selected with no slave
stream mapped to it.

Normally when there is no EL there is no track either, but since it was
exposed in PMT we still have the track, while VM may decide to unmap it.
This is bit of workaround, but harmless one.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 795cd15438 demux: let a demuxer mark a stream as absent
Media can stop providing a stream mid-playback, for example a disc
program that does not carry the Dolby Vision enhancement layer the
previous one had. Since mpv always try to keep at least 1 packet
buffered in all tracks, it can keep reading it till EOF.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 79186bd167 discnav: poll the disc VM while playback idles
The BD-J VM progresses only on reads. With playback at EOF or on a
held still nothing reads anymore, applet playlist switches never
surface and menus freeze, libbluray documents this polling requirement
for idle states. Drive one nav read per tick and keep the core awake.
The slave demuxer latches its EOF and stops touching the stream, so
the read path peeks the stream itself to run its event loop. Poll the
same way while paused in a menu, highlight redraws would otherwise
wait for the next core wakeup. dvdnav stills release on user input,
not reads, and the pump bypasses the cache limits, so no pumping
outside these states.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 6f6bedec72 demux_disc: reset the slave demuxer in place on BD time seeks
A seek repositions the disc under the live slave demuxer, and mpegts
spliced its buffered PES state together with data of the new position
into broken packets, an AU worth of decode errors per seek. mpegts
already flushes all per-PID packet state when it sees the avio
position jump, so restart the byte position counters on the seek and
resync the avio position to the stream in drop_buffers.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 0c87e46c83 demux_disc: gate slave reopens on buffered stream data
While the disc is between positions the open only probes an EOF and
fails, and retrying at the read rate logged a warning every attempt.
Peek before opening, this pumps the stream's event loop and the peeked
bytes stay buffered for the probe.
2026-08-26 01:47:41 +02:00
Kacper Michajłow abbbffe0dd demux_disc: hold BD jump boundaries for the player's resync seek
The resync seek that follows a disc-initiated jump must not reposition
the VM. On BD the jump is handled out-of-band by reopening the slave
demuxer on the packet-read path, and the VM already sits at the jump
destination. Repositioning it with the (stale) seek target time would
hop to the wrong place. Adopting the discontinuity counter here would
leave the old slave demuxer parsing the new playlist's data with the
wrong codec context. Flush our side only, and keep the counter stale so
the read path still performs the reopen.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 4a110f39cf demux_disc: map timestamps with per-stream timeline generations
Discs restart the in-stream timestamps at cell/segment boundaries. The
single global base_dts/last_dts pair rebased on any |diff| >= 5s between
consecutive packets of any stream, which broke down in several ways:

- Backward resets smaller than the threshold (e.g. a 1.5s menu
  transition cell followed by the looping button cell) were not detected
  at all, producing non-monotonic playback timestamps.
- The global last_dts ping-ponged between streams that cross a boundary
  at different packet positions (interleave skew), rebasing on nearly
  every packet and spamming the log.
- Menu subpictures timestamped for the next cell were mapped with the
  previous cell's offset and displayed at the wrong time.

Map each segment as a timeline "generation" with its own raw->playback
offset instead. Boundaries are detected per stream, as a backward step
beyond muxing jitter or a forward gap that would stall playback (sparse
still-image streams exempt). The previous generation stays valid until
every stream has moved past the boundary, and the new generation is
placed so the triggering stream continues seamlessly.

DVD subpictures are muxed around cell boundaries and their raw
timestamps alone cannot tell whether they continue the current segment
or belong to the next one. Defer each by one a/v packet (or EOF) and
let the packet that follows settle which generation it belongs to.
2026-08-26 01:47:41 +02:00
Kacper Michajłow fee9b14d46 demux_disc: handle slideshow DVD titles
Audio DVDs commonly author each song as a chapter whose video is a
single (or more) still frame, with only the audio really interleaved.
Detect this at runtime and mark the video still_image. The player then
treats it as sparse, and the starved stream stops forcing eager reads
that would scan through the source hunting for the next still.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 4109805d0c demux_disc: follow the disc's initial audio stream selection
The player's default track selection picks the first probed audio
track, but the substream the disc's current PGC actually plays is
decided by the DVD VM and may be a different one. On discs whose menu
domain plays LPCM while the mux also carries an (unreferenced) AC3
substream, mpv selected the AC3 track, which has no data. We have to
keep track of what VM wants to output and what LAVF expects.
2026-08-26 01:47:41 +02:00
Kacper Michajłow c855d13170 stream_dvdnav: resync disc-initiated jumps through the seek path
When the disc VM jumps (menu activation, end of title), the demuxer
and player have to flush and reinitialize exactly at the boundary.

Hold an EOF at the boundary until the demuxer acks it, and resync
the player with a regular seek, which already orders flush, decoder
reset and restart. d_seek recognizes the held boundary and releases
it instead of repositioning the VM.

Also log the VM domain in nav event messages.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 34c649b864 demux: allow demuxers to update still_image during demuxing
Whether a DVD title's video track consists of sparse still images (a
slideshow) is only observable from the packet timestamps after
streaming started, and demux_disc wants to flip sh->still_image
accordingly, so that the stream stops being read eagerly.
2026-08-26 01:47:41 +02:00
Kacper Michajłow a2448f3b81 stream_dvda: expose DVD-Audio still images as a cover-art video track
DVD-Audio discs carry an Audio Still Video Set (ASVS). An MPEG-2 still
images (cover art, booklet/lyric pages) that the disc associates with
tracks.

A new STREAM_CTRL_GET_STILL returns the still for a given playback time.
demux_disc exposes it as a selectable mpeg2video track and injects the
still as a keyframe at the track's playback time. It changes as playback
crosses track boundaries (and follows onset times when a disc encodes
them).

--dvda-page (with the new STREAM_CTRL_SET_STILL_PAGE) forces a specific
still page instead of the scheduled one. The still state is guarded by
a mutex, runtime page changes arrive from the player thread while the
demuxer thread extracts stills.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 263bea5261 demux: read sparse still-image streams lazily
still_image streams (AV_DISPOSITION_STILL_IMAGE) consist of sparse
frames that can be minutes apart. When such a stream was selected
alongside an eager audio/video stream, its empty packet queue forced
the demuxer to keep reading ahead, buffering the whole file trying to
chase min_secs of "video". Read them lazily when other eager streams
exist, so playback drives read-ahead instead.
2026-08-26 01:47:41 +02:00
Kacper Michajłow b48fb6c86c demux_lavf: seek avfc only on seekable streams in drop_buffers
Disc streams are only seekable through the higher-level stream
controls, a byte seek on the slave can only fail and log errors.
avformat_flush() below already resets the demuxer state.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 2c8d954d21 demux_disc: only add the menu edition for streams with menu support 2026-08-26 01:47:41 +02:00
Kacper Michajłow 8356c7f674 demux_disc: keep one-shot DVD menu subpictures deliverable
Keep DVD SPU streams always enabled at the slave level and retain the
last subpicture per substream. Re-deliver the retained copy when a sub
track is (re)selected and when the player resets playback state.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 47a53910a5 demux: parent chapter metadata to the chapter list
demux_disc replaces the chapter list at runtime. Parent each chapter's
tags to the list that contains them, so a replaced list keeps owning
its metadata instead of accumulating it on the demuxer.
2026-08-26 01:47:41 +02:00
Kacper Michajłow b0c4e8cab6 demux_disc: soft-reload the slave demuxer on DVD discontinuities
A full slave reopen makes libavformat re-probe the stream, which eats
packets around the jump (losing the one-shot menu SPU) and re-bases the
timestamps. DVD cannot change codecs across a jump, so flushing the
slave and refreshing duration/chapters/edition is enough. BD keeps the
full reopen because codecs can change across titles.
2026-08-26 01:47:41 +02:00
Kacper Michajłow dbe496e6be demux_lavf: clear sticky avio EOF in drop_buffers
A stream can report a one-shot EOF and then continue producing data
(disc-nav jump boundaries). Also clear a sticky read error, the
underlying source is being reused.
2026-08-26 01:47:41 +02:00
Kacper Michajłow f0bc30aaf1 demux: adapt caching to interactive disc navigation
The disc VM advances when the stream is read, so in nav mode the cache
must not run ahead of what the user is seeing. Never read ahead (a
looping menu is endless). Never satisfy seeks from the cache either.

This applies to the whole disc session, not only while a menu is on
screen. The VM can jump at any time (BD-J timers, end of title), so
the cache must never run ahead of playback.

Activations still must be able to drive the VM while nothing is
consumed. demux_drive_nav() forces a single read for them.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 4725c2e492 demux_disc: set partially_seekable to true
This suppresses refresh seeks (e.g. on track switches). A refresh seek
would reposition the disc VM, which breaks menus and can hop to a
different position entirely. Track switching relies on the always-on
readahead of the disc demuxer instead.
2026-08-26 01:47:41 +02:00
Kacper Michajłow e204c1cfd9 demux_disc: add a synthetic "Disc Menu" edition
Expose the disc menu in the edition list, after the titles, so it can
be reached like any title.
2026-08-26 01:47:41 +02:00
Kacper Michajłow af9a11a8e5 demux_disc: reopen slave demuxer on disc-nav hops
Playing a disc through the menu means crossing playlist boundaries at
runtime: menu -> title (and back), sub-menu -> sub-menu, title -> title.
Each new playlist can have a different stream count and/or a different
codec mix. The previous slave demuxer was probed once at open and never
refreshed, so after a hop the player either kept decoding stale streams
or saw nothing at all. Tear down and re-open the slave on every nav
discontinuity.
2026-08-26 01:47:41 +02:00
Kacper Michajłow b17feb1f79 stream_dvdnav: implement DVD menu navigation
Drive libdvdnav's menu state through the new disc-nav protocol so the
player can show and interact with VMGM and VTSM menus.

STREAM_CTRL_NAV_CMD maps the nav actions onto libdvdnav.

STREAM_CTRL_GET_NAV_STATE reports whether a menu with buttons is on
screen, the highlight rectangle and palette of the focused button, and
the ids of the disc-selected audio/SPU streams.

Menu mode is entered by opening with dvd://menu or --disc-menu, which
starts at the disc's root menu instead of a title.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 5db1db2852 demux: allow updating duration, chapters and editions during playback
Disc demuxers switch titles and playlists at runtime, which changes the
duration and replaces the chapter/edition lists. Add
demux_set_duration() and demux_lists_changed() for demuxers to publish
such updates, and DEMUX_EVENT_LISTS for the player to react.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 3ba4f76096 demux_disc: drop "PTS discontinuity" logs to verbose
Discs restart timestamps on every title or menu transition, so these
are expected in normal operation, not a sign of broken input.
2026-08-26 01:47:41 +02:00
Kacper Michajłow f73dd13376 demux_lavf: parent mp_codec_params members to it
The members were parented to the demuxer or sh_stream, while they really
commute better with mp_codec_params.
2026-08-26 01:47:41 +02:00
Kacper Michajłow 57b0a0a695 stream_dvdnav,demux_disc: clean up log messages 2026-08-26 01:47:41 +02:00
Kacper Michajłow e7191f2a65 demux_lavf: select initial edition only by the program's variant_bitrate
The per-stream variant_bitrate is not a reliable selection key. FFmpeg
attributes the whole variant's bandwidth to individual streams, and for
streams shared by multiple variants the value was bogus or missing
depending on the number of variants sharing the stream. The program
metadata always carries the exact HLS variant BANDWIDTH, so use it as
the only key and drop the per-stream fallback.

Note that with `--flatten-editions` tracks are selected by their own
tags, so they may still use bogus values.

For context, there is currently a bug in lavf where, depending on the
number of variants sharing a stream, the stream may or may not get
`variant_bitrate`. This will be fixed in a future FFmpeg release.
2026-08-17 19:57:13 +02:00
Kacper Michajłow e167836802 demux/ebml: interpret zero-length elements as their default value
An EBML element is permitted to have a zero-length data payload. It's
then interpreted as the element's RFC declared default value, or zero.

Before this change all zero-length elements were initialized to empty or
rejected in case of uint.

Fixes: #18351
2026-08-14 23:24:50 +02:00
Kacper Michajłow 48e6c35c0e demux: interrupt blocking reads whose result would be discarded
Add demux_read_interrupted(), true when the demuxer is being canceled,
its thread is terminating, or a seek is queued. Poll it from
demux_lavf's AVIO interrupt callback so blocking network I/O (e.g. the
HLS demuxer waiting on a live playlist reload) doesn't stall queued
seeks or shutdown for multiple seconds. The aborted read's result is
discarded in all of these cases anyway.

In normal operation demuxer reads are fast enough to not notice the
issue. However, in the HLS/DASH demuxers, FFmpeg blocks read() calls
on the playlist reload timeout. This is expected, but can mean multiple
seconds of dummy wait before we can actually proceed. This change
significantly speeds up seeking in HLS playback when it hits a playlist
reload.
2026-07-26 18:15:18 +02:00
Kacper Michajłow b27573a239 demux: fail probing when the stream cannot be rewound to the start
Each probe attempt assumes it reads from position 0. If the rewind
fails, the demuxer would silently misparse the stream from a wrong
position.

On non-seekable streams this may fail when the probe size > buffer size,
which would cause us to drop the buffered data and not allow to rewind
the stream. This can be improved in the future.
2026-07-26 12:59:51 +02:00
Kacper Michajłow 115b87b521 demux_mkv: init ff-index for backward compatibility
lavf basically always provide tracks in order as they appear, so this
will match it. There is no hard guarantee, but this is documented.

`ff-index` is now optional, but both demux_mkv and demux_lavf set it, so
it should cover most cases where it's used. In all other cases it would
be wrong anyway.
2026-07-09 05:04:11 +02:00
Kacper Michajłow 5b087dfb18 demux: remove falsification of ff-index
The `ff-index` is just a index of in the track order as seen by lavf.
However, there are many cases where tracks are not related to lavf, when
it's not used or there are virtual tracks addded like DOVI EL track or
merged image tile grid. Don't conjure `ff-index` out of nothing.

Some scripts may be confused when this index does not much ffmpeg. Even
though it has always stated in docs.

Fixes #18244
2026-07-09 05:04:11 +02:00
Kacper Michajłow 7f93b06797 demux_lavf: sort AVProgram array by id
AVProgram array contains programs in order as they were discovered by
lavf. For MPEG-TS depending on the point where we start demuxing it is
effectivelly random. Sort by the id, so we have consistent order of our
editions, regardless of probing.
2026-07-08 20:42:25 +02:00
Kacper Michajłow 02baec9701 demux_lavf: change demuxer-lavf-{probesize,buffersize} to OPT_BYTE_SIZE
It's way easier to type 10M instead of typing all those zeros.

Our C API uses int64_t for properties and string for setting options, so
it's safe to bump this option type.
2026-07-08 20:42:25 +02:00
Kacper Michajłow 99b4c12ccc demux_mkv: map hvcE into side-data 2026-07-01 04:46:42 +02:00
Kacper Michajłow f53e8f3fed demux_mkv: group EL+BL dovi tracks 2026-07-01 04:46:42 +02:00
Kacper Michajłow 103aee7261 demux_disc: mirror lavf demuxer stream groups to parent
This joins Dolby Vision EL track with base one. libbluray current (as of
1.4.1) doesn't even expose this info, it will be available in next
version, but it is less code to ask lavf for this info in fact.
2026-07-01 04:46:42 +02:00
Kacper Michajłow 6b4a205c99 demux_lavf: handle AV_STREAM_GROUP_PARAMS_DOLBY_VISION 2026-07-01 04:46:42 +02:00
Kacper Michajłow 95f8f18d13 demux_mkv: add support for splitting EL track from in-band 2026-07-01 04:46:42 +02:00
Kacper Michajłow 116c8ecf3c demux_lavf: add support for splitting EL track from in-band 2026-07-01 04:46:42 +02:00
Kacper Michajłow 998c20af67 demux/dovi_split: add helper to split interleaved HEVC with BL and EL 2026-07-01 04:46:42 +02:00
Kacper Michajłow 5058e6e737 f_output_chain: pair BL+EL and output as single frame 2026-07-01 04:46:42 +02:00
Kacper Michajłow 25606307ff demux: propagate track selection only from base track
Don't try to select dependent track as main and feedback to base track.
To avoid situations where tracks are deselected only to select the same
group again.
2026-07-01 04:46:42 +02:00
Guido Cella 9dbf5d681d misc/path_utils: rename bstr_strip_ext() to mp_strip_ext() 2026-06-27 21:43:18 +02:00
Guido Cella 089c172dff misc/path_utils: rename bstr_get_ext() to mp_get_ext() 2026-06-27 21:43:18 +02:00
Guido Cella 1a09154988 various: replace mp_splitext() with bstr_get_ext()
This will allow removing mp_splitext().
2026-06-27 21:43:18 +02:00
Kacper Michajłow 449944e25f demux/avio_crypto: add AES decryption
This mirrors `crypto+` protocol from ffmpeg, which is used in HLS
demuxer. Unfortunatelly ffmpeg doesn't use custom io_open in their
crypto.c, so we spin our own decryption.
2026-06-27 19:04:03 +02:00