基于Docker部署go2rtc:打通RTSP/RTMP/WebRTC多协议摄像头接入
玩摄像头久了,几乎都会遇到同一个问题:家里、工作室、公司园区里的设备五花八门,海康、大华、小米、TP-Link、杂牌 IPC,有的走 RTSP,有的只能吐 RTMP 或 MJPEG,还有的干脆是 HLS 直播地址。手里一堆链接,想在浏览器里随时看,浏览器又不原生支持 RTSP,装 VLC 也就算了,想接到自己的平台、App、Home Assistant 里统一预览,更是折腾。
后来我换成了 go2rtc,这个问题基本就终结了。它用 Go 写的,单二进制就能跑,专治各种协议不互通,把 RTSP、RTMP、HLS、MJPEG、WebRTC 全部揉在一起,对外统一输出。再用 Docker 一包,配置写在 yaml 里,几行搞定,重启不丢配置,迁移也方便。这篇文章就基于我的实操经历,从部署、配置、协议接入、播放测试到问题排查,完整过一遍这套基于 Docker 的 go2rtc 多协议流媒体平台搭建过程。不管你是刚接触自建监控,还是想把现有摄像头体系盘活,都可以直接照着抄。
1. go2rtc 是什么?为什么摄像头接入这么麻烦
1.1 摄像头协议分裂的现状与核心痛点
说 go2rtc 之前,得先理解摄像头接入为什么让人头大。IPC 摄像头行业虽然发展了这么多年,协议层面却一直很分裂。主流的接入方式大概是这几类:
-
RTSP:大部分专业安防品牌和网络摄像头都支持,比如海康、大华、TP-Link 的不少型号,默认端口 554。但每个厂家的 RTSP 路径长得都不一样,有的叫
/h264/ch1/main/av_stream,有的叫/Streaming/Channels/101,甚至同品牌不同型号还有差异。 -
RTMP:很多国产云台摄像头、直播类设备首选,端口常见 1935,路径通常是
/live/stream这种。 -
HLS:云摄像头、纯网络平台输出用的多,最典型的就是
http://ip/live/xxx.m3u8这种地址,延迟几秒起步,胜在兼容性好。 -
MJPEG:古董级摄像头和一部分 USB 摄像头会提供
http://ip/video.cgi这种裸 MJPG 流,几乎所有浏览器都能看,但帧率和体积感人。 - 私有协议:小米、萤石这类带云服务的品牌,虽然底层也是 RTSP,但官方常常不直接给你地址,要自己在 App 里打开“局域网协议”开关,或者用 ONVIF 去探测。
问题就来了:你手上有 5 个摄像头,可能就有 3 种协议、5 种网址格式。有些能 VLC 看,有些只能在网页里看,有些只能用厂商 App 看。如果要做一个统一的监控页面,或者接到自己写的后端里,就得自己写代码去适配每一种协议、处理底层解码和推流,这个工作量非常大,而且视频流这东西,UDP 丢包、TCP 阻塞、时间戳错乱,每一样都够折腾一阵。
1.2 go2rtc 的工作原理与设计理念
go2rtc 的思路很直接:你只需要把每一路摄像头源写在配置文件里,它负责按需往对应的摄像头拉流,然后对外统一提供多种访问协议。它拉流是“按需”的,不是把所有流都常驻拉进来,这点对 CPU 和带宽非常友好。
它的核心处理流程,说白了就是“源协议进、目标协议出”:上游是 RTSP 也行、RTMP 也行、HLS 也行,输出端可以是 WebRTC、HLS、MSE、RTSP、RTMP、MP4 文件,随便挑。它内部有转发和转码两条链路。能直接转发的协议就尽量转发,不改编码、不重编码,消耗极低;遇到浏览器播放不了的情况,再调用内置的 FFmpeg 做一次转码。
这里最值得关注的其实是 WebRTC。传统 RTSP 摄像头没法直接在浏览器里播放,HLS 延迟又高,拉流播放总是好几秒。go2rtc 利用 WebRTC 把视频包通过 UDP 直接推给浏览器,延迟能压到 0.2 到 0.5 秒左右,差不多是实时看监控的体验。这个能力是 go2rtc 相比很多传统转流服务的核心竞争力。
1.3 它和 ZLMediaKit、MediaMTX、FFmpeg 工具链的区别
很多接触过流媒体的人会问,用 FFmpeg 自己转流不就行了?为什么还要上 go2rtc?说实话,FFmpeg 命令行确实能做单路转推,但那只是“一次性任务”,每增加一个客户端、每增加一路摄像头,都要手动起进程、维护端口、做鉴权,长期用非常痛苦。ZLMediaKit、MediaMTX 这类流媒体服务可以做统一收流和分发,但部署重、配置复杂,对普通玩家来说更像是一个正规军武器,用来搞大规模商业场景没问题,自己家里看几路监控就有点杀鸡用牛刀了。
go2rtc 的定位更像是轻量全能中转站,部署成本极低、配置简单、自带 Web 界面和 API,而且它明显是为 Home Assistant 生态设计的,和智能家居联动非常方便,很适合个人用户、家庭监控、中小型工作室的场景。一句话总结:FFmpeg 是工具,ZLMediaKit 是平台,go2rtc 是“即插即用”的中转节点。
2. Docker 部署前的准备与镜像拉取
2.1 部署前需要确定的环境
开始部署前,建议先把下面的信息理一遍,省得装到一半才发现网络或目录不对。
- 运行环境:一台能跑 Docker 的机器即可,可以是群晖/威联通 NAS,也可以是家里的旧电脑、云服务器,甚至树莓派。go2rtc 本身很轻,内存占用低的时候只有几十 MB,对硬件没有要求,但如果你有大量摄像头需要转码,建议给 CPU 留点余量。
- Docker 版本:Docker 20.10 以上基本都行,不需要特殊内核模块。Windows 上用 Docker Desktop,Linux 上直接用 docker engine,macOS 也一样。注意不要用特别古老的版本,拉镜像、挂载卷、端口映射这些基础功能稳定就好。
- 端口规划:go2rtc 默认 Web 管理界面和 API 都在 1984 端口,WebRTC 相关的端口默认是 8555(TCP 和 UDP)。如果 1984 被占了,Docker 映射时改成如 8084:1984 就行。需要 UDP 8555 这个端口能被访问到,否则 WebRTC 的媒体协商和数据传输会出问题。
-
目录规划:准备一个专门放配置的目录,我习惯放在
/data/go2rtc/config下面,里面就放一个 go2rtc.yaml 文件。容器内统一挂载到/config,这样以后升级镜像,配置不会丢。
2.2 拉取官方镜像与加速配置
go2rtc 的镜像名是
alexxit/go2rtc
,这是项目作者自己维护的,跟着 GitHub 主仓库持续构建。拉取命令很简单:
docker pull alexxit/go2rtc
在国内网络环境下,有些用户会遇到镜像拉取慢、超时的情况。这通常不是 go2rtc 的问题,而是 Docker Hub 的默认源访问不畅。可以在 Docker 的 daemon.json 里配置 registry-mirrors 来走加速源。修改配置文件之后,一定要重启 Docker 服务和容器,这个很关键。
配置大致这样:
{
"registry-mirrors": ["https://docker.mirrors.example.com"]
}
至于具体填哪个加速地址,不同地区的网络情况差别很大,我不做统一推荐,你可以结合自己实际能访问的加速源来配置。如果不想折腾加速,也可以从 GitHub Packages 拉取
ghcr.io/alexxit/go2rtc
这个镜像地址,两条路二选一。
2.3 最简部署:一条 docker run 命令
不想写复杂配置的话,直接用 docker run 也能跑起来。先看看最快能跑通的样子:
docker run -d --name go2rtc \
--restart unless-stopped \
-p 1984:1984 \
-p 8555:8555/tcp \
-p 8555:8555/udp \
-v /data/go2rtc/config:/config \
alexxit/go2rtc
这里每个参数都值得说一句:
-
-d --name go2rtc:后台运行,名字叫 go2rtc。 -
--restart unless-stopped:容器挂了或者机器重启之后自动拉起,除非你手动停止,否则它不会死。 -
-p 1984:1984:把容器里的 Web/API 接口映射到宿主机。 -
-p 8555:8555/udp:WebRTC 的媒体通道,走的是 UDP,记得把 UDP 也映射出来。这个特别容易被漏掉,漏了之后浏览器画面能出,但经常会卡顿或连接失败。 -
-v /data/go2rtc/config:/config:配置目录挂载。
第一次启动后,浏览器访问
http://你的IP:1984
,看到 go2rtc 自带的 Web 界面,就说明跑起来了。这时候页面里没有流,因为配置文件还没写,界面上有一个输入框可以临时测试拉流地址,这个我们后面再说。
3. docker-compose 完整部署与目录规划
3.1 为什么更推荐 docker-compose
docker run 适合快速验证,但真正长期使用,我更推荐用 docker-compose。原因很简单:run 命令一长串,升级的时候容易记混;而 compose 文件把镜像、端口、卷、重启策略、环境变量全部固化下来,放在项目目录里,团队协作或换机器部署时,一条
docker compose up -d
就能还原整套环境。
版本兼容性方面,新的 Docker 版本已经直接用
docker compose
子命令,老的 compose 也可以继续用
docker-compose
,二者区别不大。
3.2 docker-compose.yml 逐段解析
我的 go2rtc 部署目录大概是这样的:
/data/go2rtc/
├── docker-compose.yml
└── config/
└── go2rtc.yaml
docker-compose.yml 内容如下:
version: "3.8"
services:
go2rtc:
image: alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
ports:
- "1984:1984" # Web 管理界面 / API
- "8555:8555/tcp" # WebRTC 信令辅助
- "8555:8555/udp" # WebRTC 媒体通道
volumes:
- ./config:/config
environment:
- TZ=Asia/Shanghai # 日志时间按本地时区显示
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
这个配置里有几个细节:
-
restart: unless-stopped是家庭服务器、NAS 场景的首选。机器重启后,容器只要没被手动 stop,就会自动拉起。 -
./config:/config用的是相对路径,要求你创建这个目录后再cd进去执行 compose 命令。如果你有强迫症,写成绝对路径/data/go2rtc/config:/config也可以。 -
TZ=Asia/Shanghai保证容器内日志时间不是 UTC,查日志的时候不会差 8 小时。 -
logging里给日志加上轮转,防止跑几个月之后日志把磁盘塞满。这个对长期运行的项目很重要。
然后启动:
cd /data/go2rtc
docker compose up -d
查看状态和日志:
docker ps
docker logs -f go2rtc
看到日志里出现
api listening on :1984
之类的信息,就代表启动成功了。
3.3 配置文件与容器内路径的对应关系
go2rtc 兼容两种配置文件后缀,新版本推荐用
go2rtc.yaml
,旧版本习惯写
go2rtc.conf
,实际内容都是 YAML 格式。挂载到
/config
后,容器会自动去
/config
找配置文件。
需要注意:如果你改完配置想让 go2rtc 重新加载,不一定非要重启容器。Web 界面上有一个“Reload”按钮,点击后会重新读取配置,不断开现有连接,这个体验很省心。如果是大改、改了 API 鉴权这类配置,保险起见就直接
docker restart go2rtc
。
4. 摄像头多协议接入的配置实战
4.1 config 文件基本结构
go2rtc 的配置文件核心就是
streams
字段,它定义“哪些流叫什么名字、去哪里拉流”。一个最基础的配置长这样:
streams:
front_door: rtsp://admin:password@192.168.1.108:554/h264/ch1/main/av_stream
backyard: rtsp://admin:password@192.168.1.109:554/Streaming/Channels/101
living_room_mjpeg: http://192.168.1.110/video.cgi
media_server: rtmp://192.168.1.120:1935/live/stream
每一行的格式都是
流名称: 流地址
。流名称建议用英文和下划线,后面在播放地址里会用到,比如
http://IP:1984/api/webrtc?src=front_door
。如果源地址带有用户名密码,直接嵌在 URL 里;注意密码里如果有特殊字符,要先把
@
、
:
、
#
、空格这类字符做 URL 编码,比如
@
编码成
%40
,不然解析会出问题。
4.2 不同协议摄像头源的接入写法
不同品牌、不同协议,在 go2rtc 里的写法差别不大,本质上就是把能拉流的地址填进去。但有几个典型场景值得单独记录:
- RTSP 摄像头
绝大多数专业摄像头都用这种。常见写法:
streams:
cam1: rtsp://user:pass@192.168.1.64:554/stream1
如果走的 UDP 传输不稳定,或者画面经常卡顿,可以在地址后面加 TCP 参数,强制走 TCP 通道:
streams:
cam1: rtsp://user:pass@192.168.1.64:554/stream1?tcp
我自己实测下来,局域网内大部分摄像头走 TCP 反而更稳定,尤其是无线摄像头,UDP 丢包会导致花屏、卡顿。
- RTMP 推流源
很多直播摄像头、编码器支持 RTMP 输出。go2rtc 直接支持:
streams:
live_encoder: rtmp://192.168.1.50:1935/live/cam
- HLS 直播地址
如果你手上的源本身就是一个 m3u8 地址,比如一些云平台的直播流,go2rtc 也能直接拉:
streams:
cloud_cam: http://example.com/live/stream.m3u8
- MJPEG 源
老摄像头或者 USB 摄像头常见:
streams:
old_cam: http://192.168.1.88:8080/video.cgi
- “不知道是什么协议”的源
go2rtc 支持在源地址前加一个
ffmpeg:
前缀,强制用 FFmpeg 进程去拉流。这个方法适合那些协议不标准、RTSP 解析失败或者需要额外转码的源:
streams:
weird_cam: ffmpeg:rtsp://user:pass@192.168.1.123:554/live
加了前缀之后,go2rtc 会调起内置的 FFmpeg 去建立连接,把源处理成标准流,再给 go2rtc 转发。代价是多一次进程开销,但兼容性确实好很多。
4.3 常见品牌摄像头的 RTSP 地址参考
很多新手卡在“不知道摄像头 RTSP 地址到底填什么”。我整理了常见的几个品牌参考,覆盖大多数家用和半专业设备:
| 品牌 | 常见 RTSP 地址模式 |
|---|---|
| 海康威视 |
rtsp://user:pass@IP:554/h264/ch1/main/av_stream
|
| 大华 |
rtsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0
|
| TP-Link |
rtsp://user:pass@IP:554/stream1
|
| 小米 |
rtsp://user:pass@IP:554/ch0_0.h264
(部分型号需在米家开启局域网协议)
|
| 萤石 |
rtsp://user:pass@IP:554/h264/ch1/main/av_stream
|
需要说明两点。第一,同一品牌的固件版本不同,地址可能完全不同,这个表只能作为起步参考。第二,很多家用摄像头默认关闭 RTSP 服务,需要先去厂商 App 里找到“局域网 RTSP”、“ONVIF 开关”之类的选项打开,然后再去探测地址。如果实在找不到,建议按摄像头 IP 段扫描一下,比如用 ONVIF Device Manager 这类工具批量探测支持的流地址,能省不少事。
4.4 用 FFmpeg 转码解决 H.265 / 格式不兼容
现在很多摄像头默认主码流是 H.265/HEVC 编码,画质好、码率低,但问题在于浏览器原生是不支持 H.265 直接播放的,至少 Chrome、Firefox 都不行。你通过 WebRTC 拉流,结果显示黑屏或者无法解码,大概率就是编码格式问题。
go2rtc 解决这个问题的思路是:要么改编码格式,要么转码。改编码格式的方法,是在 RTSP 地址后面加
#video=h264
这样的过滤器,让摄像头输出 H.264 编码:
streams:
cam_h265: rtsp://user:pass@IP:554/stream1#video=h264
这个方案的原理是,go2rtc 会尝试通过 RTSP 的编码参数协商,要求摄像头以 H.264 格式输出。部分摄像头支持,部分不支持,所以它不是万能的。
如果摄像头不支持改编码,就只能硬转码了,也就是强制走 ffmpeg 通道:
streams:
cam_h265: ffmpeg:rtsp://user:pass@IP:554/stream1#video=h264
这个
#video=h264
在 ffmpeg 模式下,会作为 FFmpeg 的输出编码参数,把 H.265 源转成 H.264 再交给 go2rtc。代价是 CPU 占用会上去,我实测一路 1080P H.265 转 H.264,普通 x86 平台大概占 10% 到 20% 的单核资源,低功耗 ARM 设备可能更吃力,所以没必要时不建议每路都转。
5. 多协议播放:从 WebRTC 到 HLS 的完整链路
5.1 WebRTC 低延迟播放,实测视频秒开
go2rtc 部署完成的最高光时刻,就是在浏览器里直接看到摄像头的实时画面,而且延迟低到几乎无法察觉。
go2rtc 的 Web 页面
http://IP:1984
会把配置文件里所有流都列出来,点击任意一个流,它会默认通过 WebRTC 协议播放。播放页面上显示的延迟,我实测局域网环境下通常在 0.2 到 0.5 秒之间,基本就是“摄像头拍到手、屏幕立刻显示”的效果,非常适合看猫、看门、看打印机这种需要实时交互的场景。
如果自己写页面或者用第三方前端,WebRTC 播放的 API 地址是:
http://IP:1984/api/webrtc?src=front_door
前端通过 RTCPeerConnection 和 go2rtc 做 SDP 协商,go2rtc 负责把摄像头源转成 WebRTC 需要的 RTP 包直接推到浏览器。这整个链路不需要你写任何服务端推流代码,go2rtc 全包了。
5.2 用 VLC、ffplay、HLS 兼容其他播放端
WebRTC 虽然体验好,但并不是所有场景都适合。比如你要在电视盒子、手机原生播放器、老终端上看,或者要拿给不支持 WebRTC 的程序消费,那就得用其他协议。go2rtc 支持把同一路流同时以多种协议输出,不需要额外配置,直接拼 URL 就行:
-
RTSP 转发播放:
rtsp://IP:1984/front_door,可以直接用 VLC、ffplay 打开。 -
HLS 播放:
http://IP:1984/api/hls?src=front_door,适合 iPhone 自带播放器、网页 Video 标签。 -
MSE 播放:
http://IP:1984/api/mse?src=front_door,适合浏览器里用 MSE 方式播放,兼容性更好。 -
MP4 录像回放:
http://IP:1984/api/stream.mp4?src=front_door,适合临时录像。
我常用的临时验证方法是:
ffplay -rtsp_transport tcp rtsp://127.0.0.1:1984/front_door
这样一条命令就能确认 go2rtc 转发链路是否正常。如果这条能出画面,说明源和 go2rtc 之间没问题,剩下的只是在浏览器端选择合适协议。
5.3 多路同时预览与 API 接口
把好几个摄像头一起放到同一个页面里预览,是监控场景的刚需。go2rtc 的 Web 界面本身就支持多路同时打开,每个画面一个独立窗口,可以用 MSE 方式多开。如果你要自己做一个监控面板,可以直接调用 go2rtc 的 JSON API 获取流列表和状态信息:
curl http://IP:1984/api/streams
返回结果是一个 JSON,包含所有已配置和已连接的流,以及当前的转发状态、客户端数等。有了这些数据,你可以很容易在自建页面里做一个动态的摄像头列表,再配合 WebRTC 播放器和 HLS 回退,基本就是一个完整的自建监控平台了。
6. 常见问题与排查技巧实录
6.1 容器启动失败、反复重启
出现这种情况,先用一句话定位问题:
docker logs go2rtc
如果日志里显示
yaml: line X: mapping values are not allowed
,那就是配置文件里有语法错误,比如冒号后面没加空格、缩进不统一。YAML 对缩进非常敏感,这个只能仔细检查。如果日志显示
bind: address already in use
,说明 1984 端口被宿主机上的其他程序占用了,换一个宿主机映射端口即可,比如
-p 1985:1984
。
6.2 画面黑屏或一直 loading
黑屏大概率是编码格式不兼容,优先检查摄像头的主码流是不是 H.265。可以临时加一个
ffmpeg:
前缀做转码,或者让摄像头把主码流切到 H.264。另一个常见原因是浏览器放不了 WebRTC 需要的媒体格式,这时候可以先临时改用 MSE 播放,如果 MSE 能出画面,基本就是 WebRTC 协商或网络层面的问题。
还有一种情况是摄像头源本身拉不通,但配置没报错。这种建议先打开
http://IP:1984/api/streams
看流的状态,如果显示连接错误,多半是用户名密码、RTSP 路径不对,或者摄像头没有开启局域网访问。也可以直接在 Web 界面的输入框里粘贴原始流地址测试,go2rtc 会返回拉流是否成功,非常直观。
6.3 延迟高、画面卡顿
延迟高先区分是哪种播放方式。如果用 HLS,自带几秒延迟是物理特性,不是 go2rtc 的问题。要低延迟就切 WebRTC。如果 WebRTC 还是卡,重点检查网络和摄像头码流:
- 摄像头码流太大,建议用子码流做实时预览,主码流只用来录像。
-
RTSP 默认 UDP 传输,局域网内如果丢包率偏高,在地址后面加
?tcp强制走 TCP。 - 确认容器的 UDP 8555 端口映射了,且宿主机防火墙放行了 UDP。WebRTC 的媒体数据走 UDP,映射少了画面会断断续续或者干脆出不来。
6.4 暴露公网时的鉴权与安全
go2rtc 的 Web 界面默认没有任何鉴权,谁拿到 IP 端口就能看你的摄像头。如果只在内网使用问题不大,但如果要远程访问,不建议直接把 1984 端口裸暴露到公网。有两个更稳的选择:一是给 go2rtc 的 API 加上用户名密码,在配置里增加:
api:
listen: ":1984"
username: admin
password: 改成强密码
二是用带身份认证的反向代理,比如 Nginx 或 Caddy,把 Web 和 API 路径走 HTTPS + Basic Auth,甚至只暴露 Tailscale 虚拟局域网内的地址。我自己就是三层防护:内网直接用,外网走带鉴权的反代,摄像头本身再用 VLAN 隔离,这样基本就不担心隐私泄露的问题了。
6.5 与 Home Assistant 等智能家居平台联动
如果你家里装了 Home Assistant,go2rtc 几乎是无缝接入的。Home Assistant 的 Stream 组件本身支持 go2rtc 作为默认流提供者,配置好之后,HA 里的摄像头实体可以直接调用 go2rtc 的流地址。最典型的用法是:把海康的 RTSP 流接入 go2rtc,然后 HA 里直接通过 go2rtc 的方式生成摄像头实体,再配合自动化做有人检测、录像、推送到手机,基本一套组合拳就能把厂家的云服务完全扔掉。
我自己实际踩过几次坑之后,最后的体会是:go2rtc 这个项目理念非常克制,它不追求做一个大而全的流媒体平台,而是把所有协议转换的脏活累活都藏在背后,让使用者只关心“源是什么、想用什么方式看”。Docker 又把这个过程变成了目录里一个几十行的 yaml 文件。你甚至可以把这个 compose 目录打包带走,换一台机器
docker compose up -d
,摄像头配置原样恢复。
如果后面想继续扩展,我建议可以从这几个方向入手:给 go2rtc 配上强制转码缓存,接入多路 4K 摄像头时显著降低重复拉流压力;用断言 API 做流健康检查,把断流自动重启的脚本接到容器里;或者把 Web 界面放到自己的导航页里,做成一个真正的家庭监控中心。这套基础打好了,往上加东西会非常顺手。
更多推荐
所有评论(0)