docs(native): define mobile sender capability gates

This commit is contained in:
AAA committed 2026-08-19 23:50:26 +08:00
1 parent 69f3dddca4
commit f1b6502c2e
7 files changed
+155 -2

No files matched your search

+1
View File
@@ -23,6 +23,7 @@ Last updated: 2026-08-19
- Browser relays resend remote `MediaStreamTrack` values and re-encode at each hop; WebRTC does not guarantee a shared encoder across peer connections, so measure the cost.
- Keep experiments bounded: the standard representation sequence is below; native RTP relay, encoded-object striping, and FEC remain separate.
- ADR-0006's fixed-`HIGH` canary reached host setup and one encoder output but retained no downstream checkpoint before the first-viewer timeout. It is no-go-unclassified; no native product code is accepted, and any revisit starts at its staged evidence gate.
- Mobile Web Host is unsupported: current Android Chrome/Firefox and iOS Safari lack reliable `getDisplayMedia`; feature-detect and fail clearly, never by UA. Mobile Viewer stays leaf-only. After P1 Windows native/deploy, P2 may gate Android 14+ fixed-`HIGH`; iOS waits for stable iOS 27 ScreenCaptureKit without ReplayKit.
- ADR-0005 accepts SFU roots as the primary central fallback after direct/peer UDP. The candidate has no TURN consumer or credential wire; any future exceptional-edge grant is a complete separate change. PR #12 is superseded.
- Keep the failure-only controller default-off until the isolated STUN/SFU exact-room gates pass. Preserve break-before-make, sticky healthy subtrees, mobile leaves, and PR #20 prewarm; public transport/load remains open.
- Local reparenting is a later candidate: start with unassigned relay admission rescue; do not block current work.
@@ -192,6 +192,35 @@ connectivity smoke. Correlate capture settings, negotiated codec/derived fmtp,
actual outbound/inbound bitrate, loss, jitter, concealment, jitter buffer, and
A/V playout timing using a distinguishable stereo fixture plus game/film audio.
## Deferred Android Application-Audio Candidate
This is a P2 input for the later Android 14/API 34+ native sender, not a current
Web or Windows requirement. Android's `AudioPlaybackCapture` API can constrain
captured playback with `addMatchingUid()` when the target UID is already known,
or with `addMatchingUsage()` for eligible usages. The source player still must
use `USAGE_MEDIA`, `USAGE_GAME`, or `USAGE_UNKNOWN`, run in the same user
profile, and permit capture under its manifest/runtime/player policy. The capture
app also needs `RECORD_AUDIO` permission and an approved `MediaProjection`
session. The most restrictive source policy wins, so silence or refusal is a
valid result and must not trigger a whole-system fallback.
Android 14 QPR2's system `MediaProjection` picker can limit video to one selected
app window. However, the public API 34 result contract passes the consent result into
`getMediaProjection()` and documents only a projection grant; it does not
promise the capturing app the selected package or UID. It is therefore an
inference, not an API guarantee, that the video selection can be associated with
the UID required by `addMatchingUid()`. Screener must separately prove the
selected-app-video plus selected-app-audio pairing on target devices with an
independently established UID, or keep that mode unsupported. Usage-only
filtering is broader and cannot be relabeled as per-app isolation.
The bounded audio gate covers a known target UID, an unknown picker target,
`USAGE_GAME`/`USAGE_MEDIA`, source opt-out, no active render stream, process
restart, voice/notification leakage, secure/protected video, rotation, lock-stop,
thermal load, one/two Web viewers, and A/V sync. Missing audio remains a visible
silent/unsupported state. PID/UID/package, source labels, raw audio, and device
identity stay local and are not retained or uploaded.
## Revisit Gates
Add a user-facing audio setting only when all of these are true:
@@ -225,3 +254,8 @@ Add a user-facing audio setting only when all of these are true:
- [Windows process-loopback parameters](https://learn.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params)
- [Windows 10 release information](https://learn.microsoft.com/en-us/windows/release-health/release-information)
- [Microsoft WASAPI loopback recording](https://learn.microsoft.com/en-us/windows/win32/coreaudio/loopback-recording)
- [Android capture video and audio playback](https://developer.android.com/media/platform/av-capture)
- [Android `AudioPlaybackCaptureConfiguration.Builder`](https://developer.android.com/reference/android/media/AudioPlaybackCaptureConfiguration.Builder)
- [Android `MediaProjectionManager`](https://developer.android.com/reference/android/media/projection/MediaProjectionManager)
- [Android media projection](https://developer.android.com/media/grow/media-projection)
- [Android secure-window capture boundary](https://developer.android.com/reference/android/view/WindowManager.LayoutParams#FLAG_SECURE)
@@ -93,6 +93,55 @@ Audio, per-process WASAPI capture, WGC/DDA fallback, source selection, Electron,
installers, updating, and cross-platform abstractions stay outside the first
spike.
## Deferred Mobile Sender Boundary
This section records later capability work; it does not broaden ADR-0006's
Windows scope or authorize mobile runtime code. Priority remains the current
deployment and staged Windows fixed-`HIGH` gate. The first mobile candidate is a
separate P2 Android 14/API 34+ spike only after those P1 gates complete.
Android's official `MediaProjection` path captures the display into a `Surface`;
selected-app-window capture starts with Android 14 QPR2. API 34 requires fresh
consent for each session, one use of each projection token, and a foreground
service declared as `mediaProjection`. Rotation or selected-content resizing requires
`onCapturedContentResize()` handling; user stop, screen lock, another projection,
or process death invalidates the session and requires bounded cleanup through
`onStop()`. These OS consent and foreground indicators are part of the product
experience and cannot be made invisible.
The bounded Android data path is:
```text
MediaProjection Surface -> one hardware MediaCodec fixed-HIGH encoder
-> existing Host admission / room / WSS / standard WebRTC
-> one or two unmodified Web viewers
```
It adds no mobile-only topology, `LOW`, AV1, codec ladder, third downstream edge,
or software fallback. `MediaCodecInfo.isHardwareAccelerated()` is metadata
provided by the manufacturer that Android explicitly says cannot be tested for correctness,
so it is only an admission signal. The spike must also prove the selected named
encoder, supported fixed format, interval encode cost, CPU/GPU, temperature and
throttling, power, queue bounds, actual bitrate/quality, and game impact on real
devices. Missing or failed hardware evidence is no-go; the implementation must
not silently select a software codec. Network changes reuse the existing
per-edge WebRTC recovery contract rather than restarting capture or creating a
new transport.
iOS work remains later and separate. Apple's current ScreenCaptureKit
documentation says the framework replaces ReplayKit for screen streaming and
that a broadcast extension is no longer required, but the iOS sample requires
iOS 27 and the current APIs are still beta. Screener therefore does not build a
ReplayKit/Broadcast Upload Extension compatibility stack. After the final iOS
27 SDK and stable OS ship, one narrow ScreenCaptureKit system-picker spike may
feed a fixed-`HIGH` VideoToolbox encoder into the same one/two-Web-viewer
contract. It must set
`kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder` and verify
`kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder`; otherwise it
fails closed. Rotation, background execution, lock-screen behavior, network
switching, thermal throttling, audio scope, and A/V sync remain explicit gates
on real devices, not implied framework capabilities.
## Pion Validation Route
Pion is the released-public-API validation route while the larger libwebrtc
@@ -273,6 +322,14 @@ evidence does not retain its policy.
- [RFC 4588 RTP retransmission](https://www.rfc-editor.org/rfc/rfc4588.html)
- [RFC 8888 congestion-control feedback](https://www.rfc-editor.org/rfc/rfc8888.html)
- [Electron desktop capture](https://www.electronjs.org/docs/latest/api/desktop-capturer/)
- [Android media projection](https://developer.android.com/media/grow/media-projection)
- [Android media-projection foreground service](https://developer.android.com/develop/background-work/services/fgs/service-types#media-projection)
- [Android `MediaCodecInfo`](https://developer.android.com/reference/android/media/MediaCodecInfo)
- [Android `MediaCodecList`](https://developer.android.com/reference/android/media/MediaCodecList)
- [Apple ScreenCaptureKit](https://developer.apple.com/documentation/ScreenCaptureKit)
- [Apple iOS ScreenCaptureKit sample](https://developer.apple.com/documentation/screencapturekit/capturing-screen-content-on-ios)
- [Apple required hardware encoder key](https://developer.apple.com/documentation/videotoolbox/kvtvideoencoderspecification_requirehardwareacceleratedvideoencoder)
- [Apple hardware encoder readback](https://developer.apple.com/documentation/videotoolbox/kvtcompressionpropertykey_usinghardwareacceleratedvideoencoder)
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
@@ -1,6 +1,7 @@
# P2P 优先游戏屏幕共享调研与可行性评估
- 调研日期:2026-08-18
- 移动端采集能力复核:2026-08-19
- 目标场景:一名玩家向少量熟人私密分享,观看者可用手机/桌面浏览器加入,低延迟,尽量不消耗媒体服务器带宽
- 结论状态:本文记录已部署 PoC 的 P2P/coturn 基线。ADR-0005 与[低服务器成本媒体路由](./low-server-media-routes.md)已取代本文早期“每条 peer edge 必带 TURN”的旗舰建议;生产代码尚未迁移
@@ -173,6 +174,28 @@ await sender.setParameters(parameters);
- [RTCRtpSender 参数](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/setParameters)
- [Chromium capture 架构](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/media/capture/)
### 移动浏览器分享端边界
截至 2026-08-19,普通手机浏览器不能作为可靠的 Screener Host。当前
MDN Browser Compatibility Data 把 `getDisplayMedia()` 标为 Chrome Android、
Firefox Android 和 Safari iOS 均不支持;Chrome Android 72--88 与 Firefox
Android 66--79 曾暴露方法,但调用恒定以 `NotAllowedError` 失败。屏幕音频在
Chrome Android 也标为不支持。桌面模式、UA 或设备性能不能改变这个平台 API
边界。
Web UI 后续只做最小诚实处理:运行时检查
`navigator.mediaDevices?.getDisplayMedia`,缺失时在建房/发布前明确显示不支持;
方法存在也只表示可以尝试,真实调用失败仍区分 denied/cancelled/capture-failed,
不能按 UA 宣称兼容。移动 Viewer 不需要该 API,继续使用同一响应式 Web 播放端并保持
leaf-only。移动 Host 若要落地,必须走后续原生 sender,而不是在 Web 端模拟
screen track 或加入私有媒体协议。
来源:
- [MDN Browser Compatibility Data: `MediaDevices.getDisplayMedia`](https://github.com/mdn/browser-compat-data/blob/main/api/MediaDevices.json)
- [MDN `getDisplayMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia)
- [W3C Screen Capture](https://w3c.github.io/mediacapture-screen-share/)
### Electron 分享端
Electron 可以固定 Chromium 版本,枚举屏幕/窗口,改善选源、热键和分享状态 UX,并使用 loopback 系统音频。它底层仍是 Chromium,单靠封装不会自动获得 Discord 级捕获质量。
+1 -1
View File
@@ -31,7 +31,7 @@ unconfigured, so production remains one host connection per viewer and P2P/TURN.
## Unverified Boundaries
- Full-resolution 30-minute runs, relay CPU/GPU, controlled loss/RTT, and depth latency are unverified. Production `769de201f7cc` game sharing is reported low-FPS/high-load; latest `main`, preview cost, codec, and hardware encoding are not yet compared.
- Android Chrome/iOS Safari leaves are unverified; runtime conservatively marks detected mobile/iPad clients as leaves.
- Android Chrome/iOS Safari Viewer leaves remain unverified and conservatively leaf-only. Mobile Web Host is unsupported; native senders are planned only.
- Silent partitions can wait 30 to 60 seconds for heartbeat detection before the default 5-second grace; this remains unverified.
- Real audio and heterogeneous clients remain unverified. Production reports poor film audio and voice-call self-echo under system capture; there is no app audio ceiling or Web process isolation. Diagnose A/B/C and sync, then gate Windows 11 game-process audio; Windows 10 stays unresolved without system fallback.
- Production `769de201f7cc` reportedly sustains bandwidth-limited blur on a capable LAN; Host refresh recovered while Viewer refresh did not, and persistence across churn was also reported. This prioritizes, but does not prove, host sender/PC/GCC/capture generation.
+35
View File
@@ -191,6 +191,36 @@ SVC 的标准能力门禁已经静态关闭为 `no-go-web-svc-cross-path-hardwar
标准表示能力排在任何自定义双表示媒体实现之前。当前 Web P2P simulcast 因 receiver 不能选 RID 且跨 PeerConnection 无 shared-encode 契约而静态 no-go;当前 Web/LiveKit SVC 因 direct/peer 无独立选层、硬件不可证明且 pinned screen share 为 `L1T3` 而静态 no-go。下一 gate 优先验证 pinned LiveKit 的标准 exactly-two screen-share simulcast:Dynacast off 常驻 `HIGH+LOW`,zero-descendant SFU leaf 以 `HIGH` 为 maximum,由 SFU BWE 独立降层和恢复;通过后不实现自定义媒体 selector。Root-with-children 的 suspect/确认/有界迁移是 default-on 前硬门。只有内建选择不满足门槛时才显式设置标准 subscriber quality;常驻成本违反硬件、游戏或上传预算时才测标准 sender 手动启停,全部标准路径失败后才进入 custom/native dual encode。当前 subscriber 未 attach `RemoteTrack`,因此不直接启用 `adaptiveStream`,但这不阻塞 SFU BWE leaf 候选。Cumulative Dynacast-on 只作为另一种 always-on 形态。本 SVC 静态审查不跑 browser。具体门槛由 ADR-0007 和[实时质量调研](./research/realtime-quality-adaptation.md)持有,任何方案都不得复用 PR #28 的 minimum-of-two 或绕过 stock WebRTC congestion control。
## 移动分享端能力边界与排期
普通移动浏览器当前只承担 Viewer。Chrome Android、Firefox Android 与 Safari
iOS 都没有可靠的 `getDisplayMedia()`;Web Host 入口必须检查运行时 API 并明确
区分 unsupported、用户拒绝/取消与 capture failure,不能用 UA、desktop mode
或设备型号放行。该边界不影响普通手机浏览器观看;手机仍是 leaf-only,并继续通过真机矩阵。浏览器版本证据由
[WebRTC 调研](./research/webrtc-p2p-screen-sharing.md)持有。
优先级保持 P1 Windows native sender 与当前 deployment,移动 Host 不得抢跑。
之后的 P2 Android 14/API 34+ 整屏 spike 只做一条窄纵向链路:系统
`MediaProjection` selection -> `mediaProjection` foreground service -> 一个经筛选
的硬件 `MediaCodec` fixed-`HIGH` encoder -> 现有 Host admission/room/WSS/标准
WebRTC -> 一至两名未修改 Web Viewer。它不增加移动端专属信令、拓扑、`LOW`、
AV1、codec ladder 或第三条 Host 下游边。`MediaCodecInfo.isHardwareAccelerated()`
是厂商声明而非充分证明,所以还必须用真实设备持续测量 codec、encode time、
CPU/GPU、温度/降频、功耗、游戏 FPS、队列、实际码率与画质;无合格硬件路径、
创建/配置失败或性能门槛失败都停在 no-go,不静默切软件编码。
Android 的逐 session 授权、系统指示、旋转/尺寸回调、锁屏/撤销终止与资源释放
都是产品行为,不能隐藏或用重启 capture 掩盖 transport 问题。应用音频可按已知
UID 或 usage 过滤,但 Android 14 QPR2+ 单应用 picker 未承诺返回目标 UID;“所选应用画面 +
同一应用音频”必须单独实测,来源策略或 secure/protected content 可产生静音、
空白或拒绝,不能自动扩大为整机捕获。API 证据与矩阵由[原生 sender 调研](./research/native-shared-encode-sender.md)和[音频调研](./research/browser-screen-audio-quality.md)持有。
iOS 当前不建设 ReplayKit Broadcast Upload Extension 兼容栈。待 iOS 27 与
ScreenCaptureKit SDK final/stable 后,只做 system picker -> VideoToolbox
fixed-`HIGH` -> 现有 WebRTC/一至两名 Web Viewer 的 P2 窄实验。硬件编码必须要求
并读回,未证明时 fail closed;系统授权/指示和全部生命周期/性能边界留给真机
矩阵,不追加兼容 fallback。
## 媒体传输配置
当前仓库候选已经删除旧全房 TURN contract。`ServerConfig` 只接受
@@ -302,6 +332,8 @@ hop 按[低服务器成本媒体路由](./research/low-server-media-routes.md)
- CGNAT、双 NAT、移动热点和普通家宽下使用隔离 candidate,LiveKit 显式关闭 TCP fallback 且不配置 TURN,验证 host 与 roots 使用 SFU/UDP;人为封锁 UDP 时必须有界明确失败。旧 production release 留在独立实例作为回滚,不在同进程保留旧 transport;
- Windows Chrome/Edge 的 tab/window/monitor 音轨有/无、同时语音通话和 video-only;Windows 11 原生候选另测 game process tree、通知、无渲染流与进程重启,Windows 10 记录明确不支持/未解决;
- Android Chrome 与 iOS Safari 的播放手势、旋转、前后台和网络切换;
- 仅在 Windows native/deploy P1 完成后,Android 14/API 34+ 原生 sender 才运行一至两名 Web Viewer 真机 gate:整屏、授权拒绝与撤销、旋转、锁屏停止、Wi-Fi/蜂窝切换、持续热降频、硬件 codec 读回/实测、目标 UID 已知/未知的 audio capture policy、secure/protected content、A/V sync 与资源释放;单应用窗口只在 Android 14 QPR2+ 测试;
- iOS 27 ScreenCaptureKit 在 final SDK/OS 前不进入实现或矩阵;稳定后只运行 system picker、VideoToolbox 硬编 fail-closed、一至两名 Web Viewer、后台/锁屏/旋转/网络/热状态与 A/V sync 的窄 gate;
- 一名分享者、八名异构观看者连续 30 分钟,同时记录较低人数基线、实际上行、编码负载和首帧时间。真实 1:8 未完成前不得把默认上限写成性能承诺。
- 真实桌面 Chrome 对 balanced 与 clarity 做控制传播和连接身份 smoke;该项只证明直播中应用与不重建,不代替清晰度、负载或公网矩阵。
@@ -316,6 +348,9 @@ hop 按[低服务器成本媒体路由](./research/low-server-media-routes.md)
5. 完成 automatic peer/SFU controller 与 exact-room canary 边界。
6. 用有界网络矩阵验收 direct/peer UDP、SFU/UDP 和明确失败;selected-edge 兼容 transport 只有出现真实缺口后才另立完整 PR。
7. 在媒体主线阶段验收后,用一个原子 PR 迁移 Host admission、`screener-v2`、room-scoped Viewer grant 和 SQLite v2;同一部署切换客户端/服务端/环境和数据库备份,随后删除 v1 配置、wire 与测试。用户名/roster 保持独立。
8. P1 继续完成 Windows native fixed-`HIGH` staged gate 与 deployment;移动 Host 不阻断或先于它。
9. P2 只在上述门槛后运行 Android 14/API 34+ fixed-`HIGH` 原生 sender spike;失败即停,不扩展为跨平台 framework。
10. iOS 27 ScreenCaptureKit final 后再决定是否运行独立 P2 spike;不实现 ReplayKit compatibility path。
任何一步只实现当前消费者需要的接口。不得为未来账号、第三种媒体拓扑或原生 sender 预建抽象层。
+4 -1
View File
@@ -55,6 +55,8 @@
### P2 - 可选功能
- [ ] 原生 Windows 捕获辅助程序,使用 WGC/DDA、WASAPI 和硬件编码器降低复制与编码开销。
- [ ] Android 原生分享端有界实验:只在 Windows native/deploy 的 P1 门槛完成后,以 Android 14/API 34+ 整屏捕获为首个范围,使用 `MediaProjection`、`mediaProjection` foreground service 和经实测合格的硬件 `MediaCodec` 发送一份 fixed-`HIGH` 给一至两名普通 Web Viewer;单应用窗口选择只在 Android 14 QPR2+ 验证。复用现有 room、Host admission、WSS 与标准 WebRTC 契约,不新增移动端专属拓扑、`LOW`、AV1、codec ladder 或软件编码兜底。系统选源、授权和持续前台指示必须保留;旋转、锁屏终止、Wi-Fi/蜂窝切换、持续热降频、资源释放和真机画质/延迟都是硬门槛。
- [ ] iOS 原生分享端有界实验:当前不实现 ReplayKit/broadcast-extension 兼容栈。待 iOS 27 与对应 ScreenCaptureKit SDK 脱离 beta 并稳定后,只评估 ScreenCaptureKit system picker + fixed-`HIGH` + 一至两名 Web Viewer 的窄路径;VideoToolbox 必须要求并读回硬件编码,无法证明时 fail closed,不增加 ReplayKit fallback、`LOW`、AV1 或新拓扑。
- [ ] 多地区 SFU-root 调度,以及可选 TURN 兼容层的按地区、运营商可达率。
## 非功能需求
@@ -67,7 +69,7 @@
- 隐私:P2P 会向房间内好友暴露网络地址,首版威胁模型仅覆盖可信熟人;若要求隐藏 IP,必须切换为服务器转发。
- 可维护性:代码、需求、调研、项目记忆、`AGENTS.md` 和 `.codex/` 均在 Git 中跟踪;开发配置不得依赖单一操作系统的绝对路径。
- 迁移完整性:同一 release 的客户端、服务端和部署配置原子迁移;canary 结束后删除被替代的配置、wire、parser、双写与测试,不为无当前消费者的旧版本保留兼容层或第二套架构。Git 历史承担旧实现追溯。
- 兼容性:分享端首个目标矩阵为 Windows 10/11 的当前 Chrome/Edge;观看端还必须覆盖当前 Android Chrome 和 iOS Safari,并实测自动播放限制、横竖屏、锁屏恢复与移动网络切换。Peer-assisted 实验只允许能力完整的前台桌面 Chrome/Edge 作为 relay,手机必须保持 leaf。
- 兼容性:分享端首个目标矩阵为 Windows 10/11 的当前 Chrome/Edge;普通移动浏览器当前只属于 Viewer 矩阵。截止 2026-08-19,Chrome Android、Firefox Android 和 Safari iOS 都没有可靠的 `getDisplayMedia()`;Web Host 必须用运行时能力检测,API 缺失时明确显示不支持,API 存在但调用拒绝时区分用户拒绝/取消与 capture failure,不能用 UA/设备型号伪造兼容。观看端仍必须覆盖当前 Android Chrome 和 iOS Safari,并实测自动播放限制、横竖屏、锁屏恢复与移动网络切换。Peer-assisted 实验只允许能力完整的前台桌面 Chrome/Edge 作为 relay,手机必须保持 leaf。
## 边界与约束
@@ -110,6 +112,7 @@
### 浏览器与移动端观看
- Given 观看者只有受支持的手机浏览器,When 打开有效邀请链接并执行浏览器要求的播放手势,Then 无需安装应用即可进入房间、看到首帧并听到可用音频。
- Given 用户在普通手机浏览器尝试成为 Host,When `navigator.mediaDevices.getDisplayMedia` 缺失,Then 页面在创建发布路径前明确显示该浏览器不支持移动端屏幕分享,并保留加入房间入口。Given API 存在但真实调用失败,When 失败原因是拒绝/取消或其他 capture error,Then 分别显示对应状态而不是一律重标为不支持;判断使用运行时能力/结果而不是 UA、平台或设备型号,也不能把移动 Viewer 支持宣传成移动 Web Host 支持。
- Given 观看者调节音量或静音,When 媒体切流、换父或恢复连接,Then 当前页面的本地播放音量保持不变,其他观看者和发送端不受影响;刷新页面后恢复默认音量,不产生信令、持久化或媒体重协商。
- Given 用户在本机设置显示名,When 以 Host 或 Viewer 加入房间,Then 所有在线参与者看到同一份服务端权威 roster、对应房间标题和“名称 + room-scoped peer 后缀”;刷新后本机名称仍在,清除站点数据后恢复未命名默认值,数据库中不存在显示名或成员记录,展示身份不参与授权或路由。
- Given 手机从 Wi-Fi 切换到蜂窝网络或页面从后台恢复,When 原候选对失效,Then 客户端执行 ICE restart 或重建连接,并显示恢复进度而不是永久黑屏。