diff --git a/docs/README.md b/docs/README.md index 2618aa5..86233f9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,7 @@ - [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. +- [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. diff --git a/docs/project-memory.md b/docs/project-memory.md index d1fba36..ff82a8f 100644 --- a/docs/project-memory.md +++ b/docs/project-memory.md @@ -39,7 +39,9 @@ Last updated: 2026-08-19 - 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 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. -- The recorded peer-assisted full check passes type checking, 12 Vitest files with 141 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. +- 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. - 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 @@ -64,6 +66,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`, and `docs/research/low-server-media-routes.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 - Deployment and maintenance: `docs/deployment.md`, `docs/maintenance.md`, `AGENTS.md`, and `.codex/` diff --git a/docs/research/low-server-media-routes.md b/docs/research/low-server-media-routes.md index e6bd9d4..16fbb93 100644 --- a/docs/research/low-server-media-routes.md +++ b/docs/research/low-server-media-routes.md @@ -16,7 +16,9 @@ The smallest current plan is: eight viewers, and decodes and re-encodes at every relay. 3. Plan a separate native shared-encode sender regardless of the browser relay result. It reduces duplicate host encoding while retaining at most two - standard WebRTC edges, so it does not remove their upload cost. + standard WebRTC edges, so it does not remove their upload cost. The bounded + risk spike is specified in + [Native Shared-Encode Sender](./native-shared-encode-sender.md). 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 @@ -58,7 +60,7 @@ lost. | --- | --- | --- | --- | | Direct host P2P | Host emits one copy per viewer | Host upload and sender pipelines grow with viewers | Keep for one or two viewers | | 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 encodes once for at most two standard WebRTC edges | Native sender packaging and custom libwebrtc encoder fanout | Planned separate sender phase; still pays per-edge upload | +| 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 | diff --git a/docs/research/native-shared-encode-sender.md b/docs/research/native-shared-encode-sender.md new file mode 100644 index 0000000..3b0ee11 --- /dev/null +++ b/docs/research/native-shared-encode-sender.md @@ -0,0 +1,140 @@ +# Native Shared-Encode Sender + +- Research date: 2026-08-19 +- Scope: one Windows game-capture sender, one encoded video stream, and at most + two independent standard WebRTC media edges +- Status: candidate risk spike; not implemented or accepted for production + +## Decision Input + +TeamSpeak-style shared encoding is a required later sender phase. It can remove +duplicate host encoding work, but it cannot remove the upload copy, RTP state, +or congestion control required by each outgoing edge. The first implementation +must prove one narrow property before adding capture, audio, UI, or packaging: + +> Two unmodified browser viewers receive two independent WebRTC sessions while +> one physical encoder invocation produces the compatible encoded access unit +> consumed by both sessions. + +The preferred first experiment is a standalone Windows C++ sender using a fixed +libwebrtc revision. It keeps libwebrtc's mature PeerConnection, RTP/RTCP, pacing, +NACK/RTX, DTLS-SRTP, ICE, and TURN behavior on each edge. Electron is not part of +the media experiment; it can only wrap a proven helper later. + +## Minimal Risk Spike + +```text +existing Node signaling + | +Windows sender.exe +synthetic source -> shared encoder coordinator -> encoder proxy A -> PC A -> browser A + `--------> encoder proxy B -> PC B -> browser B +``` + +- Windows x64, C++, video-only, fixed 720p30, and exactly two outgoing edges. +- Inject a custom `VideoEncoderFactory` into libwebrtc. It returns two proxy + encoders, one for each normal send pipeline. +- Each proxy keeps its own `EncodedImageCallback`; one coordinator owns the + physical counting encoder. +- The coordinator deduplicates matching input-frame timestamps and publishes + the same reference-counted encoded buffer to both callbacks. +- Reject the second edge when codec, profile, dimensions, or layer configuration + differ. Never hide incompatibility by creating a second physical encoder. +- In this fixed single-layer spike, aggregate the two `SetRates()` requests by + taking the minimum total target bitrate and minimum requested frame rate, + bounded by the selected profile. Do not attempt to merge per-edge loss, RTT, + bandwidth-allocation, or multilayer state. A weak edge can therefore lower + quality for the strong edge; independent quality requires another encode or + a separately accepted layered design. +- Coalesce PLI/keyframe requests so one physical keyframe is delivered to both + packetizers. RTP sequence numbers, SSRC, TWCC, pacing, retransmission caches, + RTCP, DTLS-SRTP, and ICE remain independent per edge. + +libwebrtc officially permits an application to inject a `VideoEncoderFactory` +and defines `VideoEncoder::Encode`, `SetRates`, loss/RTT notifications, and the +encoded callback. It does **not** promise cross-PeerConnection shared encoding. +The proxy/coordinator design is therefore an inference that this spike must +validate, not a supported feature claim. + +Stop the libwebrtc route if two send pipelines produce incompatible cadence, +timestamps, adaptation, or callback behavior and correctness would require a +fork of `VideoStreamEncoder`, `RtpVideoSender`, or another libwebrtc internal. +Do not carry a permanent media-engine fork for this feature. + +## Capture And Hardware Encoding Come Later + +Only after the synthetic two-edge property passes: + +1. Capture a window or display with Windows Graphics Capture into a D3D11 + surface. +2. Convert BGRA to NV12 once. Do not claim zero-copy until profiling proves the + actual texture path. +3. Enumerate hardware Media Foundation H.264 encoders with `MFTEnumEx` and + `MFT_ENUM_FLAG_HARDWARE`, then probe every required input/output type and + `ICodecAPI` control. Enumeration alone is not a capability guarantee. +4. Require proven support for low-latency operation, runtime bitrate control, + and explicit keyframe requests; reject that adapter instead of silently + weakening the contract when a property is unavailable or read-only. +5. Request one H.264 Constrained Baseline, packetization-mode 1 stream, then + inspect the emitted SPS and negotiated `profile-level-id` before accepting + the path. Browser baseline interoperability does not guarantee a particular + hardware MFT or 1080p60; test every target GPU/browser combination. + +Audio, per-process WASAPI capture, WGC/DDA fallback, source selection, Electron, +installers, updating, and cross-platform abstractions stay outside the first +spike. + +## Pion Fallback + +Pion is the fallback when public libwebrtc encoder APIs cannot satisfy the +shared property without internal changes. `TrackLocalStaticSample` owns one +packetizer and writes through a `TrackLocalStaticRTP` that can bind to multiple +PeerConnections, making encoded-payload fanout explicit. + +This certainty moves more responsibility into the application. Pion's default +interceptors provide NACK, RTCP reports, statistics, and transport-wide feedback, +but an external encoder still needs explicit send-side bandwidth estimation, +aggregation of both edge targets, encoder bitrate control, PLI handling, and +bounded RTP/RTX queues. A Pion route must use the same minimum-edge rate rule and +must not invent a custom SRTP, ICE, or congestion protocol. + +## Acceptance And Failure Gates + +- Two current unmodified browsers decode continuously. +- Unique physical encode calls equal unique input frames, not twice that count. +- Direct and TURN edges can coexist; rebuilding one edge does not create a + second physical encoder or interrupt the other edge. +- Limiting one edge lowers the common encoder target without an unbounded pacer + queue; removing that weak edge lets the remaining target recover. +- One viewer's PLI produces one physical keyframe and both viewers recover. +- Measurements include encode count/time, target and actual bitrate, PLI/NACK, + RTT/loss, queue growth, CPU, GPU, and host upload. + +Reject a libwebrtc fork, deprecated internal-source encoder APIs, SVC/simulcast, +a second codec, FFmpeg/x264, a third host edge, custom RTP/SRTP or congestion +control, automatic topology switching, audio, UI, and packaging in the risk +spike. These are separate decisions after the shared property is proven. + +## Primary Sources And License Boundary + +- [libwebrtc PeerConnection factory injection](https://webrtc.googlesource.com/src/+/refs/heads/main/api/create_peerconnection_factory.h) +- [libwebrtc VideoEncoder API](https://webrtc.googlesource.com/src/+/refs/heads/main/api/video_codecs/video_encoder.h) +- [libwebrtc license](https://webrtc.googlesource.com/src/+/refs/heads/main/LICENSE) + and [PATENTS](https://webrtc.googlesource.com/src/+/refs/heads/main/PATENTS) +- [Windows Graphics Capture](https://learn.microsoft.com/en-us/windows/apps/develop/media-authoring-processing/screen-capture) +- [Media Foundation `MFTEnumEx`](https://learn.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex) +- [Media Foundation `ICodecAPI::IsSupported`](https://learn.microsoft.com/en-us/windows/win32/api/icodecapi/nf-icodecapi-icodecapi-issupported) + and [`IsModifiable`](https://learn.microsoft.com/en-us/windows/win32/api/icodecapi/nf-icodecapi-icodecapi-ismodifiable) +- [Media Foundation H.264 encoder](https://learn.microsoft.com/en-us/windows/win32/medfound/h-264-video-encoder) +- [Media Foundation low-latency codec property](https://learn.microsoft.com/en-us/windows/win32/medfound/codecapi-avlowlatencymode) +- [Media Foundation force-keyframe property](https://learn.microsoft.com/en-us/windows/win32/medfound/codecapi-avencvideoforcekeyframe) +- [WebRTC video codec requirements, RFC 7742](https://www.rfc-editor.org/rfc/rfc7742.html) +- [WebRTC congestion control requirements, RFC 8836](https://www.rfc-editor.org/rfc/rfc8836.html) +- [Pion track fanout implementation](https://github.com/pion/webrtc/blob/main/track_local_static.go) +- [Pion send-side bandwidth estimator](https://github.com/pion/interceptor/blob/main/pkg/gcc/send_side_bwe.go) +- [Pion WebRTC license](https://github.com/pion/webrtc/blob/main/LICENSE) +- [Electron desktop capture](https://www.electronjs.org/docs/latest/api/desktop-capturer/) + +libwebrtc uses a BSD-style license and Pion uses MIT, but distribution still +requires a full libwebrtc third-party notice review and an H.264 patent/licensing +review. No source code from these projects was copied into Screener. diff --git a/docs/research/peer-assisted-media.md b/docs/research/peer-assisted-media.md index b1da0d5..e29530c 100644 --- a/docs/research/peer-assisted-media.md +++ b/docs/research/peer-assisted-media.md @@ -129,21 +129,21 @@ sending the result directly to viewers. This removes repeated encoding and memory cost, but it does not remove one network copy per viewer. It therefore does not satisfy a hard host fanout of two by itself. -A planned native Screener sender can preserve standard browser receivers without -writing a new transport. libwebrtc accepts an application-supplied -`VideoEncoderFactory`; a factory can return per-send-stream proxy encoders -backed by one shared hardware encoder and fan the resulting `EncodedImage` into -each stream's `EncodedImageCallback`. libwebrtc can then retain independent RTP +A candidate native Screener sender could preserve standard browser receivers +without writing a new transport. libwebrtc accepts an application-supplied +`VideoEncoderFactory`, which creates a possible boundary for per-stream proxy +encoders backed by one shared hardware encoder. If the bounded spike proves the +callback and adaptation contract, libwebrtc could retain independent RTP sequence numbers, packetization, SRTP, RTCP, NACK/RTX, ICE, and congestion state for the two host edges. This is an engineering inference from the native interfaces, not a ready-made libwebrtc feature. The coordinator must deduplicate input frames, reconcile independent rate requests, combine keyframe requests, preserve timestamps, and -require one common codec/profile/resolution/layer. It is high-complexity native -work, but it is the viable long-term way to **guarantee** one host encode while -keeping normal browser first-hop receivers. Browser JavaScript cannot provide -that guarantee. +require one common codec/profile/resolution/layer. The separate bounded risk +spike must prove whether this route can guarantee one host encode while keeping +normal browser first-hop receivers; requiring a libwebrtc internal fork rejects +the route. Browser JavaScript cannot provide that guarantee. Sources: @@ -208,6 +208,15 @@ 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. + Peer multicast research such as SplitStream demonstrates why load-balanced, failure-tolerant overlays normally introduce multiple trees and content striping. Those mechanisms are intentionally excluded: needing them is a reason @@ -273,6 +282,14 @@ 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. +The in-app per-frame encode/decode fields use adjacent `getStats()` counter +deltas and skip a tick while the same connection already has a sample in +flight. A first sample, zero-frame interval, changed stats object, or counter +reset reports unknown and rebases; a connection-lifetime average is not +accepted as relay-load evidence. Long-run percentiles and event timing still +come from a tracked run manifest plus browser WebRTC diagnostics, not a new +telemetry service. + All hard gates must pass: 1. The host has no more than two outgoing media edges and every viewer has no diff --git a/docs/research/realtime-quality-adaptation.md b/docs/research/realtime-quality-adaptation.md index 06d0cd4..1f25438 100644 --- a/docs/research/realtime-quality-adaptation.md +++ b/docs/research/realtime-quality-adaptation.md @@ -80,6 +80,13 @@ resolution, frame rate, or bitrate. - Pausing the picture disables the existing video track, producing black video without closing the room or media connection. Audio remains enabled. +A Chromium 151 loopback smoke with one host, three viewers, and a synthetic +640x360/30 source kept the same relay peer, sender, and signaling generations +while applying 8 Mbps/60 fps, 5 Mbps/30 fps, and 3 Mbps/30 fps sender ceilings. +The leaf continued decoding after both live switches. This verifies control +propagation and connection preservation, not actual 1080p output, visual +quality, CPU/GPU cost, public-network behavior, or sustained performance. + ## Deliberate Non-Goals - No canvas pixel-difference detector, machine-learned rate controller, or @@ -99,6 +106,15 @@ dropped frames, RTT, packet loss, and host CPU/GPU utilization. A live profile change must preserve peer connection IDs, avoid a second source prompt, and visibly converge to the requested bounds. +Compute per-frame encode and decode cost from adjacent samples of cumulative +`totalEncodeTime`/`totalDecodeTime` and frame counters. The first sample, a +zero-frame interval, changed stats object, or a counter reset is unknown and +establishes a new baseline. Skip overlapping sampling ticks on the same peer so +reports cannot complete out of order. Do not use a connection-lifetime average +to judge a later overload. Use browser WebRTC diagnostics plus a small run +manifest for time series and percentiles rather than adding a server telemetry +pipeline. + Compare image readability and motion continuity instead of declaring success from FPS alone. If reproducible evidence later shows that `balanced` still oscillates or makes the wrong tradeoff on supported machines, revise the fixed diff --git a/docs/status.md b/docs/status.md index 2605f7b..e215692 100644 --- a/docs/status.md +++ b/docs/status.md @@ -4,10 +4,9 @@ Last updated: 2026-08-19 ## Phase -The WebRTC proof of concept is deployed at `https://share.bonfire.icu`. Commit -`5b2fb005f6f7` adds live 1080p60/1080p30/720p30 quality changes, video-only -pause, clearer connection states, protected SQLite persistence, sequential room -IDs, reusable links, and a waiting state after sharing stops. Production room ID +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. Production still uses one host `RTCPeerConnection` per viewer. Draft PR #13 adds @@ -21,8 +20,6 @@ Draft SFU PR #12 is also unmerged and undeployed. ## Current Snapshot -- Product scope is private game sharing for one broadcaster and a small trusted friend group; public broadcasting remains out of scope. -- Runtime is Node.js 24, React, TypeScript, Vite, native browser WebRTC, `ws`, Zod, Vitest, and a separate coturn deployment. - Capture precedes room creation. Live source and quality changes preserve healthy peers; picture pause keeps audio and connections active. `contentHint = "motion"` and explicit `balanced` degradation do not guarantee resolution-first behavior. - 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. @@ -32,18 +29,19 @@ Draft SFU PR #12 is also unmerged and undeployed. ## Verified Evidence -- Production release `5b2fb005f6f7` passed type checking, 108 Vitest tests, both production builds, loopback smoke, public HTTPS/access-gate checks, and clean activation under `/opt/screener/current`. The new 1080p30 asset loaded, Screener had zero automatic restarts, and nginx, coturn, and the existing blog remained healthy. +- 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 141 tests, and both client and server production builds. +- 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. +- 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. -- Headless Chromium 151 synthetic checks cover direct UDP media, offer/answer/create-offer recovery, generation isolation, live video/audio replacement, quality parameter updates, and video-only pause. Layout checks used actual inner widths 500, 781, and 820 px; a true 390 px viewport remains unverified. ## Unverified Boundaries -- Room profile propagation is implemented, replacing the fixed relay `1080p60` envelope. Actual cross-hop resolution, bitrate, frame rate, encode cost, and generational quality under each profile remain unverified. +- 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. - 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. @@ -55,7 +53,7 @@ Draft SFU PR #12 is also unmerged and undeployed. Complete only the bounded ADR-0004 experiment, with distribution work ahead of UI polish: -- validate authentication, live switching, stream replacement, and child rebuild against the latest room profile in real browsers; +- 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 diff --git a/docs/方案设计.md b/docs/方案设计.md index 91d1943..16d7e0f 100644 --- a/docs/方案设计.md +++ b/docs/方案设计.md @@ -176,7 +176,7 @@ TURN/TLS 是可选能力,不是生产启动门槛。启用时默认使用 RFC - 本地 relay candidate 的 `relayProtocol`;规范禁止远端 candidate 暴露该字段,因此不推断远端使用 TURN/UDP、TCP 还是 TLS; - `currentRoundTripTime`、发送码率和 `availableOutgoingBitrate`; - 实际帧率、分辨率和丢包; -- codec、编码实现、每帧平均编码耗时和 `qualityLimitationReason`。 +- codec、编码实现、最近采样区间内的每帧编解码耗时和 `qualityLimitationReason`;同一连接不并发采样,首个样本、无新帧、统计对象换代或计数器重置时显示未知并重新建立基线,不用连接生命周期均值掩盖后续过载。 观看者显示接收码率、帧率、分辨率、丢包、jitter、解码/丢帧和当前路径。字段不可用时显示“未知”。RTT 只标为网络往返时间,不能冒充玻璃到玻璃延迟。