From 7dcb5af9c1426edc587f8cbc60c532bc67b4f7dc Mon Sep 17 00:00:00 2001 From: AAA <35992542+TNTcraftHIM@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:56:20 +0800 Subject: [PATCH] fix(web): explain locked codec selection [skip ci] * fix(media): isolate forced H.264 regression [skip ci] * feat(media): add explicit Web codec selection [skip ci] * fix(media): raise peer Opus bitrate target [skip ci] * fix(web): explain locked codec selection [skip ci] --- docs/project-memory.md | 4 ++-- docs/research/realtime-quality-adaptation.md | 6 ++++++ docs/status.md | 7 +++---- docs/需求理解.md | 2 +- src/client/pages/HostPage.tsx | 14 +++++++++++++- src/client/pages/host-page-notices.ts | 6 ++++++ src/client/styles.css | 6 ++++++ tests/host-page-notice.test.ts | 15 +++++++++++++++ 8 files changed, 52 insertions(+), 8 deletions(-) diff --git a/docs/project-memory.md b/docs/project-memory.md index 47dfa28..d6168fe 100644 --- a/docs/project-memory.md +++ b/docs/project-memory.md @@ -43,14 +43,14 @@ Last updated: 2026-08-21 ## Current Implementation - The repository is one npm package using Node.js 24, React, TypeScript, Vite, native WebRTC, `ws`, Zod, Vitest, and separate coturn. -- Production is exact `6ccb516a47261054f91dfa2fafa408d39ced59fc`; source/dist artifacts are 619,674/358,621 bytes, with hashes in deployment; cutover 2,048 ms lock/1,272 ms stop-health/888 ms switch-health; rollbacks `1331fbdd`, `261e980`, `691863e`. +- Production is exact `7dd38fc39ce8e91b913b1f45e053c00e0520f3b9`, release `7dd38fc39ce8`, wire `screener-v3`; rollback is `f7d667c4d32d`. - Web keeps one SFU stream, clears unavailable video, exposes evidence-backed routes/transports and paused preview, and drops signals to a grace-retained offline current-edge target. Routing is automatic; entry/nickname UI is live; Native remains source-only. - Web relay is cap2 with one upstream; child3 rejects and no UA/visibility split exists. - Four services are active/running at `NRestarts=0`; local/public health are 200. SQLite v3 has five rooms, SHA-256 `aef724ae52c4107be8ec8791e72f8dcaa11b0ec849fb432d022e2cfb9cfe0974`, owner/mode `screener:screener`/0600. - Production keeps ordinary ICE STUN-only, SFU roots <=2, selected-edge UDP TTL 120, and one pending/answered `peer-selected` attempt per room excluding Host ingress. This rollout ran no browser/media/SFU/TURN/performance canary; earlier exact-`16f6eab` evidence proves active Host ingress only. - Production requires independent `SITE_ACCESS_PASSWORD`: its stateless cookie authorizes creation/Host and code-only Viewer attempts; private grants stay direct, while public/private code entry needs site access alone/plus room password. Env/endpoint/cookie/nginx naming migrated atomically; failures remain neutral; there are no accounts/JWT/session rows. Local `gate:access-privacy` proves fragment isolation, site/grant transport-log absence, and raw room-password SQLite absence; room-password WebSocket/log ingress and live/headful evidence remain open. - Production uses nginx, Node.js 24.19.0, coturn 4.17.2, and LiveKit 1.13.5 on UDP 7882 (TCP fallback off). Ordinary peer ICE receives no TURN; the selected-edge tuple is configured only for the current controller edge, with no credential pre-advertised to ordinary peers. Coturn retains the old authenticated-relay ports. Services are healthy with zero restarts. -- ADR-0005 rejects `PEER_ASSISTED_ROOM_IDS`; `PEER_ASSISTED_MEDIA=true` enables every room and ordinary peers stay STUN-only. Initial or active Host ingress may consume one bound relay-only grant; ready/abort clears it and failure restores peer baseline. Stale room-ID config fails startup; `screener-v2` is the only deployed wire. +- ADR-0005 rejects `PEER_ASSISTED_ROOM_IDS`; `PEER_ASSISTED_MEDIA=true` enables every room and ordinary peers stay STUN-only. Initial or active Host ingress may consume one bound relay-only grant; ready/abort clears it and failure restores peer baseline. Stale room-ID config fails startup; `screener-v3` is the deployed wire. - Defaults are balanced with automatic codec; clarity/fluid and H.264/VP8 remain explicit. Codec is locked during a share. Web SFU uses LiveKit 2.22.0 `q,h`; SFU/UDP and selected-TURN work; BWE, Host A+B and production remain open. - Chrome 151 five-Viewer 720p30 cap2 evidence: Host/relay fanout two; both relay children +80 decoded frames/~1.1 MB; every Viewer decoded; clean stop. Functional only; resource and heterogeneous-network behavior remain open. diff --git a/docs/research/realtime-quality-adaptation.md b/docs/research/realtime-quality-adaptation.md index 11a7129..0a1d342 100644 --- a/docs/research/realtime-quality-adaptation.md +++ b/docs/research/realtime-quality-adaptation.md @@ -209,6 +209,12 @@ does not prove shared encode: separate browser PeerConnections may construct separate encoders, and the SFU still publishes only the configured `q,h` representations. +The retained follow-up is one direct-Viewer comparison using existing stats, +not a new benchmark: actual codec/profile, encoder implementation, power +efficiency, source/send/receive FPS, encode time and limitation reason. It must +distinguish software fallback from a hardware queue or driver limit before any +H.264-specific policy changes. + Open-source distribution is not itself a patent-license exemption. This Web change only requests a codec already implemented by the browser/LiveKit path and ships no H.264 codec binary or new codec dependency, so licensing uncertainty is diff --git a/docs/status.md b/docs/status.md index 3c2cd21..0975191 100644 --- a/docs/status.md +++ b/docs/status.md @@ -4,8 +4,7 @@ Last updated: 2026-08-21 ## Phase -Production at `https://share.bonfire.icu` is exact `6ccb516a47261054f91dfa2fafa408d39ced59fc`; immutable source/dist artifacts are 619,674/358,621 bytes, with hashes in deployment. -Cutover: 2,048 ms lock/1,272 ms stop-health/888 ms switch-health. SQLite v3/five rooms has SHA-256 `aef724ae52c4107be8ec8791e72f8dcaa11b0ec849fb432d022e2cfb9cfe0974`, owner/mode `screener:screener`/0600. Four services are active/running at `NRestarts=0`; rollbacks are `1331fbdd`, then `261e980` and `691863e`. +Production is exact `7dd38fc39ce8e91b913b1f45e053c00e0520f3b9` at `https://share.bonfire.icu`, release `7dd38fc39ce8`, wire `screener-v3`; rollback is `f7d667c4d32d`. SQLite v3/five rooms retains SHA-256 `aef724ae52c4107be8ec8791e72f8dcaa11b0ec849fb432d022e2cfb9cfe0974`, owner/mode `screener:screener`/0600. Four services and local/public health are green. Ordinary ICE is STUN-only; limits are SFU roots <=2, one `peer-selected` attempt excluding Host ingress, and selected UDP TTL 120. This rollout ran no browser/media/SFU/TURN/performance canary; earlier exact-`16f6eab` Host-ingress proof remains separate. Stable SFU, route/transport truth, preview notice, entry/nickname UI and Web relay cap2 are live. Native is source-only. The controlled production A/B uses browser/LiveKit codec defaults; explicit codec controls are source-complete. @@ -16,7 +15,7 @@ Parallel flagship work; minimum relevant checks and rollback proof. Research plu ## Current Snapshot - Capture precedes rooms; source/quality changes and AV pause preserve peers. Defaults are balanced and automatic codec; clarity/fluid plus H.264/VP8 stay explicit, with codec locked during a share. Browser degradation and Host SFU A+B remain open. -- Production `screener-v2` admits private fragment grants directly. Code-only Viewers need site access, then public rooms accept the code while private rooms also require their password. `SITE_ACCESS_PASSWORD`, `/api/site-access`, and its cookie are deployed atomically; raw credentials are never stored. +- Production `screener-v3` admits private fragment grants directly. Code-only Viewers need site access, then public rooms accept the code while private rooms also require their password. `SITE_ACCESS_PASSWORD`, `/api/site-access`, and its cookie are deployed atomically; raw credentials are never stored. - Source UI uses adjacent nickname editing, distinct site/room prompts, and a shared room-code form. Status and details stay neutral before media proof; current evidence labels green P2P/yellow `SFU fallback`, while relay evidence adds `TURN` plus only a locally observed protocol. - Native v2 is source-only (memory rooms, 300s reclaim); it follows authenticated direct-child assignments up to two and fails unsupported SFU/selected ingress boundedly. - Production is roots/Web relay <=2; every Viewer has one upstream and rejects child3. One-root healthy-SFU MBB is deployed but media-unverified; capacity `0 -> 1`, multi-root and browser gates remain open. Room `1` is historical; HTTPS/WSS is TLS/TCP. @@ -56,7 +55,7 @@ Recovery uses one ICE restart, one same-parent rebuild, one alternate peer, then Native WGC/MF passed one-Viewer hardware; package download, Viewer2/FIFO and game A/V remain. Use representative target-device or production evidence for performance, not an ordinary-PC synthetic gate. Ordinary peers stay STUN-only; initial ingress, `peer-selected`, and healthy-reselection browser gates remain open. ADR-0004 still needs its resource/quality matrix; the 20-viewer gate precedes a default change. Mobile uses the same capacity and is a compatibility observation. -Stereo: open. +Peer/SFU stereo targets 128 kbps in production; user route comparison remains. Next: codec-lock explanation, then existing-stats H.264 encoder follow-up. ## Blockers And Decisions diff --git a/docs/需求理解.md b/docs/需求理解.md index 46c1fec..9b6264b 100644 --- a/docs/需求理解.md +++ b/docs/需求理解.md @@ -55,7 +55,7 @@ - [ ] Windows 应用音频:Windows 11 Native sender 使用本地 opaque window target 绑定 HWND、PID 与进程创建时间,只捕获所选游戏及其子进程的音频,避免把语音聊天或通知声一并分享;“全部系统声音”只能由分享者显式选择。普通 Windows 10 不具备当前 process-loopback API 门槛,必须明确标为未解决/不支持而不是伪兼容。浏览器无法证明或隔离任意进程音频时必须如实提示,原生捕获失败、无声或进程退出不得静默扩大为整机捕获。 - [ ] 连接自检:在开始分享前验证信令、STUN 和 SFU ICE/UDP 可达性,并给出不暴露地址或凭据的诊断结果。启用 selected-edge TURN canary 时只报告脱敏的授权阶段与 selected candidate type,不输出 URL、username、credential、地址或原始 candidate;自检只覆盖当前 UDP 媒体梯级。 - [ ] 受限网络增强:短期鉴权 TURN 只能由完整、默认关闭的部署 tuple 与当前 room controller 启用,并且只能在 direct/peer UDP 与 SFU/UDP 均已按有界梯级失败后,向 controller 选中的当前异常 edge 一次性授权。普通 peer PC 从创建起保持 STUN-only;不得向整个 participant、房间或所有 peer connections 预发 TURN candidates。凭据必须绑定当前授权 session 与 route/connection generation、短期有效、内存态、一次性消费且不进入 URL、日志、浏览器持久化或 SQLite。媒体保持 UDP-only,全部路径耗尽后明确失败;TURN 不得被描述为更高清的传输方式。 -- [x] Web 编码选择:高级视频设置提供 `自动 | H.264 | VP8`,默认自动使用浏览器/LiveKit 协商;显式选择只在下一次分享前用标准 codec preference 生效,直播中锁定。P2P/浏览器 relay 保留完整 repair 与其他 codec 回退列表,SFU 保持 `backupCodec = false`,不维护第二套应用编码链。实际 codec/profile、encoder implementation 与 stats 是结果真相;H.264 可选不等于浏览器必走硬编,AV1 仍不得仅凭压缩率推断默认启用。 +- [x] Web 编码选择:高级视频设置提供 `自动 | H.264 | VP8`,默认自动使用浏览器/LiveKit 协商;显式选择只在下一次分享前用标准 codec preference 生效。直播中控件锁定并明确提示停止分享后可修改。P2P/浏览器 relay 保留完整 repair 与其他 codec 回退列表,SFU 保持 `backupCodec = false`,不维护第二套应用编码链。实际 codec/profile、encoder implementation 与 stats 是结果真相;H.264 可选不等于浏览器必走硬编,AV1 仍不得仅凭压缩率推断默认启用。 - [ ] 诊断页面:用户可导出不含敏感地址和凭据的连接诊断信息。 ### P2 - 可选功能 diff --git a/src/client/pages/HostPage.tsx b/src/client/pages/HostPage.tsx index 763fc1f..25a25e8 100644 --- a/src/client/pages/HostPage.tsx +++ b/src/client/pages/HostPage.tsx @@ -101,6 +101,7 @@ import { import { shouldPauseLocalPreview, sourceSwitchNotice, + videoCodecLockNotice, } from "./host-page-notices"; type HostPhase = "idle" | "starting" | "live" | "ended" | "error"; @@ -1525,6 +1526,7 @@ export function HostPage({ onAuthorizationRequired }: HostPageProps = {}) { }).format(new Date(room.expiresAt))} 过期` : "长期有效" : null; + const codecLockNotice = videoCodecLockNotice(phase); return (