Vista generada de dec-0045: r32 — Bitrate NUNCA es gate de elegibilidad de direct-play
docs/decisions/dec-0045-bitrate-not-a-gate.mdVista generada desde
docs/decisions/dec-0045-bitrate-not-a-gate.md. No se edita a mano:bun run docs:genla regenera ybun run docs:checkfalla si difiere. El estado aquí es el del model: si discrepa con otra página, manda el model.
| Campo | Valor |
|---|---|
| Estado | LOCKED |
| Fecha | 2026-06-24 |
| Referencia legada | r32-bitrate-not-a-gate-2026-06-24 |
| Fichero | docs/decisions/dec-0045-bitrate-not-a-gate.md |
Nodos del roadmap que lo citan en refs: ninguno.
Páginas de la documentación que lo citan: ninguna todavía.
Leído de docs/decisions/dec-0045-bitrate-not-a-gate.md, el fichero canónico.
docs/research/reference-harvest/ (Batch A+, CODE_VERIFIED contra clones reales)El root cause #1 del "se corta a veces" de Jellyfin con un REMUX 4K de 80 Mbps, verificado contra el código (no inferido):
DeviceProfile.cs:30 → MaxStreamingBitrate = 8000000 (8 Mbps default server-side).StreamBuilder.cs:712-727,1631-1655 → IsBitrateLimitExceeded →
TranscodeReason.ContainerBitrateExceedsLimit fuerza transcode aunque el codec sea
direct-play-capable. El default tira un REMUX de 80 Mbps a transcode sin tocar el slider.Antídoto verificado: lunarr decidePlaybackMode decide solo por codec/container
(transcoding/capabilities.ts:219-301, grep maxBitrate = 0 hits).
40_000_000 en StreamBuilder.cs:1643 es una sobre-estimación que FUERZA
transcode cuando el bitrate real es desconocido, no un valor que "salta" el gate.IsRemote (StreamBuilder.cs:1633-1636) — no es universal.capabilities.ts:219-301 (el :263+ original era erróneo; la
sustancia es CODE_VERIFIED). Ver claims-register/downgrades.md.El PlaybackPlanner decide mode=direct ⟺ el cliente puede decodificar el codec/container.
El bitrate NUNCA rehúsa direct-play. Es input explicable, jamás gate.
decideDirectPlay(asset, client, link):
if !client.canDecode(asset.codec, asset.container):
return adapted(reason: codec-unsupported)
# bitrate NUNCA veta. availableBps entra como reason, no como gate.
return direct(reasons: [
codec-native,
link.availableBps < asset.peakBps
? 'link-tight (prefetch deepens — ver r33)'
: 'link-ample' ])maxBitrate/MaxStreamingBitrate que devuelva
adapted/transcode por exceso de bitrate.bitrate-unknown = estado UNKNOWN de primera clase, no una constante mágica. Un asset
con bitrate desconocido fuerza purpose=probe (medir antes de decidir), nunca se asume un
default que dispare transcode.availableBps (medido vía cwnd QUIC, ref iroh-live util.rs:46-110) es un reason
explicable en el plan, no un veto. Si el link va justo, la respuesta es prefetch más
agresivo (r33), nunca rehusar direct-play ni bajar resolución.ClientCapabilities declara decode-capability (VideoToolbox HEVC Main10 / AV1 / DV
profile), jamás MaxStreamingBitrate (matiza r20 §3).reasons[] lleva el estado del link sin convertirlo en
decisión de elegibilidad.DeviceProfile.MaxStreamingBitrate que mató a Jellyfin.mode=direct, reasons incluye codec-native.UNKNOWN → purpose=probe, no transcode por default.dec-0044
Vista generada de dec-0044: Governance reconciliation
dec-0046
Vista generada de dec-0046: El planner modela direct-play con tres estados ortogonales — DIRECT_ELIGIBLE (codec/container decodificable), DIRECT_SUSTAINABLE (link medido sostiene el envelope, informativo no veto), DIRECT_POLICY_SELECTED (policy de sesión decide) — junto con la 'regla canónica' que l