news 2026/9/18 12:27:51

go2rtc统一接入多品牌摄像头:Docker部署与低延迟播放实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
go2rtc统一接入多品牌摄像头:Docker部署与低延迟播放实践

1. 摄像头协议割裂的痛点,才是 go2rtc 真正擅长的事

1.1 一个真实场景:三种摄像头,三套接入方式

我先说一个让我彻底转向 go2rtc 的经历。前年帮一个做门店的朋友改造监控,他店里同时有海康的枪机、萤石的云台、还有一台米家的室内摄像头。正常人的第一反应是:这不就是装个 NVR 的事吗?但实际情况远比想象的恶心。海康老款摄像头的 RTSP 地址和新固件不一样,萤石的摄像头走自己的私有协议,米家摄像头在局域网里搜不到标准 RTSP 流,最后只能三套 App 轮流看。

这种场景并不罕见。做弱电的、玩智能家居的、甚至自己家里装了三五个不同品牌摄像头的,都会撞上同一个问题:摄像头的接入协议五花八门,RTSP、RTMP、私有协议、ONVIF 自动发现,各家有各家的玩法,标准永远停留在纸面上。

go2rtc 解决的就是这个收口问题。它是一款用 Go 编写的多协议流媒体服务器,可以把不同来源的摄像头流——RTSP、RTMP、HTTP-FLV、HLS、WebRTC,以及部分品牌的私有协议——统一接入进来,再用统一的协议输出给浏览器、播放器、Home Assistant、NVR 系统。说白了,你只需要维护一个 go2rtc 的配置,它替你搞定“翻译”和“转发”。

1.2 go2rtc 到底是转码还是转发

很多第一次接触 go2rtc 的人会问,它是不是类似 FFmpeg 那种转码工具?这里要澄清一下。go2rtc 默认不做转码,它做的事叫“直接转发”和“协议转换”。比如摄像头推出来的是 RTSP,浏览器那边 WebRTC 播放不了 RTSP,go2rtc 就负责把 RTSP 重新封装成 WebRTC 需要的数据格式,视频编码层基本不动。这意味着你几乎不会因为多接了一个 go2rtc 而损失画质,也不会疯狂消耗 CPU。

打个比方,摄像头说的是“方言”,播放器说“普通话”,go2rtc 就是那个翻译官。他不是把这段内容重新创作一遍,而是原封不动地转述出来。所以就算树莓派那种性能孱弱的小板子,也能同时挂几路摄像头流,CPU 占用率照样低得可怜。

如果你遇到实在无法直接读取的流,go2rtc 还支持调用 FFmpeg 去拉,用ffmpeg:前缀指定一条流即可。这种灵活度很关键,后面我会专门讲。

1.3 为什么部署一定要走 Docker

go2rtc 本身是一个单一二进制文件,理论上官网直接下载解压就能跑。但我建议所有人、包括新手,第一步就上 Docker。原因有三:

  • 环境隔离干净,不污染宿主机,Go 程序依赖库少,但配置文件、日志、二进制散落在系统目录里,将来升级或卸载都很麻烦。
  • 容器化的升级回滚太方便了,镜像更新后重新docker compose pullup -d就完成,旧版本镜像还在本地,随时可以回退。
  • go2rtc 镜像支持 amd64、arm64、armv7 多种架构,无论是 x86 的迷你主机、群晖 NAS、飞牛 NAS,还是树莓派,一套 compose 文件通吃。

还有一点容易被忽略:go2rtc 在 Docker 里可以配置成 host 网络模式,这样摄像头设备和容器之间能直接通信,对于局域网内设备发现和 WebRTC 打洞都有好处。这个细节后面我会展开说清楚。

2. Docker 部署 go2rtc:从镜像选择到实际跑通

2.1 镜像选择和架构注意

go2rtc 的官方 Docker 镜像一般用alexxit/go2rtc,Docker Hub 上直接能拉。镜像是多架构的,x86、ARM 设备拉取时会自动选择对应架构,不需要手动指定。

部署之前先确认你手上的设备是什么芯片。群晖、威联通、飞牛 NAS 大多数是 x86_64,树莓派 4B/5 是 arm64,树莓派 Zero 2 W 是 armv7。如果镜像拉下来跑不起来,先检查是不是架构不匹配,docker inspect可以查看镜像的架构信息,老版本 Docker 需要手动处理 multi-arch 时容易踩坑,现在 Docker 19.03 以上的版本基本都自动处理了。

2.2 用 docker run 快速跑起来

不需要先写配置文件,先看最快的启动方式。在宿主机上建一个目录专门放 go2rtc 的配置,比如/opt/go2rtc,然后执行:

mkdir -p /opt/go2rtc cd /opt/go2rtc docker run -d --name go2rtc \ --restart unless-stopped \ -p 1984:1984 \ -v $(pwd)/go2rtc.yaml:/config/go2rtc.yaml \ alexxit/go2rtc:latest

容器起来后,打开浏览器访问http://服务器IP:1984,就能看到 go2rtc 的 Web 管理界面。如果这时候go2rtc.yaml还不存在,容器会自己建一个默认的空配置文件,界面里看不到任何摄像头通道,只显示一个空列表。

这里有个容易搞混的地方:go2rtc 的 Web 管理界面和流媒体服务共用端口 1984,所以它既是 API 端口、Web UI 端口,也是 WebRTC 信令端口。别傻傻地以为还要开一个什么“服务端口”,1984 一个端口把管理和信令都包了。

2.3 生产环境还是用 docker compose 固化

docker run用来验证没问题,但真正常期跑,我建议写成 docker-compose.yml。这样配置文件、端口映射、重启策略、环境变量都能一眼看到,换机器迁移也很轻松。

services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped ports: - "1984:1984/tcp" - "50000-50100:50000-50100/udp" volumes: - ./go2rtc.yaml:/config/go2rtc.yaml

启动命令一行:

docker compose up -d

查看日志:

docker compose logs -f go2rtc

更新的流程是:

docker compose pull docker compose up -d

迁移到另一台机器只需要把 go2rtc.yaml 和 compose 文件一起拷贝过去。

这里关于端口映射我多说一句。go2rtc 的 WebRTC 播放需要 UDP 端口传输音视频数据,如果在桥接网络模式下不映射 UDP 端口,浏览器里打开画面可能会卡在“连接中”,日志还会时不时的报ICE相关错误。上面这个 compose 文件里我预留了 50000 到 50100 的 UDP 端口区间。

2.4 host 网络模式还是桥接模式

可能有人看到过 go2rtc 官方文档里推荐使用network_mode: host,但这事得分平台。在 Linux 环境的 NAS、服务器、树莓派上,host 模式确实省心,容器直接共享宿主机网络,不需要手动映射端口,WebRTC 的 UDP 端口也能自由使用,配置方式如下:

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

但如果你用的是 Windows 上的 Docker Desktop 或者 macOS 上的 Docker,host 网络模式并不是真正意义上的“宿主机网络”,它实际上是跑在 Docker Desktop 的轻量虚拟机里,端口映射行为会比较别扭。这种情况下老老实实用桥接模式,映射 TCP 1984 和一段 UDP 端口,反而更稳定。

3. 摄像头接入实战:RTSP 基础、海康/大华、小米和树莓派

3.1 标准 RTSP 摄像头接入,这是最能直接抄作业的部分

绝大多数安防品牌摄像头都支持 RTSP 协议,这是最基础也是最通用的一种接入方式。go2rtc 不需要额外插件,只要在go2rtc.yamlstreams字段里写清楚 RTSP 地址就行。

比如海康威视摄像头的 RTSP 地址通常长这样:

rtsp://admin:你的密码@192.168.1.64:554/Streaming/Channels/101

地址里的路径是有含义的。Channels/101表示第一通道的主码流,102就是第一通道的子码流。子码流分辨率低、码率低,适合多路同屏预览和手机端查看,主码流适合录像存储和回放。如果你摄像头画面一直加载不出来或者延迟大,先试试换上子码流地址。

在 go2rtc 配置里,为这个摄像头起一个方便记忆的名字,例如:

streams: gate_cam: - "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102" storage_cam: - "rtsp://admin:password@192.168.1.65:554/cam/realmonitor?channel=1&subtype=1"

保存配置后重启容器:

docker compose restart

Web 界面里就能看到gate_camstorage_cam两路画面了。

3.2 海康私有协议和 ONVIF 自动发现

很多人不知道,go2rtc 接入海康摄像头的姿势不只是 RTSP。它还能通过 ONVIF 协议自动发现局域网内的摄像头设备,并且在不知道密码的情况下列出可用的流地址,这对前期排查特别有用。

如果摄像头开启了 ONVIF 功能,你可以在 go2rtc 的 sources 配置里直接写:

streams: camera_discovery: - "onvif://admin:password@192.168.1.64"

go2rtc 会通过 ONVIF 去探测这台设备的媒体信息。实际用下来,ONVIF 探测更多是用于发现和验证配置,真正跑稳定之后我还是建议换回 RTSP 地址,毕竟 ONVIF 会话建立比 RTSP 重一点,多路并发时稳定性不如直接写死 RTSP。

大华摄像头的 RTSP 路径又是另一种格式:

rtsp://username:password@IP:554/cam/realmonitor?channel=1&subtype=0

subtype=0是主码流,subtype=1是子码流。这类地址网上资料很多,但最好还是用官方客户端或者 ONVIF 工具看一下实际路径,别直接套模板。

3.3 小米/米家摄像头:没有 RTSP 就想办法拿到一个 RTSP

热搜里“go2rtc 小米”这个搜索词很火,我也重点讲一下。小米家的摄像头比较复杂,因为米家生态的摄像头默认不用标准 RTSP,而是走米家的私有局域网协议和云平台。你拿着rtsp://192.168.x.x/ch0_0.h264这种地址去连,大多数新款摄像头根本不会理你。

但是小米摄像头确实有办法接入 go2rtc。前提是你要先拿到摄像头的局域网 RTSP 地址。

目前常用的路子是通过米家 App 开启开发者模式。具体操作是在米家 App 里反复点击设备设置里的版本号,打开开发者模式后,局域网通信相关设置里会出现 RTSP 服务的开关和地址信息。不同型号位置不同,有的在“局域网通信协议”里,有的在“账号与安全”下面。开启后会得到一个类似rtsp://用户名:密码@IP:554/stream1的地址,把这个地址填进 go2rtc 配置里,再把频道源指向 go2rtc 统一输出,就可以实现小米摄像头接入 Home Assistant 或者其他 NVR 系统了。

这里有个现实问题:不是所有小米摄像头固件都提供这个开关,有些旧型号或阉割版固件不支持。如果你手头的小米摄像头死活开不出 RTSP,那就只能考虑中间层方案了,比如用 FFmpeg 去读米家私有协议再转成 RTSP 推给 go2rtc,但这需要额外的设备和更多调试成本。所以买摄像头之前如果早就打算玩本地化接入,选支持 RTSP/ONVIF 的型号会省心得多。

3.4 树莓派摄像头模块(OV5647)怎么绕一圈接进来

树莓派摄像头模块跟 IP 摄像头不一样,它本身不输出 RTSP,只有一个本地设备节点。go2rtc 不能直接读取/dev/video0,需要先在树莓派本地跑一个 RTSP 推送服务,把摄像头的视频转成 RTSP 流,再由 go2rtc 接入。

最常见的做法是树莓派上跑mediamtx这个轻量级 RTSP 服务器,再用libcamera-vid采集视频并通过管道转推:

libcamera-vid -t 0 --width 1280 --height 720 --framerate 25 --inline --listen -o - | ffmpeg -i - -c copy -f rtsp rtsp://localhost:8554/cam_yuv

这样树莓派本机的rtsp://树莓派IP:8554/cam_yuv就是一个标准 RTSP 地址了,在 go2rtc 里直接引用这个地址,等于把树莓派摄像头接进了统一的流媒体平台。

树莓派 Zero 这种性能偏弱的板子建议用低分辨率、低帧率推流,720p 25fps 已经是比较稳妥的上限,1080p 会占满 CPU,后面再挂 go2rtc 就有点撑不住了。

3.5 其他奇奇怪怪的流,用 ffmpeg 前缀兜底

除了上面说的设备,还有一些摄像头只提供 HTTP 拉流地址,或者是一个 .m3u8 的 HLS 地址,又或者只有厂商私有视频流协议。这种情况下你可以在 go2rtc 配置里用ffmpeg:前缀,让 go2rtc 自动调用内置的 FFmpeg 逻辑去拉取这些流:

streams: weird_cam: - "ffmpeg:http://192.168.1.103/video.cgi" iphone_rtmp: - "ffmpeg:rtmp://192.168.1.104/live/room1"

这里需要注意一个原则:能直接 RTSP 就尽量直接 RTSP,实在不行才用 FFmpeg 兜底。因为走 FFmpeg 意味着至少有一步转封装甚至转码的过程,CPU 开销和延迟都会上升。go2rtc 取名“go 2 rtc”的核心卖点就是尽量少转码、低延迟,滥用 FFmpeg 前缀等于自废武功。

4. 输出侧:浏览器直接看、接入 Home Assistant、对接 NAS 存储

4.1 go2rtc 的 Web 管理界面不只是“看看而已”

go2rtc 的 Web 界面做的还挺顺手。打开http://IP:1984后,左边是已配置的流列表,点击任一通道就能实时预览。多路摄像头都接入后,界面会以列表形式展示,你可以快速切换不同画面。

界面里每路视频下面会给出可复制的播放地址,包括 RTSP、HLS、WebRTC、MSE 等。这个功能很实用,你不需要自己拼 URL 了。比如你做了一个自己的网页监控面板,需要内嵌这几路画面,直接把这个 HLS 地址丢给前端播放器就行。

4.2 WebRTC 低延迟播放的关键配置

如果把 go2rtc 当作纯内部工具,在局域网里直接播放,WebRTC 基本是零配置工作。但一旦你用了桥接网络模式,或者想从浏览器跨网络访问,就要注意 WebRTC 的端口和 ICE Server 配置。

WebRTC 的工作流程是先通过 HTTP 信令交换 SDP,也就是流媒体描述信息,然后再用 UDP 端口传输真正的视频数据。如果 go2rtc 容器在桥接网络里没有暴露对应的 UDP 端口,浏览器能拿到 SDP 却收不到媒体数据包,画面就会一直卡在加载状态。

如果遇到这种情况,在 go2rtc.yaml 里显式指定 UDP 端口范围:

rtc: listen: ":1984" port_min: 50000 port_max: 50100 ice_servers: - urls: - "stun:stun.l.google.com:19302"

我在前面 compose 文件里预留的 50000-50100 UDP 端口,就是为了配合这里的配置。ice_servers里的 STUN 服务主要用于公网环境下的 NAT 穿越,如果你的摄像头和播放器都在内网,不配置也能正常播放。

4.3 把 go2rtc 接进飞牛 NAS、群晖的存储体系

飞牛 NAS 或者群晖上装完 go2rtc 后,很多人的下一步是想把录像存下来。最常见的方案是再起一个 Easynvr 容器做 NVR 存储,go2rtc 负责给 Easynvr 提供稳定的 RTSP 源。

做法不复杂:在 Easynvr 的接入配置里添加设备,填写的地址直接用 go2rtc 输出的 RTSP 地址。比如你在 go2rtc 里配置了一个叫front_gate的摄像头流,在 Easynvr 里添加这个地址:

rtsp://go2rtc宿主机IP:1984/front_gate

go2rtc 输出的 RTSP 地址默认就是rtsp://容器IP:1984/流名称。如果你的 go2rtc 是 host 网络模式,直接用宿主机 IP。如果用的是桥接模式并且映射了端口 1984,那就用宿主机的 1984 端口。

这样做的价值在于:go2rtc 把所有摄像头的流统一成了内部 RTSP 地址,Easynvr 不需要感知每个摄像头品牌和协议差异,接入配置简洁很多。飞牛 NAS 上常见的剩余问题反而是存储目录的权限和录像分段时间,这些和 go2rtc 无关,就不展开了。

4.4 和 Home Assistant 搭配:从流接入到自动化

go2rtc 和 Home Assistant 的整合是很多人入坑的原因。Home Assistant 的官方集成里直接内置了 go2rtc,也就是说你在 HA 里配置的每个摄像头,都可以选择把它的视频流地址交给 go2rtc 统一管理和输出。

流程一般是:先在 go2rtc 里把各路摄像头都接好,确认 Web 界面能正常预览,然后在 Home Assistant 的设置里添加 go2rtc 集成,把摄像头实体与 go2rtc 的流名称对应起来。这样 HA 的 Dashboard 就能实时显示监控画面,而且打开速度比 HA 自带的 stream 组件快很多。

配合 Frigate 做 NVR 检测也是常见玩法。Frigate 通过 go2rtc 获取低延迟的实时流用于物体检测和录制,这样米家摄像头、树莓派摄像头都能参与 AI 识别。go2rtc 在这里的角色是“统一视频入口”,它自己不做检测,但一旦视频流能统一接入,后面挂多少分析模块都方便。

5. 部署中踩的坑和排查方法

5.1 Docker 拉取镜像慢或超时

这个问题在部署任何 Docker 项目时都会遇到,go2rtc 也不例外。网络环境不稳定的时候,直接docker pull alexxit/go2rtc可能要卡很久。

常规做法是给 Docker 配置镜像加速器。以 Linux 系统为例,编辑/etc/docker/daemon.json文件,写入以下内容:

{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }

保存后重启 Docker 服务:

systemctl restart docker

如果你是 Docker Desktop,在图形界面设置里找到 Docker Engine 或 Registry Mirrors 选项,把那行镜像地址加进去,Apply & Restart 即可。

这里额外提醒一下:镜像加速器只影响镜像拉取,不影响容器内程序的网络访问。如果 go2rtc 容器访问摄像头或者其他服务不通,那是网络层面的问题,跟镜像加速器无关。

5.2 端口冲突:1984 被占用了怎么办

1984 是 go2rtc 的默认端口,但如果你机器上已经有服务占了 1984,容器会启动失败,日志里会报bind: address already in use

这种场景下你可以修改 go2rtc 配置里的监听地址。在 go2rtc.yaml 中加入:

api: listen: ":1985"

然后 docker-compose 里的端口映射也改成 1985:

ports: - "1985:1985/tcp"

这里有个容易踩的坑:很多人改了api.listen却忘了改 compose 的端口映射,导致外部访问不到。另外,WebRTC 的信令也走这个端口,最好保持 TCP 和 UDP 一致,避免出现浏览器能打开界面但画面卡住的情况。

5.3 摄像头不显示画面,先从这几件事查起

遇到画面黑屏或者加载不出来,不要急着怀疑 go2rtc。我的排查顺序是:

  • 先拿 VLC 或 ffprobe 直接访问摄像头原始 RTSP 地址,确认摄像头本身流是否正常。VLC 能放、go2rtc 不能放,问题大概率在 go2rtc 配置。
  • 检查docker logs -f go2rtc里有没有401 Unauthorizedconnection refused。401 说明是账号密码不对,connection refused 说明摄像头地址或端口不对。
  • 确认容器与摄像头是否在同一网络。容器用桥接模式时,如果宿主机和摄像头不在同一网段,需要先解决路由或 VLAN 的问题。
  • 如果日志显示User-agent或者RTSP/1.0 200 OK之后画面仍然不显示,那可能是摄像头码流格式不兼容,尝试换子码流,或者在流的配置最后加上#video=copy之类的参数。

用 VLC 验证原始流是最快的方法,没有之一,这个习惯我一直保持到现在。

5.4 大量接入后的安全底线

go2rtc 默认没有任何认证,Web 界面打开就能看所有摄像头画面,配置文件里存的又是摄像头明文账号密码,这个风险必须正视。

我的建议是无论如何不要直接把 1984 端口映射到公网。如果确实需要在外网查看,请务必在前面加一层带认证的反向代理,或者在 go2rtc 配置里启用 HTTP Basic 认证,只允许指定账号访问管理界面和视频流。

同时强调一下:go2rtc.yaml 文件里保存的是摄像头明文密码,这个文件本身要做好权限管理。不要为了图方便把它放到一个所有用户都可读的共享目录里,更不要在没有加密的情况下丢到公开的配置仓库中。

5.5 延迟高的原因排查

如果你的画面延迟从正常的几百毫秒涨到了好几秒,先检查是不是使用了 HLS 协议播放。HLS 为了保证兼容性会切片,延迟天然比 WebRTC 高。默认的 HLS 切片长度和播放缓冲加起来,延迟达到 3 到 10 秒都很正常,这并不代表 go2rtc 出故障了。

局域网内追求低延迟就用 WebRTC,公网无法连通 UDP 端口时再考虑 HLS。另一个常见因素是摄像头本身推的是主码流,分辨率高、码率大,传输和播放都会增加延迟,改成子码流后延迟立刻会降下来。

5.6 多个地址源互为热备,一个小技巧收尾

go2rtc 有一个很实用但很多人不知道的功能:同一个流可以配置多个源地址,它会自动选择并支持故障切换。比如某个摄像头同时支持主码流 RTSP 和通过 FFmpeg 拉取的备选流,你可以这么写:

streams: front_cam: - "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102" - "ffmpeg:http://192.168.1.65/video.cgi"

当时第一个源连不上,go2rtc 会自动尝试第二个源。在多摄像头场景下,这意味着你不需要人工干预就能实现基础的容灾切换。这个特性很少被提及,但实际用起来非常省心。

写在最后

go2rtc 这个项目给我最大的感受就是“轻”。它没有复杂的前端项目,没有压垮小主机的依赖关系,一个容器、一个 yaml 文件、一个 1984 端口,就把各路摄像头全部收编了。相比于折腾各家 SDK 和平台对接,这种统一的协议收口思路,才是真正能在长期运维中省时间的做法。

如果你是刚开始接触,我的建议是先把一台海康或者大华的标准 RTSP 摄像头接进去,在 Web 界面把预览跑通,再逐步加上其他协议设备和 HAL 集成。别一上来就追求大而全,那只会让排查问题的难度叠加。等这套链路稳定了,你会发现以后加再多的摄像头,都只是往 yaml 文件里多写一行而已。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 12:25:00

2024论文降重工具评测与使用技巧

1. 论文降重工具的市场现状论文查重和降重已经成为学术写作中不可或缺的环节。随着学术规范的日益严格,越来越多的学生和研究人员开始重视论文的原创性。根据我的观察,2023-2024学年,高校对论文重复率的要求普遍提高,很多院校将硕…

作者头像 李华
网站建设 2026/9/18 12:24:46

15万条Excel导出选型:POI、SXSSF、EasyExcel、CSV实测对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 12:18:22

Excel列宽与像素映射原理及跨平台实测方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华