我家里现在有三个品牌的摄像头,海康、大华、还有个杂牌,它们的取流方式没有一个是一样的。海康给了RTSP地址,大华直接连ONVIF能发现,杂牌那台只放了RTMP推流地址。之前我把这些流分别交给VLC播放器、NVR和FFmpeg轮询,维护起来非常割裂。后来我在 Docker 里部署了 go2rtc,只用一个 YAML 文件就把这几路流的接入统一了,再通过浏览器 WebRTC 秒开预览,延迟基本体感不到。这篇文章把这套部署方式完整记录下来,包含端口规划、摄像头型号参数、断流恢复这些实战细节,适合正好想把手里杂牌摄像头统一管理的朋友。

1. go2rtc 解决的痛点:摄像头协议割裂与高延迟播放

1.1 摄像头接入的三个层面对不齐

先说场景。很多时候我们发现问题不是某一路摄像头接不上,而是所有摄像头在一起时非常混乱。协议层面,海康喜欢用 rtsp://ip:554/Streaming/Channels/101 ,大华是 rtsp://ip:554/cam/realmonitor?channel=1&subtype=0 ,TP-LINK 又是 rtsp://ip:554/stream1 ,彼此之间没有统一规律。再遇到只支持 RTMP 推流的设备,情况就更麻烦,很多老平台的 RTMP 地址没法直接在浏览器里播放。播放层面,浏览器原生不支持 RTSP,如果非要看画面,要么装 VLC 这类客户端,要么临时起 FFmpeg 把 RTSP 转成 HLS 挂到网页上。转换的进程一多,端口管理和进程守护就成了新负担,摄像头一重启,那一堆 FFmpeg 命令基本都要手动再搞一轮。转发层面也存在问题,多个人同时点开同一个摄像头地址,摄像头本身要同时编码输出多路流,很多设备的硬件编码能力根本承受不住,画面卡顿、设备过热都很常见。

我当时的诉求很明确:把各协议统一到一个入口,浏览器直接打开就能看,延迟尽量低,多人观看时不要给摄像头增加负担。go2rtc 正好把这几个问题都覆盖了。

1.2 go2rtc 的定位:流媒体网关,而不是转码器

go2rtc 是 Go 语言写的单体服务,官方 Docker 镜像很小,部署就是拉一个容器再挂一个配置文件的事。它的核心能力是协议转换和流分发,支持 RTSP、RTMP、HLS、LL-HLS、MSE、WebRTC、ONVIF、SIP 这些常见协议。你可以把不同来源的流都汇聚到它这里,再由它统一对外输出;它也内置了 Web UI 和一组 HTTP API,方便管理和调试。项目最初在 Home Assistant 社区里很流行,后来很多玩摄像头、做多路流预览的人都开始单独部署它,正是因为协议覆盖面广且切换链路很干净。

有一点需要提前建立认知:go2rtc 本身不是完整转码服务。它默认做的事情是“直接转发原始流”,也就是摄像头输出的 H.264/H.265 画面是什么编码,它转到浏览器里还是什么编码,只是换了个传输方式。这样 CPU 占用极低,也基本不引入画质损失。真正的转码它也能做,通过内置 FFmpeg 模块以 ffmpeg: 前缀的方式启动,但那是兜底方案,不是默认选项。

1.3 和常见方案放在一起比

很多人可能会问:既然有 FFmpeg、nginx-rtmp、甚至厂商自己的 SDK,为什么还要单独上 go2rtc?我做过一个简单对比:

方案 协议接入 浏览器低延迟播放 多客户端共享上游 配置维护成本
go2rtc 多协议统一 WebRTC,亚秒级 天然支持 一个 YAML
FFmpeg 转 HLS 需自己写命令 高,约 3-10 秒 需配合 Nginx 进程多、易乱
nginx-rtmp 主要收 RTMP 需再转 HLS 支持一般 配置分散
厂商 SDK 自研 单一厂商 看实现 看实现 开发量最大

我实际用下来,go2rtc 最省心的地方就是“只维护一个配置文件”和“WebRTC 播放不需要额外插件”。其它方案也不是不能跑,但要从零把多协议接入、低延迟播放、多客户端分发全部串起来,工作量会涨好几倍。

1.4 哪些场景不适合 go2rtc

说实话,go2rtc 也不是万能。如果你的目标是把各路流统一转码成固定的 HLS 切片给几千人看直播,那它不太合适,更合理的做法是 FFmpeg 负责转码,CDN 负责分发,go2rtc 只需要作为推流入口之一。如果你手头全是完全封闭的私有协议设备,比如某些老平台只允许用自家 App 连接,不支持 RTSP/RTMP/ONVIF 任何一项,那 go2rtc 也帮不上忙,只能先把设备接到能转出标准流的边端设备上,再接进来。理解了它的边界,后面调参就不会走偏。

2. 部署前先定几件事:镜像、端口、配置挂载和网络模式

2.1 镜像选择与版本习惯

go2rtc 的 Docker 镜像名是 alexxit/go2rtc ,直接拉下来就能用。它更新节奏比较快,我建议日常使用固定一个大版本 tag,不要长期追 latest,否则某天升级可能遇到配置格式不兼容或者启动参数变化。我自己会在项目 Releases 页面看当前推荐 tag,然后写进 compose 文件里。配置备份也很轻松,整个服务就一个 YAML 文件,升级前复制一份出来就够了。

镜像本身很小,基础环境干净,容器里只跑它一个进程,不会像某些全家桶镜像那样塞一堆系统工具进来。这在磁盘空间紧张的小主机上尤其舒服。

2.2 端口职责先说清楚:1984、8554、8555

go2rtc 默认使用三个端口,很多人第一次部署就是被这三个端口搞糊涂的。

端口 协议 作用 是否必开
1984 TCP Web UI、HTTP API、WebRTC 信令 必开
8555 UDP WebRTC 的媒体数据传输 浏览器播放时必开
8554 TCP go2rtc 对外提供 RTSP 转发服务 按需开

1984 是核心入口,浏览器打开 Web UI、调用 API 都走这里。8555 是 WebRTC 传输视频流的 UDP 端口,如果你希望用浏览器低延迟预览,这个端口必须映射到宿主机,而且最好保持端口号不变。8554 则是当你想把 go2rtc 里已经统一的流再以 RTSP 方式提供给外部 NVR、录像机或其它播放器时才会用到。我一开始只开了 1984,浏览器界面能打开但画面一直转圈,后来检查了一番才发现是 8555 的 UDP 端口没有映射出来。

2.3 配置文件的挂载方式

go2rtc 的服务配置都写在 go2rtc.yaml 里,Docker 部署时直接把目录挂到容器内的 /config 就行。我习惯在主目录下建一个 go2rtc 文件夹,里面只放这个 YAML 文件,方便备份。

容器启动后,它默认会读取 /config/go2rtc.yaml 。如果你用不同文件名,可以通过启动参数指定路径。只要挂载目录不出错,后续修改配置可以用 Web UI 在线编辑,保存后热重载,不需要重启容器。

2.4 网络模式:Linux 上用 host,Windows/macOS 上要有心理准备

这是 go2rtc 部署里最大的一个坑,也是很多人 WebRTC 连不上的根本原因。在 Linux 主机、NAS 或树莓派上,我强烈建议使用 network_mode: host 。host 模式下容器直接共享宿主机网络,1984、8555、8554 这些端口天然对应宿主机的真实 IP 和端口。WebRTC 在做协商时会把宿主机的 IP 信息带进信令,浏览器拿到这个地址就能直接建立 UDP 通道,链路最顺畅。

但在 Docker Desktop 这类运行在虚拟机里的环境上,host 网络模型的表现不一样,尤其 UDP 端口的透传和地址映射偶尔会有问题。症状很典型:Web UI 里能看到摄像头列表,也能看到流已经开始拉取,但播放画面一直停在 connecting。如果你必须用 Docker Desktop,可以先尝试标准的 bridge 模式加端口映射:

ports:
  - "1984:1984"
  - "8555:8555/udp"
  - "8554:8554"

如果仍然连不上,别浪费时间折腾了,直接把 go2rtc 挪到局域网内的 Linux 小主机或者 NAS 上跑。长期做流媒体服务,一个稳定的 Linux 运行环境比什么都重要。

3. 用 docker compose 起服务,接入第一路摄像头

3.1 一份可以直接用的 compose 文件

我自己的部署环境是局域网里一台 J4125 小主机,Debian 系统,Docker 跑的 compose。文件长这样:

services:
  go2rtc:
    image: alexxit/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/go2rtc:/config

如果网络环境不支持 host 模式,就把 network_mode: host 注释掉,改成我上面说的 ports 映射。 /opt/go2rtc 是我自己定义的宿主机目录,你可以改成任意路径。 restart: unless-stopped 保证小主机重启后 go2rtc 能自动起来,这点对无人值守运行很重要。

启动之前先把配置目录建好,并放一个最小的 go2rtc.yaml 进去,否则容器起来可能因为找不到配置直接退出,或者启动一个空服务。

3.2 最小配置文件:只加一个 RTSP 摄像头

先写一个最简单但能立刻工作的配置:

log:
  level: info

streams:
  down_hall: rtsp://admin:YourPassword@192.168.1.10:554/Streaming/Channels/101

这里的 down_hall 是我给这路流取的名称,后面所有播放地址里都会用到它。 rtsp:// 后面是摄像头的实际地址和账号密码。在写进配置前,我建议先用 VLC 试播一下这个地址,确认摄像头本身能出画面,再交给 go2rtc。这样可以缩小排障范围,避免把摄像头侧的问题误判成 go2rtc 的问题。

3.3 启动、验证和第一个流的确认方式

执行:

docker compose up -d
docker compose logs -f

看到日志里没有报错、摄像头状态变成 online 之后,打开浏览器访问:

http://小主机IP:1984

Web UI 里能看到 down_hall 这路流,点播放如果秒出画面,第一步就算跑通了。如果还是想用命令行确认,可以执行:

curl http://127.0.0.1:1984/api/streams

返回的 JSON 里会列出当前所有流的状态。online 表示正常,offline 表示连接失败,这个接口在我们后面排查问题时非常有用。

3.4 后续加流:在线配置与热重载

go2rtc 的 Web UI 里有一个 Config 页面,可以直接编辑 go2rtc.yaml 并保存。保存后服务会自动应用新配置,不需要重启容器。这个功能非常实用,我后来加摄像头都是直接在网页上补一个 streams 条目,几十秒就生效。需要注意,在线编辑失败时页面上会给出错误提示,此时不要强行刷新丢弃错误信息,先复制内容留底,再修正。

4. 多品牌摄像头接入:RTSP 参数、ONVIF 发现与 FFmpeg 兜底

4.1 常见品牌的 RTSP 地址规律

不同品牌的默认取流地址各不相同,我把遇到过的规格整理了一下:

品牌/设备 常见 RTSP 地址格式 备注
海康威视 rtsp://user:pass@ip:554/Streaming/Channels/101 101 主码流,102 子码流
大华 rtsp://user:pass@ip:554/cam/realmonitor?channel=1&subtype=0 subtype=0 主码流,1 子码流
TP-LINK rtsp://user:pass@ip:554/stream1 不同型号可能是 stream2
宇视 rtsp://user:pass@ip:554/camera1 部分需要 /media/video1
通用 ONVIF rtsp://user:pass@ip:554/onvif1 很多第三方设备都支持这个路径

这些只是最常见的路径,同品牌不同固件也可能不一样。我一般打开摄像头后台的管理页面,在“音视频”或“媒体设置”里都能看到 RTSP 取流地址示例,照抄最稳。

4.2 不想猜地址,就先用 ONVIF 发现

如果摄像头支持 ONVIF,go2rtc 的 Web UI 里可以直接做设备发现。它会扫描局域网内的 ONVIF 设备,弹出来之后你填上摄像头用户名密码,系统会自动尝试探测 RTSP 地址并生成对应的 streams 配置。对于大华这类 ONVIF 支持比较完整的设备,这一步基本不需要手写地址。前提是摄像头后台里没有把 ONVIF 功能关闭,很多设备默认是开启的,但也有一些安全模板会顺手把它关掉。

ONVIF 发现机制本质上是通过 WS-Discovery 广播去找支持 ONVIF 的设备,再通过设备服务接口拿媒体流地址。它帮你省去的是“查文档、找路径”的时间,但认证信息仍然以摄像头本身的账号密码为准。

4.3 接不动的协议,用 ffmpeg 前缀兜底

遇到只开放 RTMP 或者其它不好直接接入的流,可以在 streams 配置里加 ffmpeg: 前缀,让 go2rtc 调用内置的 FFmpeg 把流抓进来。比如:

streams:
  old_cam: ffmpeg:rtmp://192.168.1.30/live/main

这样 go2rtc 会启动一个 FFmpeg 进程去连接 RTMP 源,再把数据转交给 go2rtc 做 WebRTC 或 HLS 输出。这个模式也可以用来做转码,比如把摄像头输出为 H.265 的画面转成 H.264 给浏览器用,代价是 CPU 占用会明显上升。所以我把它定位成兜底方案,能用原生 RTSP 直连就尽量直连。

4.4 编码类型选择:H.264 还是 H.265,直接决定浏览器能不能播

很多新型摄像头默认输出 H.265 编码,因为同等画质下码率更低。但浏览器对 H.265 的 WebRTC 播放支持很有限,Chrome 经常直接黑屏,Safari 则相对好一些。如果只想图省事,优先到摄像头后台把视频编码改成 H.264 使用。你会发现接入 go2rtc 之后浏览器秒开,CPU 也轻松。如果确实要保留 H.265 以节省带宽,那就要做好部分浏览器播放不了的心理准备,或者在 go2rtc 里对那路流单独特转码。

5. WebRTC 低延迟链路与几种播放地址的实测

5.1 为什么最终选 WebRTC,而不是 HLS

最开始我也试过 HLS,兼容性确实很好,但延迟太高。我实测局域网环境下 HLS 从画面事件发生到浏览器显示,大约滞后 3 到 5 秒,开门、找东西这种场景根本没法用。WebRTC 是浏览器原生支持的实时传输方案,走 UDP,建立连接后画面延迟能压到 0.2 到 0.5 秒,我自己的体感是“基本实时”。

5.2 几种常用的播放地址写法

go2rtc 对外输出几种方式,在浏览器和外部播放器里的玩法不同:

方式 地址示例 适用场景
Web UI 播放 http://host:1984/?src=down_hall 日常预览、调试
HLS http://host:1984/api/stream.m3u8?src=down_hall 兼容性要求高的播放器
MSE http://host:1984/api/stream.mse?src=down_hall 自定义页面播放
MP4 http://host:1984/api/stream.mp4?src=down_hall 片段播放、录制
RTSP 转发 rtsp://host:8554/down_hall 外部 NVR、VLC

这些地址里的 down_hall 就是配置文件里定义的流名,换成你自己的即可。

5.3 多客户端同时预览,摄像头侧只有一路流

这是 go2rtc 分发机制里最有价值的一点。多个用户同时通过 WebRTC 看同一路摄像头时,go2rtc 只在源头维持一条到摄像头的 RTSP 连接,其余观看者从 go2rtc 这里拿转发的数据。摄像头不会因为观看人数变多而被迫输出多路编码流。相比之下,直接把摄像头 RTSP 地址给多个人播放的方式,每多一个人,摄像头就要多承担一路编解码压力,设备很快就会卡顿。做了统一接入之后,这个问题基本消失。

5.4 实测下来的延迟数据与设备负载

我在局域网里做过一次粗略测试:摄像头画面中秒表跳动,浏览器 WebRTC 窗口显示的画面比真实秒表慢约 0.3 秒。换到 HLS 后,差距拉到 4 秒以上。设备负载方面,在 J4125 小主机上接 8 路 1080P 摄像头,全部通过 go2rtc 以 WebRTC 分发给两三个客户端同时观看,整体 CPU 占用在 15% 左右。如果开了转码,这个数字会大幅上升,所以日常预览我都是让摄像头直接输出 H.264,go2rtc 只做转发。

6. 排错实录:密码转义、断流重连、H.265 黑屏与公网安全

6.1 密码里的特殊字符,坑了我半小时

有一次我添加一路大华摄像头,密码里带了 @ 符号,写进 YAML 之后日志一直报 401 Unauthorized,但设备本身是好的。原因很简单:RTSP URL 里的用户名密码部分遇到 @ 会被解析器拆错。解决办法是做 URL 编码, @ 写成 %40 , # 写成 %23 , : 写成 %3A 。我后来都会提前用一个小命令把密码处理一下:

python3 -c "from urllib.parse import quote; print(quote('Admin@123'))"

输出 Admin%40123 ,再把这段粘到 RTSP 地址里。如果日志里出现 “error connection refused” 或者 “host not found”,先别怀疑 go2rtc 的问题,优先检查摄像头地址里有没有需要转义的字符。

6.2 摄像头断电再恢复,go2rtc 能自己拉回来

我做过一次模拟故障:把某台摄像头网线拔掉几分钟,再插回去。go2rtc 的日志里能够看到连接断开、然后按退避策略反复重连,设备恢复后日志显示拉流成功,Web UI 里的画面自动恢复,不需要重启容器。这个特性在实际运行中很重要,因为家用摄像头偶尔会因为 WiFi 波动或电源接触不良掉线,没人愿意每次都手动去重启服务。只要摄像头本身恢复网络,go2rtc 会自动把流接回来。

6.3 H.265 黑屏:不是 go2rtc 的问题,是浏览器不认

有一路新摄像头默认开了 H.265,接入 go2rtc 后画面一直黑屏,但流状态显示正常。当时我以为是 WebRTC 端口的问题,排查了半天,最后发现是浏览器不支持 H.265 编码。Safari 能播,Chrome 不行。解决方案是到摄像头后台把编码改成 H.264,或者对那一路流单独加转码。以后再遇到自家摄像头黑屏,我会第一反应看一下编码格式,而不是先怀疑服务配置。

6.4 别把 1984 端口直接暴露到公网

go2rtc 默认没有鉴权,任何人只要能访问到 1984 端口,就能看到所有摄像头画面,这是一个非常严肃的安全隐患。如果你只是在内网用,可以接受;如果要在公网访问,一定要做两层防护。第一层,不要让 go2rtc 直接监听公网端口,Docker 端口映射写 127.0.0.1:1984:1984 ,让服务只对本机开放;第二层,在前面架一个带 TLS 和认证的反向代理,比如 Nginx Basic Auth、Authelia 这类工具。我自己的习惯是内网直接访问,远程场景统一走带身份认证的反向代理入口。摄像头本身也建议尽快改掉默认密码,并把不必要的 ONVIF、RTMP 服务关掉,减少暴露面。

7. 嵌入更大系统:给 NVR 供流、接入 Home Assistant 与性能维护

7.1 把 go2rtc 里的流再转给 NVR 和录像机

go2rtc 不只是给浏览器看,也可以作为 NVR 的上游。在你的录像机或者 NVR 里添加“自定义流媒体”通道时,地址填 rtsp://go2rtc所在主机IP:8554/down_hall ,NVR 就可以从 go2rtc 拉流进行录制和显示。这样做的收益是录像机和摄像头之间不再需要逐个建立直连,所有取流逻辑统一集中在 go2rtc 上,摄像头的并发压力也会小很多。需要注意的是,NVR 对 RTSP 路径比较敏感,如果填完发现不通,先在 VLC 里用同一个地址验证,排除路径拼写问题。

7.2 与 Home Assistant 联动

go2rtc 在 Home Assistant 社区很流行,很多人直接把它作为摄像头接入层。它提供自定义集成能力,也可以在 Home Assistant 的 WebRTC Camera 卡片里直接输入 go2rtc 的流地址,实现低延迟页面展示。我并没有把每路流都做进自动化,只是把 go2rtc 暴露的流地址交给 HA 的仪表盘,使用时直接拖卡片,配置过程很轻。如果你已经有一套 HA 在跑,这一步属于低成本高收益的整合。

7.3 性能优化的大方向:能不转码就不转码

性能方面最核心的一条经验:保持默认的中转模式,不轻易开转码。H.264 就能看的情况下,绝对不为显示效果强行转 H.265。大路数场景最好把主码流和子码流分开,预览用子码流,录像用主码流,这样一个页面同时显示八路画面时负载会低很多。当摄像头数量超过十路,我建议用目录划分或者命名前缀管理 streams,比如 hall_main 、 hall_sub ,避免配置越来越难维护。

7.4 升级、备份与长期维护

go2rtc 升级很快,但升级本身压力不大,因为配置只有一个 YAML 文件。我每次升级前都会先 docker compose pull ,把旧配置备份到一个 backup 目录,再执行 docker compose up -d 。启动后看一遍日志和 /api/streams ,确认所有流的状态正常。如果某个版本引入的行为变化比较大,回滚也很容易,直接把镜像 tag 改回上一个版本重新 up 就行。长期运行下来,这个服务基本不需要额外维护,我唯一要做的只是偶尔清理一下日志,以及定期检查摄像头固件和 go2rtc 版本。

对我来说,go2rtc 现在就像是一个流媒体的统一入口,不管后面接几只摄像头,浏览器打开就是画面。如果你也手里一堆不同协议的设备,按这套部署流程走一遍,十分钟左右应该就能把第一路流跑起来。

Logo

邀请您加入社区

更多推荐