go2rtc实践:Docker部署统一接入多品牌摄像头与WebRTC低延迟播放
家里陆续加过几台不同品牌的摄像头之后,你就会发现最麻烦的不是“能不能看到画面”,而是“怎么把它们弄到一个地方统一看”。海康有海康的 RTSP 地址,大华有自己的一套 URL 规则,萤石默认还走自家云平台,杂牌摄像头多半只给你一个 ONVIF 入口,再混一个群晖 NVR、Home Assistant、飞牛 NAS,协议之间来回折腾,光是理清这些就能耗掉一晚上。我现在的固定做法是直接在一台 Linux 小主机上用 Docker 跑 go2rtc。go2rtc 是一个用 Go 写的轻量级多协议流媒体网关,原生支持 RTSP、RTMP、HLS、HTTP-FLV、WebRTC,把不同品牌、不同协议的摄像头统一成一套流媒体平台只需要一个容器。这篇就是完整部署过程的记录,包括端口规划、摄像头接入、浏览器低延迟播放,以及我踩过的各种坑,适合手里有多台摄像头想统一接入、又不愿意被厂商云平台绑定的朋友参考。
1. 为什么摄像头统一接入首选 go2rtc:从协议混杂说起
1.1 家用摄像头的协议困局
家用和小型工程场景里的摄像头,协议乱得像抽屉里的旧充电线。最常见的出口是 RTSP,但问题从不缺:有的摄像头 RTSP 地址隐藏得很深,需要去后台开启“RTSP 认证”才能访问;有的只支持 ONVIF,画面要靠 ONVIF 的媒体服务拿;还有一部分 4G 摄像头出厂只支持厂商私有云,你得先确认它是否开放了 RTSP 推流或者拉流能力。就算搞定了地址,编码格式也是个大坑。老一点的头是 H.264,新买的基本跑 H.265,个别还能输出 MJPEG。浏览器对 H.265 的支持比 H.264 差很多,摄像头录下来播放没问题,一旦要实时预览,兼容性就直接卡脖子。
更现实的一个问题是,很多摄像头从出产到安装,厂家根本没指望你会自建一套流媒体平台。手机 App 扫码添加,画面走厂商云服务器,看着方便,但摸不到原始流地址。等你想接进自己的 NVR 或者 Home Assistant,才发现厂商云和本地 RTSP 根本不是一个概念,本地拉流默认是不开的,要在摄像头后台翻半天设置。go2rtc 解决的痛点就在这里:它负责把乱七八糟的协议源统一接进来,再对外提供一套规范的访问入口,上层应用不需要知道源到底是海康还是萤石,只要访问 go2rtc 的地址就行。
1.2 go2rtc 的核心能力拆解
go2rtc 的核心能力我总结了四个层面:
- 多协议拉流 :能直接接入 RTSP、RTMP、HLS、HTTP-FLV、WebRTC,也能接本地文件、麦克风等设备源。对 IP 摄像头来说,最常用的就是 RTSP 和 ONVIF。
- 自动协议转换 :一个源进来之后,go2rtc 会自动向外提供 WebRTC、HLS、MSE、RTSP、RTMP 等多种输出协议。摄像头只给了你 RTSP 地址,浏览器却可以原生播 WebRTC,这个转换过程对客户端是透明的。
- 毫秒级 WebRTC 低延迟播放 :这是它和很多传统流媒体服务器的关键区别。WebRTC 基于 UDP,浏览器原生支持,配合 go2rtc 内置的信令,能实现几百毫秒级别的延迟,接近实时画面。
- HTTP API 与动态管理 :提供了一套 JSON API,可以实时查询所有流的状态、动态增删流、获取播放地址。基于这套 API,你可以自己写监控面板,或者把它接入 Node-RED、Home Assistant 这类自动化平台。
go2rtc 最初是 Home Assistant 社区开发者 AlexxIT 的项目,所以它和 HA 的集成度特别高,这也是很多人选它的原因。但即使不用 HA,它作为一个独立的流媒体网关也完全够用。
1.3 与其他流媒体服务器的取舍
很多人会拿 ZLMediaKit、SRS、MediaMTX 和 go2rtc 做对比。我实际都折腾过,简单说下它们的区别。ZLMediaKit 功能全面,支持 GB28181、SRT、WebRTC 等一堆协议,但配置和部署成本明显更高,也更适合有一定规模的平台型项目。SRS 更偏直播场景,适合做高并发推流和 CDN 分发,你在家只有几路摄像头用 SRS 属于大炮打蚊子。MediaMTX 和 go2rtc 定位最接近,都很轻量,MediaMTX 的协议转换能力很强,但在 WebRTC 低延迟和 Home Assistant 原生集成上,go2rtc 更顺手。
我的选型逻辑不复杂:如果只是家里或者小办公室,摄像头数量在十路以内,不需要 GB28181 这种国标协议,go2rtc 是最省心的方案。如果后续要接国标平台或者做大规模直播分发,那再去考虑 ZLMediaKit 或 SRS。这个边界搞清楚,就不会出现“装了个重型服务器,结果只跑了两路摄像头”的尴尬情况。
2. Docker 部署前的端口、网络与最小配置
2.1 镜像和版本选择
go2rtc 的官方 Docker 镜像是
alexxit/go2rtc
,直接拉最新版就行。不过我个人习惯固定版本号,而不是用 latest。流媒体这类工具一旦升级,配置文件格式可能出现不兼容,固定版本能让“之前能跑”的结果保持稳定。你可以先拉 latest,跑起来之后用
docker inspect go2rtc
看当前版本,再用固定版本号重新部署。
拉镜像的命令很简单:
docker pull alexxit/go2rtc:latest
有些网络环境下拉 Docker Hub 镜像会比较慢甚至超时。如果你遇到这种情况,可以在 Docker 的 daemon.json 里配置 registry mirror,也就是镜像加速地址。不同网络环境下可用的加速地址变化很快,你可以根据自己的实际网络情况搜索“docker registry mirror 配置”来找到可用的地址。配置完之后重启 Docker 服务再拉镜像,速度会有明显改善。
2.2 端口规划:一个 HTTP 端口加一个 UDP 端口
go2rtc 默认的 HTTP API 和 Web UI 端口是 1984。另外还需要一个 WebRTC 媒体传输端口,默认是 8555(UDP)。这个 UDP 端口非常关键,如果缺失,会出现“Web UI 能看到流,但浏览器播放一直转圈”的奇葩现象。
端口规划可以参考下表:
| 端口 | 协议 | 用途 |
|---|---|---|
| 1984 | TCP | Web UI、HTTP API、WebRTC 信令 |
| 8555 | UDP | WebRTC 媒体数据传输 |
如果你只在内网使用,且浏览器和 go2rtc 处于同一局域网,映射 1984 端口后 WebRTC 也能工作,因为信令协商会把内网 IP 带过去,媒体流直接走局域网。但为了保险,我建议部署时就把 8555/udp 映射上,不然以后换网络环境排查问题会很痛苦。
2.3 网络模式:优先 host 模式
go2rtc 经常要和局域网内的摄像头打交道,而摄像头地址通常是
192.168.x.x
这种内网地址。如果容器走默认的 bridge 网络,容器访问宿主机的局域网服务通常没问题,但涉及 WebRTC UDP 端口映射、DDNS、反向代理这些环节时,容易绕晕。
我的建议是,在 Linux 服务器上部署时,直接用 host 网络模式,容器和宿主机共享网络栈,不用纠结端口映射,WebRTC 的 UDP 端口也天然可用。
docker run -d \
--name go2rtc \
--restart=always \
--network host \
-v /opt/go2rtc:/config \
alexxit/go2rtc:latest
如果你在 Windows 或 macOS 上用 Docker Desktop 做开发测试,host 网络模式的行为和 Linux 上不完全一致,反而容易出现端口监听异常。这种情况建议退回 bridge 模式,手动映射端口:
docker run -d \
--name go2rtc \
--restart=always \
-p 1984:1984 \
-p 8555:8555/udp \
-v /opt/go2rtc:/config \
alexxit/go2rtc:latest
注意这里我把配置目录挂载到了宿主机的
/opt/go2rtc
,而不是直接挂载单个文件。这样 go2rtc 的日志、配置、临时文件都在一个目录里,排查问题方便很多。
2.4 最小可运行配置
go2rtc 的配置文件是 YAML 格式。如果
/config
目录下没有配置文件,容器首次启动时会自动生成一个默认配置。但我建议你手动创建,从最小配置开始,避免默认配置里带了用不上的内容。
log:
level: info
api:
listen: ":1984"
webrtc:
listen: ":8555"
streams:
# 摄像头流稍后在这里添加
把这个文件保存为
/opt/go2rtc/go2rtc.yaml
,然后启动容器。启动后查看日志:
docker logs -f go2rtc
看到类似
server is listening
的日志,就说明服务起来了。浏览器访问
http://宿主机IP:1984
,可以看到 go2rtc 的管理界面,里面能查看所有流的状态,也可以直接在界面上添加流地址做测试。
注意:如果你改了 go2rtc.yaml 的配置,需要重启容器才能生效,go2rtc 不会自动热加载配置文件。早期我以为是热更新,改完配置等半天没反应,后来才意识到要 restart。
3. 摄像头接入配置实操:RTSP、URL 与 FFmpeg 转码
3.1 海康、大华、萤石的 RTSP 地址写法
摄像头接入 go2rtc 的第一步是拿到正确的流地址。每个品牌的 RTSP URL 格式差异很大,我把常用的整理在这里。
海康威视的 RTSP 主码流地址格式是:
rtsp://用户名:密码@摄像头IP:554/Streaming/Channels/101
其中
101
表示通道 1 的主码流,
102
表示通道 1 的子码流。子码流分辨率低,适合手机预览;主码流清晰度高,适合录像。
大华的地址格式:
rtsp://用户名:密码@摄像头IP:554/cam/realmonitor?channel=1&subtype=0
subtype=0
是主码流,
subtype=1
是子码流。
萤石摄像头需要在后台开启 RTSP 认证,然后用管理员账号才能访问。开启后的地址一般是:
rtsp://用户名:密码@摄像头IP:554/h264/ch1/main/av_stream
如果你的萤石摄像头只支持 H.265 编码,地址里的
h264
要换成
h265
。
我实际接摄像头时习惯先拿 VLC 验证一遍地址,确认能出图再写进 go2rtc 配置。这一步能省下大量排错时间。VLC 能打开,说明源流本身没问题,剩下的就是 go2rtc 侧的事情。
3.2 把摄像头流写进配置
拿到地址后,在 go2rtc.yaml 的
streams
段里添加。流名称建议用英文短横线命名,方便后续通过 API 和播放地址访问。
streams:
front-gate:
- rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101
backyard:
- rtsp://admin:password@192.168.1.65:554/cam/realmonitor?channel=1&subtype=0
garage:
- rtsp://admin:password@192.168.1.66:554/h264/ch1/main/av_stream
保存配置并重启容器后,在 Web UI 里就能看到这三路流的状态。go2rtc 会自动探测视频的分辨率、编码格式、帧率等信息,不需要手工指定。
一个流条目下面可以写多个源地址,用
-
符号排列。go2rtc 会在第一个源不可用时自动尝试第二个源,这个能力可以用来做主备切换。比如一个摄像头同时提供 RTSP 和 RTMP 出口,或者你有两个不同网段的地址,都写进去,能提高稳定性。
3.3 接入后先验证延迟再谈其他
摄像头接进来之后,第一件事不是去搞录像,而是验证延迟。在 Web UI 里点击流的播放按钮,go2rtc 默认用 WebRTC 拉流,延迟体感上几乎实时,通常在一秒以内。如果走 HLS 播放,延迟会飙升到 5 秒以上,这是 HLS 的分片机制决定的,不是故障。
建议新接入的流先让它在后台跑 10 分钟,观察几个东西:会不会自动断线重连、画面有没有花屏、CPU 占用是否异常。go2rtc 对断了之后会自动重连,但如果源流地址本身不稳定,日志里会反复出现重连记录,这时候要排查摄像头端的网络和码流设置,而不是折腾 go2rtc。
3.4 H.265 摄像头浏览不了的问题与 FFmpeg 转码
前面提到,很多新摄像头默认输出 H.265。go2rtc 作为网关,可以做协议转换,但如果源是 H.265,WebRTC 输出时浏览器端可能无法硬解,导致画面黑屏。这是因为 Chrome、Firefox 对 H.265 的支持一直不完整,尤其是硬件解码这块,受版权和平台限制很大。
解决思路有两个。第一个是在摄像头后台把编码改成 H.264,代价是文件体积变大,录像占用空间更多。第二个是在 go2rtc 里用内置的 FFmpeg 做转码,把 H.265 转成 H.264。
go2rtc 内置了 FFmpeg,可以直接在流地址前面加
ffmpeg:
前缀,并用
#
符号传递转码参数:
streams:
living-room:
- ffmpeg:rtsp://admin:password@192.168.1.66:554/h265/ch1/main/av_stream#video=copy#audio=copy
#video=copy#audio=copy
表示复制视频流和音频流,不做重编码,开销非常低。如果复制码流还是无法播放,再考虑重新编码成 H.264:
streams:
living-room:
- ffmpeg:rtsp://admin:password@192.168.1.66:554/h265/ch1/main/av_stream#video=h264#audio=aac
重新编码会占用 CPU 资源,转码一路 1080p 的视频大概需要一两个 CPU 核心,放在有 GPU 加速的机器上更合适。官方文档里也有 VAAPI、NVIDIA 硬件加速的配置模板,需要的话可以去翻一下。
提示:能用
video=copy就不要轻易重编码。FFmpeg 转码一旦开启,CPU 占用会线性上升,尤其多路摄像头同时转码时,小主机可能直接吃满。
4. 多协议输出与客户端播放:从 WebRTC 到 HLS/RTSP
4.1 WebRTC 低延迟的原理
go2rtc 最吸引人的一点是浏览器原生支持低延迟播放,靠的是 WebRTC。WebRTC 本身是点对点的实时通信协议,基于 UDP,数据不用像 HTTP 那样等待每个请求的响应,也没有 HLS 那种分片累积延迟。go2rtc 做的工作是:把 RTSP 源转换成 WebRTC 兼容的封装格式,同时充当 WebRTC 的信令服务器,协调浏览器端和媒体端的连接建立。
整个流程可以简单理解成:浏览器向 go2rtc 请求播放某个流,go2rtc 从摄像头源拉流并封装成 WebRTC 媒体包,通过 UDP 传给浏览器。因为 UDP 没有 TCP 的重传和拥塞控制机制,延迟可以压到 300ms 以内,这个延迟水平对看实时画面、远程开门、看小孩房间这类场景完全够用。
4.2 HLS 输出作为保底方案
WebRTC 并不所有端都能用。比如一些智能电视上的 App、老旧的嵌入式播放器,对 WebRTC 支持不够好。这时候 HLS 是最稳妥的保底方案。go2rtc 会自动为每个流生成 HLS 分片,播放地址是:
http://宿主机IP:1984/hls/流名称.m3u8
任何支持 HLS 的播放器,包括 VLC、ffmpeg、各种 Web 播放器,都能直接拉这个地址。代价就是延迟高,5 到 15 秒都有可能,毕竟 HLS 要等一个分片生成完毕才能播。对回放场景问题不大,对实时监控只能作为辅助方案。
4.3 输出 RTSP 和 RTMP
go2rtc 不仅支持拉流,也支持推流和对外的 RTSP/RTMP 输出。如果你有一个 NVR 或者录像软件对 RTSP 协议更友好,可以让它直接拉 go2rtc 的输出地址。这样摄像头只和 go2rtc 建立一条连接,其他消费者(NVR、Home Assistant、浏览器)都从 go2rtc 拿流,减轻摄像头端的连接压力。
启用 RTSP 和 RTMP 输出需要在配置里指定监听端口:
rtsp:
listen: ":8554"
rtmp:
listen: ":1935"
配置后的输出地址分别是:
rtsp://宿主机IP:8554/流名称
rtmp://宿主机IP:1935/流名称
4.4 通过 HTTP API 构建自己的监控面板
go2rtc 的 API 设计很简洁,适合二次开发。几个最常用的接口:
| 接口 | 说明 |
|---|---|
GET /api/streams
| 获取所有流的信息,包括状态、分辨率、码流 |
GET /api/streams?src=流名称
| 获取指定流的详细信息 |
GET /api/frame/流名称
| 获取流的一帧截图,用于预览 |
WS /api/ws
| WebSocket 接口,用于信令和实时状态推送 |
我在实际项目中用 Node-RED 调用这些接口,做过一个简单的摄像头状态面板。后端定时拉取
GET /api/streams
,把每个流的在线状态、码率、延迟数据推到前端展示。如果你用 Vue 或 React 写前端,也可以直接基于这套 API 做一个更漂亮的矩阵页面。go2rtc 的作用变成了一个“摄像头适配层”,上层平台完全不用关心底下是海康还是萤石。
5. 排错记录:黑屏、WebRTC 连不上与重启丢配置
5.1 H.265 导致的黑屏与播放失败
我在第一次接入一台 4G 家用摄像头时,遇到的情况是:VLC 播放没问题,go2rtc Web UI 也能看到流在运行,但点播放就是黑屏。日志里没有任何报错,看起来一切正常。后来才意识到是摄像头默认输出 H.265,而浏览器 WebRTC 解码不了 H.265 的视频轨道。
排查思路很简单:先看 Web UI 里流的编码信息,确认是 H.265 之后,直接在摄像头后台把主码流改成 H.264,问题立刻消失。有些摄像头固件不支持改编码,那就只能在 go2rtc 里走 FFmpeg 转码,参考前面 3.4 的配置。这类问题最容易误判成网络问题,实际上和网络一点关系都没有。
5.2 WebRTC 一直在转圈但 HLS 能播
这个现象的本质是 WebRTC 的 UDP 媒体端口不通。HLS 走 TCP 1984 端口,能正常访问;WebRTC 需要 UDP 8555 端口,如果这个端口被防火墙挡住了,或者 Docker 没有映射 8555/udp,就会出现“HLS 能看,WebRTC 一直转圈”。
我用 bridge 模式部署时遇到过这个问题,排查过程是:先在容器里确认 8555/udp 端口在监听,再确认宿主机端口映射,然后用 tcpdump 看 UDP 包是否到达宿主机。最后发现是云服务器的安全组没放行 UDP 8555,加上安全组规则后立即恢复。
如果你用的是 host 模式网络,这个问题基本不会出现,这也是我推荐 host 模式的原因。
5.3 源流频繁重连与摄像头连接数限制
有些摄像头对 RTSP 并发连接数有限制,默认只允许一路或两路同时拉流。你一边用 VLC 看流,一边让 go2rtc 拉流,摄像头可能直接拒绝新的连接,go2rtc 就会反复重试。表现是日志里一直出现
connect failed
或
timeout
。
解决方法是减少对摄像头源流的直接拉取,所有客户端都走 go2rtc,而不是各自直连摄像头。另外,如果摄像头后台有“最大连接数”或“匿名登录”选项,把它调到合理值,也能缓解这个问题。
5.4 Docker 重启后配置丢失:挂载目录别挂错
go2rtc 配置文件的默认路径在容器内是
/config
。如果你用
docker run
时没有挂载
/config
目录,或者挂载方式不对,容器重建后配置就回到默认状态。这个坑我踩过,当时频繁更新镜像版本,每次
docker rm
再
docker run
之后配置全丢,还以为是镜像的问题。
保险做法是挂载整个目录,而不是单个文件:
-v /opt/go2rtc:/config
这样配置文件、日志都落在宿主机,更新镜像不会丢配置。还有个小技巧:每次改动配置之前先复制一份备份,比如
go2rtc.yaml.bak
。配置语法错误导致容器起不来时,能快速回滚。
5.5 常见问题速查表
| 现象 | 主要原因 | 处理方式 |
|---|---|---|
| 浏览器播放黑屏 | 源流是 H.265 | 改成 H.264 或启用 FFmpeg 转码 |
| WebRTC 转圈,HLS 正常 | UDP 8555 端口不通 | 放行 UDP 端口,或用 host 网络模式 |
| 日志频繁重连 | 摄像头连接数限制 | 限制直连摄像头,统一走 go2rtc |
| 重启后配置丢失 | /config 没挂载 | 挂载 /config 目录 |
| 画面卡顿、延迟升高 | 带宽不够或码流设置过高 | 换子码流,或降低码率 |
6. 进阶玩法:HA 集成、录像搭配与公网安全访问
6.1 与 Home Assistant 的深度集成
如果你用 Home Assistant,go2rtc 的价值会体现得更明显。第一种方式是直接在 HA 里安装 go2rtc 的 Add-on,它会自动和 HA 的 stream 组件整合。第二种方式是把 go2rtc 独立跑在 Docker 里,然后在 HA 的
configuration.yaml
里调用 go2rtc 作为摄像头流的统一来源。
我自己用得最多的是配合 HA 的 WebRTC Camera 卡片。摄像头通过 ONVIF 接入 HA,画面由 go2rtc 统一转成 WebRTC,在 HA 仪表盘里的延迟和一秒以内。相比之前用 HLS 嵌入页面,体验提升非常明显。如果你还没有 HA,单纯为了这个集成装一套也值得。
6.2 录像与回放:搭配 Frigate 或其他 NVR
go2rtc 本身定位是实时流媒体网关,不做长期录像管理。想录像是另一层需求。我的选择是 Frigate。Frigate 本身是一个带 AI 检测的 NVR,从某个版本开始内置了 go2rtc 作为摄像头源统一管理入口,既可以用它录 Full HD 以上的高清视频,也可以做人体、车辆检测。
当然你也可以不用 Frigate,直接让群晖 Surveillance Station、飞牛 NAS 或者任何支持 RTSP 的 NVR 拉 go2rtc 的输出地址。这样摄像头端只需要维护到 go2rtc 的连接,录像端从 go2rtc 统一取流,架构上会清晰很多。
6.3 树莓派和开发板摄像头接入
不是所有摄像头都是 IP 摄像头。树莓派的 CSI 摄像头、ESP32-S3 的 USB 摄像头这类板载设备,没有现成的 RTSP 地址。它们可以通过 ffmpeg 或者 gstreamer 在设备本地推流,把视频流推给 go2rtc。
比如树莓派上可以用 ffmpeg 从 V4L2 设备读取画面,然后以 RTSP 或 RTMP 的方式推给 go2rtc,再统一对外分发。这种玩法适合 DIY 项目,go2rtc 作为一个汇聚点,接收各种各样的视频源。需要注意的是,这类设备的编码能力有限,推流前最好在设备端就把分辨率调低,否则传输和编码开销会拖垮整条链路。
6.4 公网安全访问:反向代理与 WebRTC 穿透
外出时要看家里的摄像头,比较稳妥的做法是用带 TLS 认证的反向代理,把 go2rtc 的 1984 端口代理出去。Caddy 配置简单,自动申请证书,适合家用。nginx 也可以,但需要手动维护证书。
用反向代理时有一个额外的问题:WebRTC 的信令走的是 HTTPS 代理,但媒体流的 UDP 候选地址还需要客户端能访问到。如果 go2rtc 在 NAT 后面,需要在 webrtc 配置里指定公网地址或 TURN 服务器,否则手机在外面能看到流信息,却拉不到媒体数据。这涉及 ICE 候选地址的协商,网上资料不少,但一定要提前配置,别等出门了再排查。
安全性方面,我的建议是:
- go2rtc 本身不带用户管理,暴露公网一定放在反向代理后面,加 Basic Auth 或 OAuth
- 摄像头账号不要用默认密码,go2rtc 配置文件里的密码要定期改
- 如果只是自己看,优先考虑用 Tailscale 之类的组网工具,服务不出公网,风险小很多
- 不要图省事直接把 1984 端口映射到公网
最后再分享一个我自己的使用习惯:go2rtc 配置改动前先备份,所有路径都写在
/opt/go2rtc
目录下,用 docker compose 管理而不是裸的 docker run。之前我图方便直接 docker run,后来机器重启、镜像升级、配置回滚都靠补丁式操作,手忙脚乱。改成 compose 之后,部署和迁移只需要一个 YAML 文件,复制到新机器上直接
docker compose up -d
就完事。我自己现在维护的摄像头平台,就是这套配置,跑了大半年没出过问题。
更多推荐
所有评论(0)