docs: define automatic low-server media routing

This commit is contained in:
AAA committed 2026-08-19 09:06:11 +08:00
1 parent bd37869c2e
commit de08d716e8
12 files changed
+535 -92

No files matched your search

+3 -1
View File
@@ -8,12 +8,14 @@
- [Maintenance guide](./maintenance.md): Git workflow, context hygiene, document lifecycle, research policy, and automation.
- [P2P WebRTC research](./research/webrtc-p2p-screen-sharing.md): evidence, bandwidth model, browser constraints, reference implementations, and feasibility assessment.
- [Peer-assisted media research](./research/peer-assisted-media.md): browser shared-encode limits, standard relay re-encoding, rejected custom encoded transport, deterministic sticky topology, and abandon gates.
- [Low-server-cost media routes](./research/low-server-media-routes.md): traffic accounting, route screening, conditional native encoded-RTP relay, and explicit SFU fallbacks.
- [Low-server-cost media routes](./research/low-server-media-routes.md): traffic accounting, route screening, conditional native encoded-RTP relay, and automatic use of enabled SFU capacity as the final fallback.
- [Advanced peer distribution](./research/advanced-peer-distribution.md): multi-tree/SVC, encoded-object relay, native RTP forwarding, FEC/network-coding, MoQ, and measurable go/no-go gates.
- [Native shared-encode sender](./research/native-shared-encode-sender.md): bounded libwebrtc encoder-proxy risk spike, Pion fallback, and hard failure gates.
- [Realtime quality adaptation](./research/realtime-quality-adaptation.md): browser degradation policy, live profile changes, offline-encoding boundaries, and measurement gates.
- [Agent context governance research](./research/agent-context-governance.md): official Codex, Claude Code, Hermes Agent, and GitHub practices adopted by this repository.
- [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 automatic P2P/peer/SFU preparation, commit, 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.
+8 -2
View File
@@ -3,6 +3,12 @@
- Status: Accepted for the MVP
- Date: 2026-08-18
Current clarification, 2026-08-19: the MVP remains unchanged, but statements
below that exclude automatic topology migration do not describe the product
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
@@ -22,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.
- No SFU is the automatic or default path for the normal product envelope. Severe user-observed multi-viewer degradation triggered Draft ADR-0003/PR #12 as an explicit optional experiment, but it is not merged, deployed, or accepted while that PR remains Draft.
- 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.
## Consequences
@@ -39,7 +45,7 @@ Negative:
- Browser APIs do not guarantee that several peer connections share a single hardware encode.
- Direct peers learn one another's network addresses; this is acceptable only for the initial trusted-friends threat model.
- If the site-wide password is disabled, the numeric room code is the sole viewing capability and does not provide a strong privacy guarantee.
- A room-level P2P-to-SFU migration adds state, keyframe, and reconnection complexity and is not part of the first prototype.
- A room-level P2P-to-SFU migration adds state, keyframe, and reconnection complexity and is not part of the first prototype. This implementation boundary does not remove the product requirement for automatic, viewer-transparent fallback.
- TURN over TCP can suffer head-of-line blocking, and its `turn:` client-to-server transport is not TLS-wrapped. The WebRTC media remains protected by DTLS-SRTP independently of that TURN transport.
- Optional TURN/TLS on port 5349 or 443 can improve compatibility but cannot guarantee success through every authenticated proxy or policy-controlled network. A normal Cloudflare HTTP proxy does not proxy TURN; using Cloudflare for TURN requires a compatible layer-4 product such as Spectrum.
@@ -33,8 +33,12 @@ Preserve the product priority in this order:
1. direct P2P for one or two viewers;
2. peer-assisted forwarding for later viewers when this experiment is active
and every required capability is present; and
3. the optional SFU as an explicit deployment alternative, not a silent
in-room migration.
3. an enabled optional SFU as the final automatic fallback in a later route
controller.
This bounded experiment implements only the first two levels. That scope must
not be read as requiring users to select a topology or rejecting automatic
cross-mode fallback as a product goal.
The experiment is gated by `PEER_ASSISTED_MEDIA`, which defaults to `false`.
Enabling it requires `MAX_VIEWERS_PER_ROOM` at or below eight; larger configured
@@ -77,8 +81,9 @@ 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
background relay, and automatic SFU migration are excluded. They are not rescue
work if standard track relay fails.
background relay, and cross-mode SFU migration are excluded from this spike.
They are not rescue work if standard track relay fails; a separate automatic
route-controller ADR owns cross-mode fallback.
## Shared Encoding Boundary
@@ -138,8 +143,9 @@ meeting a gate requires Encoded Transform/DataChannel/WebCodecs media, custom
congestion control, FEC/RTX changes, multiple trees, relay scoring, transcoding,
a codec ladder, relaxed host fanout, or a browser-specific RTP injection hack,
stop. Change this ADR to Rejected and delete the experimental browser-relay
runtime path and dependencies. That result keeps standard P2P plus explicit
user-operated or central SFU fallbacks. It does not cancel the separate planned
runtime path and dependencies. That result keeps standard P2P plus enabled
user-operated or central SFU fallbacks for the later automatic controller. It
does not cancel the separate planned
native shared-encode sender, and that sender cannot be used to mark an otherwise
failed browser-relay topology as passing.
@@ -174,8 +180,9 @@ 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 explicit optional-SFU proposal.
This proposal neither accepts/removes it nor adds automatic P2P/SFU switching.
- 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.
- If a later ADR accepts peer-assisted media, it must state exactly which parts
of ADR-0001 and ADR-0003 it supersedes.
@@ -0,0 +1,232 @@
# ADR-0005: Automatic Hybrid Media Routing
- Status: Proposed - Experiment Only
- Date: 2026-08-19
## Context
The product has always required route selection and recovery to be automatic
and invisible to the broadcaster and viewers. Earlier repository wording that
prohibited silent migration was an incorrect interpretation and is superseded
by this proposal. Simplicity constrains the controller and user experience; it
does not require users to choose a media topology.
The ordered preference remains:
1. direct P2P edges;
2. peer-assisted forwarding;
3. an SFU supplied by the deployment; and
4. bounded waiting or failure when no route can satisfy the budgets.
Every P2P edge independently tries direct ICE and authenticated TURN fallback.
TURN is not a separate room topology. The host and every future native/encoded
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.
## Proposed Experiment
Treat the SFU as a virtual parent for selected fallback roots, not as an
all-room replacement. Keep the existing peer-assisted topology as the active
baseline and add one in-memory `MediaRouteController` per room.
```text
normal: host -> root A -> ...
host -> root B -> ...
fallback: host -> remaining peer root -> ...
host -> SFU -> selected fallback roots -> existing descendants
```
Only fallback roots subscribe directly to the SFU. Each root can continue to
feed its existing peer child, so SFU egress is approximately `K*B`, where `K`
is the number of SFU roots, rather than necessarily `N*B` for all viewers.
Deployment decides whether SFU capacity exists by configuring the complete
LiveKit endpoint/key/secret tuple. There is no user-facing topology selector and
no process-wide `MEDIA_MODE` union. Without the tuple, the same controller ends
at peer assistance and bounded waiting/failure.
## State And Wire
The server holds one room revision with a participant assignment for every
retained member, plus at most one pending room plan:
```ts
type Upstream =
| { kind: "none" }
| { kind: "peer"; peerId: string }
| { kind: "sfu" };
interface ParticipantRoute {
upstream: Upstream;
childPeerIds: string[];
}
interface RoomMediaRoute {
revision: number;
assignments: Map<string, ParticipantRoute>;
sfu: {
publicationGeneration: string | null;
rootPeerIds: string[];
};
}
```
The server mapping is authoritative. Each participant receives only its own
assignment for that room revision; it never receives or edits the complete
mapping. The explicit SFU publication generation prevents an old and a new
publisher from being counted as one logical edge during commit or rollback.
Minimal protocol additions:
- `route-update { revision, phase: "prepare" | "active", assignment }`;
- `sfu-config { revision, url, token }`;
- `route-ready { revision, phase: "prepare" | "active" }`;
- `route-failed { revision, phase, connectionId }`; and
- `refresh-sfu { revision }`.
Authentication carries only the current participant assignment and room
revision. Existing peer signaling keeps its connection generation and
assigned-edge authorization. The server
derives the failed edge from the authenticated session, active revision, and
connection ID instead of accepting a client-supplied parent or arbitrary reason.
Late, duplicate, or stale revision messages have no effect.
## Transition
1. Keep two ordinary peer roots while those routes work.
2. A failed edge first exhausts its current ICE restart/rebuild and per-edge
direct/TURN options.
3. The server computes revision `R+1` without mutating the active topology. It
sends a prepare plan only to the host and required fallback roots.
4. Those participants establish actual LiveKit connections with
`autoSubscribe:false`, but the host does not publish and roots do not
subscribe yet. `prepareConnection()` may warm DNS/TLS earlier but is not a
ready signal.
5. After every required participant acknowledges prepare, the server commits
the active revision. The host synchronously closes the selected direct root
before publishing to the SFU. Thus:
`directHostChildren + activeSfuPublicationGenerations <= 2`.
`activeSfuPublicationGenerations` is zero or one. A new generation cannot
publish until the replaced direct edge and any old SFU generation are
inactive.
6. Allowlisted fallback roots explicitly subscribe. On their first new video
track, they atomically replace their upstream stream, acknowledge active,
and feed the existing downstream relay without rebuilding descendants.
7. A prepare timeout increments the revision and restores the unchanged active
route. A failure after commit creates a new rollback/failure revision; stale
acknowledgements cannot revive the abandoned plan.
SFU fallback is sticky until the current share stops. A new sharing generation
starts from the cheapest available route. The first experiment does not
continuously fail back during a live share, avoiding oscillation without adding
a score or hysteresis framework.
Use make-before-break when a downstream slot is free. When both slots are in
use, strict fanout requires warm break-before-make: participants prepare the new
transport, then the host releases one media edge before publishing. Zero
interruption, no standby path, and a hard two-edge maximum cannot all be
guaranteed at once.
## Triggers And Budgets
The first controller reacts only to discrete events:
- no eligible peer slot or the accepted maximum depth;
- exhausted ICE/rebuild for an assigned edge;
- relay participant departure;
- unsupported route capability; or
- a hard bounded-send-queue overflow on a future encoded-object route.
It does not combine RTT, CPU, bitrate, geography, or a synthetic health score.
Measurements may add a new explicit trigger only through a reviewed change.
`MAX_SFU_ROOTS_PER_ROOM` bounds server egress. Exhausting that budget waits or
fails explicitly; it never creates a third host edge or silently fans the SFU
out to the whole room.
Sub-second failure recovery is a target after failure detection. The current
30-second control heartbeat cannot meet it for silent partitions, so the media
plane needs a small 100-200 ms liveness/queue signal or an equivalent native
transport event. This signal must be measured before its interval is fixed.
## Security And Privacy
LiveKit credentials remain short-lived, room-bound, role-bound, and memory-only.
Only the host may publish screen tracks. Subscription permissions allow only
the current fallback-root identities. Ordinary SFU media is not end-to-end
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.
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.
4. Implement server prepare/commit/abort and strict authorization.
5. Integrate host/viewer first-frame switching while retaining relay children.
6. Extend the tracked `1/3/5/8` benchmark with automatic fallback, direct/TURN,
source/profile/pause/stop, and server-egress measurements.
## Acceptance Gates
- Host active media edges never exceed two across every prepare, commit,
rollback, reconnect, and stale-message sequence.
- Current browser relays never exceed one child; future capacity two is exposed
only by a proven native/encoded capability.
- Only necessary fallback roots receive SFU media and the configured root/egress
budget is never exceeded.
- Prepare failure leaves the old active route unchanged.
- First new decodable picture arrives within one second after a route failure is
detected in the reference regional network.
- Existing descendants, source selection, quality profile, pause, audio, and
persistent-room stop/restart semantics survive the transition.
- No LiveKit configuration preserves the existing P2P/peer behavior and wire.
- LiveKit UDP, TCP, and TURN fallback are independently verified before the SFU
can be called a reliable final route.
## Consequences
Positive:
- Users do not select or understand a topology.
- Host fanout remains bounded while server media egress is paid only for
fallback roots.
- The ordinary peer path and its direct/TURN behavior remain reusable.
- Revisioned prepare/commit isolates stale asynchronous results without a
continuous optimizer.
Negative:
- Strict two-edge migration can include a bounded interruption.
- LiveKit adds an optional operational dependency and can inspect ordinary SFU
media.
- A media liveness signal and versioned cross-client transition expand the
state space and require real failure-injection tests.
## Relationship To Existing ADRs
- 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.
- Native shared encoding and two-tree striped distribution remain orthogonal
data-plane experiments under separate decisions.
## References
- [LiveKit selective subscription](https://docs.livekit.io/transport/media/subscribe/#selective-subscription)
- [LiveKit track subscription permissions](https://docs.livekit.io/transport/media/publish/#track-permissions)
- [LiveKit connection preparation](https://docs.livekit.io/reference/client-sdk-js/functions/Room.prepareConnection.html)
- [WebRTC](https://w3c.github.io/webrtc-pc/)
+11 -10
View File
@@ -7,8 +7,9 @@ Last updated: 2026-08-19
- Build Discord/KOOK/Oopz/TeamSpeak-like low-latency game screen sharing for one broadcaster and a small trusted friend group. Public or large broadcasts belong on OBS/Twitch-class services.
- Viewers should join from a normal desktop or mobile browser. A packaged/native sender is a planned later stage for shared encoding and may also improve capture and audio without changing the Web viewer requirement.
- Use a Photon-style central rendezvous service for access, rooms, signaling, deterministic topology, STUN, TURN fallback, and observability while clients carry media whenever practical.
- Server bandwidth cost is a primary constraint. The media priority is direct P2P, then measured peer assistance, then an explicit user-operated or central SFU fallback. Never migrate topology silently.
- The broadcaster has a hard target of at most two downstream media edges. Every assigned edge independently uses ICE and authenticated TURN/UDP or TURN/TCP when direct connectivity fails; P2P-first never means direct-only.
- Server bandwidth cost is a primary constraint. The ordered media ladder is direct P2P, then measured peer assistance, then an enabled user-operated or central SFU fallback. Route selection and failure recovery must be automatic and viewer-transparent, while remaining deterministic, budget-driven, and small enough to reason about. This has always been the product intent; earlier text prohibiting silent migration was an incorrect interpretation and is superseded.
- Host and relay-capable endpoints each have a target budget of at most two downstream media edges. Every P2P edge independently uses direct ICE or authenticated TURN fallback. With two edges, the long-term native/encoded-object path should reuse one compatible encoded output; the current browser experiment is stricter at one viewer child and re-encodes.
- Packet or layer striping across two peer trees is a recorded candidate for reducing endpoint upload toward one full-stream bitrate. It requires a separate bounded experiment for multi-parent assembly, loss recovery, synchronization, churn, and sub-second latency; it is not part of the current full-stream two-chain implementation.
- Browser convenience does not override reachability, measurable performance, or honest resource accounting. Shared encoding reduces compute, not the last-hop copy required by every viewer.
- Durable decisions, current snapshots, research, code, `AGENTS.md`, and `.codex/` belong in Git. Memory and status are rewritten in place rather than kept as transcripts.
- Research current official sources and established implementations before material work. Apply Occam's razor and reject speculative protocols, services, scoring, and scale.
@@ -18,9 +19,10 @@ Last updated: 2026-08-19
- 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`.
- 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.
- Reject the browser experiment if it needs Encoded Transform/DataChannel/WebCodecs media, custom congestion or RTP recovery, multiple trees, relay scoring, transcoding, codec ladders, browser RTP injection, or more than two host edges.
- 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 explicit fallbacks. They transfer fanout egress to their operator and do not justify automatic switching, Redis, or multi-node infrastructure.
- 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.
- Add automatic routing only through a separate, measured controller contract. It must keep the host at no more than two media edges, prefer endpoint-carried traffic over central egress, and use discrete capacity/failure events instead of continuous multi-metric scoring. Cross-mode live migration, make-before-break overlap, and rollback semantics remain unimplemented and require an ADR before code.
- 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 1080p60 at 8 Mbps as a best-effort ceiling. Keep the deployed 1080p60 at 8 Mbps, 1080p30 at 5 Mbps, and 720p30 at 3 Mbps profiles switchable during sharing. `balanced` does not guarantee resolution-first behavior; measure actual browser tradeoffs.
- 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.
@@ -35,13 +37,11 @@ Last updated: 2026-08-19
- 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, standard remote-track relay, and synchronized 1080p60/1080p30/720p30 room profiles. 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.
- Chromium 151 demonstrated the intended three-viewer shape: exactly two connected host outbound peers; viewer 1 with one inbound plus one outbound forwarding to viewer 3; viewer 2 direct from the host; and decoded frames at all three viewers.
- A Chromium 151 one-to-eight synthetic functional smoke formed two depth-four chains: two connected host outbound peers, one inbound plus one outbound at viewers 1 through 6, inbound-only viewers 7 and 8, and decoded frames everywhere. This proves topology only, not profile quality, resource cost, latency, or endurance.
- Closing the first-level relay reattached its branch and resumed decoding in about 5.3 seconds while host active connected outbound edges peaked at two. A silent partition instead waits for the 30-second heartbeat, so detection can take 30 to 60 seconds before the default 5-second viewer grace; that path is unverified.
- The tracked Chrome 151 harness passed short synthetic `1/3/5/8` runs: host active edges peaked at two, relay edges at one, every viewer kept increasing decoded frames through the measurement window, and the slowest first frame was about 1.05 seconds. A three-viewer first-level relay close recovered in about 5.32 seconds without exceeding host fanout. These are topology/control checks, not quality, load, or endurance proof; silent partitions remain unverified.
- The peer-assisted authenticated snapshot and host-only update synchronize one bounded, non-persistent room profile. Online viewers receive changes; `ViewerRelay` uses the latest desired profile for current and future children, while `HostPeer` serializes sender setup, stream replacement, and last-wins profile updates. The ordinary P2P wire is unchanged. Real 1/3/5/8 quality, resource, and latency measurements remain pending.
- Chromium 151 kept the same relay peer, sender, and signaling generations while changing its sender ceilings from 8 Mbps/60 fps to 5 Mbps/30 fps and 3 Mbps/30 fps; the leaf continued decoding. The synthetic 640x360/30 source proves profile propagation and connection preservation only, not full-resolution quality or load.
- 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.
- The recorded peer-assisted full check passes type checking, 12 Vitest files with 144 tests, and both client and server production builds. Production release `5b2fb005f6f7` separately passed 108 Vitest tests, both builds, loopback and public HTTPS/access-gate checks, and a clean activation without disturbing nginx, coturn, or the blog.
- The recorded peer-assisted full check passes type checking, 13 Vitest files with 152 tests, and both client and server production builds. Production release `5b2fb005f6f7` separately passed 108 tests and deployment checks.
- Draft SFU PR #12 and ADR-0003 remain unmerged, undeployed, and independently reversible. There is no infrastructure blocker for the deployed Web baseline or bounded peer-assisted experiment.
## Provisional Quality Targets
@@ -57,6 +57,7 @@ These are measurement gates, not performance claims.
- Sustainable viewer count by broadcaster and relay hardware, quality profile, network class, and media route; use instrumented 1/3/5/8 comparisons rather than a 1:8 claim.
- Whether ADR-0004 passes fanout, re-encoding, depth-four latency, reparenting, silent-partition, and mobile-leaf gates.
- The smallest safe automatic route and migration contract for the ordered P2P, peer-assisted, and optional-SFU ladder, including its bounded overlap and rollback budget.
- Exact mobile lifecycle behavior and whether Windows per-application audio is required for the first release.
- Project license and distribution model, which determines whether GPL/AGPL sources can move beyond study-only use.
- Initial deployment regions and expected mainland China, Hong Kong, and overseas network mix.
@@ -66,6 +67,6 @@ 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`, and `docs/research/native-shared-encode-sender.md`
- Architecture: ADR-0001, ADR-0002, proposed experiment ADR-0004, and Draft PR #12's unaccepted ADR-0003
- 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
- Deployment and maintenance: `docs/deployment.md`, `docs/maintenance.md`, `AGENTS.md`, and `.codex/`
+196
View File
@@ -0,0 +1,196 @@
# Advanced Peer Distribution
- Research date: 2026-08-19
- Scope: at most eight trusted viewers, sub-second interactive media, endpoint
downstream fanout at most two, and minimal central-server media egress
- Status: candidate routes and bounded spike gates; none is implemented
## Terms
- **Multiple trees:** split one media representation into two stripes and send
each stripe through a different peer tree. A viewer receives both trees; an
internal relay forwards only its assigned stripe. This balances endpoint
upload but requires two parents, assembly, loss recovery, and churn handling.
- **SVC:** one encoder produces a base layer plus dependent temporal or spatial
enhancement layers. It lets a forwarder drop enhancement data, but it does
not create a distribution topology or move layers between transports.
- **Network coding:** send repair symbols formed from several source symbols so
a receiver can recover loss after collecting enough independent symbols. It
adds bytes and CPU; it can reduce retransmission delay but cannot compress the
video or replace route migration after a complete path failure.
Complexity alone is not a rejection reason. A route is retained only when a
bounded experiment shows lower endpoint compute or bandwidth, keeps the user
experience automatic, and meets the latency and queue limits below.
## Cost Accounting
Let `B` be one complete stream bitrate, `N` the viewer count, and `r` repair
overhead. Protocol headers, retransmission, and TURN overhead are additional.
| Route | Host upload | Central egress | Encode work |
| --- | ---: | ---: | --- |
| Current full-stream browser chains | at most `2B` | zero except TURN edges | host up to two encoders; every relay encodes again |
| Native full-stream RTP relay | at most `2B` | zero except TURN edges | relay zero encode; native host can share one encode |
| Two encoded-object stripe trees | about `(1+r)B` | zero except TURN edges | host one encode; relay zero encode |
| Central SFU or MoQ relay | about `B` | about `N*B` | host one encode; server forwards without transcoding |
Useful last-hop traffic remains approximately `N*B`; these routes only decide
which nodes emit the copies. Every P2P edge still runs its own ICE process and
may independently select direct connectivity or TURN.
## Candidate 1: Native RTP Relay
This is the nearest-term high-confidence optimization. A native helper can read
encoded RTP from one WebRTC receiver and write the payload to at most two
independent downstream browser PeerConnections without decoding and encoding
the video again. Each downstream edge keeps its own SSRC, pacing, RTCP,
DTLS-SRTP, ICE, and TURN behavior.
The first Go/Pion spike is video-only VP8 at 720p30 with one browser upstream
and two unchanged browser children. It must prove:
- zero relay video-encoder calls and at least 40% lower relay CPU than browser
remote-track forwarding;
- no more than 25 ms added p95 relay latency;
- per-child bytes no more than 110% of input payload bitrate;
- bounded queue age below 100 ms;
- PLI, NACK/RTX, 1% loss, one direct edge, and one TURN edge; and
- reparented first picture within one second after failure detection.
This lowers relay compute and generational quality loss. It does not lower host
or total upload until combined with a native shared sender or striped media.
Relay-capable participants need the helper; ordinary desktop/mobile browsers
remain valid leaves.
## Candidate 2: Two Stripe Trees With SVC
The target shape assigns every relay-capable endpoint at most two downstream
edges. The host sends stripe A to one seed and stripe B to another. An internal
node forwards its assigned roughly `B/2` stripe to at most two children, so its
total upload stays near `B`. Every viewer receives both stripes from two parents.
The host encodes once and sends about `B`, central egress stays near zero, and
relay nodes do not encode.
SVC may make one stripe a base temporal layer and the other enhancement data,
or duplicate selected base data for resilience. SVC alone cannot implement this
shape: the WebRTC SVC API keeps layers in one encoded RTP stream, and WebRTC
Encoded Transform does not permit moving encoded frames across streams. The
data plane therefore requires either native RTP/object nodes or WebCodecs plus
DataChannel.
Start with single-layer 50/50 frame/object striping, then test `L1T2` temporal
SVC only after transport works. Use two deterministic, interior-node-disjoint
trees; do not add a continuous optimizer.
Go gates:
- host and relay upload no more than `1.1B` without repair;
- unique host encode calls equal input frames and relay encode calls stay zero;
- whole-room useful bytes no more than `1.1*N*B` without repair;
- no more than 20 ms p95 overhead per hop and depth-four p95 no more than
350 ms;
- a path failure returns a decodable picture within one second after detection;
- protection still saves at least 30% host upload relative to `2B`; and
- target desktop relays and mobile leaves pass runtime codec capability probes.
Reject that spike if endpoint upload remains near `2B`, relay encoding returns,
queues cannot be bounded, or supported clients require two unrelated decoders
and application-side picture composition.
## Candidate 3: Browser Encoded Objects
WebCodecs can encode once and expose `EncodedVideoChunk` values. WebRTC
DataChannel can carry framed chunks over peer ICE/TURN paths. This is the
credible pure-browser route to encoded forwarding and later two-tree striping,
but it replaces the browser's RTP media receiver with application framing,
pacing, a jitter buffer, keyframe requests, a WebCodecs renderer, and eventually
A/V synchronization.
The first spike is only `host -> relay -> leaf`, 720p30 video, reliable control,
and unordered media with bounded retransmission. Use chunks no larger than
16 KiB and a hard `bufferedAmount` ceiling.
Go gates:
- one host encode and zero relay encode;
- at least 30% lower host encode CPU than two browser send pipelines and at
least 40% lower relay CPU than remote-track forwarding;
- framing bytes no more than 110% of the RTP baseline;
- two-hop glass-to-glass p95 no more than 250 ms;
- bounded queues and first picture below one second after reroute; and
- required mobile decoders pass, with later A/V drift no more than 50 ms.
## Candidate 4: FEC And Network Coding
FEC is useful only when repair bytes avoid slower RTX or visible freezes. It
does not reduce clean-path bandwidth or survive permanent loss of a full tree
unless redundancy approaches another complete stream.
Before media integration, replay captured RTP/object traces through a simulator:
- random loss at 0%, 1%, 3%, and 5%;
- 20, 50, and 100 ms bursts at 20, 40, and 80 ms RTT;
- baseline NACK/RTX versus XOR or Reed-Solomon `k=4/8` with one/two repair
symbols, then sliding-window RLC only if those results justify it.
Go gates:
- at least 50% lower residual loss/freeze at 1-3% loss or 20-50 ms bursts;
- repair p95 below 100 ms and at least 25% faster than RTX;
- total media plus repair and residual RTX bytes no more than 110% of baseline
media plus RTX bytes;
- no more than 16 ms generation buffering and 15% active repair overhead; and
- coding CPU below roughly 5% of one reference core.
Clean paths should keep repair disabled or below 3-5%. Whole-path failure still
belongs to the route controller.
## Candidate 5: MoQ
Browser WebTransport connects a browser to a server; it does not make a normal
browser an Internet-listening peer relay. A central MoQ deployment therefore
has the same server-egress class as an SFU. Benchmark it only as an optional
central fallback and keep it only if, at equal quality and egress, latency or
server CPU improves at least 20% over the SFU reference and draft-version churn
is isolated behind a small adapter.
## Automatic Route Controller Boundary
The product order is:
`direct -> peer full/native -> peer striped-object -> enabled SFU root -> enabled central fanout -> wait/fail`
The controller must be automatic and invisible. It uses explicit capability
bits, route revisions, media generations, edge budgets, and discrete failures;
it does not expose topology choices to host or viewers. Initial hard triggers
are capacity, maximum depth, ICE/media failure, unsupported codec, and bounded
send-queue overflow. Measurements may later justify additional triggers.
Use make-before-break when a downstream slot is free. With all two slots in
use, strict fanout means break-before-make; zero interruption, no standby, and a
hard two-edge limit cannot all be guaranteed simultaneously. Sub-second recovery
also requires a media/data heartbeat near 100-200 ms rather than the current
30-second control heartbeat.
## Sources And License Boundary
Sources checked on 2026-08-19:
- [WebRTC SVC](https://www.w3.org/TR/webrtc-svc/),
[Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/),
[WebCodecs](https://www.w3.org/TR/webcodecs/), and
[WebTransport](https://www.w3.org/TR/webtransport/) - W3C specifications.
- [WebRTC Data Channels, RFC 8831](https://www.rfc-editor.org/rfc/rfc8831.html)
and [Sliding-window RLC, RFC 8681](https://www.rfc-editor.org/rfc/rfc8681.html).
- [Pion WebRTC](https://github.com/pion/webrtc) and
[klauspost/reedsolomon](https://github.com/klauspost/reedsolomon) - MIT; no
source copied.
- [SplitStream](https://www.microsoft.com/en-us/research/publication/splitstream-high-bandwidth-multicast-in-a-cooperative-environment/)
and [Network Coding for Large Scale Content Distribution](https://www.microsoft.com/en-us/research/publication/network-coding-for-large-scale-content-distribution/)
- research evidence, not reusable source licenses.
- [MOQT draft](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/) and
[moq-dev/moq](https://github.com/moq-dev/moq) - core MIT/Apache-2.0; its OBS
plugin is separately GPL-2.0-or-later and remains study-only.
No GPL/AGPL implementation code was copied.
+23 -18
View File
@@ -22,13 +22,15 @@ The smallest current plan is:
4. Only if browser relay re-encoding is the isolated failure should another
experiment add opt-in native volunteer encoded-RTP relays.
5. Keep a user-operated mini-SFU and a centrally operated single-node SFU as
explicit fallbacks. They transfer fanout bandwidth to their operator; they do
not make it disappear.
optional capacity. When configured, a later route controller uses that
capacity automatically only after cheaper endpoint-carried routes fail.
They transfer fanout bandwidth to their operator; they do not make it
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. There is no automatic topology
migration in this plan.
SFU PR #12 remains unmerged and undeployed. The required automatic cross-mode
fallback is not implemented in the current spike and needs its own bounded ADR.
## Traffic Conservation And The Impossible Triangle
@@ -62,11 +64,11 @@ lost.
| Fixed two-chain browser relay | Host emits at most two copies; each relay emits at most one | Ordinary browser, but every relay decodes and re-encodes and adds a hop | Current default-off, maximum-eight-viewer spike |
| 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 | Explicit fallback |
| Central single-node SFU | Service SFU emits viewer copies | Lowest endpoint relay burden; service pays approximately `N * B` egress | Explicit fallback; Draft PR #12 only |
| SVC plus multiple trees | Peers emit striped layer copies across several trees | Layer scheduling, reassembly, redundancy, and more churn state | Reject for the current product |
| Network coding | Peers or servers emit coded blocks | Generations, buffering, decoding, integrity, and a custom media plane | Reject for the current product |
| MoQ | Publishers and MoQ relays emit object copies | New transport, packaging, player, relay, and auth stack | Reject for the current WebRTC product |
| 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 |
| 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 |
| Peer-assisted CDN | Peers cache or upload segments/objects | Discovery, locality, scheduling, incentives, abuse, and privacy systems | Reject for this trusted group |
| IP multicast | Multicast routers replicate packets | Requires multicast-enabled hosts and routed networks unavailable to ordinary Internet browsers | Reject outside managed networks |
@@ -93,20 +95,20 @@ sole browser-spike failure. They must not rescue failures in deterministic
assignment, host fanout, relay loss recovery, ICE/TURN connectivity, mobile
leaves, or end-to-end latency.
## Rejected For The Current Implementation
## Outside The Current Browser-Track Spike
- **SVC and multiple trees:** WebRTC SVC controls layered encoding; it does not
distribute replicas. SplitStream shows how striping across multiple trees can
spread peer load, but the required tree maintenance, reassembly, scheduling,
and redundancy contradict the fixed two-chain experiment.
spread peer load. A separate encoded-object/native spike now owns its tree
maintenance, reassembly, scheduling, and redundancy gates; none belongs in
the fixed full-stream two-chain experiment.
- **Network coding:** published work improves block distribution in large
overlays. Generations, coding/decoding, pollution protection, and buffering
create a custom transport and add latency; it is not a small extension to live
browser WebRTC.
overlays. A trace-first FEC experiment must prove lower freeze/repair delay
within strict byte and buffering budgets before any media integration.
- **MoQ:** Media over QUIC provides promising relay-oriented publish/subscribe,
priorities, and partial reliability, but MOQT and its streaming format remain
active Internet-Drafts. Adopting it would replace the media plane and still
leave relay operators paying fanout egress.
priorities, and partial reliability, but MOQT remains an active Internet-Draft.
Benchmark it only as a central fallback because a browser cannot act as an
inbound WebTransport peer and the relay still pays fanout egress.
- **Peer-assisted CDN:** PCDN research addresses large file or segmented VoD
distribution and ISP/provider cost. Its discovery, caching, locality,
scheduling, incentives, and abuse controls are disproportionate for at most
@@ -116,6 +118,9 @@ leaves, or end-to-end latency.
candidate-pair transports; normal browser WebRTC across NAT and mobile networks
exposes no Internet multicast delivery path.
The measurable gates and exact staged experiments are in
[Advanced Peer Distribution](./advanced-peer-distribution.md).
## Sources And License Boundary
Primary sources checked on 2026-08-19:
+4 -4
View File
@@ -21,11 +21,11 @@ cost is unavoidable in ordinary browsers: every relay decodes and re-encodes
the screen stream. The first spike measures whether that cost is acceptable; it
does not hide it or claim shared encoding.
The product preference is `direct P2P -> peer-assisted -> optional SFU`. This is
an engineering priority, not permission for silent in-room migration. Direct
The product preference is `direct P2P -> peer-assisted -> optional SFU`. Direct
P2P remains the simplest path for one or two viewers. The experiment assigns the
third and later viewers to peers automatically. The optional SFU remains an
explicit, independently reversible deployment choice.
third and later viewers to peers automatically. Deployment decides whether SFU
capacity exists; when it does, a later small route controller must use it as an
automatic last fallback without asking host or viewers to select a topology.
## What Browsers Can Share
+4 -4
View File
@@ -16,8 +16,8 @@
|
+---- 仅直连失败的观看者 ---- TURN relay -------+
首选顺序:一至两名观看者直接 P2P -> 通过门槛后的 peer-assisted -> 部署者显式选择的可选 SFU。
该顺序不允许静默拓扑迁移;大规模公开分享仍直接使用现有直播服务。
首选顺序:一至两名观看者直接 P2P -> 通过门槛后的 peer-assisted -> 部署已提供的可选 SFU。
系统必须无感完成拓扑分配、恢复和必要迁移;大规模公开分享仍直接使用现有直播服务。
```
“让画面跑起来”难度不高;“像 Discord/TeamSpeak 一样在不同 GPU、浏览器、NAT、运营商和弱网中都保持清晰、60 fps、低延迟”难度高。建议把产品分层:
@@ -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 作为独立对照。两者都不允许在房间内静默切换,超过小房间上限时仍建议使用外部直播服务。观察项包括:
新的产品目标要求分享端 fanout 永远不超过二。Proposed ADR-0004 用一个可整体删除的实验验证第三名及后续 viewer 能否由客户端转发;Draft PR #12 则保留可选单节点 SFU 作为独立对照。自动跨模式兜底是产品要求,但当前两个 Draft 都未实现它;超过小房间上限时仍建议使用外部直播服务。观察项包括:
- 正常工作负载持续超过实测可承载的 P2P 人数。
- 第一名或多名观看者已使用 TURN,继续 P2P 会重复占用服务器上行。
@@ -324,4 +324,4 @@ WebRTC 标准没有承诺固定毫秒延迟。工程目标必须带网络条件
4. 观看端优先做成免安装响应式 Web;分享端先 Web 验证,再按捕获/音频实测升级 Electron。
5. 产品代码优先直接使用浏览器 WebRTC API;借鉴 MiroTalk BRO 和 Screego,不在许可证未定前直接 fork GPL/AGPL 代码。
6. 分享端 hard fanout 为二;一至两名 viewer 走直接 P2P,第三名及以后只通过 ADR-0004 的 bounded peer-assisted spike 验证,不得隐藏回退为更多 host 连接。
7. 可选 SFU 保持显式、可逆且非默认。peer-assisted 若未通过逐跳重编码负载、延迟、兼容和换父门槛就整体放弃,不通过 encoded custom media、评分器、多树或自研 RTP 来挽救。
7. 可选 SFU 保持可逆且非默认;部署提供后可被后续路由控制器自动用作最后兜底。peer-assisted 若未通过逐跳重编码负载、延迟、兼容和换父门槛就整体放弃,不通过 encoded custom media、评分器、多树或自研 RTP 来挽救。
+31 -39
View File
@@ -4,19 +4,14 @@ Last updated: 2026-08-19
## Phase
The WebRTC proof of concept is deployed at `https://share.bonfire.icu` on commit
`5b2fb005f6f7`. It supports live quality/source changes, video-only pause,
protected SQLite persistence, sequential room IDs, and reusable links; room ID
`1` survived the deployment restart.
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 uses one host `RTCPeerConnection` per viewer. Draft PR #13 adds
a default-off, standard-WebRTC peer-assisted experiment for later viewers: two
sticky chains with host capacity two, viewer capacity one, and decode/re-encode
at every browser relay hop. The Draft now also propagates the host-selected
1080p60/1080p30/720p30 room profile to current and future relay senders.
`PEER_ASSISTED_MEDIA=false` remains the default, the mode cannot be enabled above
eight viewers, and it is neither merged, deployed, nor production-validated.
Draft SFU PR #12 is also unmerged and undeployed.
Production still creates one host `RTCPeerConnection` per viewer. Draft PR #13
adds default-off, maximum-eight-viewer peer assistance: two sticky chains,
host capacity two, viewer capacity one, per-hop decode/re-encode, and synchronized
quality profiles. Draft SFU PR #12 is also unmerged and undeployed.
## Current Snapshot
@@ -24,57 +19,54 @@ Draft SFU PR #12 is also unmerged and undeployed.
- Whole-site `ACCESS_PASSWORD` is optional. Protected sessions use a stateless 12-hour HMAC HttpOnly `SameSite=Strict` cookie; host authentication remains internal and signaling is role-bound.
- Without `ROOM_DATABASE_PATH`, rooms are random and temporary. With both the database path and site password, room IDs start at `1`, links persist, and stopping a share leaves viewers waiting. SQLite stores only room ID and host-token digest.
- Direct ICE is preferred independently per media edge. Authenticated TURN/UDP and TURN/TCP are required production fallbacks; TURN/TLS is optional.
- The media priority is direct P2P for one or two viewers, peer assistance only after every ADR-0004 gate passes, then user-operated or central single-node SFU as an explicit fallback. There is no automatic migration.
- The required media priority has always been direct P2P, then peer assistance, then an enabled user-operated or central SFU fallback, with route allocation and recovery hidden from host and viewers. The current peer-assisted Draft already assigns the first two viewers to the host and later viewers to peers automatically. Cross-mode failure fallback and live migration are not implemented yet; they must preserve the host two-edge budget, minimize server egress, and use a small deterministic state machine rather than composite health scoring.
- Peer-assisted profile state is bounded, server-memory-only, and absent from the ordinary P2P wire. The host reasserts its choice after authentication; online viewers receive changes, and each relay applies the latest desired profile to its current or future child through serialized sender mutations.
## Verified Evidence
- Production release `5b2fb005f6f7` passed type checking, 108 tests, both builds, loopback/public HTTPS and access-gate checks, and clean activation without disturbing nginx, coturn, or the blog.
- The protected production database and permissions survived restart, and room ID `1` remained present. Stop-and-republish and link reuse still require a browser cycle.
- The recorded peer-assisted full check passes type checking, 12 Vitest files with 144 tests, and both production builds.
- Chromium 151 one-to-three evidence held exactly two connected host outbound peers. Viewer 1 held one inbound plus one outbound peer and forwarded to viewer 3; viewer 2 stayed direct; all three decoded frames.
- A Chromium 151 one-to-three loopback smoke kept the same relay peer, sender, and signaling generations through 8 Mbps/60, 5 Mbps/30, and 3 Mbps/30 ceilings; calls succeeded and the leaf kept decoding. Its synthetic 640x360/30 source proves propagation and connection preservation, not 1080p quality or load.
- A Chromium 151 one-to-eight synthetic functional smoke formed two depth-four chains. The host held two outbound peers; viewers 1 through 6 each held one inbound plus one outbound; viewers 7 and 8 were leaves; all decoded frames. This proves topology only, not quality, resource cost, latency, or endurance.
- Closing the first-level relay caused its branch to reattach and decode again after about 5.3 seconds while host active connected outbound edges peaked at two.
- Production passed 108 tests, both builds, public HTTPS/access/TURN checks, clean activation, and a database restart with room `1` retained.
- 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.
- 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.
- A separate live-profile smoke kept the same relay peer, sender, and signaling generations through 8 Mbps/60, 5 Mbps/30, and 3 Mbps/30 ceilings while its leaf kept decoding.
- Local WebRTC diagnostics derive per-frame encode/decode cost from adjacent non-overlapping `getStats()` samples. First, empty, changed-stream, and reset intervals stay unknown and rebase instead of publishing a misleading lifetime average.
- Production HTTPS/WSS, access cookie, room/WebSocket authorization, certificate renewal, public STUN, and authenticated TURN/UDP and TURN/TCP relay-only bidirectional paths are verified. TURN/TLS is intentionally disabled.
## Unverified Boundaries
- Room profile propagation and live sender-ceiling changes are verified on the synthetic loopback path. Actual full-resolution cross-hop output, sustained bitrate and frame rate, encode cost, visual quality, and heterogeneous-network behavior under each profile remain unverified.
- Peer assistance has not completed 1/3/5/8-viewer 30-minute runs, relay CPU/GPU and generational-quality measurements, controlled loss/RTT tests, or depth-four latency gates.
- The short harness uses synthetic headless capture and proves topology, controls, stats collection, and recovery only. Full-resolution 30-minute runs, relay CPU/GPU, generational quality, controlled loss/RTT, depth latency, and actual game-capture behavior remain unverified.
- Android Chrome and iOS Safari remain required leaves but are not verified for this topology. There is no runtime relay-capability bit; controlled join order is the only mobile-leaf enforcement, so arbitrary-user deployment is excluded.
- The observed recovery starts from a page close immediately seen by the server. A silent partition can wait 30 to 60 seconds for heartbeat detection before the default 5-second grace; it remains unverified.
- Real screen/game audio, heterogeneous machines and networks, mobile lifecycle behavior, the production live quality/pause cycle, room `1` stop-and-republish/link reuse, and sustained profile performance remain unverified.
- Browser relays do not provide shared encoding. Encoded Transform, DataChannel/WebCodecs media, custom congestion control, multiple trees, relay scoring, and automatic SFU switching remain outside the experiment.
- Browser relays do not provide shared encoding. Encoded Transform, DataChannel/WebCodecs media, custom congestion control, multiple trees, and network coding remain researched alternatives rather than current implementation. They may enter a separate bounded experiment only when measurements identify a specific bottleneck and show a plausible sub-second benefit.
- 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.
## Next Milestone
Complete only the bounded ADR-0004 experiment, with distribution work ahead of
UI polish:
Complete ADR-0004 before UI polish: run full-resolution 30-minute `1/3/5/8`
matrices with network shaping and resource/quality/latency capture; exercise source,
profile, stop/restart, rebuild, reparent, signaling and silent-partition paths; and
verify Chrome/Edge relays with Android Chrome and iOS Safari leaves.
- repeat live switching with full-resolution high-motion capture, then validate stream replacement, stop/restart, child rebuild, reparenting, and signaling interruption against the latest room profile in real browsers;
- preserve host fanout at two and viewer fanout at one across joins, reconnects, grace expiry, and reparenting;
- run reproducible 1/3/5/8-viewer measurements using the deployed profiles, recording topology depth, selected ICE path, host/relay upload, encode/decode work, first picture, decoded FPS, quality limitation, and glass-to-glass latency;
- repeat first-level relay loss with a server-observed close and separately measure silent heartbeat-detected partition recovery; and
- verify current Chrome/Edge relays plus Android Chrome and iOS Safari leaves.
In parallel, Proposed ADR-0005 defines the smallest automatic route controller
for `direct P2P -> peer-assisted -> optional SFU`. It must use explicit
budgets and discrete failure events, avoid continuous composite scoring, and
specify make-before-break or a bounded visible interruption before any live
migration is implemented. The current Draft still performs no automatic
cross-mode migration.
For a disconnect immediately observed by the server, the gate retains the
default 5-second grace followed by at most 3 seconds to restore a decodable
picture, about 8 seconds total. Silent partitions include their detection delay.
All gates fail closed. If success requires a custom browser media plane, changed
RTP recovery, multiple trees, scoring, transcoding, a codec ladder, or more than
two host edges, remove the browser-relay experiment. A separate native
shared-encode sender remains planned regardless of that result; it lowers host
encode work, does not remove per-edge upload, and cannot rescue another failed
topology gate.
ADR-0004 gates fail closed for the current full-stream browser path. Native
shared encode and packet/layer striping are separate measured experiments, not
ways to relabel a failed browser-relay result.
## Blockers And Decisions
No infrastructure blocker remains. Production adoption of peer assistance is
blocked on the measurement matrix and ADR-0004 gates above. Native shared encode
requires its own later ADR and is not part of Draft PR #13.
Production peer assistance remains blocked on the matrix and ADR-0004 gates.
Automatic cross-mode routing, native shared encode, and multi-tree striping each
require their own bounded ADR/spike.
- Whole-system versus selected-game audio for the first release.
- Initial deployment region and network cohort.
+2 -2
View File
@@ -241,7 +241,7 @@ TURN/TLS 是可选能力,不是生产启动门槛。启用时默认使用 RFC
状态:Proposed / Experiment Only。该扩展不改变上面的已部署 PoC,也不表示正式采用。
新的产品硬目标是分享端任何时刻最多两条下游媒体边。首选顺序为一个或两个观看者直接 P2P,更多观看者尝试 peer-assisted,部署者仍可显式选择可选 SFU;这不是静默的房间内 P2P/SFU 迁移。
产品硬目标是分享端任何时刻最多两条下游媒体边。系统按一个或两个观看者直接 P2P、更多观看者 peer-assisted、部署已提供的 SFU 最后兜底的顺序自动分配和恢复,分享者与观看者不选择拓扑。当前实验只实现前两级;跨模式迁移由后续独立 ADR 定义。
实验只实现一个由服务端确定的广度优先拓扑:host 最多两个 child,每名 viewer 最多一个 child,因此形成两条按加入顺序平衡的 sticky 链。父节点只按深度和 `serverJoinSequence` 选择第一个空 slot。新 viewer 加入时不搬动已有节点;parent 离开时只把失去 parent 的 subtree root 重新分配到第一个空 slot,其 descendants 保持连接。该规则不采集或组合 RTT、带宽、CPU、地理位置与质量评分,也不主动重平衡。没有 parent 时明确等待或拒绝,不能创建分享端第三条边。
@@ -251,7 +251,7 @@ Peer-assisted 信令只接受 `1080p60`、`1080p30`、`720p30` 三个 `qualityPr
Viewer 在处理 peer-assisted 鉴权快照时先更新 `ViewerRelay` 的 desired profile,再应用拓扑分配;同一个串行队列协调 remote stream、当前 child 更新和以后重建的 child,因此它们读取同一最新目标。每个 `HostPeer` 也把初始 sender 配置、`replaceStream()` 和 `updateProfile()` 放进同一 sender-mutation queue;`desiredProfile` 在入队前同步更新,已排队操作执行时读取最新值,使快速连续换档保持 last-wins,而不增加版本号或确认协议。这里同步的是浏览器 sender 的目标上限,不是实际分辨率、帧率、码率或 shared encoding 的性能保证。
spike 复用现有 screen stream 和浏览器 codec 协商,覆盖当前桌面 Chrome/Edge relay、按测试顺序位于链尾的当前 Android Chrome 与 iOS Safari leaf、最多八名 viewer;捕获存在时沿同一标准 WebRTC 路径带上音频。Encoded Transform/DataChannel/WebCodecs 媒体、SVC/simulcast、多树、转码、codec ladder、综合评分、移动端后台 relay 和自动 SFU 迁移均不实现。验收与失败即放弃条件以 ADR-0004 为准;成功也只能触发新的生产 ADR。
spike 复用现有 screen stream 和浏览器 codec 协商,覆盖当前桌面 Chrome/Edge relay、按测试顺序位于链尾的当前 Android Chrome 与 iOS Safari leaf、最多八名 viewer;捕获存在时沿同一标准 WebRTC 路径带上音频。Encoded Transform/DataChannel/WebCodecs 媒体、SVC/simulcast、多树、转码、codec ladder、综合评分、移动端后台 relay 和跨模式 SFU 迁移不在本次 spike 内实现。该范围限制不能解释为拒绝产品所需的自动兜底;验收与失败即放弃条件以 ADR-0004 为准,自动路由由后续独立 ADR 定义。
浏览器不保证多个 `RTCPeerConnection` 共享一次编码。实验允许 host 对一至两个 seed 各自编码。后续独立的 native sender 阶段必须用 shared encoder 将一份编码结果交给最多两套标准 WebRTC packetizer,并由新的 ADR 约束;它降低 host 编码负载,但两条出边仍各自消耗上传带宽。该计划不依赖本次浏览器 relay 实验成败,也不在本 spike 中提前抽象或实现。
+6 -4
View File
@@ -4,7 +4,7 @@
## 需求概述
构建一个面向熟人游戏场景的低延迟屏幕共享工具。分享者可以在私密房间内分享游戏、应用窗口或显示器画面及声音,多名朋友可以即时观看。系统采用类似 Photon Host Mode 的产品抽象:中心服务负责房间、身份、确定性拓扑和建立连接,媒体偏好依次为小规模直接 P2P、实验性的 peer-assisted 转发、部署者显式选择的可选 SFU;STUN 协助打洞,直连失败的单条边才使用 TURN。该顺序不是静默运行时迁移许可。分享端 Web 优先并允许后续 Electron/原生增强;观看端必须可通过桌面或手机浏览器打开邀请链接,不绑定专用客户端。
构建一个面向熟人游戏场景的低延迟屏幕共享工具。分享者可以在私密房间内分享游戏、应用窗口或显示器画面及声音,多名朋友可以即时观看。系统采用类似 Photon Host Mode 的产品抽象:中心服务负责房间、身份、确定性拓扑和建立连接,媒体优先级依次为小规模直接 P2P、peer-assisted 转发、部署已提供的可选 SFU;STUN 协助打洞,直连失败的单条边才使用 TURN。系统必须自动、无感地完成分配、故障恢复和必要的路线迁移,分享者与观看者不手动选择拓扑。分享端 Web 优先并允许后续 Electron/原生增强;观看端必须可通过桌面或手机浏览器打开邀请链接,不绑定专用客户端。
当前 PoC 默认允许一名分享者和最多八名观看者,部署者可在 1 至 16 之间配置上限,超额观看者会被明确拒绝。八人只是当前容量默认值,不是已验证的性能承诺;真实 1:8 上行、编码和稳定性仍须测量。产品不承担公开直播或大规模观看,相关需求交给 OBS/Twitch 等成熟直播服务。
@@ -17,7 +17,7 @@
- [ ] 屏幕采集:分享者可主动选择显示器或应用窗口,开始、停止、临时暂停画面和切换分享来源。
- [ ] 游戏声音:Windows Chrome/Edge 目标环境可请求系统音频;没有返回音轨时必须明确提示,而不是静默无声。
- [ ] 一对多观看:一名分享者可向配置允许的多名观看者发送画面;默认上限为 8,可配置范围为 1 至 16。当前 PoC 的逐观看者连接是测量基线,不再是超过两名观看者时的目标拓扑。
- [ ] 分享端 fanout 硬上限:分享端任何时刻最多维护两条下游媒体边,包括加入、断线、换父和恢复过程。第三名及后续观看者只能由通过验收的 peer-assisted 拓扑、部署者显式选择的可选 SFU 承载,或被明确拒绝;不能隐藏地回退为分享端第三条连接。
- [ ] 分享端 fanout 硬上限:分享端任何时刻最多维护两条下游媒体边,包括加入、断线、换父和恢复过程。第三名及后续观看者由系统自动分配给通过验收的 peer-assisted 拓扑,必要时自动回退到部署已提供的可选 SFU;两者都不可用时才明确拒绝,不能隐藏地回退为分享端第三条连接。
- [ ] P2P 优先:每条逻辑媒体边的 ICE 必须先尝试直接 UDP 路径,服务器不得默认中转可直连的媒体。一个或两个观看者优先直接连接分享端;更多观看者的 peer-assisted 行为须先通过 ADR-0004 的实验门槛。
- [ ] NAT 兜底:每条 parent-child 边独立进行 ICE;生产环境必须提供 STUN、TURN/UDP 与 TURN/TCP candidates,通过优先级偏好直连和 UDP,同时允许无法使用 UDP 的网络选择 TCP,不能因为某条边的网络与其他边不同而静默失败。
- [ ] 质量档位:提供 1080p60(8 Mbps)、1080p30(5 Mbps)、720p30(3 Mbps)三档上限;分享期间可直接换档而不重新选择来源或重建健康连接。Peer-assisted 房间只允许这三个档位 ID,并把分享者当前选择同步给现有和后续 relay sender;普通 P2P 信令结构保持不变。保留 `contentHint = "motion"` 并显式设置 `degradationPreference = "balanced"`;该偏好允许浏览器在帧率和分辨率之间权衡,不保证清晰度优先,具体行为必须实测。自定义动态 FPS 或画面检测策略必须在统计证明有必要后再实现。
@@ -30,7 +30,9 @@
- [ ] Peer-assisted 媒体实验:分享端最多两个 child、每名 viewer 最多一个 child;信令服务只按深度和服务端加入序号确定性地形成两条 sticky 平衡链,不加入综合评分或主动重平衡。每个 viewer 将收到的标准 remote track 通过新的 `RTCPeerConnection` 转发,因此会逐跳解码和重编码;必须测量而不能称为 shared encode。服务端只在受房间数限制的内存状态中保存当前档位,不写入 SQLite;host 鉴权后先重报档位,在线 viewers 接收更新,后加入者从鉴权快照取得当前值。该功能在 ADR-0004 的实验门槛通过并由后续 ADR 正式接受前不得成为产品路径。
- [ ] 原生 shared encode 分享端:后续独立的 Electron/原生分享端阶段必须保证一份兼容编码输出扇出到最多两套独立 WebRTC packetizer,并由单独 ADR 和测量约束。它降低分享端重复编码负载,但不消除每条出边各自的上传带宽。浏览器不保证跨 `RTCPeerConnection` 复用编码器,因此当前 Web spike 不实现、也不能宣称满足 shared encode 保证。
- [ ] 可选 SFU:部署者可显式选择单节点 SFU 来降低分享端上传;它保持非默认、可逆,并承担每名观看者的服务器出口成本。Draft PR #12 仍在评审,不能视为已合并、已部署或推荐路径。
- [ ] 原生 shared encode relay:具备原生 relay 能力的 audience 同样最多维护两条下游媒体边,并应让一份兼容编码输出供两条边复用。普通浏览器 audience 继续作为 leaf 或使用当前会逐跳重编码的实验路径,不伪称 shared encode。
- [ ] 分包/分层多树实验:把编码包、对象或可独立解码层拆到多个 peer 路径以分散上传,只能在独立 spike 中验证。必须保持亚秒级,处理多父重组、排序、丢包恢复、关键帧、同步和节点退出;未通过门槛前不进入产品路由控制器。
- [ ] 可选 SFU:部署者可选择是否提供单节点 SFU 资源;配置后由路由控制器把它作为 P2P 与 peer-assisted 之后的自动兜底,而不要求用户选模式。它保持非默认、可逆,并承担每名观看者的服务器出口成本。Draft PR #12 仍在评审,不能视为已合并、已部署或推荐路径。
- [ ] 分享端桌面封装:用 Electron 固定 Chromium 版本、改善选源/热键/系统音频体验,同时保留 Web 观看端。
- [ ] Windows 应用音频:仅捕获所选游戏及其子进程的音频,避免把语音聊天或通知声一并分享。
- [ ] 连接自检:在开始分享前验证信令、STUN、TURN/UDP、TURN/TCP 及部署实际配置的可选 TURN/TLS 可达性,并给出不暴露凭据的诊断结果。
@@ -62,7 +64,7 @@
- 默认八名观看者和可配置的 1 至 16 范围是接入容量边界,不承诺任意机器、档位或网络都能稳定承载该人数,也不承诺跨洲网络下的固定低延迟。
- 浏览器规范要求每次由用户主动选择分享源,权限不能永久保存;纯网页不能保证指定游戏窗口或始终获得系统音频。
- 1080p60 是能力目标,实际分辨率、帧率和硬件编码由浏览器、GPU、驱动、画面复杂度及网络共同决定。
- 已部署首版仍是标准 WebRTC P2P。Peer-assisted spike 只复用标准 remote-track WebRTC,不加入 Encoded Transform/DataChannel/WebCodecs 媒体协议,也不自研 UDP、编解码器、拥塞控制、FEC/RTX、多树、转码、codec ladder 或综合评分器;需要其中任一项即放弃本次浏览器 relay 实验。独立计划中的 native shared encode sender 不在本 spike 实现,也不能挽救其他拓扑门槛失败。SFU 只允许作为单独评审、显式配置的可选模式,不自动切换或双发。
- 已部署首版仍是标准 WebRTC P2P。Peer-assisted spike 只复用标准 remote-track WebRTC,不加入 Encoded Transform/DataChannel/WebCodecs 媒体协议,也不自研 UDP、编解码器、拥塞控制、FEC/RTX、多树、转码、codec ladder 或综合评分器;需要其中任一项即放弃本次浏览器 relay 实验。独立计划中的 native shared encode sender 不在本 spike 实现,也不能挽救其他拓扑门槛失败。跨 P2P、peer-assisted 与可选 SFU 的自动路由是产品要求,但由单独 ADR 约束;当前 spike 尚未实现跨模式迁移或短时双发。
- GPL/AGPL 项目在许可证策略确认前仅用于研究,不能直接复制到计划闭源的产品中。
## 验收标准