go2rtc摄像头接入教程:Docker部署实现RTSP转WebRTC低延时播放
玩摄像头或者智能家居的朋友,大概率都遇到过这种场景:手里一台支持 RTSP 的摄像头,想在厂商 APP、Home Assistant、网页预览之间来回切换,结果要么被厂商协议锁死,要么浏览器不支持 RTSP 播放,折腾半天只能在手机上装专用客户端。其实解决思路很简单:在中间加一个“会翻译的”流媒体网关,把所有摄像头的流统一收进来,再按需输出成浏览器能直接看的格式。这个网关,我用的是 go2rtc,跑在 Docker 里,干净、好升级、迁移也方便。
go2rtc 是一个用 Go 语言写的轻量流媒体中转服务,名字里的“rtc”主要指 WebRTC,但它实际支持的协议远不止这些。它能接收 RTSP、RTMP、HLS、HTTP-FLV、MJPEG 等常见协议输入,也能把它们互相转换输出,最常用的场景就是把摄像头的 RTSP 流转成 WebRTC,让浏览器直接低延时播放。配合 Docker 部署,不用在宿主机装一堆依赖,一条命令就能跑起来,特别适合家庭实验室、小型监控项目、Home Assistant 玩家,以及需要在多个系统之间打通视频流的开发者。
下面我会从选型思路、Docker 环境准备、配置写法、部署步骤、问题排查到长期运行经验,完整过一遍我自己的落地过程。
1. 为什么要用 go2rtc 做摄像头接入
1.1 传统取流方式到底卡在哪
摄像头本身通常只提供 RTSP 或 RTMP 流,浏览器默认不支持这些协议。以前想做网页预览,主流方案是跑一个 Nginx,然后把 RTSP 拉过来转成 HLS,或者用 FFmpeg 推 RTMP 再让 Flash 播放。麻烦点很多:
- 浏览器兼容问题:RTSP 需要插件或特定客户端,HLS 有 5 到 10 秒延迟,不适合需要实时操作的场景。
- 协议转换链路太长:RTSP 到 HLS 再到播放器,中间涉及 FFmpeg 拉流、切片、Nginx 分发,配置复杂,出问题不知道查哪一环。
- 厂商锁定:很多摄像头的私有协议只能在自家 APP 里看,想把视频流接到自己的平台很麻烦。
- 多路摄像头接入时,手工维护一条条拉流命令,痛苦。
go2rtc 解决的核心问题,就是把这些协议打通。它默认不做转码,只做“转封装”,也就是把视频流从一个容器格式搬到另一个容器格式,所以 CPU 占用很低。摄像头原本是什么编码格式,到浏览器端还是什么格式,画面质量不会因为中转而明显下降。
1.2 go2rtc 的核心能力与协议矩阵
go2rtc 最吸引我的地方,是它对“输入协议”和“输出协议”的处理都很灵活。输入侧,它原生支持 RTSP、RTMP、HLS、HTTP-FLV、MJPEG 等;输出侧,支持 WebRTC、HLS、MJPEG、RTSP、RTMP 等。也就是说,一个源可以同时让不同端去拉,互不影响。
它还有一个很实用的机制:当源是 RTSP 时,go2rtc 会优先尝试用原生 RTSP 客户端去拉流,拉不到或格式不兼容时,它会自动调用内置的 FFmpeg 去拉流。这个“自动回退”机制让它在面对杂牌摄像头时格外省心,不需要你手动去纠结到底用哪种方式取流。
WebRTC 是它最值钱的能力。WebRTC 延迟可以做到几百毫秒,浏览器原生支持,不需要装插件。你可以直接在网页里打开摄像头画面,拖动、监控、做简单的弹窗告警,体验接近本地播放。这在智能家居方案里非常香。
1.3 和传统流媒体服务器相比,选它值不值
如果你已经在用 Nginx-RTMP、MediaMTX、ZLMediaKit 这类方案,不一定非要换。但如果是新项目,尤其以摄像头接入为主,go2rtc 的轻量特性很加分。我自己对比下来大概是这种感觉:
| 对比项 | go2rtc | Nginx-RTMP | MediaMTX | ZLMediaKit |
|---|---|---|---|---|
| 部署复杂度 | 低,单容器 | 中等,需要编译模块 | 低,单二进制/容器 | 偏高,依赖较多 |
| 浏览器直连 | 强,WebRTC | 弱,需配合 HLS/FLV | 支持 WebRTC | 支持较多协议 |
| 摄像头 RTSP 回源 | 原生支持 | 需要 FFmpeg 中转 | 原生支持 | 原生支持 |
| 资源占用 | 很低 | 取决于 FFmpeg 进程数 | 很低 | 中等 |
| 多路管理 | 配置简单 | 配置稍繁琐 | 配置简单 | 配置中等 |
我也用过 MediaMTX,它同样很好用,但 go2rtc 的配置风格更偏向智能家居场景,尤其是跟 Home Assistant 集成时,插件生态更成熟。如果你只是想快速把摄像头流接到网页或 HA 里,go2rtc 是上手成本比较低的一条路。
2. Docker 环境准备与容器选型
2.1 宿主机环境要求
go2rtc 本身很轻,对宿主机要求不高。树莓派、软路由、NUC、旧笔记本、云服务器都能跑。我建议至少 1 核 CPU、512 MB 内存,硬盘给容器留 1 GB 以上空间。如果是多路 4K 摄像头,内存和带宽要相应加大,但 go2rtc 不做转码,CPU 压力不大。
Docker 的安装方式这里不展开,但有几个关键点要确认:
- Linux 上建议直接用官方脚本安装 docker-ce,不要用系统源里的老版本。
- Windows 上使用 Docker Desktop,需要开启 WSL2 或 Hyper-V。安装前先检查 BIOS 里的虚拟化是否打开,否则 Docker Desktop 启动会失败。
- macOS 上 Docker Desktop 比较省心,内存分配建议至少 4 GB。
在 Linux 服务器上,装完 Docker 后记得把当前用户加入 docker 组,避免每次 sudo。这会减少很多后续操作上的小摩擦。
2.2 网络模式怎么选
Docker 跑 go2rtc,网络模式是第一个需要想清楚的点。官方推荐的是 host 模式,尤其在需要 WebRTC 的场景下。原因很简单:WebRTC 不只是 HTTP 协商,还涉及 UDP 端口的动态协商,容器端口映射在这种场景下容易丢包、绕路。
我用 host 模式跑了很久,好处是端口直接监听在宿主机上,不用手动映射,延迟低;坏处是端口占用要自己数清楚。如果你在一个环境里同时跑多个要占用端口的服务,比如 ZLMediaKit、Nginx,需要把端口规划好。默认情况下 go2rtc 会用:
- 1984:Web 管理页面和 API
- 8554:RTSP 输出
- 8555:WebRTC 的 UDP/TCP 端口
如果这些端口跟你现有服务冲突,可以在配置里改。
如果你的 Docker 环境在 Windows 或 macOS 的虚拟化网络里,host 模式可能不是真正意义上的宿主机网络,建议直接用 bridge 模式加端口映射。先确认几点:1984 要映射出去管理,8554 要留给 RTSP 拉流,8555 是 WebRTC 通信端口,UDP 也需要映射。
2.3 镜像选择与目录规划
go2rtc 官方提供了 Docker 镜像,镜像名是
ghcr.io/alexx2per/go2rtc
。注意没有 Docker Hub 官方同名镜像,网上有些第三方镜像不一定同步最新版。我一般只认 GitHub Container Registry 的官方镜像。
目录规划上,建议给 go2rtc 建一个专用目录,比如:
mkdir -p /opt/go2rtc/config
cd /opt/go2rtc
配置文件和录像、日志分开存放。go2rtc 支持把录像写到自定义目录,后续做长时间存储时,最好单独挂一个磁盘,避免系统盘被撑满。
如果打算长期使用,我还会顺手把时区、日志轮转、自动重启这些事在 compose 文件里处理好,省得跑一段时间后容器挂了没人管。
3. 配置 go2rtc 实现多协议接入
3.1 配置文件结构说明
go2rtc 默认会在工作目录读取
go2rtc.yaml
。它的配置结构很简单,核心就几个段:
api
、
rtsp
、
webrtc
、
streams
。打开配置文件,第一眼不会觉得吓人。
一个最简配置长这样:
log:
level: info
api:
listen: ":1984"
rtsp:
listen: ":8554"
webrtc:
listen: ":8555"
streams:
cam1: rtsp://admin:password@192.168.1.64:554/stream1
streams
是最关键的部分,每行是一个流源的名称和地址。
cam1
是流名称,后面是取流地址。管理页面打开
http://宿主机IP:1984
,就能看到这个流,点击即可预览。
3.2 接入 RTSP 摄像头
RTSP 是摄像机最通用的协议。配置里直接写地址就行:
streams:
garage: rtsp://admin:123456@192.168.1.100:554/ch1/main
yard:
- rtsp://admin:123456@192.168.1.101:554/h264
有些摄像头有主码流和子码流之说。主码流清晰度高,适合本地存储;子码流分辨率低,适合长时间预览和移动端。我通常会把同一个摄像头的主码流和子码流都配进去,名称带上
_main
和
_sub
,方便后续切换:
streams:
front_main: rtsp://admin:pass@192.168.1.50:554/Streaming/Channels/101
front_sub: rtsp://admin:pass@192.168.1.50:554/Streaming/Channels/102
有些摄像头的 RTSP 地址需要参考厂商文档,但大概结构都类似:IP 地址、端口、用户名、密码、通道号、编码类型。如果拿不准,可以用 VLC 先测试一下这个 RTSP 地址在电脑上能不能播放,能播再写进 go2rtc。
3.3 接入 RTMP / HLS / HTTP-FLV 流
go2rtc 不只接摄像头。如果你有一个无人机、直播软件或其他流媒体服务,输出的是 RTMP 或 HLS,也能把它当成一个流源收进来。例如:
streams:
live_rtmp: rtmp://192.168.1.200/live/room1
hls_news: http://192.168.1.200:8080/live/playlist.m3u8
flv_source: http://192.168.1.200:8080/live/stream.flv
这里要注意,go2rtc 原生支持 RTSP 和某些协议,但遇到 HLS 或一些复杂源时,会自动调用内置 FFmpeg 去转。自动调用的好处是省心,代价是 CPU 会高一些,因为 FFmpeg 解析 HLS 切片时需要更多的处理。实际用下来,一两个 HLS 源没问题,大量源同时接入时,建议评估 CPU 负载。
接入这些外部流之后,你仍然可以通过 go2rtc 的 WebRTC 接口输出到浏览器。也就是说,它可以当作一个“协议汇聚点”,把多路流统一管理起来。
3.4 启用 WebRTC 与浏览器低延时播放
WebRTC 是 go2rtc 的重点能力。配置也很直观:
webrtc:
listen: ":8555"
candidates:
- 192.168.1.10:8555
candidates
是给 WebRTC 协商用的地址。如果只在局域网内访问,填宿主机局域网 IP 就行。如果要从公网访问,这里要填公网 IP 或域名,同时还要在路由器上把 8555 的 UDP 转发到宿主机。不知道公网 IP 的,可以临时用域名解析代替,比如:
webrtc:
listen: ":8555"
candidates:
- stream.example.com:8555
浏览器播放地址就是:
http://宿主机IP:1984/api/webrtc?src=garage
把这个地址嵌到一个网页的
<video>
标签里,或者用支持 WebRTC 的播放器,就能实现秒开级预览。相比 HLS 动辄几秒的延迟,WebRTC 的体验好了不止一个档次。
4. 通过 docker-compose 正式部署
4.1 编写 docker-compose.yml
我推荐用 docker-compose 管理 go2rtc,理由很简单:配置、重启、升级、清理都方便。下面是我实际在用的 compose 文件,去掉了一些无关内容后保留核心结构:
version: "3.8"
services:
go2rtc:
image: ghcr.io/alexx2per/go2rtc:v1.9.9
container_name: go2rtc
restart: unless-stopped
network_mode: host
environment:
- TZ=Asia/Shanghai
volumes:
- ./config:/config
command:
- -c
- /config/go2rtc.yaml
版本号建议固定,不要用 latest。latest 的好处是自动更新,坏处是某次升级可能改变默认行为,导致配置突然不生效。我踩过几次坑,后来都改成固定版本,需要升级时再手动改版本号重启。
4.2 启动与验证
写好 compose 文件后,启动命令就两条:
docker compose up -d
docker logs -f go2rtc
日志里能看到启动信息,以及每个流源的连接状态。如果摄像头地址填错了,日志里会出现
connection refused
或
unauthorized
之类的提示,可以直接对照排查。
启动完成后,打开浏览器访问:
http://宿主机IP:1984
页面里能看当前配置的所有流。点击流的名称,浏览器会调用 WebRTC 去拉流,如果画面能出来,说明核心链路已经通了。
我还会用命令行验证 RTSP 输出是否正常:
ffprobe rtsp://宿主机IP:8554/garage
如果 ffprobe 能解析出视频流,说明 go2rtc 的 RTSP 转发端口也正常工作了。这一步适合后续接 NVR 或录像软件时确认。
4.3 对外开放访问的安全策略
go2rtc 的 Web 页面和 API 默认没有强鉴权,直接暴露到公网风险很大。我把 go2rtc 跑在内网,需要外部访问时,通过反向代理加一层 Basic Auth 或更复杂的认证。
Nginx 反向代理配置示例:
server {
listen 8443 ssl;
server_name stream.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:1984;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
WebRTC 的 UDP 端口不能像 HTTP 一样简单反代,还是需要在路由器或防火墙上按实际 UDP 端口去转发。安全原则很简单:1984 端口不要裸奔到公网,8555 的 UDP 只对需要的来源开放。
5. 常见问题与排查技巧实录
5.1 画面卡顿、延迟高
我遇到最多的卡顿原因不是 go2rtc 本身,而是源端码率太高或网络带宽不够。摄像头主码流动辄 4 Mbps 到 8 Mbps,如果预览端所在网络带宽不够,画面就会频繁缓冲。
排查思路:
- 先确认卡的是本地局域网还是远程访问。本地也卡,大概率是摄像头端到 go2rtc 之间的链路问题,或者摄像头开启了很多路并发,超过了自身性能。
- 浏览器播放时,优先用 WebRTC,不要用 HLS 做实时预览。HLS 本身切片就有延迟,不适合高频交互场景。
- 如果多路预览,最好用子码流,分辨率降低后,带宽占用会小很多。
- 在管理页面里观察每路流的码率和帧率,如果不正常,可能是 go2rtc 在自动调用 FFmpeg 转码,CPU 不够也会卡。
5.2 容器日志里反复出现 connection refused
这是典型取流地址不通的表现。可能原因有:
- IP 地址写错,或者摄像头 IP 变化了。建议在路由器里给摄像头绑定静态 DHCP,或者直接在摄像头端配置固定 IP。
- 用户名密码错误。有些摄像头密码包含特殊字符,在 YAML 里要正确转义。我习惯把密码用单引号包起来,避免 YAML 解析出错。
- 端口写错。RTSP 默认端口是 554,但有些摄像头自定义了端口。
- 摄像头开启了“仅允许白名单 IP 访问”,需要把 go2rtc 所在宿主机 IP 加白。
- 防火墙拦住了 outbound 请求,虽然少见,但排查时不要忽略。
定位方式是先用 VLC 或 ffprobe 在宿主机上直接测试这个 RTSP 地址能不能拉通。能拉通,问题就在 go2rtc 配置或容器网络;不能拉通,问题在摄像头、网络或认证。
5.3 公网环境下 WebRTC 连不通
本地 WebRTC 一切正常,到了公网就黑屏或连接失败,基本是
candidates
配置和端口转发的问题。WebRTC 协商过程需要候选地址,如果 go2rtc 给出的候选地址是内网 IP,公网客户端自然连不进来。
解决办法:
-
把
webrtc.candidates配置成公网 IP 或域名。 - 路由器和防火墙上把 8555 的 UDP 转发到 go2rtc 所在宿主机。
- 如果运营商封锁了端口,或者网络结构特别复杂,可以给 go2rtc 配 TURN 服务。go2rtc 本身支持配置 TURN 服务器作为候选路径,这是最稳妥的兜底方案。
我在没有公网 IP 的家里,就是用一台云服务器做 TURN 中转,效果也不错。当然 TURN 会走中转流量,带宽成本要控制在预期范围内。
5.4 多路摄像头同时接入的资源开销
go2rtc 默认不转码,只是 remux,所以多路接入时 CPU 占用通常不会飙升。但风险点在于:
- 每一路流都有人实时预览时,带宽占用就是“源端码率 × 预览人数”。10 路摄像头、每路 4 Mbps,同时 10 个人预览,就是接近 400 Mbps 的带宽需求,普通家庭上行根本扛不住。
- 一旦某路源需要 FFmpeg 转码,CPU 占用会明显上升。可以给 FFmpeg 相关进程加资源限制,或者不要让太多路同时触发转码。
-
长时间运行时,建议在 compose 里加上
mem_limit和cpus,防止异常流拖垮整个宿主机。
我实际跑 8 路 1080p 摄像头,go2rtc 的内存占用大约在 200 MB 以内,CPU 常年个位数百分比,前提是不触发大规模转码。这个表现已经很理想了。
6. 从本机测试到长期运行的几点体会
6.1 先小规模验证再批量接入
我第一次用 go2rtc 时,一口气写了一堆流,结果好几个地址有问题,日志里全是报错,根本分不清谁是谁。后来改成“一个一个加”:先用一台摄像头跑通 RTSP 到 WebRTC 的完整链路,确认画面、延迟、并发都没问题,再逐步增加其他摄像头。
这样做的另一个好处是,能清晰记录每台摄像头的取流地址、性能和稳定性。摄像头多起来之后,一份准确的地址清单比什么都值钱。我是在 go2rtc 配置里按区域命名,比如
room1_main
、
room2_sub
,管理页面看起来一目了然。
6.2 监控与自动恢复
长期运行的容器,最怕半夜摄像头掉线后 go2rtc 一直重连,或者容器卡死后没人管。我在 compose 里已经设置了
restart: unless-stopped
,Docker 会在容器异常退出时自动拉起。
但 go2rtc 容器活着,不代表取流正常。摄像头偶尔会假死,RTSP 连接虽然没断,但没有数据帧。我一般再加一个轻量监控脚本,定时用 ffprobe 或 HTTP API 检查流的存活状态,发现异常就通过企业微信或钉钉机器人推一条告警。
告警脚本很简单,核心就是定时检测,不在这里贴完整代码,思路是:检查
http://宿主机IP:1984/api/streams
的返回,确认对应流是否在线。
6.3 录制与存储思路
go2rtc 本身有一些录制功能,但不是专业 NVR。它更适合做实时预览和协议中转。如果要做长时间录像,我建议把 go2rtc 的转出流接到 NVR、Frigate 或者 Frigate 的 Docker 实例中,让专业软件去处理运动检测和录像存储。
存储目录建议单独挂载,不要放在系统盘。录像文件增长速度取决于码率和帧率,我的一路 1080p 摄像头,每天大约产生 30 到 50 GB 数据。如果不做运动检测只做连续录像,硬盘空间要提前规划好。
在 go2rtc 配置里,也可以为某些流单独开启录制设置,例如把子码流存成低码率录像,主码流只做实时预览,这样既保留细节又省空间。具体按需取舍,没有标准答案。
最后再分享一个小技巧:go2rtc 配置完成后,建议把
go2rtc.yaml
纳入版本管理,或者至少备份到 Git 仓库。里面的摄像头地址、编码信息、输出参数都是你花时间摸索出来的资产,一次误改或者容器重装,如果没有备份,重新排查的代价会很高。我现在每次改动配置都会做一次提交,标签写清楚这次调整了什么,后面回滚就非常方便。
更多推荐
所有评论(0)