go2rtc实战:统一接入多品牌摄像头,实现低延迟WebRTC播放
家里装了三个不同品牌的摄像头:海康的录像机、大华的枪机,还有一个树莓派 DIY 的 USB 摄像头。问题来了——每台设备都有自己的 App,看这个换一个软件、看那个再换一个,网页和微信里又没法直接打开 RTSP 流。折腾过 FFmpeg 推流、Nginx 加 RTMP 模块,甚至写过一段 Python 轮询截图,最后都因为延迟、稳定性或维护成本放弃了。直到用 Docker 跑起 go2rtc,多协议流媒体接入这件事才算真正解决。
go2rtc 是一个用 Go 语言写的轻量级流媒体网关,支持 RTSP、RTMP、HLS、MJPEG、WebRTC 等多种协议之间的互相转换和转发。它的核心价值不是又一个播放器,而是把不同来源、不同协议的摄像头流统一收进来,再按需输出成浏览器能直接看的 WebRTC、HLS 格式,并且自带一个可用的 Web 管理页面。我这一套从 Docker 安装、摄像头接入到外网访问,跑了大半年基本没重启过,这篇就完整记录一下整个搭建过程、配置思路和踩过的坑。
1. 为什么是 go2rtc:摄像头接入的痛点与解法
1.1 摄像头接入的三大尴尬现实
先说第一个痛点:绝大多数网络摄像头的原生协议是 RTSP,但这个协议在浏览器和手机上几乎没有原生支持。你用 Chrome 打开一个 rtsp:// 地址,浏览器直接拒绝,更别说分享链接给朋友看。市面上有的解决方案是让摄像头走厂家私有协议,比如海康的 SDK、大华的 SDK,但这样等于被厂家绑死,每个品牌都得单独对接一遍。
第二个痛点是设备多、协议杂。家用或小型项目里,很可能出现好几年前买的杂牌摄像头,只支持 RTMP 推流;也有树莓派接的 CSI 摄像头,走的是 MJPEG HTTP 输出;还有监控录像机(NVR)后面挂着一堆 IPC。要把这些全统一到一个页面上看,协议转换是绕不开的。
第三个痛点是延迟。很多人第一反应是转 HLS 流,毕竟手机和 PC 浏览器都支持。但 HLS 的延迟普遍在 5 到 10 秒,有时候摄像头拍到的画面已经过去了,你才知道门口有人按门铃。对于看监控这种场景,延迟超过一秒就很难受了。
1.2 go2rtc 比 FFmpeg 加 Nginx 那套方案强在哪
我以前也是 FFmpeg 转 RTMP,再让 Nginx 的 RTMP 模块切 HLS,再在前端用 hls.js 播放。这套方案能用,但维护起来很费劲:Nginx 要额外编译 rtmp 模块,FFmpeg 转码命令得写很长一串,推流进程挂了还得写守护脚本,HLS 切片多了要手动清理。整体是"能用,但不优雅"。
go2rtc 把这一整套收进了一个容器里。它最大的优势是原生支持 WebRTC,也就是浏览器端可以直接播放的实时传输协议,延迟可以压到 0.2 到 0.5 秒,真正做到了"打开网页就能看,画面几乎是实时的"。它内部集成了协议适配层,RTSP 输入、RTMP 输入、MJPEG 输入都可以收,HLS、WebRTC、MSE、MP4 输出都可以给。更贴心的是它支持按需拉流(on_demand),没人看的时候不去连接摄像头,既省带宽又减少摄像头被频繁拉流导致的死机。
另外一个实用点是 go2rtc 有 ONVIF 自动发现能力。如果你的摄像头启用 ONVIF,go2rtc 在局域网内会自动枚举到设备,打开 Web 页面就能看到,不用手动去查 IP 和写 RTSP 地址。
2. Docker 部署 go2rtc:两种方式任意选
2.1 准备工作与镜像说明
部署 go2rtc 前,你需要准备一台一直开机的机器,可以是 NAS、旧电脑、树莓派或者云服务器。机器上装好 Docker 环境即可。Docker 这个环节反而不用多操心,go2rtc 本身只有几十兆大小,内存占用大概几十兆到一百多兆,在树莓派上也能跑得很轻松。镜像名我一般用
ghcr.io/alexxx-io/go2rtc:latest
,也可以从 Docker Hub 拉取同名镜像。
需要注意,NAT 网络(就是容器默认的 bridge 网络)会影响 go2rtc 的自动发现能力。如果在局域网内使用,并且你的 Docker 宿主机是 Linux,我强烈建议后面使用 host 网络模式。在 Windows 的 Docker Desktop 上没法用 host 模式,那就用正常的端口映射,自动发现功能可能受影响,但手动配置流不受影响。
2.2 docker run 快速部署
如果你想先跑起来看一眼效果,一条命令就够了:
mkdir -p ~/go2rtc && cd ~/go2rtc
docker run -d \
--name go2rtc \
--restart unless-stopped \
-p 1984:1984 \
-p 8555:8555/tcp \
-p 8555:8555/udp \
-p 8554:8554 \
-v $(pwd)/go2rtc.yaml:/config/go2rtc.yaml \
-e TZ=Asia/Shanghai \
ghcr.io/alexxx-io/go2rtc:latest
这条命令里,1984 是 go2rtc 的 Web 界面和 API 端口,8555 的 TCP 和 UDP 是 WebRTC 通信端口,8554 是它对外提供 RTSP 输出的端口。如果你的场景不需要把 go2rtc 收到的流转给别的 RTSP 客户端,8554 甚至可以不开。TZ 环境变量一定要带,否则日志时间会差 8 个小时,排查问题的时候非常别扭。
第一次启动后,访问
http://宿主机IP:1984
,能看到 go2rtc 自带的管理页面。这时候还没有配置任何摄像头,页面是空的。注意我挂载了 go2rtc.yaml 作为配置文件,但该文件还不存在,go2rtc 启动时会自己创建一个空配置并保持打开,所以先启动再改配置也行,改完直接重启容器加载新配置即可。
2.3 Docker Compose 部署:推荐的方式
如果是正式使用,我更推荐用 Docker Compose 管理,配置清晰、方便迁移:
services:
go2rtc:
image: ghcr.io/alexxx-io/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
ports:
- "1984:1984"
- "8555:8555/tcp"
- "8555:8555/udp"
- "8554:8554"
volumes:
- ./config:/config
environment:
TZ: Asia/Shanghai
在
docker-compose.yml
所在目录执行
docker compose up -d
,go2rtc 就起来了。把配置文件放在
./config/go2rtc.yaml
目录下,后期改配置、备份都方便。这套 Compose 文件在 Linux 服务器、群晖 NAS、Unraid 上都能直接用,只要你的 Docker 版本支持 Compose v2 就行。
如果你在 Linux 上追求零门槛自动发现摄像头,可以把 ports 删掉,改成:
network_mode: host
host 模式下容器直接使用宿主机网络,无需端口映射,go2rtc 的自动发现能力最强,WebRTC 也不用额外配置候选地址。当然,网络模式的选择取决于你的实际情况,这个在后面的排查部分会展开说。
3. 摄像头接入实操:RTSP、RTMP、文件流全兼容
3.1 go2rtc 配置文件的灵魂:streams 段
go2rtc 的配置格式是 YAML,核心结构很容易理解。最常用的几个段落是
api
、
webrtc
、
streams
。其中
streams
就是流配置列表,每个条目定义一路输入源,可以给这路流起一个简短的名字,方便后面引用。
下面是一个典型的配置文件,直接改摄像头 IP、用户名和密码就能用:
api:
listen: ":1984"
webrtc:
listen: ":8555"
candidates:
- 192.168.1.10:8555
- stun:8555
streams:
hallway:
- 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
living_room_fallback:
- rtsp://admin:password@192.168.1.66:554/main
- rtsp://admin:password@192.168.1.67:554/main
注意看
hallway
和
backyard
这两个名字,它们会出现在 Web 页面上,也会成为播放 URL 的一部分。所以名字尽量用有意义的英文或拼音,方便记忆和调用。同一个流名下面可以有多个地址,这一特性可以用于主备切换,当第一个地址拉流失败时,go2rtc 会自动尝试第二个,这就是
living_room_fallback
示例的作用。
3.2 海康、大华等 RTSP 摄像头接入实例
不同品牌的 RTSP 地址格式不太一样,我贴几个常用的参考。海康威视摄像头的主码流地址一般是:
rtsp://用户名:密码@IP:554/Streaming/Channels/101
末尾的 101 中,第一个 1 是通道号,后面的 01 代表主码流,想改用子码流就把 101 改成 102。子码流分辨率低、带宽小,适合网速一般或只要预览的场景。大华摄像头的地址通常长这样:
rtsp://用户名:密码@IP:554/cam/realmonitor?channel=1&subtype=0
subtype=0
是主码流,
subtype=1
是子码流。还有一些摄像头用的是类似的
/h264/ch1/main/av_stream
这种格式,以至于干脆到摄像头的 Web 管理页面的"网络"栏目里直接查看官方给的 RTSP 地址,那是最准的,我这里只是给个大概参考。
配置里最容易踩坑的是密码里的特殊字符。如果密码里有
@
、
:
这些符号,需要做 URL 编码。一个常见的错误是密码里带
@
,结果整条 RTSP 地址被解析错了。解决办法是把密码中的特殊字符换成
%40
(对应
@
)、
%3A
(对应
:
)等编码。也可以直接用引号把整个地址括起来,但最保险的还是编码干净。
改完配置后重启容器,然后在 go2rtc 的 Web 页面里点击对应的流名称,它会自动通过 WebRTC 开始播放。画面能出来,说明配置没问题。
3.3 RTMP 推流、HTTP MJPEG 等非常规源接入
RTSP 摄像头是最常见的情况,但总有一些家里的旧设备不走 RTSP。比如一些便宜的 WiFi 摄像头支持 RTMP 推流到自建服务器,那么 go2rtc 可以直接作为 RTMP 服务端接收推流。在配置里写:
streams:
rtmp_cam:
- rtmp://0.0.0.0:1935/live/cam1
然后摄像头端把 RTMP 推流地址设置为
rtmp://go2rtc地址:1935/live/cam1
,go2rtc 就能把这路 RTMP 流转发出来了。注意如果你用这种模式,需要把 1935 端口映射进容器(Linux host 模式则不需要),并且在防火墙中放行。
树莓派加 USB 摄像头或者 ESP32-CAM 这类设备,一般走的是 MJPEG HTTP 输出,地址形如
http://192.168.1.80:8080/video
,这种流在 go2rtc 里也能直接接:
streams:
esp_cam:
- http://192.168.1.80:8080/video
go2rtc 拿到 MJPEG 流后,会转成 WebRTC 或 HLS 输出。这种场景特别适合做一个"低成本多路监控面板",几个 ESP32-CAM 加一块树莓派就搞定了。
再提一个稍高级的用法:如果遇到很老的摄像头输出 MPEG4 编码画面,而浏览器普遍不支持这种编码,可以在源地址前加
ffmpeg:
前缀,让 go2rtc 调用内置 FFmpeg 转码:
streams:
old_cam:
- ffmpeg:rtsp://admin:admin@192.168.1.90:554/mpeg4#video=h264#audio=aac
#video=h264
的意思是要求转码输出 H.264,
#audio=aac
是把音频转成 AAC。这样的代价是会占用一部分 CPU,在树莓派上尤其要注意,一般只对个别老旧设备使用。
4. 播放层核心配置:WebRTC、HLS、MSE 三选一
4.1 WebRTC 低延迟播放:为什么延迟能压到 0.2 秒
go2rtc 最吸引我的功能就是原生 WebRTC。在浏览器端,WebRTC 是一套浏览器内置的实时通信能力,不需要任何插件。go2rtc 从摄像头拉流后,直接把流的编码和数据通过 WebRTC 传给你,不需要像 HLS 那样切片、分段拉取,所以画面延迟极低。
要正常使用 WebRTC,必须保证 UDP 端口 8555 能从浏览器所在的网络访问到 go2rtc 容器。如果你的浏览器和 go2rtc 在同一个局域网,只要映射好 UDP 端口就行。如果需要跨网络或公网访问,就要重点检查
webrtc.candidates
配置,它告诉浏览器"你应该往哪个地址发送音视频数据"。填错了,WebRTC 就表现为"一直连接中,然后黑屏"。
典型配置是在局域网环境里,把宿主机 IP 填进去:
webrtc:
listen: ":8555"
candidates:
- 192.168.1.10:8555 # 局域网内直接访问用这个
- stun:8555 # 有公网环境可以用 STUN 探测
stun:8555
表示通过 STUN 工具自动探测公网 IP。不过国内网络环境对 STUN 的友好度一般,所以我更建议在局域网里直接填宿主机 IP。如果有公网映射或内网穿透,就把映射后的公网地址或穿透域名填进去,可以填多个候选地址,浏览器会自动选能连通的那个。
4.2 HLS 播放:兼容性最好的兜底方案
虽然 WebRTC 是低延迟之王,但如果你的需求是"把监控画面发到微信群,大家点击链接就能看",HLS 依然是兼容性最稳的方案,覆盖 iOS 原生浏览器、微信内置浏览器、各种 Android 播放器。go2rtc 的 HLS 输出地址格式如下:
http://go2rtc地址:1984/api/hls?src=hallway.m3u8
其中
hallway
对应你在
streams
里配置的流名称。在页面里直接用 video 标签播放即可:
<video src="http://go2rtc地址:1984/api/hls?src=hallway.m3u8" controls></video>
go2rtc 实现的是低延迟 HLS(LL-HLS),比传统 HLS 延迟更低,实测一般在 1 到 3 秒,完全够用。不过要注意,HLS 转发会持续拉流,如果没人观看,go2rtc 也会持续工作,这可以通过流的按需配置缓解。
4.3 MSE、MP4 与播放地址清单
除了 WebRTC 和 HLS,go2rtc 还支持 MSE 和 MP4 输出。MSE 是浏览器 Media Source Extensions 的能力,可以用一个自定义播放器实现低延迟播放,一般在一些需要自定义 UI 的集成项目里使用。地址格式:
http://go2rtc地址:1984/api/mse?src=hallway
MP4 播放用于简单场景,例如直接在浏览器里打开视频文件流,或者给一些视频软件当网络源:
http://go2rtc地址:1984/api/playlist?src=hallway
在实际使用中,我在手机浏览器里用 HLS 链接做快速预览,在局域网 PC 上直接用 WebRTC 做低延迟观测。这套组合基本覆盖了我日常接触的所有设备,不管用什么系统、什么浏览器,总有一个能用的入口。
音频方面,很多摄像头支持双向语音,go2rtc 对音频的处理也相当省心。摄像头输出的常见音频格式是 G.711 或 AAC,go2rtc 会根据目标播放端自动转码或直通。WebRTC 播放端一般会转成 Opus,HLS 端则尽量保留 AAC。真遇到音频格式太怪、浏览器不买账的情况,可以给流增加
ffmpeg:
前缀并指定音频转码参数,比如
#audio=opus
。
5. 实战踩坑记录:常见问题与排查清单
5.1 WebRTC 打不开、画面一直黑屏
这是我被问得最多的问题。WebRTC 黑屏排查顺序是:先看 Web 页面显示的是什么状态,是连接中还是播放异常。如果是持续连接中,基本就是 8555 端口连通性有问题。我自己的树莓派部署就遇到过几次防火墙放行了 TCP 却漏了 UDP 的情况,WebRTC 就卡死了。解决办法是在宿主机上确认:
# 通过 ss 查看端口监听
ss -ulnp | grep 8555
ss -tlnp | grep 8555
# 有 ufw 的话检查规则
sudo ufw status
如果用的是 Docker 端口映射,还要确认
docker ps
里的端口映射正确。如果用的是 host 网络,就不用担心映射问题,但要单独放行宿主机的 8555 UDP。
另外,
candidates
配置要排除"回环地址"的干扰。有时候
localhost
或
127.0.0.1
会被加入到候选列表里,浏览器尝试连接这个地址自然会失败。手动填写宿主机局域网 IP 是最有效的解决办法。
5.2 摄像头连接不稳定、频繁掉线
摄像头掉线涉及的坑很多,但最常见的原因是同时拉流数量太多。很多家用摄像头只允许 1 到 2 路并发 RTSP 连接,go2rtc 在有人看的时候会建立连接,但如果不小心在配置里写了两条不同的地址引用同一个摄像头,或者同时有多个客户端在播放,摄像头就可能拒绝服务甚至直接重启。我遇到过一次摄像头死机后 DNS 和路由表都正常,但就是拉不到流,后来才发现是摄像头的弱电供电不稳导致的间歇性离线。这个问题排查方向太多,我建议把摄像头、交换机、路由器的供电都检查一遍,排除硬件问题再折腾配置。
如果摄像头在 go2rtc 里表现为"一会儿有画面,一会儿断流",可以在配置里对每个流增加按需或保活选项,并观察 go2rtc 的日志输出。通常日志里会出现
connection refused
或
timeout
字样,这有助于判断是网络问题还是摄像头主动拒绝。
5.3 Docker 网关导致的时间与网络异常
Docker 容器默认走 NAT 网络,时间久了会遇到几个细节问题。一是容器内时间是 UTC,日志和录像时间戳都对不上,解决方法是在环境变量里加
TZ=Asia/Shanghai
。二是容器内源 IP 在摄像头看来永远是 Docker 网关的 IP,部分摄像头会限制单个 IP 的连接数,进而出现"连上了但画面卡"的情况。这两个问题在切换到 host 网络后基本都能解决,也是我在 Linux 机器上极力推荐 host 模式的原因。
5.4 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| WebRTC 一直连接中 | 8555 UDP 不通 / candidates 没配好 | 放行 UDP,手动填宿主机 IP |
| 打开页面很卡 | 摄像头主码流分辨率太高 | 改接子码流,或加 ffmpeg 降码率 |
| RTSP 地址带着特殊字符连不上 | 密码中的字符没做 URL 编码 | 用 %40、%3A 等编码替换特殊字符 |
| 容器日志显示 UTC 时间 | 缺少时区配置 | 加 TZ=Asia/Shanghai 环境变量 |
| 摄像头频繁掉线 | 并发拉流太多或供电不稳 | 限制同时观看人数,检查摄像头电源 |
| 网页打不开 / 拒绝连接 | 1984 端口被防火墙拦截 | 放行 TCP 1984 端口 |
| 自动发现不到摄像头 | bridge 网络限制广播/多播 | 使用 host 网络模式 |
6. 从单路到整屋:多摄像头统一管理的进阶玩法
6.1 多路摄像头后台集中查看
go2rtc 自带的 Web 界面在最新版本里已经支持多路同屏预览,打开页面后可以看到所有配置好的流,点选多个画面就能组成一个简单的监控墙。如果你把页面当成一个全局监控后台,挂在内网的大屏或客厅电视上,体验相当接近商业监控软件。
更进一步,go2rtc 提供了 HTTP API,可以在不修改配置文件的情况下动态添加或删除流。例如向
/api/streams
发送 POST 请求,就能动态注册一路流。这对做自动化很有用,比如探测到新设备上线后自动加入监控列表。虽然我日常用配置文件已经够了,但如果你有集成需求,这个 API 是很灵活的补充。
6.2 外网访问与接入 Home Assistant
摄像头画面能远程看是我刚需。go2rtc 本身不解决内网穿透问题,但只要你有一个还行的内网穿透工具或者域名映射,把 1984 和 8555 UDP 端口暴露出去,并且正确配置
webrtc.candidates
为公网地址或穿透地址,就能实现远程的低延迟直播。这里有个安全提醒:摄像头画面属于隐私数据,尽量不要直接裸奔到公网,加一层身份认证或者用带鉴权的反代服务会更稳妥。我之前就见过有人只做端口映射、没有任何认证,结果公开了他家客厅的直播。
如果你在用 Home Assistant 做智能家居,go2rtc 有官方集成,方式是在 Home Assistant 的
configuration.yaml
中配置:
stream:
source: go2rtc
go2rtc:
url: http://go2rtc地址:1984
配置好之后,Home Assistant 里的摄像头实体可以直接使用 go2rtc 作为转流后端,自动化、通知卡片里的快照和流媒体都会走上 go2rtc,这套组合比每个摄像头单独接入 HA 省心很多,而且统一走 WebRTC,延迟小。
6.3 没有录像功能?与其他服务配合
一个容易踩的预期偏差是:go2rtc 主要定位是流媒体网关和转发,不内置完整的录像回放功能。如果你看重的是 NVR 那样"保存几天再回放"的功能,需要配合其他录像方案。我目前的方案是:让 go2rtc 输出一路 RTSP 流给一个轻量级 NVR 服务,或者直接靠摄像头自带的 SD 卡录制,go2rtc 只负责实时预览和对外分发。这样既有了稳定的实时画面,又不丢失录像能力,各司其职。
如果你习惯用 FFmpeg 录制,也可以直接在宿主机上跑一个定时命令,把 go2rtc 的 RTSP 输出录制成段文件:
ffmpeg -i rtsp://localhost:8554/hallway -c copy -f segment -segment_time 3600 /recordings/hallway_%Y%m%d_%H%M%S.mp4
这种组合方式可以按需扩展,不会给 go2rtc 容器增加额外负担。
写到这里,回到我开头那个场景:海康、大华、树莓派三套设备,现在都收在同一个页面里,打开
http://192.168.1.10:1984
,点一下就是实时画面。我个人在实际使用中最满意的一点是 go2rtc 对 WebRTC 的原生支持,让我终于摆脱了"看着 HLS 延迟发呆"的体验,也让家里老人用手机看监控时不用装一堆 App。
最后再分享一个小技巧:如果哪天修改了 go2rtc 的配置,最简单的重载方式是重启容器,因为目前它对配置热加载的支持还比较有限。而且升级 go2rtc 运行时要注意,新版本偶尔会调整默认参数,建议升级前把旧的容器配置备份一份。希望这篇记录能帮你把自家或手头的摄像头流聚成一套真正好用的多协议平台。
更多推荐

所有评论(0)