docs: record relay measurement boundaries

This commit is contained in:
AAA committed 2026-08-19 03:32:37 +08:00
1 parent be00ca84a0
commit d5ac1ea604
8 files changed
+201 -25

No files matched your search

+1
View File
@@ -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.
+4 -2
View File
@@ -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/`
+4 -2
View File
@@ -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 |
@@ -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.
+26 -9
View File
@@ -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
@@ -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
+9 -11
View File
@@ -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
+1 -1
View File
@@ -176,7 +176,7 @@ TURN/TLS 是可选能力,不是生产启动门槛。启用时默认使用 RFC
- 本地 relay candidate 的 `relayProtocol`;规范禁止远端 candidate 暴露该字段,因此不推断远端使用 TURN/UDP、TCP 还是 TLS;
- `currentRoundTripTime`、发送码率和 `availableOutgoingBitrate`;
- 实际帧率、分辨率和丢包;
- codec、编码实现、每帧平均编码耗时和 `qualityLimitationReason`。
- codec、编码实现、最近采样区间内的每帧编解码耗时和 `qualityLimitationReason`;同一连接不并发采样,首个样本、无新帧、统计对象换代或计数器重置时显示未知并重新建立基线,不用连接生命周期均值掩盖后续过载。
观看者显示接收码率、帧率、分辨率、丢包、jitter、解码/丢帧和当前路径。字段不可用时显示“未知”。RTT 只标为网络往返时间,不能冒充玻璃到玻璃延迟。