Docker部署go2rtc:从RTSP到WebRTC的摄像头低延迟监控方案
1. 先搞清楚 go2rtc 到底能做什么,再决定要不要部署
1.1 摄像头监控的“协议困局”,比你想象的还烦
玩摄像头的人应该都有这种体验:家里装了七八个不同牌子的 IPC,每个都有自己的 App,海康的用海康的客户端,TP-LINK 的用 TP-LINK 的 App,大华的又得装另一个。想统一看?不好意思,各家的协议和端口互相不认。就算你技术底子好,知道摄像头背后跑的是 RTSP 标准协议,真去接的时候也会被坑得体无完肤:有的摄像头 RTSP 路径是
/Streaming/Channels/101
,有的是
/live/ch0
,有的是
/h264/ch1/main/av_stream
,网上搜到的教程根本对不上。
好不容易用 VLC 能打开 RTSP 流了,你又发现新问题: RTSP 协议在浏览器里几乎无法直接播放 。Chrome、Firefox、Edge 从一开始就不支持 RTSP,手机上更不用想。你要是想做个网页端监控大屏,要么自己写个播放器搞 WebSocket 转换,要么转成 HLS 走 HTTP-FLV,要么用 WebRTC 低延迟方案——但这几个方案没有一个是开箱即用的,全都要自己搭服务、写代码、处理音视频转码。到这个阶段,你已经不是在装监控了,你是在写一套流媒体系统。
go2rtc 这个名字里的 2rtc 不是乱起的,它最核心的能力就是“转到 WebRTC”,也就是把各种协议的视频流统一“翻译”成浏览器能原生播放的 WebRTC 流。你不再需要安装任何插件,打开网页就能看,延迟还能控制在 300 到 500 毫秒以内。但它能干的不只是 WebRTC,RTSP、RTMP、HLS、MJPEG、MP4、ONVIF 这些主流协议它全都吃,输入输出都能转,等于在摄像头和你之间架了一座协议翻译桥。
1.2 go2rtc 的能力边界到底在哪里
我先给一个整体认知,方便你判断它适不适合你的场景:
| 能力 | go2rtc 支持的协议或功能 | 典型用途 |
|---|---|---|
| 拉流接入 | RTSP、RTMP、HTTP-FLV、HLS、MJPEG、ONVIF、本地文件、USB 摄像头 | 接各种品牌 IPC、NVR、DJI 无人机、树莓派摄像头 |
| 推流输出 | RTSP、RTMP、HLS、LL-HLS、MJPEG、WebRTC、MP4 | 网页直播、低延迟预览、录像归档 |
| 转码能力 | 内置 ffmpeg 调用、硬件加速(VAAPI、NVENC、QSV) | 摄像头不支持 H.264 时转码、多路并发降码率 |
| 动检集成 | 输出 WebRTC 流给 Frigate NVR 做 AI 检测 | Home Assistant 智能家居监控方案 |
| API 能力 | 完整的 HTTP REST API、流列表管理、Web UI | 二次开发、自动切换画面、状态监控 |
我自己的感受是,go2rtc 最舒服的地方不是某个单一协议有多强,而是“把一摊子杂事合成一个入口”。原来的方案是你得部署三个服务:一个负责拉 RTSP 转发,一个负责转 HLS,一个负责 WebRTC 网关,互相之间还要配置对接。go2rtc 是单个二进制文件,跑起来就全干了,配置也好写,就是一个 YAML。
适合谁来用? 你如果是折腾智能家居、准备搞 Frigate 做人体识别、或者想在公司/学校搭一个多摄像头统一监控页面的开发者,这套方案几乎是不二之选。你如果是完全不懂技术的用户,想给家里装监控,那用它可能稍微有点门槛,但你只要照着下面的配置抄,20 分钟内也能跑起来。
1.3 为什么选择 Docker 而不是裸机安装
go2rtc 官方也提供单文件可执行程序,直接下载就能跑,为什么我还要推荐 Docker 部署?原因有三条:
第一,
隔离和清理
。go2rtc 的依赖虽然不多,但它在运行过程中会拉 ffmpeg、可能会跑转码进程、还会创建配置和录像文件。放在 Docker 容器里,所有文件和进程都圈在容器内部,不污染宿主机。哪天不用了,
docker rm -f go2rtc
一条命令连文件残渣都清干净了。
第二,
升级和回滚容易
。go2rtc 迭代很快,作者经常加新协议、修 WebRTC 的 BUG。裸机部署时升级就是覆盖二进制,虽然也不难,但你要自己关注 release 版本;Docker 部署只要把镜像 tag 一换,
docker compose up -d
就完成了升级,想回滚就再换回旧 tag,一分钟内搞定。对于跑在生产环境里的监控系统来说,这个“可回滚”极其重要。
第三, 跨平台一致性 。你可以在 Windows 上开发测试、在 Linux 服务器上正式跑、在 NAS 上再放一份,只要都是 Docker 环境,行为完全一致。裸机部署的话,Windows 上还有各种路径和防火墙差异,调试成本高不少。
当然了,Docker 部署也有自己的坑——最常见的就是 Windows 下 Docker Desktop 本身起不来、国内拉镜像超时、容器网络模式选错导致 WebRTC 打洞失败。这一篇我会手把手把这些坑全踩一遍再填上,你照着走基本不会再兜圈子。
2. Docker 部署 go2rtc 实操:从环境准备到真正跑起来
2.1 开始前检查:虚拟化、Docker 版本、网络环境
先说环境检查。这一步很多人会跳过,但 80% 的安装报错都出在环境没准备好 。
如果你用的是 Windows,最常见的报错就是:
Docker Desktop failed to start because virtualisation support was not detected.
这个报错的本质是 Docker Desktop 依赖虚拟化技术来跑 Linux 容器,但你的机器可能没开启虚拟化,或者开启了但没被系统识别。检查顺序如下:
- BIOS 里开启 CPU 虚拟化 。重启机器进 BIOS(一般是开机按 Del 或 F2),找到 Intel Virtualization Technology(Intel VT-x)或 AMD SVM,确认是 Enabled。这一步必须做,软件层面怎么配置都绕不过硬件开关。
- Windows 功能里启用相应的虚拟化平台 。控制面板 → 程序和功能 → 启用或关闭 Windows 功能,勾选 Hyper-V 和 适用于 Linux 的 Windows 子系统 。如果你用的是 Windows 10/11 家庭版,里面没有 Hyper-V 选项,那就需要在 PowerShell(管理员)里执行命令安装 WSL2:
wsl --install
这个命令会同时启用 WSL2 和虚拟机平台,装完重启电脑。新版 Docker Desktop 是跑在 WSL2 后端的,所以这一步搞定后,上面那个 virtualisation support 的报错基本就消失了。
- 下载并安装 Docker Desktop 。从 Docker 官网下载稳定版,安装时保持默认设置即可。装完以后 Docker Desktop 会提示你先退出再重启,照做就行。
- 验证 Docker 是否可用 。打开 PowerShell 或 CMD,运行:
docker version
如果能看到 Client 和 Server 两段版本信息,说明 Docker 的守护进程已经正常跑起来了。这里要特别说明: Client 有信息不代表 Server 正常 ,很多人第一次运行只看到 Client 段,然后下面报 failed to connect to the docker api,这就是典型的前后端没连上。
docker 镜像源的问题也要提一下。国内直接拉 go2rtc 的官方镜像大概率超时,所以先配置 Daemon 镜像加速。打开 Docker Desktop 设置 → Docker Engine,在 JSON 配置里加一段:
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
保存之后 Docker Desktop 会自动重启 daemon。注意这个配置只影响从 Docker Hub 拉镜像的速度,不影响你运行任意镜像本身,所以放心配。
2.2 镜像选型:ghcr.io 还是 Docker Hub
go2rtc 的镜像有两个来源,官方推荐的是 GitHub Container Registry(ghcr.io),Docker Hub 上也有一份,但名字和渠道容易搞混。很多人第一次搜 go2rtc,直接
docker pull go2rtc/go2rtc
,结果拉到一个个人维护的镜像,行为跟官方不太一样,排查起来很痛苦。
正确的镜像地址有两个(任选其一):
-
ghcr.io/alexxit/go2rtc:latest -
docker.io/alexxit/go2rtc:latest
我这边的实际体验是:如果你的服务器在境外的云服务商,直接拉 ghcr.io 的镜像非常快;如果机器在国内,Docker Hub 路径配合上面的国内镜像加速反而更稳。所以我的建议是: Docker Hub 优先,失败再切 ghcr 。拉镜像的命令:
docker pull alexxit/go2rtc:latest
拉完可以用
docker images
确认镜像已经存在。这里有个小技巧:在拉镜像之前先
docker search go2rtc
看一眼,如果你看到的镜像名有一堆下划线数字后缀或者明显是个人账号,就别用了,绕过它。
2.3 最小化部署:一条 docker run 命令直接跑起来
我不想一开始就扔出一大段 docker-compose 配置把你砸晕,先来一条最简命令,把 go2rtc 跑起来看看是什么样子。打开终端执行:
docker run -d \
--name go2rtc \
--restart unless-stopped \
-p 1984:1984 \
-p 8554:8554 \
-p 8555:8555/udp \
-v /path/to/go2rtc/data:/config \
-e TZ=Asia/Shanghai \
alexxit/go2rtc:latest
如果你对 Docker 还不太熟,我给你逐个参数解释一下:
-
-d:后台运行容器,终端不阻塞。 -
--name go2rtc:给容器起名字,后面查看日志、进入容器都用这个名字。 -
--restart unless-stopped:容器异常退出或宿主机重启后,Docker 会自动拉起容器。监控服务最怕断电后没人去启动,这个参数必须加。 -
-p:端口映射。宿主机 1984 端口对应容器 1984(Web 管理界面),8554 对应 RTSP 输出端口,8555 UDP 对应 WebRTC 的媒体传输端口。注意 8555 必须用/udp标记,WebRTC 走的是 UDP,漏掉这个玩不了低延迟播放。 -
-v /path/to/go2rtc/data:/config:把容器里的配置文件目录挂载到宿主机,容器升级后配置不丢。/path/to/go2rtc/data要换成你自己的路径,Windows 下可以是D:/docker/go2rtc这种写法。 -
-e TZ=Asia/Shanghai:设置时区,防止日志时间和录像文件名差 8 个小时。
跑完以后打开浏览器访问
http://localhost:1984
,你会看到一个简单的 Web UI。第一次启动时它也允许直接通过界面添加摄像头,非常友好。不过在实际使用中,我更推荐你直接用配置文件来定义,因为界面虽然方便,但配置多了以后全靠手点很烦,而且不可回溯。
2.4 进阶部署:docker-compose 完整配置示例
当你决定正式在生产环境部署 go2rtc,或者要同时管理多个容器(比如 go2rtc + Frigate + Home Assistant),就别再用 docker run 了,上 docker-compose。它会帮你把配置写进文件、版本化管理,以后迁移机器只需要把目录带走。
下面是我验证过的 compose 文件,你可以直接复制:
version: "3.8"
services:
go2rtc:
image: alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
network_mode: host
volumes:
- ./go2rtc/config:/config
environment:
- TZ=Asia/Shanghai
等等,我用了
network_mode: host
而不是之前的
ports
映射。这是一个重要的取舍点,我专门展开讲一下。
host 网络模式 的意思是容器直接使用宿主机的网络栈,不经过 Docker 的虚拟网桥。这对 go2rtc 有两个好处:
第一,WebRTC 需要在局域网内做候选地址协商。默认桥接模式下,容器内部看到的 IP 是 172.x 之类,摄像头和浏览器如果直接访问这个内网地址,根本连不上。用 host 模式后容器直接用宿主机的局域网 IP,协商和连通都顺理成章。
第二,go2rtc 打洞穿透时需要知道自己的公网 IP,桥接模式下 Docker 每次重建容器都可能换 IP,非常影响后续访问。
代价是什么?host 模式下你没法像桥接模式那样在同一台机器上跑多个 go2rtc 各占不同端口,也没法依赖 Docker 的防火墙隔离。 如果你只是在家里用、同一台机器不会跑多个 go2rtc,那 host 模式是更优解;如果你是在云服务器上跟别的东西共享一台机,建议回到桥接模式配合显式端口映射 。
如果你用的是桥接模式,完整示例是这样的:
version: "3.8"
services:
go2rtc:
image: alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
ports:
- "1984:1984"
- "8554:8554"
- "8555:8555/udp"
volumes:
- ./go2rtc/config:/config
environment:
- TZ=Asia/Shanghai
在 compose 文件所在目录下执行:
docker compose up -d
然后用
docker compose logs -f go2rtc
实时看日志。你会看到 go2rtc 打印出监听地址和启动信息。如果是下面这种输出,说明服务已经就绪:
INFO: 1984/tcp listening on [::]:1984
INFO: 8554/tcp listening on [::]:8554
INFO: 8555/udp listening on [::]:8555
3. 接入摄像头与多协议输出:把流“翻译”成任何端都能看的格式
3.1 RTSP 接入与参数调优
服务跑起来了,接下来最重要的事:把摄像头接进来。go2rtc 的配置文件默认是
/config/go2rtc.yaml
,你打开后在里面定义一个自定义流名,然后指向摄像头的 RTSP 地址,格式如下:
streams:
front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101
backyard: rtsp://admin:password@192.168.1.101:554/live/ch0
garage: rtsp://admin:password@192.168.1.102:554/h264/ch1/main/av_stream
保存后不要重启容器,用下面的命令重新加载配置:
docker exec go2rtc kill -HUP 1
这里解释一下原理:go2rtc 收到 SIGHUP 信号后会热加载配置,不会中断正在播放的流。这个细节很多人不知道,每次改配置都
docker restart go2rtc
,结果所有正在看的画面全部卡顿几秒。你在生产环境里至少应该先用 SIGHUP,确认新流也能拉起来再决定要不要重启。
改完配置后,打开 Web UI 的 streams 页面,你应该能看到这三个流以及它们的延迟状态和帧率。如果某个流一直显示 404 或者连接失败,大概率是 RTSP 的路径不对或者摄像头限制同时连接数量。这时候可以先用 VLC 验证一下原始地址能不能播放,排除摄像头自身的问题,再来排查 go2rtc。
参数调优
:go2rtc 拉取 RTSP 流默认用的是 UDP 传输,但在 Wi-Fi 或跨网段的场景下 UDP 丢包会导致画面花屏、频繁重连。我实际遇到不止一次,有线连接好好的,换成 Wi-Fi 摄像头就卡成一帧一帧。解决方案是在流地址后面加
#tcp
强制走 TCP 传输:
streams:
front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp
或者归一配置强制所有 RTSP 走 TCP:
rtsp:
transport: tcp
有人会担心 TCP 传输重新组包会不会变大延迟,实际上在局域网内这个延迟差异微乎其微,但稳定性的提升是肉眼可见的。我的习惯是:有线的用默认 UDP,无线的强制 TCP。
3.2 ONVIF 自动发现与免配置接入
如果你家里不止三五个摄像头,而是一二十个,手动一个个写 RTSP 地址会非常痛苦,而且不同品牌路径规则都不一样,写错一个就要排查半天。go2rtc 支持 ONVIF 协议,你可以把 RTSP 地址替换成 ONVIF 的设备地址,格式:
streams:
cam01: onvif://admin:password@192.168.1.108
配置里这样写后,go2rtc 会主动向该设备的 ONVIF 服务发送探测请求,自动获取摄像头支持的媒体文件、编码格式、RTSP 路径。这对海康、大华、TP-LINK、瑞尼等支持 ONVIF 标准的设备通常有效。
ONVIF 自动接入有一个注意点: 部分厂家的摄像头为了封闭生态,会在 ONVIF 协议里故意不返回正确的 RTSP URL,或者需要额外认证 。遇到这种情况,你就只能老老实实通过录像机的网络设置页面去查流地址了。
我认为 go2rtc 做这类自动发现最有价值的地方还是 Web UI 里的“自动发现”功能——它会扫一遍局域网,把可用的 ONVIF 设备全部列出来,然后提供一键添加。对于第一次部署、又不想慢慢敲配置的人来说,这个入口比 YAML 更直观。
3.3 用 WebRTC 实现低延迟网页播放
go2rtc 最强的杀手锏就是 WebRTC 输出。
用浏览器打开
http://localhost:1984/webrtc.html
,输入你要播放的流名(比如 front_door),点 play,它就能在浏览器里直接播放。这里没有插件、没有额外的播放器,WebRTC 是浏览器原生的能力,视频编码采用 H.264 时浏览器能硬解,CPU 占用极低。
WebRTC 的超低延迟来自它使用 UDP 作为传输层,摒弃了 HLS 的 m3u8 切片机制。多说一点原理:传统的 HLS 把视频切成几秒一个的 ts 文件,通过 HTTP 下载,这个切片-下载-拼接的过程至少得有几秒延迟,慢的时候 10 秒以上。WebRTC 则是一次性建立点对点 UDP 通道,视频帧实时传,不做切片存储,延迟压缩到 300 到 500 毫秒。在监控场景里这个差别很致命,比如看门口有人送快递,你要的是实时看到人走到哪了,而不是看到一个 10 秒前的画面。
如果你要在自己的网页中集成 go2rtc 的播放,官方给的方案就是 WebRTC。大致流程是:
- 从 go2rtc 的 API 获取 SDP offer。
-
用浏览器原生
RTCPeerConnection建立连接。 -
把远端流绑到
<video>标签上播放。
官方文档里有一个简化方法:用 go2rtc 内置的
webrtc.html
页面 URL 参数直接指定流名,比如这样访问就是直接播放某个摄像头:
http://localhost:1984/webrtc.html?src=front_door
如果延迟要求没那么高、只是想做一个兼容性极好的网页直播(比如给外部用户看),那也可以直接用 HLS。go2rtc 默认会为每条流自动生成对应的 HLS 地址:
http://localhost:1984/hls/front_door.m3u8
HLS 的优势是所有设备都能播,iOS 的 Safari 原生支持,第三方播放器如 VLC、PotPlayer 都能放,代价是延迟高。所以在同一个平台上,你可以让内网的人走 WebRTC 看实时画面,外网用户走 HLS 看低精度版本,两者并存互不冲突。
3.4 与 Frigate 和 Home Assistant 联动(扩展能力)
如果你的目标不只是“在网页上看监控”,还想要人形检测、车辆识别、通知推送,那 go2rtc 通常会搭配 Frigate NVR 使用。go2rtc 和 Frigate 的联动逻辑是这样的:
- go2rtc 负责拉取摄像头的 RTSP 流,并把 WebRTC 视频流输出到浏览器。
- Frigate 通过 go2rtc 的 REST API 订阅同一路流,从中抽帧做 AI 检测。
Frigate 在配置里支持直接声明
go2rtc
的流地址,比如 Frigate 的 config.yml 里这样写:
go2rtc:
streams:
front_door: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp
配合 Home Assistant 的话,Hass 里的
stream
组件可以直接访问 go2rtc 的 HLS 地址,这样智能家居管理界面、手机 App、NVR 检测、浏览器实时预览全部串在一起,一个 go2rtc 不仅仅是个流媒体中转站,而是整个家庭监控系统的中枢。这个闭环搭好之后,你以后加摄像头就是改一行 YAML 的事。
4. 常见问题与排查技巧实录
4.1 Docker Desktop 启动失败:virtualisation support 与 failed to connect 类报错
这两个报错占了我帮助他人排障的六成以上。先说 virtualisation support not detected 。除了前面提到要在 BIOS 开 VT-x/SVM、在 Windows 功能里启用 Hyper-V 和 WSL2 之外,还有一种可能:你已经开了 Hyper-V,但 Docker Desktop 用的 WSL2 内核没更新。执行:
wsl --update
这个命令会把 WSL2 内核更新到最新,很多奇奇怪怪的启动失败都能解决。理论上讲,WSL2 发布这么多年,新的 Windows 版本基本都默认携带并启用了虚拟化平台,所以如果装好后重启一次还不行,多半是 BIOS 没开对,或者机器比较老不支持虚拟化。老电脑的话,硬件层面的限制软件救不回来,只能换新机器或用云服务器。
再说
failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen
。这个报错通常出现在 Windows 下执行
docker ps
时。原因一般是 Docker Desktop 的前端界面没起来,或者 WSL2 内核坏了。按以下顺序排查:
- 确认 Docker Desktop 图标出现在系统托盘,且状态是 “Engine running”。
- 如果显示 “Docker Engine Stopped”,点一下重启引擎,等一两分钟。
-
如果重启不行,在 PowerShell 执行
wsl --shutdown,把 WSL2 彻底停掉再重新启动 Docker Desktop。 - 最后“核弹”方案是退出 Docker Desktop,在设置里点 “Clean / Purge data”,然后重装。这个操作会清掉所有镜像和容器,只适合最后使用。
4.2 镜像拉取超时或过慢
go2rtc 镜像本身才几十 MB,拉取不应该很慢。如果你发现
docker pull alexxit/go2rtc
卡住或报 timeout,多半是因为你机器上直连 Docker Hub 的网络质量差。解决思路很直接:配置 registry-mirrors(前面已经写过),或者干脆用 ghcr.io 源替代。需要提醒的是,很多网上流传的镜像加速地址时效性很差,今天能用明天就失效了。我验证过还能用的加速地址包括
https://docker.1ms.run
、
https://docker.xuanyuan.me
等,但你自己用之前先
docker pull hello-world
试一下,有效再继续。
4.3 RTSP 接入了但画面一直缓冲或黑屏
这类问题在排查上最耗时,但原因通常是下面几个:
| 症状 | 主要原因 | 解决方案 |
|---|---|---|
| 流一直缓冲、无法播放 | RTSP 走 UDP 丢包严重 |
在流地址后加
#tcp
强制 TCP
|
| 画面有声音没图像 | 摄像头输出 H.265/HEVC,但你的播放器/浏览器不支持 | 在 go2rtc 里配置 ffmpeg 转码为 H.264 |
| 能看到第一帧但很快卡死 | 摄像头限制同时连接数,被其他客户端占用 | 降低摄像头端最大连接数限制,或给 go2rtc 分配独立账号 |
| 在线但延迟越来越高 | go2rtc 处理不过来,或源端码率过高 |
在 go2rtc 中加
ffmpeg
转码降码率
|
关于 H.265 转码,这是绕不开的一个话题。现在很多新摄像头默认输出 H.265,压缩率高一倍,但 WebRTC 和浏览器对 H.265 的支持并不统一,Chrome 桌面版能解部分硬件的 H.265,Safari 可以,Android 上很多浏览器不行。最省心的做法是在 go2rtc 中配置一个 ffmpeg 转码流,强制转成 H.264。配置示例:
streams:
front_door:
- rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101#tcp
- ffmpeg:front_door#video=copy#video=h264#hardware
我来解释一下这串参数的含义:
video=copy
表示尽量直接复制原始视频编码,不重新编码;
video=h264
表示输出目标是 H.264;
hardware
表示优先尝试使用硬件加速。这个方案是兜底性质的——go2rtc 会优先尝试用直连方式去拉流,如果发现源是 H.265 而播放端不支持,就会自动转码。唯一需要注意的是硬件加速需要容器能访问 GPU 设备,如果是在 Docker Desktop 里跑 Windows 容器,这一步可能受限,那就只能纯 CPU 转码,建议降低同时播放的路数。
4.4 局域网能看、外网/web端不能看(WebRTC 候选地址问题)
这是 go2rtc 从新手到进阶最容易踩的坑。你的 go2rtc 跑在家里的 NAS 上,内网用网页看一切都正常,但你把
http://你的公网IP:1984
暴露出去,打开网页后画面永远起不来。原因是 WebRTC 在建立连接时,会交换“候选地址”(ICE candidates),浏览器拿到的候选地址里只有内网 IP,没有公网 IP,它不知道应该往哪里发数据包。
解决方法是给 go2rtc 指定公网候选地址或让它自动探测。在 go2rtc 配置里加
webrtc
段:
webrtc:
candidates:
- 你的公网IP:8555
如果公网 IP 是动态的,可以用域名拼接端口,go2rtc 会自动解析:
webrtc:
candidates:
- yourdomain.com:8555
然后记得在路由器/防火墙里放行 UDP 8555 端口,并且把该端口转发到 go2rtc 所在主机。这个端口转发必须同时覆盖 TCP 和 UDP,很多人漏了 UDP 转发,导致 WebRTC 永远握手失败。
但这里我要多说一句安全建议:
不要直接把 go2rtc 的 Web UI 暴露到公网
。默认情况下,go2rtc 没有身份认证,谁能访问你的 Web UI 谁就能查看所有摄像头画面,还能通过 API 查看流配置。如果你确实需要远程观看,正确姿势是配置 go2rtc 内置的登录认证,或者在它前面加一个带身份认证的反向代理(nginx 的
proxy_pass
+ Basic Auth 比 go2rtc 自带密码更灵活),再或者用 Tailscale 这类组网方案把设备放进虚拟内网。公网裸奔的监控系统等于把大门钥匙挂在门把手上,这种低级错误不该犯。
4.5 容器老挂掉或者重启后配置丢失
监控服务追求的首先是稳定。docker-compose 里
restart: unless-stopped
已经保证了容器出问题后会被自动拉起,但如果你发现容器的配置改完重启后“丢失”了,那多半是挂载卷路径写错了。
先明确挂载规则:
-v /宿主机路径:/config
的冒号右边是容器内路径,不能改,是 go2rtc 固定的配置目录;冒号左边是宿主机路径,由你指定。如果你把宿主机路径指向了一个错误目录,容器里写出的配置就会“消失”(实际上写去了别的地方)。排查方法很简单:
docker inspect go2rtc --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}'
这条命令会打印容器实际挂载的宿主机路径和容器内路径,对比一下就知道是不是路径写错了。另外,容器日志也会同步打印配置目录位置,启动时看到类似
config: /config
,如果它显示的是容器内默认目录而不是挂载卷,说明你的
-v
参数没生效。
4.6 其他一些值得留意的细节
时间同步
:如果你的 go2rtc 之后还要做录像或者和 Frigate 联动,时间不准很致命。Docker 默认时区是 UTC,所以运行容器时一定记得设置
-e TZ=Asia/Shanghai
。我踩过一次亏,录像文件时间戳比实际慢 8 个小时,排查了半天才发现是时区问题。
CPU 与内存 :go2rtc 本身非常轻量,空闲时内存占用不到 30MB,CPU 几乎为 0。但一旦多路同时转码,CPU 会飙升。建议在 docker-compose 里限制资源:
deploy:
resources:
limits:
cpus: "2.0"
memory: 1G
这样即使某路流卡住或者转码任务失控,也不会拖垮宿主机上其他服务。
日志轮转 :时间久了容器日志会变得很大,Docker Desktop 默认把容器日志写到宿主机,体积膨胀可能会把磁盘挤爆。建议在 compose 里限制:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
这个配置让单个日志文件最大 10MB,最多保留 3 个,超过就滚动删除,对排查问题也够了。
5. 扩展玩法:把 go2rtc 的价值再放大一圈
5.1 把 go2rtc 变成“多协议网关”,而不是仅仅接摄像头
go2rtc 的输入源远不止 RTSP 摄像头。它可以拉取任意 HTTP/HTTPS 视频流,也可以处理 RTMP。举个例子,你想把某品牌无人机或运动相机的 RTMP 直播流接到统一监控台上看,go2rtc 里加一行配置就行:
streams:
drone_fpv: rtmp://your-stream-server:1935/live/drone#video=copy#audio=copy
它还可以接入本地文件当作一个循环播放的“虚拟摄像头”,这对测试很有用:
streams:
test_loop: /media/test.mp4
如果你在开发前端,需要一路稳定的测试视频源来调播放器,这个功能省去了很多装假流的麻烦。go2rtc 会把本地文件当作一路流,支持循环播放,WebRTC、HLS 输出方式都完全一样。
5.2 自己搞一个微型监控墙
go2rtc 的 Web UI 本身就能根据流名集中展示画面,但如果你想做一个更定制化的监控大屏,可以直接在一个 HTML 页面里通过 go2rtc 的 WebRTC API 连接多路流。好处是各路的延迟互不影响,也不会像 HLS 多路同时拉取那样把带宽吃满。我试过在 4K 带鱼屏上同时拉 16 路 WebRTC 摄像头流,CPU 占用只有个位数,非常流畅,这是 HLS 方案做不到的。
具体做法是,在页面上为每一路走一次 go2rtc 的 RTCPeerConnection 握手,拿到 MediaStream 后绑到不同的
<video>
元素。go2rtc 同时支持多个并发的 WebRTC 连接,所以再多路也扛得住。
5.3 高可用部署和备份配置
生产环境如果想要更高可用性,可以给 go2rtc 容器加健康检查:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:1984/api/streams"]
interval: 30s
timeout: 5s
retries: 3
这样 Docker 会定期探测 API 是否可用,如果连续 3 次失败就标记容器不健康。配合
restart: unless-stopped
,即使 go2rtc 进程由于某种原因僵死,Docker 也能在 30 秒内把它拉起来,监控中断时间不会太长。
备份配置的教训是我用真金白银换来的。有一次我调试完没做备份,第二天手滑把
/config
目录改了,结果所有摄像头流配置全丢了,只能重新一个个添加。从那以后,我每次改完 go2rtc.yaml,都会顺手备份一次:
cp /path/to/go2rtc/config/go2rtc.yaml /path/to/go2rtc/config/go2rtc.yaml.bak
如果整个目录都用 git 管理就更好了,加一行配置都能查到改动历史,比手动备份省心得多。
我自己现在家里的一套监控系统就是 go2rtc 做核心,总共接入了 12 路摄像头,5 路走 WebRTC 实时预览,7 路在 Frigate 里做 AI 检测,全部跑在一块 8 核小主机上,CPU 常年稳定在 15% 以下。这个组合最大的好处是“通吃”——不管摄像头牌子多杂、协议多乱,只要它能出 RTSP 流,go2rtc 就能把它纳入统一的管理体系,你的网页、手机、智能家居平台都能看到同一路画面。
如果你刚开始接触这套东西,我的建议是先从一条
docker run
命令开始跑通一个摄像头,再慢慢加配置、加流、加协议,最后过渡到 compose 编排。不要一上来就想着搞一个 20 路摄像头的完整系统,那只会让你被各种小问题淹没。按这篇的步骤来,你的第一路 WebRTC 摄像头流大概在半小时内就能看到。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)