Docker部署go2rtc:统一接入摄像头,实现WebRTC低延迟播放与多协议分发
Docker 这东西,很多人第一反应是跑个 MySQL、来个 Nginx,很少有人会往“摄像头”这个方向想。但如果你手里有海康、大华、萤石或者一堆杂牌摄像头,又想把它们统一接入到一个平台里,用 WebRTC 低延迟预览、RTMP 或 HLS 对外分发,甚至还想和 Home Assistant 联动,那 go2rtc 基本就是目前最省事的方案。今天这篇,我就用 Docker 把它跑起来,再把摄像头接进去,一步步说清楚怎么做到“一个容器,多协议接入,多端播放”。
1. 摄像头接入场景里的痛点,go2rtc 是怎么解的
1.1 不同品牌摄像头协议不统一,才是接入时最心烦的事
摄像头这东西,品牌之间差异特别大。海康威视主流的取流方式是 RTSP,路径通常是
/Streaming/Channels/101
(主码流)和
/Streaming/Channels/102
(子码流);大华的 RTSP 路径又不一样,长这样
/cam/realmonitor?channel=1&subtype=0
;萤石除了 RTSP 还有自家的 EZOpen 协议;还有些老摄像头只能输出 MJPEG 或者 RTMP。你把它们全都摆在一个网络里,如果不做一层“协议转换”或“协议统一”,前端播放器根本没法兼容。
go2rtc 这个项目,名字拆开看就是 “go to realtime communication”,它最大的特点就是能把这些不同来源的流统一接管:你给它一个 RTSP 地址,它就能对外同时提供 WebRTC、HLS、MJPEG、RTMP 等多种播放方式。也就是说,底层摄像头还是各说各话,但 go2rtc 像一个翻译官,把你的多路流汇总成一套标准接口,上层应用按需取用。
1.2 低延迟的关键在于不转码,而不是“性能很强”
说到流媒体服务器,很多人第一反应是 FFmpeg 或者 ZLMediaKit、MediaMTX 这类工具。FFmpeg 很强,但大多数情况要转码,转码就会带来延迟和 CPU 开销;ZLMediaKit 更偏生产级,配置和概念也更多。go2rtc 的做法很有意思—— 默认不转码 。
它直接把摄像头的原始编码数据(H.264/H.265)透传出去,播放端通过 WebRTC 拉流,延迟能控制在 0.2 到 0.5 秒左右。这非常接近“看监控实时画面”的体验。相比 HLS,通常有 3 到 10 秒延迟,WebRTC 的优势是碾压级的。不转码的另一个好处是 CPU 占用低,哪怕是树莓派这种小主机,也能跑得动。
注意:go2rtc 不是万能。如果你一定要把 H.265 转成 H.264,或者给流加水印、叠加文字,那还是得借助 FFmpeg。go2rtc 也支持内部调用 FFmpeg 来做转码和推流,但那是另一条路线,后面我会单独说。
2. Docker 部署 go2rtc 的前期准备
2.1 先理清楚摄像头的网络和取流地址
在拉容器之前,先把摄像头的信息摸清楚。你至少需要这四样东西:
-
摄像头 IP 地址,比如
192.168.1.100 -
取流端口,大部分摄像头默认 RTSP 是
554 - 登录用户名和密码,注意是摄像头自己的账号,不是录像机的
- 对应的 RTSP 取流路径
如果你不清楚 RTSP 路径,有两个办法:一是去摄像头管理后台的“媒体设置”或“网络设置”里翻,一般会直接显示取流地址;二是用 VLC 播放器去试,把可能的 URL 填进去,能放出画面就是对的。
这里还有一个小技巧:接入时优先用 子码流 来预览,主码流留作录像。子码流分辨率低,带宽占用小,WebRTC 拉流延迟更低。等需要回放、下载的时候再切主码流。如果你一开始就把主码流灌进来做实时预览,多路摄像头同时看的时候,网络和播放端压力都会明显上涨。
2.2 用 Docker Compose 跑起来,官方镜像一行就搞定
go2rtc 部署非常简单,官方提供了现成的容器镜像
ghcr.io/alexx2/go2rtc:latest
。我最推荐的方式是用
docker-compose.yml
,因为可以把端口映射、配置挂载、重启策略等一次性写清楚。
下面是我用的配置:
services:
go2rtc:
image: ghcr.io/alexx2/go2rtc:latest
container_name: go2rtc
network_mode: host
volumes:
- ./go2rtc.yaml:/config
restart: unless-stopped
这里有一个很多人踩过的坑:官方文档明确建议用
network_mode: host
。为什么?因为 WebRTC 要跑在 UDP 端口上,而且它还需要自动协商用于媒体传输的高位 UDP 端口。如果走 Docker 默认的 bridge 网络,端口映射会非常麻烦,很可能出现画面能出但声音断断续续,或者 WebRTC 连接一直卡住的情况。用 host 模式,容器直接共享宿主机网络,所有端口自然开放,省掉大量排查时间。
如果你出于安全考虑必须用 bridge 模式,那需要把 Web 端口 1984、RTSP 端口 8554 以及 WebRTC 端口段(默认 50000 到 50010 左右)都映射出来,还会相对繁琐。家用环境我建议直接 host 模式,简单又稳。
2.3 配置文件尽量一开始就建好,别等启动后手忙脚乱
go2rtc 的配置是一个 YAML 文件,默认路径
/config
,功能很集中。先建一个最基本的
go2rtc.yaml
:
log:
level: info
streams:
cam_front: rtsp://admin:password@192.168.1.100:554/Streaming/Channels/102
cam_back: rtsp://admin:password@192.168.1.101:554/Streaming/Channels/102
接着在配置文件同级目录执行:
docker compose up -d
启动后打开浏览器,访问
http://宿主机IP:1984
,就能看到 go2rtc 自带的 Web 界面。左边是你的流名称列表,点击任意一路,页面会直接调用 WebRTC 播放。如果这一步能出画面,说明部署成功,接下来要做的就是更完整的配置和协议对接了。
3. 从接入到分发,核心实操记录
3.1 第一步:接入 RTSP 摄像头,并验证拉流是否成功
接入 RTSP 不要多花哨,直接写流地址即可。但有些摄像头 RTSP 路径非常长,而且含特殊字符,写在 YAML 里容易出问题。我建议把密码里的特殊字符做一下 URL 编码,避免 YAML 解析和 RTSP 鉴权冲突。比如密码是
abc@123
,在 RTSP URL 里最好写成
abc%40123
。
写完后重启容器:
docker restart go2rtc
然后在 Web 界面里点对应流名称。如果页面显示成功播放,说明拉流正常。如果播放失败,去 Web 界面下方找到 Logs 区域,go2rtc 会显示拉流的详细报错,比如认证失败、超时、端口不通。
这是排查的第一道关口。日志是关键证据,比你瞎改配置有用得多。
3.2 第二步:用 WebRTC 播放,体验“近乎零延迟”
上面说的 Web 界面播放,底层用的就是 WebRTC。WebRTC 和传统 RTSP/HLS 最核心的区别在于:它走 UDP,而且是点对点或准点对点的媒体传输,不需要播放器端做大缓冲,所以延迟极低。
实际体验上,我拿手机对着电脑屏幕同时播放同一路摄像头画面,手和屏幕上的动作基本同步。而用 HLS 播放同一个源,延迟能明显感到有 3 到 5 秒。
在代码层面,go2rtc 也提供了对应的 WebRTC 播放地址。具体的 URL 格式,打开 Web UI 后,点击流名称旁边的播放链接就能看到。浏览器也好,支持 WebRTC 的播放器也好,拿这个地址直接拉流即可。这一点非常适合做“门铃监控实时查看”这类场景,你按门铃的同时,手机端画面基本同步显示来人是谁。
3.3 第三步:对外输出多种协议,实测 HLS、MJPEG、RTMP
go2rtc 的价值在于集成了多种输出协议。我实际用过的几个场景如下。
-
MJPEG 输出
:做网页内嵌监控面板。MJPEG 是最古老的视频流格式之一,几乎所有浏览器都能通过
<img>标签直接显示。go2rtc 提供的 MJPEG 地址大概长这样:
http://宿主机IP:1984/api/stream?src=cam_front&dst=mjpeg
这个地址可以直接放在 HTML 的
img
标签里,页面加载后就是实时画面,不需要安装任何插件。对不想上复杂播放器的场景,这是最省事的方案。
- HLS 输出 :给 iOS/Safari 或者对延迟不敏感但要求兼容性高的场景用。Safari 对 WebRTC 支持还行,但 HLS 在苹果生态里最稳妥。访问路径一般形如:
http://宿主机IP:1984/hls/cam_front.m3u8
用 VLC、ffplay 都能直接打开这个地址。HLS 延迟比 WebRTC 高不少,这是协议特性决定的,但它在弱网环境下更不容易断。
- RTMP 分发 :如果想做直播推流,比如把摄像头画面推到 CDN 或直播平台,go2rtc 也可以作为一套中转。API 形式大概是这样:
http://宿主机IP:1984/api/stream?src=cam_front&dst=rtmp://目标服务器/live/密钥
这里要强调一下,go2rtc 的“多协议输出”是同时的。同一路 RTSP 源,WebRTC、HLS、MJPEG 可以并行对外服务,互不干扰,不必给每个协议单独开一个转码进程。这是它相比传统方案非常舒服的一点。
3.4 第四步:接 Home Assistant,把摄像头变成自动化的一环
go2rtc 在 Home Assistant 里有官方集成,这也是很多智能家居玩家选它的核心原因。
在 Home Assistant 的集成里添加 go2rtc,填写服务器地址,它就会自动发现 go2rtc 里定义的所有流,并生成 camera 实体。之后你可以做很多事情:有人按门铃时把门铃摄像头画面弹出到电视上、检测到猫狗移动时把对应摄像头录一段视频、或者直接用语音助手喊一声“打开后院摄像头”就切换到实时画面。
我自己的一个用法是:ha 里配置了“离家布防”模式,打开的瞬间,所有摄像头的流开始录像到 NAS,同时 go2rtc 的 WebRTC 预览画面被推送到客厅电视上。整个过程延迟很低,基本感觉不到切换卡顿。
4. 部署和接入过程中,我踩过的几个坑
4.1 摄像头拉流成功但画面黑屏,多半是编码格式不兼容
这是我遇到过最多的情况。摄像头输出 H.265,但 Web 浏览器不支持 H.265 硬解,画面就会黑屏或者只有声音没图像。go2rtc 默认不转码,所以它不会主动帮你把 H.265 转成 H.264,这时候需要做两件事之一:
- 在摄像头后台把主码流或子码流的编码格式改成 H.264
- 给 go2rtc 配置转码参数,让它调用 FFmpeg 做本地转码
如果你不想动摄像头设置,第二种方案可以这么做:在
go2rtc.yaml
里给流增加一个 FFmpeg 转码分支。但要注意,转码就意味着 CPU 占用上升,树莓派这类设备尤其明显,所以能用子码流就别开主码流转码。
4.2 WebRTC 一直连接中,重点检查 UDP 端口和网络拓扑
WebRTC 连接不上,现象是页面一直转圈,但 HLS 或 MJPEG 却正常。这个问题的概率很高,原因通常是 NAT 穿透失败或者 UDP 端口没放行。
如果你用 Docker bridge 模式部署,需要检查高位 UDP 端口有没有映射;如果你在路由器后面,可能还需要配置端口转发或者 TURN 服务。家用局域网内排查时,最简单的方法是先切到 host 模式,看看是否恢复。如果 host 模式下正常,再回去慢慢调端口映射。
另外,有些路由器开了“AP 隔离”功能,两个设备之间不能互相访问。这种情况下所有流都会出问题,不只是 WebRTC,要优先排查这个因素。
4.3 多路摄像头同时看,带宽和 CPU 不够怎么办
同时接入多路摄像头,常见瓶颈有两个:
- 网络上行/下行带宽 :如果你把主码流全部拉进来,带宽消耗会很夸张。假设一路主码流 8 Mbps,10 路就是 80 Mbps,普通家庭宽带上行根本吃不消。建议对实时预览场景统一使用子码流,把主码流留给录像或回放。
- CPU 解析能力 :go2rtc 默认不转码,纯转发的情况下 CPU 占用很低,但如果你开了 FFmpeg 转码,CPU 会迅速成为瓶颈。
我的习惯是:摄像头端尽量设置“子码流优先预览,主码流按需触发”。在 go2rtc 配置里,可以对同一路摄像头写两个流名,比如
cam_front
和
cam_front_hd
,分别对应子码流和主码流。日常面板和 WebRTC 都用子码流,需要详细看某个方向时再切主码流。
4.4 各家摄像头的接入小细节,海康、大华、萤石的差异
我在这套方案里实接过海康、大华和萤石,把容易踩的点整理成一个速查表:
| 品牌 | 常见问题 | 建议处理方式 |
|---|---|---|
| 海康威视 | RTSP 地址里没有带子码流 | 确定是主码流还是子码流,路径末尾的 101 是主码流,102 是子码流 |
| 大华 | RTSP 地址带特殊符号和参数 | 放到 YAML 里用引号包裹,避免解析异常 |
| 萤石 | 老设备走私有协议,RTSP 不一定开放 | 先在萤石云 App 或后台开启本地 RTSP 开关,再用标准 RTSP 接入 |
这些细节不复杂,但容易被忽略。花几分钟在摄像头后台确认一遍协议和路径,比接好之后再一个地址一个地址去试要省心得多。
5. 进阶玩法:树莓派、NAS 录像和本地自动化
5.1 树莓派跑 go2rtc,低功耗长期在线
如果你不想为了摄像头单独开一台高功耗服务器,树莓派 4B 或更高版本的板子完全可以胜任。go2rtc 本身是 Go 语言写的,二进制非常轻,内存占用通常只有几十 MB,CPU 使用率在只有几路子码流的时候几乎可以忽略。
如果你用的是树莓派 CSI 摄像头模块,比如 OV5647,思路其实也类似:先在板子上用系统自带的 libcamera 或 raspivid 工具把摄像头画面编码后推成一个本地 RTSP 流,再让 go2rtc 去拉这个流。这样就能把树莓派摄像头也纳入同一个流媒体平台里,和 IP 摄像头统一管理。我甚至在野外环境把树莓派装进防水盒,用太阳能供电,配合 go2rtc 做远程监控,一个月都不需要维护。
5.2 把录像落到 NAS,和飞牛、群晖这类系统配合
摄像头原始 RTSP 流直接录制到 NAS,很多人都会做。go2rtc 本身不擅长录像,但它可以把流交给 FFmpeg 做分段录制。一个简单做法是在宿主机上写一个定时任务或脚本,用 FFmpeg 去拉 go2rtc 的 HLS/RTSP 地址,然后存成 mp4 文件,保存到 NAS 挂载的目录里。
以飞牛 OS 或群晖为例,你可以在 NAS 上挂载一个共享文件夹给 Docker 容器,然后把录像到这个目录的路径挂在 go2rtc 或配套的录制容器里。或者更直接一点,使用 NAS 的 Docker 套件直接跑 go2rtc,省掉独立主机的成本。这样架构就是:摄像头 → go2rtc → 实时预览/对外分发;摄像头 → FFmpeg 定时拉取 → NAS 存储。
如果你有 Frigate 这类 NVR 需求,go2rtc 也常常被用作 Frigate 的前置媒体源。Frigate 对鸟类、车辆等检测消耗资源,但对流的解析能力有限,用 go2rtc 做统一入口,Frigate 只做它擅长的检测,是社区里比较经典的组合。
5.3 关于 H.264/H.265 和转码,一个容易忽略的权衡
最后聊一下转码这个事。很多人一上来就希望全链路都走 H.265,因为同等画质占用带宽更小。但 H.265 在浏览器端的支持是个大问题,Safari 对 H.265 的硬解支持还可以,Chrome 和 Firefox 则基本不支持,至少不会默认开启。
所以如果你的播放终端主要是浏览器,我强烈建议把摄像头的子码流设成 H.264。主码流可以继续用 H.265 保存原始录像,不影响预览。这样可以做到实时预览延迟低、录像存储体积小,两全其美。
反过来,如果你是拿安卓盒子、电视这类有硬解 H.265 能力的播放器看,那 H.265 也没问题,按需取舍即可。
写在最后的个人体会
这套方案我自己跑了快半年,最大的感受是:稳定压倒一切。go2rtc 作为容器,启动和重启都快,配置简单,日志清晰,遇到问题能快速定位。而 Docker 部署的方式又让整套环境干净可复制,拆机换机时直接迁移
go2rtc.yaml
和 compose 文件就能恢复。
另一个我特别认可的设计是它把接入和分发彻底解耦。以前我接一个新摄像头,要给前端播放器做适配,要给录像系统做适配,还要单独处理延迟问题。现在所有摄像头都接入 go2rtc,前端只需要认准 WebRTC、HLS、MJPEG 这三种接口,剩下的事情全由 go2rtc 搞定。上了这套方案之后,我再也不想回到以前那种“给每个摄像头写一套取流代码”的日子了。
更多推荐
所有评论(0)