docs(specs): mpv draws desktop video, and the webview path goes
The spike proved compositing works on Linux, including Wayland, and left two blockers. One is now closed: DR-228 measured a single EXT-X-STREAM-INF in the server's master playlist, so there is no adaptive bitrate for mpv to lose and finding 3 of playback-backend-unification.md is false. The spike is updated to record that. The other — an unexplained SIGSEGV in a decoder thread — is carried into the spec as DR-231 rather than chased: the spike had no render-context teardown at all, which is DR-184 on Android restated, and removing the likeliest cause is worth doing whether or not it was the cause. The spec targets every desktop platform rather than Linux alone, because the maintenance argument runs the other way. Video has three renderers today. A Linux-only version makes it four, permanently — mpv on Linux, HTML5 on Windows, ExoPlayer on Android, hls.js underneath — and the webview path then survives indefinitely because something still needs it. Finishing the job leaves mpv on desktop and ExoPlayer on Android, and hls.js, html5Adapter.ts, videoLoaderFor and the <video> element are deleted in a phase that has its own acceptance criterion so it cannot quietly become "later". The load-bearing change is DR-233: the device profile stops being a compile-time platform constant and becomes a property of the renderer that will decode the stream. The measured 7% desktop direct-play rate and Android's 85% differ by nothing except which component decodes, so that one change is what converts the former toward the latter. It looks like configuration and is not — it decides whether the server re-encodes, and it fails silently when wrong. Windows is costed rather than waved at: the surface is genuinely different code (WebView2 in an HWND, not GTK), but everything else is shared, so nothing may be guarded on cfg!(target_os = "linux"). The real cost is build — libmpv is a Linux-only dependency while Windows cross-compiles via cargo-xwin, so a Windows libmpv must reach that build and ship in the NSIS bundle under the LGPL terms DR-216 already records. Allocates UR-080, DR-230..236, IR-033. No product code yet.
This commit is contained in:
@@ -2,7 +2,10 @@
|
||||
|
||||
**Status:** **Run 2026-08-21 — compositing works; G5 carries an open crash.**
|
||||
The compositing claim it set out to test is falsified on Linux. See "Result".
|
||||
This file stays open until the implementation spec exists; ABR is unresolved.
|
||||
This file stays open until the implementation spec exists. **ABR is resolved** —
|
||||
the playlist carries one `EXT-X-STREAM-INF`, so finding 3 is false and there is
|
||||
no adaptation for mpv to lose. The remaining blocker is the unexplained SIGSEGV
|
||||
under G5, which is a lifetime problem, not a compositing one.
|
||||
**Requirements:** none allocated. This spike produces a decision record, not
|
||||
product code — same shape as
|
||||
[playback-backend-unification.md](playback-backend-unification.md), which is
|
||||
@@ -258,10 +261,10 @@ anything.
|
||||
Tauri's existing webview into an overlay. Low risk — the same widgets, one
|
||||
extra reparent — but unproven, and it is the only place Tauri-specific
|
||||
behaviour could still bite.
|
||||
- 🔴 **ABR — finding 3's premise is in doubt.** Finding 3 says mpv would regress
|
||||
streaming quality because "the webview path already has real ABR via hls.js".
|
||||
Three pieces of evidence in this repo suggest that is **not true of the URLs we
|
||||
actually build**:
|
||||
- ✅ **ABR — resolved. Finding 3's premise is false.** Finding 3 said mpv would
|
||||
regress streaming quality because "the webview path already has real ABR via
|
||||
hls.js". Three pieces of evidence in this repo suggested that is **not true of
|
||||
the URLs we actually build**:
|
||||
|
||||
1. `get_video_stream_url` (`repository/online.rs`) requests a *single*
|
||||
rendition — one `VideoBitrate`, one `MaxStreamingBitrate`, one `MaxHeight`.
|
||||
@@ -276,21 +279,48 @@ anything.
|
||||
audio-track switch)". Manual selection by stream re-open is what you build
|
||||
when there is no adaptation, and mpv can do the same thing.
|
||||
|
||||
**The decisive test has not been run** and needs a live server plus an API key:
|
||||
count `#EXT-X-STREAM-INF` lines in a real `master.m3u8`. One line means there
|
||||
is no ABR to lose and this blocker disappears. More than one means finding 3
|
||||
stands and the work below applies.
|
||||
**The decisive test has now been run** (2026-08-21, against the development
|
||||
server, Jellyfin 10.11.5):
|
||||
|
||||
If ABR does turn out to be real, it belongs in **Rust**, not in mpv, and there
|
||||
are three designs in increasing cost: pick the variant at open; re-open at a
|
||||
new bitrate on sustained throughput drops (this is the quality-switch path the
|
||||
app already has, so it is nearly free); or run a local proxy serving mpv a
|
||||
synthesized single-variant playlist while swapping renditions underneath. The
|
||||
middle option is almost certainly sufficient.
|
||||
```
|
||||
curl -s ".../Videos/<itemId>/master.m3u8?…&TranscodingProtocol=hls&…" \
|
||||
| grep -c EXT-X-STREAM-INF
|
||||
1
|
||||
```
|
||||
|
||||
Either way the **direct-play path still does not exist** — every video play
|
||||
currently goes through the HLS transcode endpoint. Building it is the real
|
||||
project; the compositing work proven above is the smaller half.
|
||||
**One line.** The playlist carries a single `EXT-X-STREAM-INF` plus an
|
||||
`EXT-X-IMAGE-STREAM-INF` trickplay entry, which is not a rendition. Jellyfin
|
||||
builds the master playlist from the rendition the request asked for; it does
|
||||
not publish a ladder. So **there is no ABR to lose, and this blocker is
|
||||
closed** — hls.js is serving as an HLS demuxer, exactly as (2) above supposed,
|
||||
and mpv gives up nothing by replacing it.
|
||||
|
||||
Recorded as DR-228 (Won't Do) rather than deleted, because it is a
|
||||
measurement: a server that *does* publish a ladder would change the answer, and
|
||||
the re-negotiation path is the hook that work would build on.
|
||||
|
||||
**The direct-play path now exists.** It did not when this spike was written —
|
||||
every video play went through the HLS transcode endpoint. Backend-owned stream
|
||||
selection (DR-224 … DR-229) built it: Rust negotiates direct play / direct
|
||||
stream / transcode and hands every backend one `StreamSelection` carrying the
|
||||
URL, the transport and the chosen rendition. **That is the contract this
|
||||
implementation consumes** — mpv is a consumer of a decision already made, not a
|
||||
place to re-derive it.
|
||||
|
||||
It also sizes the prize precisely. Measured over the same server, 40 items
|
||||
through a real negotiation per profile:
|
||||
|
||||
| Profile | Direct play |
|
||||
|---|---|
|
||||
| Linux / WebKitGTK — `h264` only, 2ch | **7%** |
|
||||
| Android / ExoPlayer — `h264,hevc,vp8,vp9,av1,mpeg4` + `ac3,eac3`, 6ch | **85%** |
|
||||
|
||||
The library sampled is ~80% hevc. Linux sits at 7% **solely because the
|
||||
WebKitGTK profile can only claim h264** — not because of anything about the
|
||||
server or the negotiation. mpv decodes hevc, so widening the Linux device
|
||||
profile once mpv renders the picture is what converts that 7% toward the
|
||||
Android figure. That conversion is the actual product of this work; the
|
||||
compositing proven above is the mechanism that permits it.
|
||||
- 🔴 **One unexplained SIGSEGV.** A ~180s
|
||||
run died in a *decoder* thread (libavcodec -> `av_log` -> libmpv's log handler
|
||||
-> libc). No Tauri, wry, WebKitGTK, GTK or GL frame appears anywhere in the
|
||||
|
||||
Reference in New Issue
Block a user