phase: 06_sharer_quality_presets
**Phase 06 — final verification pass: 100% green.** - **Verified (all 6 tasks complete in working tree):** `#quality` select + `PRESETS` table; preset-driven capture request + `contentHint`; per-leg `legCap()`/`degradation` at both call sites; live `applyQuality()` with Overconstrained fallback + republish; `q` key cycle; `#stat-quality`/`#stat-codec` readouts; full e2e matrix (requests, committed params ×3, zero-cap, mid-share switch, unrescalable device, late-joiner first-offer, readout, `q`); realdisplay delivered-pixels `light` case. `test_sender_fidelity.py` **untouched and green** (regression canary). - **Gates:** `uv run ruff check . && uv run ruff format --check .` → clean (32 files); `uv run pyright` → 0 errors; `uv run pytest` → **284 passed, 5 skipped (realdisplay-gated), coverage 95%** (93s); `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` → **4 passed, 1 skipped** (no audio source on host — documented platform condition). - **Completion criteria:** all 11 ✓ — sharp default reproduces old request; light ≤960×600@15fps/motion/round(cap×0.35)/maintain-framerate with moving viewer frames; mid-share re-prices with `peers.size` unchanged, no renegotiation, roster matches `getSettings()`; refused-rescale toasts "next share"; `q` cycles + toasts, text-field guard holds; readout exact + codec shape, both reset to `—` idle; realdisplay: light decoded ≤990px (measured 320×200), sharp clears `SHARP_FLOOR_PX`; no new env/wire/Python/asset; suite 100% green, coverage >90%. - **Docs:** README.md reviewed in full against code — Quality table, `q` key, env-var table, routes (`/share`), kiosk commands, key table, doc paths all consistent; **no fixes needed**. AGENTS.md — verified, no stale statements, no changes. PLAN.md untouched (diff = 0). - **Reported (per phase, not edited):** PLAN.md §6 line 204 still says "per-leg `maxBitrate` cap (default **12 Mbit/s**)" — code has defaulted to **25 Mbit/s** since phase 02; needs a user-approved one-line amendment. - **Next pending phase:** none — 06 is the last phase in `todo/`; the roadmap is complete.
This commit is contained in:
@@ -107,6 +107,37 @@ a browser/platform fact, not a mirror one: Chrome on Linux gets it where
|
||||
PipeWire offers it, Firefox's desktop capture does not offer it at all. See
|
||||
[SECURITY.md](SECURITY.md) §4 "Audio".
|
||||
|
||||
**Quality.** The share page has a Quality control — `Sharp` (default),
|
||||
`Balanced`, `Light` — because a sharer's machine is a resource the share
|
||||
spends, and `MIRROR_MAX_BITRATE` alone cannot express "fewer pixels". Each
|
||||
preset is one request to the capture device and one per-viewer encoder
|
||||
budget:
|
||||
|
||||
| Preset | Requested pixel cap | Frame rate | Per-viewer bitrate | `degradationPreference` (what the encoder may sacrifice) |
|
||||
| ------------ | ------------------- | ------------ | ------------------------------ | ------------------------------------------------------- |
|
||||
| `Sharp` | ≤ 3840×2160 | 30 fps (up to 60) | 100% of `MIRROR_MAX_BITRATE` | `maintain-resolution` — frame rate |
|
||||
| `Balanced` | ≤ 1920×1200 | 24 fps (up to 60) | 60% of `MIRROR_MAX_BITRATE` | `balanced` |
|
||||
| `Light` | ≤ 960×600 | 15 fps (up to 30) | 35% of `MIRROR_MAX_BITRATE` | `maintain-framerate` — resolution |
|
||||
|
||||
`Sharp` is the default and is what the sharp-capture work shipped: its
|
||||
request, cap and preference are the old hardcoded values verbatim, so nothing
|
||||
it proved gets worse — the new presets only ever ask for less, and a preset
|
||||
can never raise a leg above the server's ceiling. `MIRROR_MAX_BITRATE` stays
|
||||
the ceiling the factor is applied to: a Wi-Fi sharer picks `Light` in the
|
||||
page, an operator lowers the ceiling for everyone.
|
||||
|
||||
Who it is for: the software-encoding sharer. A Firefox/Linux box often has
|
||||
no hardware video encoder (VA-API exposes no H.264 encode entrypoint on many
|
||||
machines), where 4K at 30 fps burns roughly a whole core — `Light` trades
|
||||
pixels and frame rate for a fan that does not spin, and under pressure the
|
||||
encoder will in fact sacrifice what its `degradationPreference` allows.
|
||||
Changing the preset applies to the share that is already running — the
|
||||
per-viewer bitrate and frame rate always, and the capture size where the
|
||||
browser allows a live rescale; when it does not, the panel says the size
|
||||
lands on the next share instead of pretending it did. `q` cycles the preset.
|
||||
The panel also shows the quality budget in force and the codec the legs
|
||||
actually negotiated — `—` where a browser does not report it, never a guess.
|
||||
|
||||
## Deployment
|
||||
|
||||
The internal deployment is `https://mirror.reeseapps.com`, described and automated
|
||||
@@ -180,7 +211,8 @@ muted, unmuting one (the tile's audio button or `m`) mutes the others, and
|
||||
leaving the stage re-mutes it. Sound never starts without a click or a
|
||||
keypress.
|
||||
|
||||
On `/mirror`: `s` starts a share, `x` stops one, `Esc` stops while live. Keys
|
||||
On `/mirror`: `s` starts a share, `x` stops one, `q` cycles the Quality
|
||||
preset, and `Esc` stops while live. Keys
|
||||
with `Ctrl`/`Cmd`/`Alt` are never intercepted, and a key typed in the display-
|
||||
name field is text, not a command. `?` is the only key that needs Shift, and it
|
||||
is matched on the character itself — on a layout where `?` is a dead key the
|
||||
@@ -189,7 +221,7 @@ list is not reachable, so keep this table to hand.
|
||||
## Development
|
||||
|
||||
```bash
|
||||
uv run pytest # unit + API + browser E2E (~50s)
|
||||
uv run pytest # unit + API + browser E2E (~95s)
|
||||
uv run pytest -m "not e2e" # everything except the browser (~0.5s)
|
||||
uv run ruff check . && uv run ruff format --check . && uv run pyright
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user