# Spec: Playback documentation corrections **Status:** Proposed **Requirements:** revises the status of DR-034; corrects the parity matrix in [requirements.md](../requirements.md) **UX spec:** n/a **Supersedes / revises:** implements the "Corrections to existing docs" section of [playback-backend-unification.md](playback-backend-unification.md) ## Summary Fix four factual errors in the requirements doc and the player source comments, all found while investigating backend unification. Each claims something the code does not do. Small change, but they are actively misleading: two of them assert a feature is implemented when it is implemented nowhere, and one cites an upstream blocker that no longer exists. Documentation and comments only — no behaviour change. ## Motivation These errors compound. DR-034 reads "Done (Linux only)", so a future session planning Android parity would reasonably assume crossfade exists on Linux and only needs porting — when in fact it is unimplemented everywhere *and* architecturally blocked on the engine it supposedly runs on. Likewise the tauri#10152 comment has been discouraging work on Android native video since the upstream capability shipped in September 2024. ## The corrections ### 1. DR-034 status is wrong `requirements.md` line ~196: ``` | DR-034 | Crossfade engine with configurable duration (0-12s) | Player | UR-031 | Done (Linux only) | ``` The code: ```rust // src-tauri/src/player/mpv_backend.rs, in set_audio_settings // TODO: Implement crossfade via MPV audio filters if needed ``` That is the entire crossfade implementation. `crossfade_duration` is plumbed through `AudioSettings` and clamped to 0–12s, but no backend ever acts on it. **Change to:** `Not implemented (blocked on MPV — see playback-backend-unification.md)` Worth stating *why* in the requirements entry, because it is not a scheduling gap: mpv's audio chain is single-stream, and FFmpeg's `acrossfade` is an `N→A` filter needing two inputs. Real crossfade requires two libmpv instances with manually ramped volumes. Upstream declined the feature (mpv#4512). ### 1b. UR-031 status is wrong for the same reason `requirements.md` line ~44: ``` | UR-031 | Crossfade between audio tracks | Low | Done (Linux only) | ``` Same error one level up: the *user* requirement is also marked done. Since no backend implements crossfade, UR-031 is not satisfied on any platform. **Change to:** `Not implemented (blocked — see DR-034)` Note line ~517 of the same file (`UR-031 (Crossfade), UR-032 (Gapless), UR-033 (Normalization) only work on Linux`) inherits the error — crossfade works nowhere, so it should read UR-032/UR-033 only. ### 2. Parity matrix crossfade row is wrong ``` | Crossfade | ✅ | ❌ | Gap | ``` **Change to** `| Crossfade | ❌ | ❌ | Not implemented |` — it is not a platform-parity gap, it is an unbuilt feature. ### 3. Parity matrix is missing the equalizer The matrix lists crossfade, gapless, and normalization but omits the EQ, which has the same Linux-only shape and the same root cause (`ExoPlayerBackend` not overriding `set_audio_settings`). `build_af_filter` and `eq_filter_entries` exist only in `mpv_backend.rs`; there is no equalizer code in the Android tree. **Add:** `| Equalizer (10-band) | ✅ | ❌ | Gap |` ### 4. `nativeAdapter.ts` cites a stale blocker `src/lib/player/adapters/nativeAdapter.ts:11-14` states native Android video is blocked upstream by tauri#10152 (transparent webview / SurfaceView compositing). tauri#10152 is open but **dead since 2024-07-01**, and it is a *feature request* that `WebviewWindowBuilder::transparent` was desktop-only — not a report that compositing is broken. The capability shipped in tauri commit `27d01834` (2024-09-02), which moved `transparent()` into the cross-platform impl block with only the tao call `#[cfg(desktop)]`-fenced. It landed as a clippy cleanup, so the issue was never closed. Separately, the black/white-screen bug (tauri#8381, tauri#9408) was a broken JNI signature for `setBackgroundColor`, fixed in wry 0.39.4; we ship wry 0.55.x. **Change to:** a comment stating the adapter is currently unreachable because `createAdapter` hardcodes the HTML5 kind, that transparency is no longer an upstream blocker, and that [android-native-video-spike.md](android-native-video-spike.md) tracks whether SurfaceView compositing actually works. Be explicit that *nobody has demonstrated* SurfaceView-behind-WebView on Tauri Android — nothing upstream blocks it, and nothing upstream proves it. ### 5. Platform capability is signalled three incompatible ways Not a doc error — a real inconsistency found during the same investigation, worth recording here even though fixing it needs its own change. Which backend a platform uses is currently expressed three ways: 1. Rust `#[cfg]` gates in `player/mod.rs` and `create_player_backend` — the truth. 2. The `useHtml5Element` / `VideoBackend` value from `get_player_status` — which the frontend discards (see the spike spec). 3. **Frontend user-agent sniffing** in `src/lib/services/webviewAudio.ts:30-41`: ```ts const ua = navigator.userAgent.toLowerCase(); const isAndroid = ua.includes("android"); const isLinux = ua.includes("linux") && !isAndroid; return !isAndroid && !isLinux; ``` The comment says it is "matching the Rust cfg gate" — i.e. the frontend re-derives a backend decision from the user-agent string and hopes it stays in sync. That is the frontend deciding *which backend exists*, which is domain knowledge, not presentation. It also breaks silently the moment a new target is added or a webview's UA changes. **This is a boundary leak of the same family the spec-review checklist exists to catch**, even though `check:boundary`'s tripwire (item-type arrays) does not match it. Rust already computes the answer; the frontend should consume it. Not fixed by this spec — it is behavioural, not documentation. It should be folded into the spike spec's factory rework, where the same "consume Rust's decision instead of re-deriving it" change is already in scope. ### Also worth fixing while here `requirements.md` IR-004 reads "In Progress (basic playback works, audio settings missing)". That stays accurate until [android-audio-settings-parity.md](android-audio-settings-parity.md) lands, but the "Future Fix" list in the parity issue proposes `ConcatenatingMediaSource` for crossfade — **deprecated in current Media3**. Drop that suggestion; the modern approach is a custom `AudioProcessor`. ## Layer assignment No logic. Documentation and comments only. | Logic / responsibility | Layer | Why it belongs there | |------------------------|-------|----------------------| | — | — | No logic introduced or moved by this spec. | ## Out of scope - Implementing crossfade. This spec only stops claiming it exists. - Implementing Android audio settings — see the parity spec. - Running the Android video spike — see that spec. - Rewriting the architecture docs. `docs/architecture/05-platform-backends.md` should be re-read for the same class of error, but that is a larger pass. ## Acceptance criteria - [ ] DR-034 status corrected, with the blocking reason stated. - [ ] UR-031 status corrected (line ~44), and the "only work on Linux" line (~517) no longer lists crossfade. - [ ] Parity matrix: crossfade ❌/❌; equalizer row added. - [ ] `ConcatenatingMediaSource` suggestion removed from the "Future Fix" list. - [ ] `nativeAdapter.ts` comment corrected and pointing at the spike spec. - [ ] `bun run check` and `bun run test` pass (a comment change still touches TS). - [ ] `bun run traces:markdown` re-run if requirement text changed. No Rust changes, so the `cargo` gates do not apply. ## Testing None beyond the standard gates — no behaviour changes. Confirm `bun run traces:markdown` regenerates cleanly, since DR-034's row is referenced by the traceability matrix. ## TRACES No code implementing requirements changes; no TRACES comments to add or update. The DR-034 row in `docs/traceability.md` will regenerate with the corrected text. ## Notes for the implementer - Do **not** silently delete DR-034. The requirement (UR-031 crossfade) is still wanted; it is the *status* that is wrong. Keeping the row with an honest status and a reason is the point. - A parallel Claude session may be active — `git diff` before "repairing" unexpected changes.