go2rtc实战:用Docker统一接入多品牌摄像头,实现WebRTC低延迟监控
前阵子折腾家里的摄像头,真被协议问题搞到头疼。不同品牌的摄像头各玩各的:海康走 RTSP、大华走 RTSP 但路径完全不一样、门口那个杂牌摄像头还只有私有协议,手机 App 一个摄像头装一个,想在电脑上一个页面把几个画面并排看,基本不可能。
后来换了 go2rtc 这个方案,一个 Docker 容器就把事情理顺了。go2rtc 是一个用 Go 写的轻量流媒体服务,专门解决多协议视频源的统一接入和输出问题。它能把 RTSP、RTMP、ONVIF、MJPEG 这些杂七杂八的输入,统一转换成浏览器能直接播放的 WebRTC、HLS、MSE 流,而且 WebRTC 延迟能压到 0.5 秒以内。对玩智能家居、自建监控墙、给 Web 前端提供低延迟视频的人来说,这应该是目前最省心的方案之一。
下面把我这次完整部署的过程、配置细节和踩过的坑都整理出来,从环境准备、Docker 启动、摄像头接入,到 WebRTC 播放、进阶玩法,一条线讲完。
1. go2rtc 到底解决了什么——一个进程搞定“协议不通”的老大难
1.1 摄像头接入的核心痛点
绝大多数网络摄像头,本质上是给你一个 RTSP 地址,比如
rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101
。但这个地址有两个问题:第一,不同厂商的 RTSP 路径格式不统一;第二,浏览器原生不支持 RTSP,你不能直接把链接丢给 Chrome 让它播放。
传统做法是拿 FFmpeg 把 RTSP 转成 HLS,再用 nginx 挂一个流媒体服务器,最后前端用 hls.js 播放。这条路能走通,但太笨重:一是要维护多套组件,二是 HLS 切片延迟普遍在 5 秒以上,画面卡不卡另说,光是来回切码流就很痛苦。
go2rtc 的思路是把这些功能收进一个 Go 二进制文件里。它内部有一个统一的流媒体路由层,输入接 RTSP、RTMP、ONVIF、MJPEG、本地文件都行,输出侧直接提供 WebRTC、MSE、HLS、MJPEG 等协议,还附带了一个 Web 管理界面和一套 JSON API。不需要额外装 nginx,不需要自己写 FFmpeg 脚本,一个容器就能把摄像头流“收进来、转出去”。
1.2 go2rtc 的协议地图
下面这个表基本概括了 go2rtc 的能力边界,也是我做方案选型时最看重的部分:
| 方向 | 支持的协议/格式 | 典型用途 |
|---|---|---|
| 输入 | RTSP、RTMP、HLS、MJPEG、WebRTC、SIP、ONVIF、本地文件、FFmpeg 管道 | 接入摄像头、拉流其他服务器、播放本地视频文件 |
| 输出 | WebRTC、MSE、HLS、LL-HLS、RTSP、RTMP、MJPEG、FLV | 浏览器低延迟预览、嵌入网页、转发给其他系统 |
| 附加能力 | ONVIF 自动发现、FFmpeg 联动、录制、Web API、WebSocket 信令 | 自动发现摄像头、处理非标编码、按需录像 |
这个能力组合在同类项目里很少见。大多数方案要么只做协议转换,要么只做拉流分发,做不到“输入侧什么都能接、输出侧什么都能发”,更不要说还自带 ONVIF 发现和录制功能了。
1.3 和传统方案对比,为什么选它
我整理了一张对比表,能比较直观地说明问题:
| 方案 | 协议覆盖 | 播放延迟 | 维护成本 | 浏览器直连 |
|---|---|---|---|---|
| FFmpeg + nginx-rtmp + hls.js | 中 | 5-15 秒 | 高(多组件) | 需要前端配合 |
| 商业 NVR | 低(绑定品牌) | 1-3 秒 | 中 | 一般需要专门插件 |
| Frigate(NVR 方案) | 高,但主要面向检测 | 1-3 秒 | 高(需要数据库等) | 间接 |
| go2rtc | 高 | 0.2-0.5 秒(WebRTC) | 极低(单容器) | 原生支持 |
go2rtc 最大的优势是“轻”和“低延迟”。它本身不依赖数据库、不依赖外部消息队列,配置文件就一个 YAML,跑起来内存占用通常在 50MB 以内。如果只做视频转发不转码,CPU 占用几乎可以忽略。这个特性让它特别适合跑在 Home Assistant 这类常驻系统里。
1.4 适合谁来用
如果你符合下面任何一种情况,go2rtc 基本能对症下药:
- 家里或办公室有多个品牌摄像头,想在浏览器里统一预览、低延迟切换。
- 玩 Home Assistant、Frigate,需要一个稳定的流媒体中间层把摄像头 RTSP 整理成统一格式。
- 给 Web 前端做实时视频展示,不想引入复杂的流媒体服务器集群。
- 需要一个能通过 API 动态增删流、能自动录制、能暴露播放地址的轻量工具。
下文从 Docker 部署开始,逐步把完整配置和排错经验铺开。
2. 部署前必须想清楚的几件事:Docker 版本、网络模式、目录规划
2.1 Docker 环境检查与 Windows 上的坑
go2rtc 官方直接提供 Docker 镜像,部署前先确认 Docker 可用:
docker --version
docker compose version
Linux 服务器一般没有问题。但如果你是在 Windows 上用 Docker Desktop,大概率会撞上论坛里常见的报错:
Virtualization support not detected
,后面跟着一句“Docker Desktop failed to start...”。这个问题的根因很简单——Docker Desktop 在 Windows 上需要虚拟化支撑,常见的是 Hyper-V 或 WSL2 后端,它检测不到虚拟化就直接罢工。
排查顺序我建议这么走:
- 打开任务管理器 → 性能 → CPU,看右下角“虚拟化”是否显示“已启用”。如果显示“已禁用”,说明 BIOS 里没开 VT-x/AMD-V,需要进 BIOS 开启(不同主板入口不一样,一般是 Advanced → CPU Configuration → SVM Mode 或 Intel Virtualization Technology)。
- 如果虚拟化已开启,多半是 WSL2 或 Hyper-V 没有完整启用。用管理员 PowerShell 执行下面的命令,然后重启:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
wsl --install
- 如果你本身是在虚拟机里跑 Windows,还要确保虚拟机配置里开启了“嵌套虚拟化”。
这个报错我反复碰到过,95% 的案例走到第二步就能解决。装好之后重新打开 Docker Desktop,等右下角图标变绿就可以了。
2.2 网络模式:为什么强烈建议 host 网络
go2rtc 的部署里,网络模式是最容易被忽视、又最容易出问题的地方。我的建议很简单:
在 Linux 上,直接用
network_mode: host;在 Windows/macOS 上,Docker Desktop 对 host 网络支持有限,才退而使用端口映射。
host 网络模式下,容器直接共享宿主机的网络栈。go2rtc 监听 1984 端口就相当于宿主机监听 1984 端口。这个模式最大的好处是 WebRTC 的 ICE 协商会简单很多——容器生成的 SDP 里的候选地址直接就是宿主机的 IP,浏览器可以直接连上。
如果用了默认的 bridge 网络,容器内部会拿到一个 172.17.x.x 的内网 IP。浏览器访问宿主机 IP 时,SDP 里却写着 172.17.0.2 这种地址,肯定连不上。虽然可以通过配置 candidates 把地址改正确,但走弯路不如一开始就选对路。
2.3 目录规划与配置文件的存放
我建议把所有 go2rtc 相关文件放在一个独立目录里,结构如下:
/opt/go2rtc
├── config
│ └── go2rtc.yaml
└── recordings
config 放配置文件,recordings 放录像文件。后面用 docker-compose 的时候,这个目录结构直接挂载进容器就行。首次启动前,你可以先创建一个空的配置文件,也可以让 go2rtc 用默认配置跑起来再从 Web 界面生成。我第一次部署时偷懒没建配置文件,容器照样跑得很好,只是 Web 界面的“配置”页里会显示默认配置内容,保存后会在挂载目录里生成 go2rtc.yaml。注意挂载目录要有写权限,否则保存会报错。
3. 用 Docker 把 go2rtc 跑起来:命令版与 compose 版双方案
3.1 一条命令快速启动
最简单的启动方式:
docker run -d \
--name go2rtc \
--restart unless-stopped \
--network host \
-v /opt/go2rtc/config:/config \
ghcr.io/alexxit/go2rtc:latest
注意几个点:
-
镜像用的是
ghcr.io/alexxit/go2rtc:latest,这是 go2rtc 在 GitHub Container Registry 上的官方镜像。主流的稳定分支就是 latest,如果你想要最新的 master 分支特性,可以切到master标签。 -
-v /opt/go2rtc/config:/config把配置目录挂载进容器的/config,go2rtc 默认从/config读取 go2rtc.yaml。 -
--network host是 Linux 推荐模式,原因见上一节。
启动之后验证容器状态:
docker ps
docker logs go2rtc
日志里出现类似
INFO: go2rtc version ...
这样的内容基本就成功了。
3.2 更推荐:docker-compose 方式
长远来看,docker-compose 更适合维护,尤其要升级、改参数的时候,一条
docker compose up -d
就够。我的 compose 文件长这样:
services:
go2rtc:
image: ghcr.io/alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
network_mode: host
volumes:
- ./config:/config
- ./recordings:/media/recordings
environment:
- TZ=Asia/Shanghai
TZ
环境变量建议加上,否则录像文件名里的时间戳会是 UTC 时间,比北京时间慢了 8 个小时,排查日志时容易懵。
如果你在 Windows/macOS 上,host 网络模式不可用或表现不稳定,我建议改用端口映射:
services:
go2rtc:
image: ghcr.io/alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
ports:
- "1984:1984/tcp" # Web 管理和 API
- "8555:8555/tcp" # WebRTC 信令/数据通道(部分场景)
- "8555:8555/udp" # WebRTC 媒体通道
volumes:
- ./config:/config
- ./recordings:/media/recordings
extra_hosts:
- "host.docker.internal:host-gateway"
注意,bridge 模式下 WebRTC 的 candidates 必须配置成宿主机 IP,否则播放端还是连不上(后面第 5 节详细说)。
3.3 首次启动后的验证
服务起来之后,浏览器访问
http://宿主机IP:1984
。go2rtc 的 Web 界面比较简洁,功能集中在几块:流列表、配置编辑、日志查看、播放器。
第一次进去可能没有任何流。为了确认服务正常,我建议先在配置里加一个本地测试流。放一个 mp4 视频文件到
/opt/go2rtc/config/
下,然后在 go2rtc.yaml 里加:
streams:
test:
- file:///config/demo.mp4
保存后,到流列表页面点击这个流,如果能在浏览器里正常播放,说明 go2rtc 的媒体链路是通的,后面接入摄像头就有了底气。
4. 摄像头接入配置:从手写 RTSP 到 ONVIF 自动发现
4.1 go2rtc.yaml 的基本结构
go2rtc 的配置文件是 YAML,顶层节点主要包括这几个:
-
log:日志级别。 -
api:HTTP API 监听地址和端口,默认:1984。 -
webrtc:WebRTC 相关配置,如监听端口、candidates。 -
streams:核心,定义所有视频流。 -
onvif:ONVIF 设备连接配置。 -
ffmpeg:内置 ffmpeg 的相关参数。
核心就是
streams
。每个流一个名字,名字下面是这个流的来源列表。go2rtc 允许一个流有多个来源,比如主码流断了自动切到子码流,这个后面再展开。
4.2 手动配置 RTSP 流
摄像头接入最直接的方式就是手动写 RTSP 地址。下面是一个真实可用的示例:
streams:
living_room:
- "rtsp://admin:password123@192.168.1.64:554/Streaming/Channels/101"
backyard:
- "rtsp://admin:123456@192.168.1.66:554/cam/realmonitor?channel=1&subtype=0"
不同厂商的 RTSP 路径差异很大,我把常见的整理成了表,方便你对着找:
| 品牌 | 常见 RTSP 地址格式 |
|---|---|
| 海康威视 |
rtsp://用户:密码@IP:554/Streaming/Channels/101
|
| 大华 |
rtsp://用户:密码@IP:554/cam/realmonitor?channel=1&subtype=0
|
| TP-Link |
rtsp://用户:密码@IP:554/stream1
|
| 宇视 |
rtsp://用户:密码@IP:554/unicast/c10/s0/live
|
| 小米/部分消费级 | 多数走私有协议,需通过 ONVIF 或专用 App 查找 RTSP 地址 |
注意
subtype=0
一般是主码流,
subtype=1
是子码流。主码流清晰度高,但带宽和存储占用大;子码流适合多画面预览。我接摄像头时通常把子码流作为默认预览源,主码流留给录制用。
这里有个新手常踩的坑:YAML 里如果 URL 包含
&
或者
#
,一定要用引号包起来。比如大华的 URL 里有
&
,不加引号的话 YAML 解析会出错,或者流的后半段直接消失。我一开始没加引号,结果流怎么都启动不了,查了半天才发现是这个问题。
如果密码里包含特殊字符,比如
@
、
:
、
/
这些,URL 会解析错。处理办法是给特殊字符做百分号编码。比如密码是
a@b:c
,要写成:
streams:
cam_special:
- "rtsp://admin:a%40b%3Ac@192.168.1.72:554/stream1"
其中
@
变成
%40
,
:
变成
%3A
。
4.3 在 Web 界面里添加临时流测试
写配置之前,我建议先在 Web 界面的流管理页面添加一条临时流试试。输入流名称和 URL,go2rtc 会尝试拉流并显示状态。如果 URL 配错了,界面上会直接显示错误状态,比如
401 Unauthorized
或者
connect timeout
。这种方式比改配置文件、重启服务再等日志要快得多。
测试通过的流,再从界面里复制配置到 go2rtc.yaml 里固化下来。这个工作流是我用下来最顺手的:界面验证 → 固化配置 → 重启加载。
4.4 ONVIF 自动发现:适合摄像头数量多的情况
摄像头数量一多,手写 RTSP 地址就容易乱。go2rtc 支持 ONVIF 协议,可以直接连到摄像头的 ONVIF 服务,自动拿到设备信息和流地址。配置方式:
onvif:
- ip: 192.168.1.64
username: admin
password: "password123"
- ip: 192.168.1.66
username: admin
password: "123456"
配置之后,go2rtc 会通过 ONVIF 去查询设备的 RTSP 地址,并从 Web 界面里看到可用的流。这样就不用记忆每家厂商的 URL 路径了。
需要注意两点:
- 部分摄像头默认没开启 ONVIF 功能,需要在摄像头的 Web 管理后台里打开(通常在“网络 → 高级 → ONVIF”一类的位置)。
- 消费级摄像头对 ONVIF 支持参差不齐。我遇到过某个杂牌摄像头,ONVIF 能发现设备,但拉流总是超时,最后还是手动配 RTSP 解决的。
所以我的建议是:数量少直接手写 RTSP,数量多先用 ONVIF 试试,不支持再单独配。
4.5 FFmpeg 管道兜底:处理“非标”摄像头
有些摄像头不提供标准 RTSP 流,或者提供的流编码格式 go2rtc 原生解析不了。go2rtc 内置了 FFmpeg,可以在流的来源前面加
ffmpeg:
前缀,把问题丢给 FFmpeg 去处理。
streams:
weird_cam:
- "ffmpeg:rtsp://admin:123456@192.168.1.70:554/live#video=copy#audio=copy"
#video=copy
表示视频编码流直接复制,不做转码,这样 CPU 开销最小。如果摄像头输出的是 H.265,而浏览器不支持硬解 H.265(大部分浏览器确实不支持 WebRTC 默认走 H.265),可以强制转码成 H.264:
streams:
h265_cam:
- "ffmpeg:rtsp://admin:123456@192.168.1.74:554/stream1#video=h264"
#video=h264
的意思是让 go2rtc 调用 ffmpeg 把视频转成 H.264。这里有个代价:转码很吃 CPU。一路 1080p 的 H.265 转 H.264,CPU 占用率轻松跑到 30% 以上。所以在实际部署里,我一般优先在摄像头后台把编码格式改成 H.264,实在改不了再走转码。
另外,如果摄像头的主码流不适合浏览器播放,而子码流是 H.264,我的做法是接入子码流地址,而不是硬转主码流。
5. 多协议输出与 WebRTC 低延迟播放的原理和配置
5.1 浏览器端播放协议怎么选
go2rtc 给你配置好摄像头流之后,输出侧有几种播放方式。我整理了一张对比表:
| 协议 | 延迟 | 浏览器兼容性 | 适用场景 |
|---|---|---|---|
| WebRTC | 0.2-0.5 秒 | 现代浏览器原生支持 | 实时预览、PTZ 控制、交互式场景 |
| MSE | 1-3 秒 | Chrome/Firefox/Edge 良好 | 需要较高兼容性、可接受轻微延迟 |
| LL-HLS | 2-5 秒 | Safari/iOS 最佳 | 苹果设备为主的场景 |
| HLS | 5-15 秒 | 全平台 | 兼容性优先,延迟不敏感场景 |
| MJPEG | 0.5-1 秒 | 全平台,但 CPU 占用高 | 低分辨率小画面墙 |
我默认用的是 WebRTC。对摄像头预览来说,延迟低就意味着云台控制跟手、门铃视频对话体验好。HLS 虽然兼容性最好,但 5 秒以上的延迟在监控场景里几乎没法接受。
5.2 WebRTC 为什么延迟低、需要什么条件
WebRTC 低延迟的秘密在于:它走的是 P2P 直连。go2rtc 把媒体流从摄像头拉过来之后,通过 WebSocket 信令把 SDP 信息交换给浏览器,然后协商出一条浏览器与服务器之间的 UDP 直连通道(或者通过 STUN/TURN 中继),媒体数据直接在这条通道上传输,不需要经过层层切片和 HTTP 请求。
用生活化类比:HLS 像是你去超市买袋装零食,超市先做好一批货等客户来拿,拿到手自然慢;WebRTC 像是直接从农场定了一条专线,菜一摘下来就直接送到你家,中间没有仓储环节。
WebRTC 需要满足几个条件:
- 浏览器和 go2rtc 之间 UDP 通道能建立。
- go2rtc 把 ICE 候选地址告诉浏览器,浏览器能找到可达的地址。
- 信令通道(WebSocket)稳定。
最后一个条件 go2rtc 已经处理好了,默认监听 1984 端口就能提供 WebSocket 信令。前两个条件,就需要配置 webrtc 节点。
5.3 candidates 配置:局域网与外网的关键差异
candidates 是 WebRTC 能连上的核心,也是 go2rtc 部署里最容易出问题的地方。它的作用是把 go2rtc 想要让浏览器连接的地址告诉浏览器。
局域网场景,我的配置是这样:
webrtc:
listen: ":8555"
candidates:
- 192.168.1.100:8555
这里的
192.168.1.100
是运行 go2rtc 的宿主机局域网 IP,
8555
是 webrtc 媒体端口。浏览器在局域网内访问时,SDP 里带着这个地址,UDP 端口一打通,视频就出来了。
如果你的 go2rtc 挂在公网服务器上,或者要通过公网远程观看,candidates 可以这样配:
webrtc:
listen: ":8555"
candidates:
- stun:8555
stun:8555
的意思是:先通过 STUN 服务器解析出公网 IP,然后用公网 IP + 8555 端口作为候选。go2rtc 内置了 STUN 客户端,默认使用
stun.l.google.com:19302
,你不需要自己搭 STUN 服务。
但这里有个隐藏条件:公网 IP 所在的路由器或云服务商安全组,必须把 8555 的 UDP(以及可能用到的 TCP)端口映射到运行 go2rtc 的机器上。否则浏览器向公网 IP 的 8555 端口发包,包根本到不了 go2rtc。
我一个朋友的案例很典型:他把 1984 端口映射到了公网,浏览器能打开管理界面,但 WebRTC 一直转圈。排查到最后才发现,8555 的 UDP 没映射,媒体通道根本不通。所以,如果你只映射了 TCP 端口,WebRTC 大概率要踩坑。
5.4 嵌入网页和 Home Assistant
go2rtc 每个流都有一个独立的播放地址,可以直接用 iframe 嵌入网页:
<iframe src="http://192.168.1.100:1984/living_room" frameborder="0" allow="autoplay; encrypted-media" style="width:100%;height:100%;"></iframe>
Home Assistant 的话,有两个方向:
- 仪表盘卡片里直接嵌入 iframe,指向 go2rtc 的流播放地址,简单粗暴。
-
使用 HA 的
generic camera平台,拉取 go2rtc 输出的 MJPEG 或 HLS 地址,把 go2rtc 流对象纳进 HA 的媒体体系里。
我目前用的是第一种,因为最低成本,改起来也快。
6. 实战踩坑记录:完整的排查链路与解决办法
6.1 浏览器连不上 WebRTC,画面一直转圈
现象:Web 界面里能打开流列表,MSE/HLS 能播放,唯独点 WebRTC 播放时一直显示连接中,最后超时。
我的排查链路:
- 打开浏览器开发者工具 → Console 和 Network 面板,看 WebSocket 有没有报错、ICE 候选是否生成。
-
如果看到类似
ICE failed, add a STUN server的日志,基本可以确定是 candidates 配置问题。 -
进入 go2rtc 日志,看有没有
webrtc: get answer error之类的记录。 - 确认当前容器用的是 host 网络还是 bridge 网络。如果 bridge,candidates 里就必须出现宿主机可达的 IP。
- 测试 8555 端口 UDP 是否可达。UDP 不好直接测,最简单的办法是在 go2rtc 配置里加一个 TCP 候选,并把 8555 TCP 映射出来,先用 TCP 跑通再回来排查 UDP。
我那次出问题的根因就是 Docker bridge 模式下没配置 candidates,SDP 给浏览器返回了一个 172.17.0.2 的内网地址,浏览器当然连不上。修改配置,加上宿主机 IP,重启容器,问题立刻消失。
6.2 摄像头流反复断开或启动后秒退
现象:流状态一直是 error,或者拉到一半画面黑了,日志里有
unauthorized
、
401
或
parse
字样。
排查链路:
- 先在 VLC 里直接打开同一个 RTSP URL,确认摄像头本身没挂、用户名密码没错。
-
检查 URL 里有没有
&、#、@、:等特殊字符,尤其是密码里的特殊字符。YAML 中务必用引号包住整个 URL。 - 确认摄像头没有被其他系统占用连接数上限。部分低端摄像头只允许 4-6 路并发 RTSP,连接满了就拒绝新的。
我碰到最坑的一次,是密码里有个
@
,结果 go2rtc 把
@
后面的内容当成了主机名的一部分,解析出来的地址是
192.168.1.66:554@...
这种鬼东西。排查了很久才发现是密码特殊字符的问题。后来我养成了习惯:所有摄像头 URL 一律加引号,密码一律做百分号编码。
6.3 H.265 摄像头在浏览器里黑屏,VLC 却能正常播放
现象:摄像头输出 H.265,go2rtc 日志显示流正常,WebRTC 和 HLS 都黑屏,但用 VLC 直接拉 RTSP 能播放。
原因很直接:主流浏览器对 H.265 的解码支持很有限,WebRTC 标准 Profile 里甚至没有 H.265 的强制要求,大多数浏览器遇到 H.265 直接拒绝渲染。go2rtc 默认走的是近乎零转码的转发路径,所以 H.265 的视频数据到达浏览器后,浏览器没有能力解码。
我的解决办法按优先级排序:
- 进入摄像头后台,把编码格式改成 H.264。这个最省资源。
- 如果摄像头只支持 H.265,就接入子码流,很多子码流默认就是 H.264。
-
实在不行再走 FFmpeg 转码:
ffmpeg:rtsp://...#video=h264。
第 1 种办法是治本,能让系统资源占用最少;第 2 种是曲线救国;第 3 种是兜底方案,除非前面的路都堵死了,否则不推荐,因为转码的 CPU 开销不小。
6.4 Docker Desktop 在 Windows 上启动失败
现象:安装 Docker Desktop 后,启动直接弹错误,提示
Virtualization support not detected
。
排查链路我已经在第 2 节讲过一部分,这里补充两个我见过的情况:
- 电脑本身是 Intel 平台但 BIOS 里 VT-x 被关掉了,很多品牌机出厂默认关闭,这是最常见的。
- Windows 10 的 Hyper-V 启用了,但 WSL2 没启用,Docker Desktop 用的 WSL2 后端一样起不来。
解决办法就是那三条命令:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
wsl --install
然后重启电脑。重启后如果还是不行,再把 Docker Desktop 的设置里“Use WSL 2 based engine”重新勾选一遍。
6.5 转码导致 CPU 居高不下
现象:接入两路 4K H.265 摄像头并强制转码 H.264 后,CPU 直接 100%,画面开始掉帧。
我的经验是先算清楚账:一路 1080p H.265 → H.264 软转码,大约需要 2 个 CPU 核心持续工作。4K 就更夸张。所以多路摄像头场景下,最优解永远不是“都转码”,而是“尽量不转码”。
具体方案:
- 接入子码流,1080p 子码流通常只有 1-2Mbps,浏览器直接播放 H.264 毫无压力。
- 需要看细节时才在主码流和子码流之间切换。go2rtc 支持一个流配置多个来源:
streams:
cam1:
- rtsp://user:pass@192.168.1.64:554/Streaming/Channels/101 # 主码流
- rtsp://user:pass@192.168.1.64:554/Streaming/Channels/102 # 子码流,主码流失败时自动切换
- 如果转码不可避免,可以考虑把 Intel QuickSync 设备挂进容器,让硬件参与转码。在 compose 文件里加:
devices:
- /dev/dri:/dev/dri
然后再在 ffmpeg 管道里开启硬件加速。这个配置依赖服务器 CPU 是否带核显,不同环境差异比较大,不确定的话还是以用子码流为主。
6.6 修改配置后服务不生效
现象:改了 go2rtc.yaml,保存后期待流列表自动变化,结果还是老样子。
那是因为 go2rtc 不会监视配置文件自动热加载(至少在稳定版里不会)。改配置后需要重启容器:
docker restart go2rtc
或者用 API 方式更新配置。Web 界面里的“配置”编辑页在保存时会自动触发重启,这一点比较方便,但直接改文件的话记得手动重启容器。
7. 进阶玩法:录制、联动、API 调用与安全建议
7.1 内置录制模块
go2rtc 自带录制功能,不需要额外挂一个录像服务。我的配置大致是这样:
record:
path: /media/recordings/video-{time}-{name}.mp4
size: 500M
timeout: 24h
然后在 streams 里给需要录制的流加
record:
前缀:
streams:
front_door:
- "record:rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102"
这样这个流在被拉流观看时就会录制,录制的文件按时间和流名称命名。注意,go2rtc 的版本迭代比较快,录制参数的确切字段名在不同版本里可能略有差异,配置完最好在日志里确认一下录制的实际行为。
录制文件放在
/media/recordings
目录,对应我前面 compose 里挂载的
./recordings:/media/recordings
。建议加一个 logrotate 或定时清理脚本,避免磁盘被录像塞满。我的做法是 crontab 里每天凌晨删掉 7 天前的录像。
7.2 与 Home Assistant / Frigate 联动
如果你用 Home Assistant,go2rtc 可以作为摄像头接入层,把各品牌摄像头的流统一成一种格式,再交给 HA 使用。HA 的仪表盘通过 iframe 嵌入 go2rtc 的播放页,或者用通用摄像头平台拉 MJPEG 流:
camera:
- platform: generic
name: Front Door
still_image_url: http://192.168.1.100:1984/front_door/mjpeg
stream_source: http://192.168.1.100:1984/front_door/webrtc
Frigate 的情况更特殊一点。新版 Frigate 内置了 go2rtc,专门用来处理摄像头 RTSP 流,减少反复建连的压力。如果你已经有 Frigate 在做人形检测,完全可以让 Frigate 接管摄像头流,再由它转发给 go2rtc,或者直接复用 Frigate 内置的 go2rtc 实例。这个组合拳下来,整个系统架构变得非常干净:摄像头 → go2rtc(拉流/转发) → HA(展示) + Frigate(检测)。
7.3 通过 API 动态增删流
go2rtc 提供 JSON API,适合在自动化脚本里动态添加或移除摄像头流。比如有人触发门铃事件时,临时把门铃摄像头的主码流切出来:
curl -X POST http://127.0.0.1:1984/api/streams \
-H "Content-Type: application/json" \
-d '{"name":"doorbell_main","url":"rtsp://admin:pass@192.168.1.68:554/Streaming/Channels/101"}'
查看当前所有流:
curl http://127.0.0.1:1984/api/streams | jq .
实际落地时,不同版本的 API 路径和请求体格式可能有一点点差别,但大方向是一致的。我不建议在没确认版本差异的情况下直接照搬,最好先访问
http://ip:1984/api
看接口文档的返回,再决定怎么调用。
7.4 暴露到公网之前的安全建议
go2rtc 默认没有认证机制,管理界面可以改配置、增删流。如果把它直接暴露到公网,相当于把整个流媒体系统的大门敞开了。我的建议:
- 不要让 go2rtc 的 1984 端口直接暴露公网。
- 通过反向代理(比如 Caddy)在前面加一层 Basic Auth:
camera.example.com {
reverse_proxy 127.0.0.1:1984
basicauth {
admin $2a$14$xxxxxxxxxxxx
}
}
- 8555 端口如果公网必须开放,只在路由器/安全组上映射到特定来源 IP,或者用防火墙限制访问范围。
- 定期备份 go2rtc.yaml,升级容器前先看更新日志。
- 如果只是局域网内使用,那就没什么好担心的,但注意不要让局域网内的陌生设备随意访问管理端口。
我目前的做法是 Caddy 反向代理加密码,公网只开 443 端口,其他端口一律不放出去。这样 WebRTC 走公网需要额外配置 TURN 中继,可能会增加一点延迟,但安全性高得多。
最后再分享一点我的实际使用心得
跑 go2rtc 这段时间,最大的感受是:这个工具把“摄像头接入”从一件需要反复试错的事情,变成了一件可以在半小时内完成的事。部署层面,它就是一个容器;接入层面,它就是一段配置;播放层面,它直接给你 WebRTC 低延迟流。稳定性也让我比较放心,目前连续跑了一个多月,中间只因为我主动升级重启过一次。
如果你手上正好有一堆品牌杂乱的摄像头,我的建议是:先从一台摄像头开始,手动配好 RTSP,确认 WebRTC 能低延迟播放,再逐步把其他摄像头加进去。等这个底子打好了,无论是接 Home Assistant、Frigate,还是做局域网监控墙,都会顺很多。还有一个容易被忽略的小技巧——每次改完配置,先在 Web 界面确认流状态,再回到 HA 或其他前端里测试,这样能快速区分问题出在 go2rtc 本身还是下游组件。走完一遍,你会发现所谓的“多协议流媒体平台”,其实并没有想象中那么复杂。
更多推荐

所有评论(0)