docs(media): reconcile quality control evidence

This commit is contained in:
AAA committed 2026-08-19 14:34:40 +08:00
1 parent 5bffd5a9f6
commit d43d7b5eb3
11 files changed
+128 -100

No files matched your search

+1 -1
View File
@@ -16,6 +16,6 @@
- [ADR-0001](./adr/0001-p2p-first-media-topology.md): accepted P2P-first media topology and its consequences.
- [ADR-0002](./adr/0002-persistent-protected-rooms.md): optional SQLite persistence, protected sequential room IDs, and room lifecycle.
- [ADR-0004](./adr/0004-peer-assisted-media-experiment.md): proposed, experiment-only standard-WebRTC peer-assisted spike with hard fanout and failure gates.
- [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md): proposed, Draft-implemented automatic P2P/peer/SFU preparation, commit, failback, and rollback with a hard host-edge budget.
- [ADR-0005](./adr/0005-automatic-hybrid-media-routing.md): proposed, default-off automatic P2P/peer/SFU preparation, commit, failback, and rollback with a hard host-edge budget.
Documentation and project memory are part of the product source of truth. Update them in the same change that alters the corresponding requirement or architecture.
+5 -5
View File
@@ -9,10 +9,10 @@ target. Automatic, viewer-transparent routing through direct P2P,
peer-assisted media, and an enabled SFU fallback is a standing requirement and
will be governed by a later ADR.
Draft ADR-0003 and Proposed ADR-0004 do not supersede this production baseline.
They isolate an optional SFU implementation and a bounded peer-assisted
experiment respectively. A later Accepted ADR is required before either changes
the default topology.
Historical ADR-0003's explicit whole-room SFU mode is rejected/superseded.
Proposed ADR-0004 and ADR-0005 do not supersede this production baseline; they
govern default-off peer-assisted and automatic optional-SFU experiments. A
later Accepted ADR is required before either changes the production topology.
## Context
@@ -28,7 +28,7 @@ Use separate control and media planes:
- Only pairs that cannot connect directly use authenticated TURN. Production requires STUN plus TURN over UDP and TCP; TURN/TLS is an optional restrictive-network enhancement, using its standard TCP port 5349 by default. Port 443 is optional and requires a dedicated public IP or a validated layer-4/SNI route when HTTPS already owns that address and port.
- Candidate selection is independent per pair. A room may simultaneously contain direct and relayed viewers without moving working peers onto the server.
- The deployed MVP broadcaster creates one peer connection per viewer. Rooms default to eight viewers and deployments may configure a limit from 1 through 16. Eight is an admission default, not a validated media-performance promise. A newer product target caps host media fanout at two; the current implementation does not satisfy that target above two viewers, and Proposed ADR-0004 owns the isolated experiment rather than silently changing this accepted baseline.
- An SFU is not the default path for the normal product envelope. Severe user-observed multi-viewer degradation triggered Draft ADR-0003/PR #12 as optional infrastructure, but it is not merged, deployed, or accepted while that PR remains Draft. If configured later, it may be selected automatically only after cheaper endpoint-carried routes cannot satisfy the route contract.
- An SFU is not the default path for the normal product envelope. Closed PR #12's explicit whole-room mode is superseded by merged PR #17's default-off automatic fallback. When a deployment configures SFU capacity, ADR-0005 permits selecting it only after cheaper endpoint-carried routes fail; that experiment is not deployed or accepted for production.
## Consequences
+32 -23
View File
@@ -11,9 +11,10 @@ custom packet-forwarding protocol. Real multi-viewer use later degraded severely
The product now also has a hard target that a broadcaster must never carry more
than two outgoing media edges.
Draft PR #12 proposes an independently reversible deployment-level SFU under
ADR-0003, but it is not merged or accepted. Making an SFU the normal path would
move every viewer's egress cost to the server. TeamSpeak-style shared encoding
Closed PR #12's explicit whole-room SFU proposal under ADR-0003 is historical
and superseded. Merged PR #17 instead supplies ADR-0005's default-off automatic
optional-SFU fallback. Making that fallback the normal path would still move
every viewer's egress cost to the server. TeamSpeak-style shared encoding
reduces host encode work but still sends one network copy to every viewer, so it
cannot meet the new fanout target alone.
@@ -67,17 +68,23 @@ downstream `RTCPeerConnection`. The browser therefore decodes and re-encodes at
every relay hop. Existing audio follows the same stream when capture provides
it; the experiment does not invent a separate audio protocol.
The peer-assisted wire accepts exactly `1080p60`, `1080p30`, and `720p30` as
room quality profile IDs. The signaling server holds the current ID in bounded,
per-room memory only; it is not persisted. An authenticated peer-assisted
snapshot includes the current value, the host reasserts its local choice before
reconciling assigned children, and live changes are sent to all online viewers.
The viewer relay keeps one desired profile for its current and future child.
Sender setup, stream replacement, and profile changes are serialized inside
each `HostPeer`, with the latest desired value winning. The ordinary P2P
authenticated wire remains unchanged and profile-control messages are forbidden
in that mode. This coordinates sender targets; it does not prove achieved
resolution, bitrate, frame rate, or resource cost.
The authoritative peer-assisted wire value is one exact `QualitySettings`
object, not a profile ID. It accepts only 720p/1080p/1440p, integer 15-60 fps,
integer 2-12 Mbps, and clarity/balanced/fluid preference, with missing, extra,
or out-of-range fields rejected. The three presets remain UI recommendations,
not protocol states. The server keeps the latest object in bounded per-room
memory only; it is not persisted. Authentication carries it, the host reasserts
its local value before reconciling children, and live changes reach online
viewers without an acknowledgement protocol.
`HostPeer`, the current and future viewer relay sender, and an enabled SFU
publisher consume that same last-wins setting. Initial sender setup, successful
track replacement, and live changes use the same serialized mutation path and
read back requested/applied bitrate, frame rate, scale, and preference. A
rejected mutation rolls back or fails closed through the existing route
controller. The ordinary P2P authenticated wire remains unchanged and rejects
room-setting messages. These are sender ceilings and preferences, not proof of
achieved resolution, bitrate, frame rate, or resource cost.
Encoded Transform, DataChannel media, WebCodecs rendering, dummy-sender byte
replacement, custom congestion control, codec ladders, multiple trees, mobile
@@ -102,9 +109,10 @@ viewing remains required.
## Acceptance Gate
Test 1, 3, 5, and 8 viewers for 30 minutes across the 1080p60, 1080p30, and
720p30 profiles with controlled per-edge RTT at or below 40 ms and loss at or
below 1%. Current desktop Chrome and Edge form the relay cohort; current Android
Test 1, 3, 5, and 8 viewers for 30 minutes across the three recommended ceiling
combinations, plus any advanced combination proposed for production, with
controlled per-edge RTT at or below 40 ms and loss at or below 1%. Current
desktop Chrome and Edge form the relay cohort; current Android
Chrome and iOS Safari join last as leaf checks. The protocol has no
relay-capability bit, so controlled join order is the only enforcement and this
spike is not safe for arbitrary-user deployment.
@@ -120,8 +128,8 @@ The proposal advances only if every condition holds:
zero-copy forwarding;
- excluding explicit TURN paths, the application server carries no media;
- host and relay upload remain within 20% of `childCount * observedBitrate`;
- authentication and live profile changes leave every current and future child
sender targeting the same latest room profile without altering the P2P wire;
- authentication and live setting changes leave every current and future child
sender targeting the same latest room setting without altering the P2P wire;
- first picture is at most 3 seconds, a 60 fps run does not remain below 50
decoded fps for more than 5 seconds, and depth-four p95 glass-to-glass
latency is at most 350 ms;
@@ -180,11 +188,12 @@ Negative:
- ADR-0001 remains the accepted production baseline. This proposal narrows its
peer-tree rejection only enough to run an isolated experiment.
- Draft ADR-0003 in PR #12 remains the separate optional-SFU proposal. This
experiment neither accepts/removes it nor implements the required automatic
P2P/peer-assisted/SFU route controller.
- ADR-0003/PR #12 is retained only as the rejected/superseded explicit
whole-room SFU history. ADR-0005 and merged PR #17 own the default-off
automatic optional-SFU fallback; this ADR does not accept that route for
production.
- If a later ADR accepts peer-assisted media, it must state exactly which parts
of ADR-0001 and ADR-0003 it supersedes.
of ADR-0001 and ADR-0005 it supersedes.
## References
@@ -1,6 +1,6 @@
# ADR-0005: Automatic Hybrid Media Routing
- Status: Proposed - Draft Implementation, Experiment Only
- Status: Proposed - Default-Off Implementation, Experiment Only
- Date: 2026-08-19
## Context
@@ -24,8 +24,9 @@ relay have a downstream budget of at most two active media edges. The current
browser relay remains stricter at one child.
ADR-0004 implements automatic peer assignment but not cross-mode fallback.
Draft PR #12 implements a mutually exclusive process-wide `p2p|sfu` mode. That
model cannot satisfy the required priority or minimize server egress.
Closed PR #12 proposed a mutually exclusive process-wide `p2p|sfu` mode; this
ADR and merged PR #17 supersede that model because it cannot satisfy the route
priority or minimize server egress.
## Proposed Experiment
@@ -207,9 +208,9 @@ encrypted from the SFU operator; the deployment and UI must not claim otherwise.
## Implementation Order
1. Port only LiveKit dependencies, token issuance, deployment templates, and
thin publisher/subscriber classes from Draft PR #12. Do not merge its
mutually exclusive protocol or page branches.
1. Reuse only the LiveKit dependencies, token issuance, deployment templates,
and thin publisher/subscriber boundary developed in closed PR #12. Do not
restore its mutually exclusive protocol or page branches.
2. Split publisher lifecycle into connect, activate, deactivate, and profile
update; make subscriber connections selective and inactive until assigned.
3. Add versioned route state and pure invariant/property tests.
@@ -259,9 +260,9 @@ Negative:
- This proposal corrects the automatic-migration interpretation in ADR-0001
without changing the currently deployed MVP.
- ADR-0004 remains the bounded full-stream browser-relay experiment.
- If accepted, this ADR supersedes Draft ADR-0003's process-wide explicit media
mode with automatic hybrid fallback using the same optional LiveKit building
blocks.
- This experiment and merged PR #17 supersede ADR-0003/closed PR #12's
process-wide explicit media mode with default-off automatic hybrid fallback.
ADR-0003 remains historical rejected/superseded context.
- Native shared encoding and two-tree striped distribution remain orthogonal
data-plane experiments under separate decisions.
+10 -10
View File
@@ -17,12 +17,12 @@ Last updated: 2026-08-19
## Current Recommendation
- Keep the deployed Windows Chrome/Edge broadcaster and responsive Web viewer as the measurement baseline. Validate current Android Chrome and iOS Safari as leaves.
- Use direct host P2P for one or two viewers. Draft ADR-0004 and Draft PR #13 own the default-off experiment for later viewers: two sticky deterministic chains, host capacity two, viewer capacity one, no proactive rebalance or composite score, and no more than eight viewers when `PEER_ASSISTED_MEDIA=true`.
- Use direct host P2P for one or two viewers. `main` carries ADR-0004's default-off experiment for later viewers: two sticky deterministic chains, host capacity two, viewer capacity one, no proactive rebalance or composite score, and no more than eight viewers when `PEER_ASSISTED_MEDIA=true`.
- Standard browser relays resend remote `MediaStreamTrack` values and therefore decode and re-encode at every hop. Browser WebRTC does not guarantee one shared encoder across peer connections; measure this cost rather than hiding it.
- Keep the full-stream browser experiment bounded. If it needs a different media plane, mark that experiment failed and evaluate native RTP relay, encoded-object striping, SVC, or FEC in separate measured spikes rather than hiding the change inside ADR-0004.
- Plan a separate native-sender ADR for TeamSpeak-style shared encoding regardless of the browser relay result. One compatible encoded output may feed at most two standard WebRTC packetizers to reduce host encode work, but each edge still consumes upload bandwidth. This work is not part of the current Draft and cannot rescue another failed browser-relay gate.
- Keep user-operated mini-SFU deployment and central single-node SFU Draft PR #12 as optional infrastructure. When configured, the route controller may select them automatically as the last fallback; their fanout egress belongs to their operator and does not justify Redis or multi-node infrastructure.
- Keep automatic routing behind ADR-0005: discrete failures drive peer recovery and optional SFU fallback, host media uses break-before-make and never exceeds two active edges, and a binary per-session relay capability keeps detected mobile/iPad clients as leaves. Corrected local recovery passes functionally but misses the sub-second gate.
- Keep user-operated or central single-node SFU capacity optional. When configured, `main`'s #17 route controller may select it only as the final fallback; its fanout egress belongs to its operator and does not justify Redis or multi-node infrastructure. Closed PR #12's explicit whole-room SFU mode is superseded, not a current option.
- Keep `main`'s automatic routing default-off behind ADR-0005: discrete failures drive peer recovery and optional SFU fallback, host media uses break-before-make and never exceeds two active edges, and a binary per-session relay capability keeps detected mobile/iPad clients as leaves. Corrected local recovery works but misses the sub-second gate.
- Prefer direct UDP, then TURN/UDP, with TURN/TCP as the required non-UDP fallback. Optional TURN/TLS uses TCP 5349 by default; TCP 443 needs a dedicated address or validated L4/SNI routing.
- Treat every quality setting as a ceiling. The current Draft defaults to clarity-first and adds bounded manual controls plus sender readback; production remains `balanced`, and its reported degradation is still unclassified. Run the 1/2/3-viewer plus TURN matrix before changing ceilings or adding any automatic controller.
- Do not add custom scene detection or dynamic-FPS control until WebRTC statistics and host resource measurements prove a material gap. Keep browser codec order until target hardware proves a more efficient common codec.
@@ -33,19 +33,19 @@ Last updated: 2026-08-19
- The repository is one npm package using Node.js 24, React, TypeScript, Vite, native WebRTC, `ws`, Zod, Vitest, and separate coturn. LAN/container listening defaults to all interfaces; bare-metal reverse-proxy deployment binds loopback and exposes `/healthz`.
- Production commit `5b2fb005f6f7` is deployed at `https://share.bonfire.icu`. It provides live source and quality changes without rebuilding healthy peers, video-only pause, clearer connection states, and the standard one-host-`RTCPeerConnection`-per-viewer path. Production therefore still exceeds the two-edge host target above two viewers.
- Production supports the three profiles with `contentHint = "motion"` and `degradationPreference = "balanced"`; its real live quality/pause cycle remains unverified.
- Draft PR #21 stacks on #19's equal idle-stage share/join actions and default-closed technical details; advanced quality stays in the common control area and actionable warnings stay visible. It is not deployed.
- `main` includes PR #19's equal idle-stage share/join actions and default-closed technical details. Draft PR #21 keeps advanced quality in the common control area and actionable warnings visible. Neither change is deployed.
- Access stays small: optional `ACCESS_PASSWORD`, a 12-hour stateless HMAC HttpOnly `SameSite=Strict` cookie, internal host token, role-bound signaling, Origin/payload checks, and no accounts, JWTs, or session map.
- With `ROOM_DATABASE_PATH` and the site password, built-in SQLite stores only auto-incremented room ID and host-token digest. Links persist and stopping sharing leaves viewers waiting. Production room ID `1` survived the `5b2fb005f6f7` deployment restart. Without the path, rooms remain random and temporary; public mode cannot use sequential rooms.
- Production runs behind nginx with Node.js 24.19.0. Authenticated coturn 4.17.2 at `turn.bonfire.icu:3478` has verified public STUN, TURN/UDP, TURN/TCP, and relay-only traffic. TURN/TLS is intentionally disabled.
- Draft PR #13 contains deterministic server/client peer-assisted assignment and standard remote-track relay. It defaults off, is capped at eight viewers, re-encodes at every relay, and is neither merged nor deployed. Native shared encoding and custom encoded browser transport are not implemented.
- Draft PR #17 stacks on PR #13: peer recovery precedes an allowlisted SFU root; revisioned prepare/commit/abort is session-bound; active SFU failure gets one token refresh, then fails back and disables SFU for that share. LiveKit is dormant when unconfigured.
- `main` contains deterministic peer-assisted assignment and standard remote-track relay. It defaults off, is capped at eight viewers, re-encodes at every relay, and is not deployed. Native shared encoding and custom encoded browser transport are not implemented.
- `main` also contains ADR-0005's default-off route controller: peer recovery precedes an allowlisted SFU root; revisioned prepare/commit/abort is session-bound; active SFU failure gets one token refresh, then fails back for that share. LiveKit is dormant when unconfigured.
- Corrected Chrome 151/LiveKit 1.13.5 smoke physically failed one leaf through peer recovery/reparent and two-root SFU. Frames resumed; 472 hook-assisted 25 ms samples saw host edge peak two. Failure-to-render took 2.257 seconds. It is localhost, headless, synthetic 720p30 video only.
- Draft PR #16 (`5b09f0a`) proves one Pion RTP write fans equivalent data to two transports with independent SSRCs. It has no encoder and proves neither physical shared encode nor browser readiness.
- Chrome 151 short synthetic `1/3/5/8` runs kept host edges at two, relay edges at one, and all viewers decoding; a three-viewer relay close recovered in 5.32 seconds. This proves topology/control only, not quality, load, endurance, or silent partitions.
- The current quality stack uses one strict, non-persistent `QualitySettings` value: 720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, and clarity/balanced/fluid preference. Host-only peer-assisted updates are last-wins for current/future relays and optional SFU; ordinary P2P wire stays unchanged. Sender updates are serialized and read back; rejection, rewriting, or three identical native limitation samples produce one visible warning without automatic action.
- Chrome 151 with one host, three viewers, and synthetic 720p30 passed balanced-to-clarity propagation, sender preference readback, stable peer-connection fingerprints, host fanout two, relay fanout one, and continued decoding. This is control evidence, not a quality, load, public-network, or endurance claim.
- The current quality stack uses one strict, non-persistent `QualitySettings` value: 720p/1080p/1440p, integer 15-60 fps, 2-12 Mbps, and clarity/balanced/fluid preference. Host-only updates are last-wins for current/future relays and optional SFU; ordinary P2P wire stays unchanged. Initial, replaced, and updated senders share serialized configure/readback; rejection, rewriting, or three identical native limitation samples produce one visible warning without automatic action.
- Chrome 151 with one host, three viewers, and synthetic 720p30 passed balanced and clarity propagation to every participant. Every baseline active video sender showed matching preference readback, peer fingerprints stayed stable, and every viewer decoded and rendered new frames after each change; host fanout was two and relay fanout one. This is control evidence, not a quality, load, public-network, or endurance claim.
- Per-frame encode/decode diagnostics use adjacent non-overlapping `getStats()` deltas rather than connection-lifetime averages; first, empty, changed-stream, and reset intervals remain unknown and rebase.
- Draft PR #21 passes CI, repository hygiene, type checking, 20 Vitest files/260 tests, and both builds. Production separately passed 108 tests and deployment checks.
- The review revision passes repository hygiene, type checking, 20 Vitest files/264 tests, both builds, and the Chrome control smoke. Production separately passed 108 tests and deployment checks.
## Provisional Quality Targets
@@ -71,5 +71,5 @@ These are measurement gates, not performance claims.
- Documentation index and current phase: `docs/README.md` and `docs/status.md`
- Requirements and design: `docs/需求理解.md` and `docs/方案设计.md`
- Media research: `docs/research/webrtc-p2p-screen-sharing.md`, `docs/research/realtime-quality-adaptation.md`, `docs/research/peer-assisted-media.md`, `docs/research/low-server-media-routes.md`, `docs/research/advanced-peer-distribution.md`, and `docs/research/native-shared-encode-sender.md`
- Architecture: ADR-0001, ADR-0002, proposed experiments ADR-0004/ADR-0005, and Draft PR #12's unaccepted ADR-0003
- Architecture: ADR-0001, ADR-0002, proposed experiments ADR-0004/ADR-0005, and historical rejected/superseded ADR-0003
- Deployment and maintenance: `docs/deployment.md`, `docs/maintenance.md`, `AGENTS.md`, and `.codex/`
+5 -5
View File
@@ -29,10 +29,10 @@ The smallest current plan is:
disappear.
Any browser-spike failure other than isolated relay re-encoding closes that
browser-relay route. It does not cancel the separate native sender plan. Draft
SFU PR #12 remains unmerged and undeployed. ADR-0005 now owns a separate Draft
implementation of the automatic cross-mode controller; it is also undeployed
and has not passed real LiveKit/browser network validation.
browser-relay route. It does not cancel the separate native sender plan. Closed
PR #12's explicit whole-room SFU mode is superseded. ADR-0005 and merged PR #17
own the default-off automatic cross-mode controller; it remains undeployed and
has not passed real LiveKit/browser network validation.
## Traffic Conservation And The Impossible Triangle
@@ -67,7 +67,7 @@ lost.
| Native shared-encode host | Host targets one encode for at most two standard WebRTC edges | libwebrtc public-API proxy risk spike, with Pion as fallback | Planned separate sender phase; still pays per-edge upload |
| Native volunteer encoded-RTP relay | Each volunteer forwards one encoded copy | Native install, RTP/RTCP forwarding, packaging, and opt-in relay policy | Conditional experiment only if relay re-encoding is the sole browser-spike failure |
| User-operated mini-SFU | User's SFU emits viewer copies | Separate deployment and its egress bill; running it on the host does not reduce that host's uplink | Optional capacity; automatic final fallback when enabled |
| Central single-node SFU | Service SFU emits viewer copies | Lowest endpoint relay burden; service pays approximately `N * B` egress | Optional capacity; automatic final fallback when enabled; Draft PR #12 only |
| Central single-node SFU | Service SFU emits viewer copies | Lowest endpoint relay burden; service pays approximately `N * B` egress | Optional capacity; automatic final fallback through merged PR #17 when enabled |
| SVC plus multiple trees | Peers emit striped layer copies across several trees | Layer scheduling, reassembly, redundancy, and more churn state | Separate conditional spike; target endpoint upload near `B` |
| Network coding | Peers or servers emit coded blocks | Generations, buffering, decoding, integrity, and a custom media plane | Trace/FEC spike only; optimize loss recovery, not clean bandwidth |
| MoQ | Publishers and MoQ relays emit object copies | New transport, packaging, player, relay, and auth stack | Optional central-fallback benchmark; still pays server egress |
+40 -24
View File
@@ -183,41 +183,56 @@ Per-edge ICE remains independent. An edge may be direct or may use authenticated
TURN, so peer assistance reduces normal server media traffic but cannot promise
zero server traffic in restrictive networks.
## Implemented Quality Profile Coordination
## Implemented Bounded Quality Coordination
The current Draft implementation coordinates one room profile across the
peer-assisted tree without adding adaptation logic. Its strict ID set is
`1080p60`, `1080p30`, and `720p30`, matching the existing capture and sender
profiles. The signaling server stores the current ID in a room-count-bounded
in-memory map, defaults to `1080p60`, includes it in peer-assisted authenticated
snapshots, and broadcasts host changes to online viewers. The value is retained
when sharing stops, removed when the room is abandoned or expires, and is not
written to SQLite. The standard P2P authenticated variant is unchanged and
profile-control messages are forbidden in that mode.
The current Draft coordinates one strict `QualitySettings` object across the
peer-assisted tree without adaptation logic. It accepts only 720p/1080p/1440p,
integer 15-60 fps, integer 2-12 Mbps, and the three standard degradation
preferences; missing, extra, or out-of-range fields fail schema validation.
The three visible presets are recommendations rather than wire IDs. The server
stores the latest complete object in a room-count-bounded in-memory map,
defaults to 1080p60 at 8 Mbps with clarity priority, includes it in
peer-assisted authenticated snapshots, and broadcasts host changes to online
viewers. The value survives a stopped share, is removed with the room, and is
not written to SQLite. Ordinary P2P authentication remains unchanged and
setting-control messages are forbidden in that mode.
After peer-assisted authentication, the host reasserts its local selection
before reconciling assigned children. A viewer records the snapshot or update
before applying its assignment. `ViewerRelay` keeps a synchronously updated
desired profile and serializes profile changes with stream replacement, so both
desired setting and serializes setting changes with stream replacement, so both
an existing child and a later replacement start from the latest target. Inside
`HostPeer`, initial sender configuration, stream replacement, and profile
updates share one mutation queue; operations read the latest desired profile at
`HostPeer`, initial sender configuration, stream replacement, and setting
updates share one mutation queue; operations read the latest desired setting at
execution time, giving rapid changes last-wins behavior without versions or
acknowledgements.
If the host changes quality while signaling cannot accept the room update, its
current local senders still apply the setting but the UI reports that room sync
is waiting for reconnect. The next successful host authentication reasserts the
latest object; no acknowledgement state machine is added.
The optional SFU publisher follows the same boundary: first publication,
successful track replacement, and live setting changes all configure the real
`RTCRtpSender`, then retain requested/applied bitrate, frame rate, scale, and
preference. Configuration rejection uses the existing rollback/fail-closed
path rather than silently leaving the publication on unverified parameters.
This only removes the previous fixed-1080p60 relay envelope. Browser constraints
and RTP sender parameters remain targets, so achieved bitrate, frame rate,
resolution, encode work, and cross-hop quality still require the measurement
matrix below. It does not add or imply shared encoding.
A controlled Chromium 151 loopback smoke used one host, three viewers, and a
synthetic 640x360/30 source. The downstream relay kept the same peer, sender,
and signaling generations while its configured ceilings changed from
8 Mbps/60 fps to 5 Mbps/30 fps and then 3 Mbps/30 fps. Each real
`setParameters()` call and both host `applyConstraints()` calls succeeded, and
the leaf kept decoding. This verifies wire ordering, relay propagation, and
connection preservation only. It does not verify 1080p output, visual quality,
CPU/GPU cost, TURN, public networks, or endurance.
A controlled Chrome 151 loopback smoke used one host, three viewers, and a
synthetic 720p30 source. Balanced and clarity settings reached every
participant; every baseline active outbound video sender displayed the matching
requested/applied preference; peer-connection fingerprints stayed unchanged;
and every viewer's decoded-frame and `requestVideoFrameCallback` counters grew
after each change. The harness brought each viewer page to the foreground for
its render check because Chrome throttles background frame callbacks. Host
fanout stayed at two and relay fanout at one. This is control and continuity
evidence only, not visual-quality, full-resolution, load, TURN, public-network,
or endurance evidence.
Peer multicast research such as SplitStream demonstrates why load-balanced,
failure-tolerant overlays normally introduce multiple trees and content
@@ -280,9 +295,10 @@ existing screen-audio track when the browser provides one. SVC/simulcast,
custom encoded transport, FEC changes, multi-tree striping, transcoding,
background mobile relay, and automatic SFU migration are excluded.
Run 1, 3, 5, and 8 viewers for 30 minutes across the 1080p60, 1080p30, and
720p30 profiles under controlled per-edge RTT at or below 40 ms and loss at or
below 1%. Record topology generation and depth, selected candidate type, host
Run 1, 3, 5, and 8 viewers for 30 minutes across the three recommended ceiling
combinations, plus any advanced combination proposed for production, under
controlled per-edge RTT at or below 40 ms and loss at or below 1%. Record
topology generation and depth, selected candidate type, host
and relay upload, packets lost, jitter, frames encoded/decoded/dropped, total
encode/decode time, decoded FPS, `qualityLimitationReason`, first picture,
reparent time, CPU, GPU, and glass-to-glass latency.
+8 -7
View File
@@ -125,13 +125,14 @@ or scene detector. Three consecutive samples of one non-`none` native
`qualityLimitationReason` produce one explanatory warning; a reason change or
recovery resets it and never triggers a media action.
A Chromium 151 loopback smoke with one host, three viewers, and synthetic
720p30 video propagated balanced then clarity settings to every participant,
displayed both sender preference readbacks, and preserved all peer-connection
fingerprints. Host media edges peaked at two, relay edges at one, and all three
viewers kept decoding. This verifies controls and connection preservation, not
visual quality, full-resolution performance, CPU/GPU cost, public networks, or
sustained behavior.
A Chrome 151 loopback smoke with one host, three viewers, and synthetic 720p30
video propagated balanced then clarity settings to every participant. Every
baseline active outbound video sender displayed the matching requested/applied
preference, peer-connection fingerprints stayed unchanged, and every viewer's
decoded-frame and foreground `requestVideoFrameCallback` counters grew after
each change. Host media edges peaked at two and relay edges at one. This verifies
controls and continuity, not visual quality, full-resolution performance,
CPU/GPU cost, public networks, or sustained behavior.
## Deliberate Non-Goals
+1 -1
View File
@@ -120,7 +120,7 @@ IETF 对 mesh/SFU 的拓扑说明见 [RFC 7667](https://www.rfc-editor.org/rfc/r
- 若一开始就有多条 `relay`,应提示服务器带宽正在增加。
- 桌面和手机观看者使用同一个 Web 播放端;分享者不要求朋友安装完整客户端。
新的产品目标要求分享端 fanout 永远不超过二。Proposed ADR-0004 用一个可整体删除的实验验证第三名及后续 viewer 能否由客户端转发;Draft PR #12 则保留可选单节点 SFU 作为独立对照。自动跨模式兜底是产品要求,但当前两个 Draft 都未实现它;超过小房间上限时仍建议使用外部直播服务。观察项包括:
新的产品目标要求分享端 fanout 永远不超过二。Proposed ADR-0004 用一个可整体删除的实验验证第三名及后续 viewer 能否由客户端转发;`main` 中已合并 PR #17 按 ADR-0005 提供默认关闭的自动可选 SFU 兜底。已关闭 PR #12 的显式整房 SFU 模式已被取代,不再作为当前方案;自动路由通过真实门槛前仍不可部署,超过小房间上限时仍建议使用外部直播服务。观察项包括:
- 正常工作负载持续超过实测可承载的 P2P 人数。
- 第一名或多名观看者已使用 TURN,继续 P2P 会重复占用服务器上行。
+15 -14
View File
@@ -8,10 +8,10 @@ The WebRTC proof of concept at `https://share.bonfire.icu` runs commit
`5b2fb005f6f7` with live quality/source changes, video pause, protected SQLite
rooms, sequential IDs, and reusable links.
Production still creates one host peer connection per viewer. Draft PR #13 adds
default-off two-chain peer assistance with per-hop re-encoding; ADR-0005 adds
optional automatic SFU fallback in Draft PR #17. Corrected recovery passes
functionally but misses its sub-second gate; neither Draft is merged or deployed.
Production still creates one host peer connection per viewer. `main` now
contains the default-off two-chain peer assistance, optional automatic SFU
fallback, and simplified entry/diagnostics from merged PRs #13, #17, and #19;
none is deployed. Corrected route recovery works but misses its sub-second gate.
## Current Snapshot
@@ -21,17 +21,18 @@ functionally but misses its sub-second gate; neither Draft is merged or deployed
- Direct ICE is preferred independently per media edge. Authenticated TURN/UDP and TURN/TCP are required production fallbacks; TURN/TLS is optional.
- Hidden routing is `direct P2P -> peer-assisted -> optional SFU`. After bounded ICE recovery it tries peer reparenting before an allowlisted SFU root. Session-bound revisions prepare, commit break-before-make under two host edges, or abort. Active SFU gets one token refresh, then fails back for that share.
- A viewer starts as a leaf each session and explicitly advertises relay capacity zero or one; the Web client reports detected mobile/iPad clients as zero and desktop-class browsers as one. Withdrawal stops future assignment without moving a healthy edge. Browser relays remain one-child; the host remains two-child.
- Draft PR #21 replaces the three-ID relay state with one strict, memory-only setting shared by current/future relays and optional SFU; ordinary P2P wire stays unchanged. It defaults clarity-first and exposes bounded manual ceilings.
- The same stack includes Draft PR #19's equal idle-stage share/join actions and default-closed technical details; actionable warnings remain visible. It is not deployed.
- Draft PR #21 uses one strict, memory-only quality setting for current/future relays and optional SFU; ordinary P2P wire stays unchanged. It defaults clarity-first and exposes bounded manual ceilings.
- It builds on `main`'s equal idle-stage share/join actions and default-closed technical details; actionable warnings remain visible. It is not deployed.
## Verified Evidence
- Production passed 108 tests, both builds, public HTTPS/access/TURN checks, clean activation, and a database restart retaining room `1`.
- Draft PR #13 now provides `npm run benchmark:peer-assisted`; the full check passes type checking, 13 Vitest files with 152 tests, and both production builds.
- PR #13 introduced `npm run benchmark:peer-assisted`; its 152-test baseline and both builds passed.
- A short Chrome 151 synthetic `1/3/5/8` benchmark passed every topology check: host active edges peaked at two, relay edges at one, every viewer kept increasing decoded frames through the measurement window, and the slowest first decoded frame was about 1.05 seconds. Closing a first-level relay in the three-viewer run recovered in about 5.32 seconds without exceeding host fanout two.
- Chrome 151 with one host, three viewers, and synthetic 720p30 passed balanced-to-clarity propagation, visible sender preference readback, unchanged peer-connection fingerprints, host fanout two, relay fanout one, and continued decoding.
- Chrome 151 with one host, three viewers, and synthetic 720p30 passed balanced and clarity propagation to every participant. Every baseline active video sender showed matching preference readback, peer-connection fingerprints stayed stable, and every viewer decoded and rendered new frames after each change; host fanout was two and relay fanout one.
- That quality smoke was localhost, headless, and video-only. It proves control propagation and continuity, not visual quality, full-resolution performance, CPU/GPU load, TURN, public networks, or endurance.
- Corrected Chrome 151/LiveKit 1.13.5 synthetic 720p30 smoke physically failed the same leaf through peer recovery/reparent and a two-root SFU route. Its frames resumed; 472 hook-assisted 25 ms samples saw host edge peak two. Failure report to active took 1.481 seconds and to new render 2.257 seconds.
- Draft PR #17 passed its 253-test baseline; Draft PR #21 passes CI, repository hygiene, type checking, 20 Vitest files/260 tests, and both production builds.
- The review revision passes repository hygiene, type checking, 20 Vitest files/264 tests, both production builds, and the Chrome control smoke above.
- Chrome 151 CDP checks at 320/375/390 CSS px keep the two idle-stage actions equal, on one row, 44 px high, and free of horizontal overflow. The Viewer waiting page also has no overflow; its details checkbox starts false, changes locally, and resets after navigation. These checks cover idle/waiting states, not live media.
- Draft PR [#16](https://github.com/TNTcraftHIM/Screener/pull/16) passes CI for one Pion RTP write fanned to two transports. It has no encoder and proves neither physical encode nor browser E2E.
- Local diagnostics use adjacent non-overlapping `getStats()` deltas; empty, changed-stream, and reset intervals rebase instead of publishing lifetime averages.
@@ -46,13 +47,12 @@ functionally but misses its sub-second gate; neither Draft is merged or deployed
- Production users report severe resolution/bitrate/FPS degradation across all profiles. The Draft now permits bounded clarity/balanced comparison, but a controlled 1/2/3-viewer and TURN sample must still distinguish capture, per-edge CPU, path, and receiver limits before any quality claim or automatic controller.
- Browser relays do not share encoding. Encoded objects, custom congestion control, multiple trees, and network coding remain separate measured candidates.
- The long-term endpoint budget is at most two downstream edges for both the host and relay-capable viewers, with one compatible encoded output reused across both edges where a native media engine can prove it. The current browser spike remains host capacity two/viewer capacity one and performs a new encode at each relay. Packet/layer striping and multi-parent assembly are recorded, not implemented.
- The corrected smoke is localhost/headless/video-only; public transport, audio, shaping, load, mobile, endurance, and sub-25 ms overlap remain open.
## Next Milestone
Finish ADR-0005: address the 2.257-second fallback, then validate public
transports, rollback, reconnect, controls, edge counts, egress, and load. The
quality/UI stack remains undeployed until its parent gates pass.
Before enabling `main`'s experimental routes, address the 2.257-second fallback
and validate public transports, rollback, reconnect, controls, edge counts,
egress, and load. The quality stack remains undeployed until those gates pass.
ADR-0004 still requires a full-resolution 30-minute `1/3/5/8` network,
resource, quality, latency, recovery, and browser/mobile-leaf matrix.
@@ -66,7 +66,8 @@ separate experiments, not ways to relabel a failed browser route.
## Blockers And Decisions
Peer assistance and automatic routing remain blocked on ADR-0004/0005 gates.
Production enablement of peer assistance and automatic routing remains blocked
on ADR-0004/0005 gates.
Native shared encode and multi-tree striping remain separate experiments.
- Whole-system versus selected-game audio for the first release.
+1 -1
View File
@@ -35,7 +35,7 @@
- [ ] 原生 shared encode 分享端:后续独立的 Electron/原生分享端阶段必须保证一份兼容编码输出扇出到最多两套独立 WebRTC packetizer,并由单独 ADR 和测量约束。它降低分享端重复编码负载,但不消除每条出边各自的上传带宽。浏览器不保证跨 `RTCPeerConnection` 复用编码器,因此当前 Web spike 不实现、也不能宣称满足 shared encode 保证。
- [ ] 原生 shared encode relay:具备原生 relay 能力的 audience 同样最多维护两条下游媒体边,并应让一份兼容编码输出供两条边复用。普通浏览器 audience 继续作为 leaf 或使用当前会逐跳重编码的实验路径,不伪称 shared encode。
- [ ] 分包/分层多树实验:把编码包、对象或可独立解码层拆到多个 peer 路径以分散上传,只能在独立 spike 中验证。必须保持亚秒级,处理多父重组、排序、丢包恢复、关键帧、同步和节点退出;未通过门槛前不进入产品路由控制器。
- [ ] 可选 SFU:部署者可选择是否提供单节点 SFU 资源;配置后由路由控制器把它作为 P2P 与 peer-assisted 之后的自动兜底,而不要求用户选模式。它保持非默认、可逆,并承担每名观看者的服务器出口成本。Draft PR #12 仍在评审,不能视为已合并、已部署或推荐路径。
- [ ] 可选 SFU:部署者可选择是否提供单节点 SFU 资源;配置后由 `main` 中 PR #17 的路由控制器把它作为 P2P 与 peer-assisted 之后的自动兜底,而不要求用户选模式。它保持默认关闭、可逆,并承担每名观看者的服务器出口成本。已关闭 PR #12 的显式整房 SFU 模式已被取代,不是当前选项;自动 fallback 仍须通过 ADR-0005 门槛才可部署。
- [ ] 分享端桌面封装:用 Electron 固定 Chromium 版本、改善选源/热键/系统音频体验,同时保留 Web 观看端。
- [ ] Windows 应用音频:仅捕获所选游戏及其子进程的音频,避免把语音聊天或通知声一并分享。
- [ ] 连接自检:在开始分享前验证信令、STUN、TURN/UDP、TURN/TCP 及部署实际配置的可选 TURN/TLS 可达性,并给出不暴露凭据的诊断结果。