Skip to main content

· 13 min read

本文记录 2026-10-21 在服务器上实际执行并验证成功的安装过程。命令在目标 Linux 服务器上以 root 身份执行。

后续更新:构建 Debian trixie 镜像时发现 Docker 18.09 存在 seccomp 兼容性问题,服务器随后升级至 Docker 29.9.2。前文保留首次安装记录,当前版本和升级注意事项见第 9 节。

1. 已验证的环境​

项目实际值
操作系统Kylin Linux Advanced Server V10 (Halberd),银河麒麟高级服务器版
CPU 架构aarch64 / ARM64
内核4.19.90-89.43.v2401.ky10.aarch64
包管理器yum / dnf
软件源麒麟官方 ks10-adv-os、ks10-adv-updates
软件源路径版本V10SP3-2403
Docker RPMdocker-engine-18.09.0-261.p07.04.ky10.aarch64(Epoch 为 1)
Compose RPMdocker-compose-1.22.0-4.ky10.noarch
Docker 存储驱动overlay2

其他麒麟版本或 x86_64 机器应先检查环境,不应直接假定包版本和依赖相同。本次使用麒麟官方软件包,未添加 CentOS Docker CE 软件源。

cat /etc/os-release
cat /etc/kylin-release
uname -m
uname -r
df -h / /var

2. 检查软件源​

ls /etc/yum.repos.d/
cat /etc/yum.repos.d/*.repo

本机曾存在错误的 /etc/yum.repos.d/docker.com.repo:

[docker.com]
name=created by dnf config-manager from https://docker.com
baseurl=https://docker.com
enabled=1

https://docker.com 是网站首页,不是 RPM 软件仓库。该配置会导致 yum 报错:

Status code: 404 for https://www.docker.com/repodata/repomd.xml
Failed to download metadata for repo 'docker.com'

本次安装通过 --disablerepo=docker.com 临时跳过该源,未删除或永久禁用它。后续 yum 命令如仍遇到此错误,同样需要跳过该源。

检查麒麟源可用版本:

yum --disablerepo=docker.com list --showduplicates docker-engine docker-compose

如果其他机器没有名为 docker.com 的源,省略该参数即可。若麒麟官方源无法访问,需要先解决该源的网络或授权问题。

3. 安装 Docker​

可以先预演依赖解析,确认待安装、升级或删除的软件包:

yum --disablerepo=docker.com install --assumeno docker-engine

实际安装并设置开机启动:

yum --disablerepo=docker.com install -y docker-engine
systemctl enable --now docker

本机此次新增的软件包为:

docker-engine-18.09.0-261.p07.04.ky10.aarch64
containerd-1.2.0-213.p10.ky10.aarch64
docker-proxy-0.8.0.dev.2-100.p04.ky10.aarch64

下载约 32 MB,安装占用约 155 MB。本机此前已有 docker-runc 和 Podman,此次安装未卸载它们。其他机器的实际依赖以 yum 输出为准。

验证:

systemctl is-active docker
systemctl is-enabled docker
docker version
docker info
docker ps

本机结果:服务 active、开机启动 enabled,客户端和服务端版本均为 18.09.0,架构 linux/arm64,存储驱动 overlay2,数据目录 /var/lib/docker。

如启动失败:

systemctl status docker --no-pager -l
journalctl -u docker -n 100 --no-pager

4. 安装 Docker Compose​

yum --disablerepo=docker.com install -y docker-compose
docker-compose version

本机输出:

docker-compose version 1.22.0, build f46880f
docker-py version: 4.0.2
CPython version: 3.7.9

这是 Compose V1,命令带连字符:docker-compose。本次没有安装 Compose V2 插件,不能将文档中的 docker compose 命令原样套用到此环境。V1 不支持 docker-compose ls。

在包含 docker-compose.yml 的项目目录中使用:

docker-compose config     # 校验配置
docker-compose up -d # 后台启动
docker-compose ps # 查看项目容器
docker-compose logs -f # 查看日志,Ctrl+C 退出日志跟踪
docker-compose down # 停止并移除项目容器和网络

Compose 1.22.0 较旧,新版 Compose 文件中的字段可能不兼容;部署前先执行 docker-compose config。更换为新版 Compose 或 Docker 需要另外评估兼容性。

本次通过标准输入传入临时配置,验证 Compose 可以连接 Docker,不拉取镜像或创建容器:

printf 'version: !!str 3\nservices:\n  check:\n    image: busybox:1.36\n' \
| docker-compose -f - -p compose-install-check ps

返回空的容器列表且退出成功,属于正常结果。

5. 排查 Docker Hub 拉取失败​

本次执行:

docker pull python:3.10-slim

遇到:

Get "https://registry-1.docker.io/v2/":
net/http: request canceled while waiting for connection
(Client.Timeout exceeded while awaiting headers)

网络检查:

curl -I --connect-timeout 8 --max-time 12 https://registry-1.docker.io/v2/
curl -I --connect-timeout 8 --max-time 12 https://auth.docker.io/
journalctl -u docker -n 50 --no-pager

本机两个域名均连接超时,说明此次失败发生在连接 Docker Hub 阶段,尚未进入镜像架构选择或镜像层下载阶段。

6. 配置镜像加速地址​

本次采用两个公共第三方服务:

https://docker.1ms.run
https://docker.xuanyuan.me

配置前测试 HTTPS 接口:

curl -sS -D - -o /dev/null --connect-timeout 5 --max-time 12 https://docker.1ms.run/v2/
curl -sS -D - -o /dev/null --connect-timeout 5 --max-time 12 https://docker.xuanyuan.me/v2/

本次均返回 401 和 WWW-Authenticate 认证挑战,表示 Registry 接口网络可达。这本身不保证目标镜像可下载,仍需实际拉取验证。

6.1 备份并编辑配置​

先查看正在运行的容器,选择合适时间重启 Docker:

docker ps
mkdir -p /etc/docker
if [ -f /etc/docker/daemon.json ]; then
cp -a /etc/docker/daemon.json "/etc/docker/daemon.json.bak.$(date +%Y%m%d-%H%M%S)"
cat /etc/docker/daemon.json
fi
vi /etc/docker/daemon.json

本机此前没有该文件、没有运行中的容器,因此新建文件,内容为:

{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
]
}

如果文件已包含其他配置,应保留原有键值,只新增或修改 registry-mirrors。不要直接覆盖已有配置。JSON 不能包含注释或末尾多余逗号。

6.2 检查语法并重启​

python3 -m json.tool /etc/docker/daemon.json
# 上一步成功后再执行:
systemctl restart docker
systemctl is-active docker
docker info

docker info 应包含:

Registry Mirrors:
https://docker.1ms.run/
https://docker.xuanyuan.me/

若重启失败,检查 JSON 和日志,并确认启动参数没有重复设置同一选项:

journalctl -u docker -n 100 --no-pager
systemctl cat docker
cat /etc/sysconfig/docker

必要时恢复备份后重启。若原先没有配置文件,可编辑文件移除本次新增的 registry-mirrors;若无其他配置,可保留内容 {},然后重启 Docker。

6.3 拉取并运行验证​

docker pull python:3.10-slim
docker image inspect python:3.10-slim \
--format '{{.Os}}/{{.Architecture}} {{json .RepoDigests}}'
docker run --rm --network none python:3.10-slim python --version

本次结果:

Status: Downloaded newer image for python:3.10-slim
linux/arm64
Python 3.10.22

镜像摘要:

sha256:6ff506466f8b1e981719b468cc0023b8ef000b983cdec0eafe119d01f379e31d

测试容器通过 --rm 自动删除。两个加速地址配置后的整体拉取和运行验证成功;本次未分别验证每个地址是否均能完整下载该镜像。python:3.10-slim 是可变标签,日后版本和摘要可能不同。

7. 后续使用与注意事项​

  • 配置生效后,正常使用 docker pull python:3.10-slim,无需在镜像名前加第三方域名。
  • ARM64 机器使用的镜像必须支持 ARM64。下载成功后可用 docker image inspect 检查;换源不能解决镜像本身没有 ARM64 版本的问题。
  • 镜像加速配置与 yum 软件源互相独立,不能用 registry-mirrors 修复 yum 的 404。
  • 这两个地址属于第三方服务,可用性和使用规则可能变化。本次成功不代表持续可用。生产环境可将已验证镜像同步至自有或云上的私有仓库,并记录摘要。
  • 华为云 SWR 账号专属加速地址需要从账号控制台获取,不能通过公开搜索获取用户自己的地址。
  • Docker 和 Compose 版本均为此次麒麟官方源提供的版本,本文不代表新版 Docker 或 Compose V2 的安装步骤。

8. 参考资料​

9. 升级 Docker,修复 Debian trixie 构建问题​

原因与验证​

python:3.10-slim 此次基于 Debian trixie。旧版 Docker 18.09 下 apt-get update 下载索引后报错:

E: Problem executing scripts APT::Update::Post-Invoke
E: Sub-process returned an error code

临时容器默认 seccomp 下失败,关闭 seccomp 后成功,确认与旧版引擎的安全过滤兼容性有关。关闭 seccomp 仅用于诊断,不作为长期配置。升级后默认 seccomp 下 APT 更新成功。

升级后的版本​

组件版本
Docker 客户端和服务端29.9.2
containerd2.4.2
runc1.5.2(静态包内 libseccomp 2.6.0)
Compose5.6.1
Buildx0.38.1

麒麟当前源仅提供 18.09 系列,因此改用 Docker 官方 ARM64 静态二进制包。此方式需要手动维护更新,yum update 不会自动升级这些新二进制。参考 Docker 二进制安装说明。

本次升级布局与流程​

  1. 先确认没有运行中容器;在本机从官方 HTTPS 地址下载 ARM64 包,再 SCP 上传,避免服务器外网慢或不稳定。
  2. 新 Docker 二进制解压至 /opt/docker/29.9.2/,保留原 RPM 程序。
  3. 停止 Docker 后,将 /var/lib/docker 和配置备份至 /root/docker-upgrade-20261021/,数据备份子目录为 docker-data,配置子目录为 config。
  4. 添加 /etc/systemd/system/docker.service.d/90-official-binary.conf,使用新引擎和配套运行时,保留 overlay2、数据目录、镜像加速地址。
  5. 创建 /usr/local/bin/docker 链接,指向 /opt/docker/29.9.2/docker,执行 systemctl daemon-reload 后启动 Docker。
  6. 下载官方 Compose、Buildx ARM64 发布文件,核验 GitHub 发布中的 SHA256 后安装到 /usr/local/lib/docker/cli-plugins/。
  7. 创建 /usr/local/bin/docker-compose 链接至新 Compose 插件,兼容旧命令写法;旧 Compose RPM 保留在 /usr/bin/docker-compose,不要直接使用它连接新版服务。

systemd 覆盖文件内容:

[Service]
Environment="PATH=/opt/docker/29.9.2:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=
ExecStart=/opt/docker/29.9.2/dockerd --live-restore --storage-driver=overlay2

此覆盖文件适用于本机已核实的配置。其他机器如有代理、额外启动参数或外部 containerd,应检查并保留相关设置后再调整。

首次启动提示旧 BuildKit 缓存格式不兼容(legacy (v1) cache metadata)。在服务停止状态下,将 /var/lib/docker/buildkit 移至 /root/docker-upgrade-20261021/buildkit-legacy-cache 后启动成功。原镜像保留,构建缓存重新生成。

升级后使用与验证​

hash -r
docker version
docker compose version
docker-compose version
docker buildx version
docker buildx ls
systemctl is-active docker
systemctl is-enabled docker
docker images
docker info
docker run --rm --network none python:3.10-slim python --version

新 Compose 推荐写法为 docker compose;兼容命令 docker-compose 也指向新版本。已验证 Python 容器运行、Compose 连接和 Buildx 默认构建器正常。

后端在项目根目录构建:

cd /root/code/lda-api
bash build.sh builder

ARM64 机器原生构建 ARM64 镜像不要求跨架构模拟。Buildx 已安装,也可以使用项目的 bash build-arm64.sh。这些脚本使用 Bash,应使用 bash 执行,避免 sh 对 pipefail 等语法支持不同。

回退注意事项​

回退需先停止 Docker,再停用此次 systemd 覆盖文件和新 CLI 链接,并恢复升级前的数据与配置后启动旧版。新引擎可能更新数据格式,不能只换回旧程序直接读取新数据。恢复前应另存升级后目录,避免覆盖后续产生的镜像、容器或业务数据。备份目录应暂时保留。

· 18 min read

本文解释这次针对“刷新 Monitor 后,画面和声音需要等待几秒,后台 ASR 出现 RTSP 404”的修改。内容以当前代码为准,范围仅限这次浏览器采集生命周期优化。

1. 先说明问题的本质​

对于浏览器摄像头和麦克风通道,Monitor 同时承担了两种职责:

  1. 调用浏览器设备接口,采集摄像头和麦克风,并向 MediaMTX 推流。
  2. 展示画面、音频可视化和后端识别结果。

因此,刷新 Monitor 实际上也会关闭正在推流的页面。旧页面的摄像头轨道、麦克风轨道、WebRTC 连接都会结束,新页面需要重新申请设备并重新建立连接。

后端读取的是 MediaMTX 上的 RTSP 地址,例如:

rtsp://localhost:8554/livecamera

这个地址不是一个永远存在的视频文件。只有对应路径上有发布者时,MediaMTX 才能提供媒体。刷新造成发布者短暂消失,后端此时发起 RTSP DESCRIBE,就可能收到 404 Not Found。

所以,这条报错通常表示“此刻该路径没有流”,不能仅据此判断通道配置错了或摄像头坏了。

2. 原来的等待由哪些环节组成​

浏览器通道展示远端画面,需要经过这样的链路:

申请摄像头/麦克风
→ 创建 WebRTC 推流连接
→ MediaMTX 接收媒体
→ 页面再从 MediaMTX 拉流
→ 播放器显示画面和声音

后端 ASR 则走另一条链路:

MediaMTX 接收媒体
→ FFmpeg 通过 RTSP 读取音频
→ 音频发送给 ASR 服务
→ 识别结果返回页面

刷新期间,这几条链路的启动和停止没有天然的同步顺序。此次修改重点处理以下问题:

  • 通道查询返回一个新对象,也可能让采集副作用重新运行,造成不必要的设备重启。
  • 推流 HTTP 协商成功,不代表已经实际发送音视频;此时启动 ASR 仍然可能过早。
  • 旧页面退出请求可能晚于新页面启动请求到达,误把新会话关闭。
  • 页面在异步申请设备或协商连接时退出,未完成的操作需要能够取消或回收。
  • 只为看本机摄像头画面,也要等待推流再拉流,增加了预览恢复环节。

3. 修改后的整体流程​

sequenceDiagram
participant Page as Monitor 页面
participant Device as 摄像头/麦克风
participant MTX as MediaMTX
participant Server as Go 后端
participant ASR as ASR 管线

Page->>Device: getUserMedia 申请设备
Device-->>Page: 返回 MediaStream
Page->>Page: 立即绑定本机预览
Page->>MTX: WHIP 协商推流
MTX-->>Page: 返回 SDP answer
Page->>Page: 等待 WebRTC connected
Page->>Page: 检查所需媒体类型已有 RTP 包发出
Page->>Server: POST browser-session,携带 session_id
Server->>ASR: 按通道配置启动运行实例
ASR->>MTX: 通过 RTSP 读取媒体
loop 每 5 秒
Page->>Server: 相同 session_id 的心跳
end

这里有两个不同的“就绪”:

  • 本机预览就绪:已经取得设备流,可以显示本机画面。
  • 推流就绪:WebRTC 已连接,并且所有需要的媒体类型都已发出 RTP 包,随后后端会话登记成功。

看到本机画面不等于后端一定收到媒体。因此页面用“本机预览 · 推流连接中 / 已推流”区分这两种状态。

4. 采集配置变化时才重建设备​

对应文件:web/src/lib/browser-capture-config.ts、web/src/hooks/use-browser-publisher.ts。

新增 browserCaptureKey(channel),从通道中提取真正影响浏览器采集的字段,序列化成稳定的字符串,作为 React effect 的依赖。

参与判断的内容包括:

  • 通道 ID 和 MediaMTX 路径。
  • 通道是否启用,以及启用的浏览器来源。
  • 摄像头设备 ID。
  • 麦克风设备 ID 列表及其顺序。

通道名称、更新时间、翻译开关等内容不进入这个采集配置。即使查询结果变成一个新对象,只要采集配置不变,就不会重新打开摄像头和麦克风。

这条规则只约束前端设备采集。后端是否因为 ASR 等配置变化而调整运行实例,仍由后端自己的逻辑决定。

5. 本机预览为什么更早出现​

对应文件:web/src/pages/monitor-page.tsx 中的 VideoPanel 和 LocalPreview。

取得摄像头流后,页面直接执行:

video.srcObject = stream

同一个流继续用于向 MediaMTX 推送,所以本机预览不用等待“先推到服务器,再拉回浏览器”。网络推流尚在连接时,本机画面也可以先出现。

几个需要区分的行为:

  • 本机预览展示的是本地设备流,不能用于证明远端播放、录制和 ASR 已正常工作。
  • 本机预览没有强制静音,保留了之前要求的默认声音开启行为;浏览器仍可能限制有声自动播放,需要用户操作播放器。
  • 只有主流中包含麦克风时,该预览才有对应声音。额外音频来源仍按各自路径处理。
  • 预览组件卸载只暂停视频并解除 srcObject;真正停止设备轨道由采集 hook 统一负责,避免播放器重挂载误停推流。
  • 没有本机预览的来源,仍使用 MediaMTX 播放器;浏览器发布尚未就绪时先展示等待状态。

6. 怎样判断可以启动后端 ASR​

对应文件:web/src/lib/browser-publisher.ts。

BrowserPublisher.start() 现在依次等待:

  1. ICE 候选收集完成。
  2. WHIP POST 请求成功,得到 SDP answer。
  3. RTCPeerConnection.connectionState 进入 connected。
  4. getStats() 中,对当前流包含的每一种媒体类型,都出现 outbound-rtp 且 packetsSent > 0。

例如,一个流同时有视频和音频,就需要看到视频和音频都已发包。只看到 HTTP 成功,或只看到视频开始发送,都不算这个流完成就绪检查。

所有需要发布的流都通过检查后,前端才调用后端 browser-session 接口。

这个检查确认的是浏览器已经发包,并不是 MediaMTX 的 RTSP 可读性探测。 它能减少过早启动 ASR 的窗口,但不能保证任何情况下都完全没有瞬时 404;网络中断、MediaMTX 重启和路径异常仍可能使读取失败。

页面也使用同一个状态:浏览器推流未就绪时,不启动音频卡片的远端拉流,开始录制按钮保持不可用,避免明知流尚未准备好就发起读取。

7. session_id 如何防止旧页面关闭新会话​

对应文件:web/src/lib/api.ts、server/internal/httpapi/server.go、server/internal/channelruntime/manager.go。

每次发布尝试都会生成新的随机 session_id。这个 ID 表示一次发布尝试,不是用户登录凭证,也不是 MediaMTX 返回的 WHIP 资源地址。

登记和心跳请求:

POST /api/channels/{id}/browser-session
Content-Type: application/json

{"session_id":"new-session"}

退出请求:

DELETE /api/channels/{id}/browser-session?session_id=new-session

后端同时保存最近心跳时间和当前会话 ID。最关键的规则是:DELETE 只能停止与当前 ID 相同的会话。

刷新可能发生这样的请求乱序:

顺序请求后端处理
1旧页面 A 正在发布当前会话为 A
2新页面 B 发布就绪,登记 B当前会话切换为 B
3旧页面 A 的退出请求迟到ID 不匹配,忽略
4B 发心跳更新时间,继续使用 B

如果新会话接替了仍登记的旧会话,后端会取消旧运行实例并创建新实例,让新发布者不再继承旧 ASR 管线的重试等待。同一个 ID 的普通心跳不会仅因心跳本身重建运行实例。

这个机制解决的是旧退出请求误关新会话。它不是完整的多标签页发布仲裁协议,也没有为所有乱序心跳建立版本排序;不应据此认为多个标签页可以任意同时发布同一个路径。

8. 连接失败后如何恢复​

对应文件:web/src/hooks/use-browser-publisher.ts。

设备采集和网络发布现在分开管理:

设备采集成功
→ 保留 MediaStream
→ 尝试发布
→ 成功:登记后端会话,定期心跳
→ 失败:关闭这次连接,稍后用同一 MediaStream 重试

这样在页面仍然存活、只是网络连接失败时,无需每次重连都重新打开摄像头。本机预览也可以继续显示。

当前等待参数如下,都是超时上限或重试间隔,不代表每次必须等待这么久:

环节参数
ICE 收集最长 5 秒
WHIP POST最长 10 秒
等待 WebRTC connected最长 10 秒
等待各媒体类型实际发包最长 5 秒,每 50 毫秒检查
disconnected 状态观察期1 秒,恢复连接则取消处理
确认断连后再次尝试发布约 500 毫秒后
一次发布尝试失败后重试约 1 秒后
后端会话心跳每 5 秒
后端心跳有效期15 秒,周期检查时回收
WHIP 资源 DELETE最长 2 秒

摄像头权限拒绝、设备不可用等采集错误会显示错误并释放已取得的轨道,不进入网络发布重试循环,避免不断重复设备申请。

9. 刷新、切换通道时如何释放资源​

这次补充了异步操作中的清理逻辑:

  • 页面退出或采集配置变化时,取消当前发布尝试,停止心跳和重试定时器。
  • 用 AbortController 取消尚未结束的连接流程;相关 WebRTC peer 会关闭。
  • 如果设备申请在页面退出后才返回,立即停止新返回的轨道,避免摄像头残留占用。
  • 保存 WHIP 响应的 Location,结束发布时 DELETE 对应 MediaMTX 资源。
  • 后端会话退出请求和 WHIP DELETE 使用 keepalive,提高页面离开时请求送出的机会。
  • 如果协商响应在取消后才到达,也尝试清理其返回的 WHIP 资源。
  • stop() 可重复调用,避免多处清理重复操作资源。

页面监听 pagehide 主动释放资源;如果从浏览器前进/后退缓存恢复,使用重新加载重新初始化采集。初始采集通过微任务启动并检查是否已释放,也减少 React 开发模式 StrictMode 短暂挂载造成的重复申请。

keepalive 不是送达保证。浏览器崩溃、直接断网等情况下,后端通过 15 秒心跳有效期兜底,并在运行管理器后续检查时回收会话,而不是承诺恰好第 15 秒清理。

10. 文件与职责对照​

文件这次改动的职责
web/src/lib/browser-capture-config.ts提取稳定采集配置,避免无关数据变化重启设备
web/src/hooks/use-browser-publisher.ts管理设备、预览、发布尝试、重试、心跳和退出
web/src/lib/browser-publisher.tsWHIP 协商、连接与 RTP 就绪判断、取消和资源释放
web/src/lib/api.ts给浏览器会话请求增加 ID、取消信号和退出 keepalive
web/src/pages/monitor-page.tsx本机预览、状态提示、录制及音频拉流的就绪限制
server/internal/httpapi/server.go接收并传递 session_id
server/internal/channelruntime/manager.go记录当前会话、拒绝旧退出请求、切换运行实例及过期回收
web/scripts/test-browser-publisher.mjs推流协议时序、取消和稳定配置回归测试
server/internal/channelruntime/browser_session_test.go会话切换、旧退出请求、心跳和过期回归测试
server/README.md补充浏览器采集生命周期说明

11. 验证做到了什么​

此次实现时已完成前端 TypeScript 检查、改动文件的 ESLint 检查、生产构建,以及后端 go test ./...。

前端三组脚本合计 13 项测试通过,其中这次新增的 5 项覆盖:

  1. HTTP 协商成功后仍等待 WebRTC 连接。
  2. ICE 协商期间取消会关闭未完成的 peer。
  3. connected 后仍等待实际 RTP 发包。
  4. 发布 HTTP 失败会释放 peer。
  5. 无关通道字段变化不会改变采集配置,而更换设备会改变配置。

后端新增测试覆盖:新会话接替旧会话、旧页面或无 ID 的迟到退出请求不能关闭当前带 ID 的会话、同会话心跳不重启实例,以及心跳过期回收。

这些测试验证的是协议和会话逻辑。前端使用模拟设备及连接状态,并未测量真实摄像头刷新后多少毫秒恢复,也没有证明特定浏览器、网络和设备组合一定零报错。

建议实机观察以下现象:

  • 刷新后先显示本机预览,再变为“已推流”,随后恢复识别。
  • 修改翻译等无关采集配置时,摄像头不会反复关闭、打开。
  • 推流网络失败时,本机预览仍在,连接自动重试。
  • 快速刷新后,新会话不会被旧页面的退出请求关闭。
  • 浏览器发布未就绪时,不能提前开始录制。

12. 仍然存在的限制与下一步选择​

这次减少了恢复过程中不必要的设备重启、往返拉流等待和会话竞态,没有改变“Monitor 页面拥有采集推流”这一架构。

因此,整页刷新仍会关闭设备和推流。新页面仍需重新采集、协商,后台录制和 ASR 仍可能短暂断流。仅凭这些修改,不能承诺录制文件或识别内容连续无缺口。

如果业务要求“任意刷新 Monitor,录像和识别都不中断”,需要把采集与 Monitor 生命周期分开,例如:

  • 独立采集页面:持续持有设备并发布,Monitor 只负责观看和控制;采集页面仍必须保持运行。
  • 本地常驻采集程序或独立推流设备:持续向 MediaMTX 发布,Monitor 完全作为观看端。

这是后续架构调整,尚未包含在本次改动中。