家里陆续加过几台不同品牌的摄像头之后,你就会发现最麻烦的不是“能不能看到画面”,而是“怎么把它们弄到一个地方统一看”。海康有海康的 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 的核心能力我总结了四个层面:

  1. 多协议拉流 :能直接接入 RTSP、RTMP、HLS、HTTP-FLV、WebRTC,也能接本地文件、麦克风等设备源。对 IP 摄像头来说,最常用的就是 RTSP 和 ONVIF。
  2. 自动协议转换 :一个源进来之后,go2rtc 会自动向外提供 WebRTC、HLS、MSE、RTSP、RTMP 等多种输出协议。摄像头只给了你 RTSP 地址,浏览器却可以原生播 WebRTC,这个转换过程对客户端是透明的。
  3. 毫秒级 WebRTC 低延迟播放 :这是它和很多传统流媒体服务器的关键区别。WebRTC 基于 UDP,浏览器原生支持,配合 go2rtc 内置的信令,能实现几百毫秒级别的延迟,接近实时画面。
  4. 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 就完事。我自己现在维护的摄像头平台,就是这套配置,跑了大半年没出过问题。

Logo

邀请您加入社区

更多推荐