TASK 4's ladder and TASK 33's worst-case table both quote numbers the code no longer produces. Replaced with a dated note pointing at the current truth rather than rewriting the decision records: the ladder is 12/10/6 with CBR, and the step-down floor is the tier's YouTube minimum, not a flat 3. Scope justification: Assets/starting-background.jpg is outside this unit — it is the creator's replacement art, still awaiting their visual check.
77 lines
4.5 KiB
Markdown
77 lines
4.5 KiB
Markdown
# TASK 33 — Automatic step-down when the show can't hold its tier (queued 2026-09-01; v1)
|
|
|
|
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
|
|
> Design settled with the creator 2026-11-09. **Not a magenta question of taste: this is the app
|
|
> admitting it is part of the problem when it is.**
|
|
|
|
**Goal:** the stream degrades *predictably and honestly* instead of drowning viewers in freeze frames
|
|
— and when LlamaCasty itself is the starving process, it says so out loud instead of blaming the
|
|
creator's connection. The `variable` reusable stream (shipped) makes every step zero-API: a tier or
|
|
bitrate change is just an encoder relaunch at the same URL.
|
|
|
|
## Cause first, lever second
|
|
|
|
| Cause | Signal | Lever |
|
|
|---|---|---|
|
|
| **Machine starvation** — the PC (often the game) can't make the frame | pump stalls, render time over the frame budget, frames never produced | reduce work: **60 → 30 fps**, then **1080 → 720** |
|
|
| **Upload congestion** — the socket can't carry the bits | frames produced on schedule but rejected; encoder queue full; **rising submit block time** (pump already logs `avg submit` / `avg wait`) | reduce bits: **8.0 → 6.4 → 5.1 → 4.1 Mbps** (~20% steps), geometry untouched |
|
|
|
|
Resolution is **not** a bandwidth lever: 1080p30 and 720p60 are both 6 Mbps in the shipped table, so
|
|
that step relieves nothing. Fps is **not** a congestion lever: same bitrate, same socket.
|
|
|
|
## Shed our own load before asking the viewer to pay
|
|
|
|
1. If our capture/convert cadence is what starves, **halve our own capture rate first**
|
|
(`ScreenCaptureFrameSource.ShedLoad`, 10 ms → 33 ms between conversions) — same output tier, less of
|
|
the creator's machine. It costs no step and changes nothing the viewer sees.
|
|
2. Only then step fps, then resolution.
|
|
|
|
**Shipped 2026-11-09** — `StepDownPolicy` + `StepDownPlanner` + `FramePump.ChangeEncoderAsync` +
|
|
`MainViewModel.StepDown`, 18 tests. The shed runs on machine evidence before the budget is touched.
|
|
|
|
## Budgets, floors, terminal state
|
|
|
|
| Start | Steps allowed | Worst case it can reach |
|
|
|---|---|---|
|
|
| 1080p60 | **3, any mix of the two ladders** | 720p30 @ 6.4 Mbps |
|
|
| 720p60 | **1** | 720p30 @ 4.8 Mbps |
|
|
| Vertical 1080p60 | **1** | Vertical 1080p30 @ 6.4 Mbps |
|
|
|
|
> **Superseded 2026-11-12:** the bitrate floor is the tier's YouTube minimum — 6 Mbps at 1080p60, 5 at
|
|
> 1080p30, 3 at 720p (`Models/RateLadder`, passed into `StepDownPolicy`) — and the ladder's defaults are
|
|
> 12/10/6. The table above records the 8-Mbps-era examples, not current output.
|
|
- Floors: 16:9 never below **720p30**; vertical never below **Vertical 1080p30**.
|
|
- Budget exhausted → **hold**, keep streaming, one persistent warning naming the cause, manual
|
|
step-up stays available. Never disconnect, never nag repeatedly.
|
|
- Trigger: ≥5% dropped over 15 s, twice, 30 s cooldown between steps. Mixed/ambiguous evidence does
|
|
nothing. One-way within a broadcast (no climb-back; revisit with session data).
|
|
|
|
## Own the mistake in plain words
|
|
|
|
A toast that reports a mechanism is a bug in the toast. Every message names **what happened, who
|
|
caused it, and what the app already did** — no exception text, no error codes:
|
|
|
|
- ours: *"LlamaCasty couldn't keep up with your scene, so it lowered the frame rate to 30. Your
|
|
connection wasn't the problem."*
|
|
- theirs: *"Your upload couldn't hold 8 Mbps, so LlamaCasty dropped it to 6.4 to stop the freezing.
|
|
It will stay there for this stream."*
|
|
|
|
One toast per step, plus one **Logs-tab** line (`quality stepped down 12:04 — encoder` / `— upload`,
|
|
cause-tagged) so support answers from data, not memory. It is logged rather than added to the session
|
|
report on purpose: that report is the money summary, and quality chatter in it would bury the numbers. Wording rules live in
|
|
[`docs/ai/ui-surface.md`](../docs/ai/ui-surface.md) → Toast notifications.
|
|
|
|
## Tests
|
|
|
|
Pure `StepDownPolicy` units (classifier, both ladders, budgets, floors, cooldown, one-way, ambiguity →
|
|
no action) + the ONE integration: a fake health stream with sustained drops of each cause produces the
|
|
matching step and exactly one encoder restart.
|
|
|
|
## Reference
|
|
|
|
OBS Dynamic Bitrate (27.2) is the established answer for the congestion half: bitrate-only, small
|
|
steps, hysteresis. **Adopted:** bitrate as the sole congestion lever, stepped with cooldown.
|
|
**Discarded for v1:** its automatic climb-back (the creator's one-way ruling). **Ours:** the
|
|
machine-side ladder and "shed our own cost first" — OBS has no equivalent, and that is the half that
|
|
matters when the game and the app share one machine.
|