1. 为什么摄像头接入要单独搭一个平台:go2rtc 的定位和优势

先说说我自己的背景。家里装了六个摄像头,有海康的、大华的、还有两个树莓派接的 CSI 摄像头,本来各自厂商的 App 都能看,但问题很快就来了——我想在 Home Assistant 的仪表盘里统一看画面,海康的走 RTSP,大华的走 RTSP 但路径格式不一样,树莓派那个是 RTSP 转发出去的,再加上一个 ESP32-S3 USB 摄像头只能输出 MJPEG。想在同一个页面上全部看齐,传统做法是每个摄像头单独转码拉流,或者靠 Frigate、Mediamtx 之类各搞一套,配置起来很零散。

后来我找到 go2rtc,这个项目解决的就是“摄像头协议五花八门,统一汇聚转发”这个问题。它本身是一个轻量的流媒体网关,原生支持 RTSP、RTMP、HLS、WebRTC、MJPEG、HTTP-FLV 等一堆协议之间的互相转换和转发。也就是说,你不用管摄像头源是什么协议,接入 go2rtc 之后,统一对外出流;浏览器直接看 WebRTC 或 HLS,VLC 直接拉 RTSP,OBS 可以直接抓流当虚拟摄像头。配合 Docker 部署,一个容器就搞定,不用在宿主机上装一堆依赖。

什么人适合用 go2rtc?如果你只是想单看一个摄像头,厂商 App 就够用,没必要折腾。但如果你有多个不同品牌的摄像头、想把画面统一接入自己的平台、想在浏览器里低延迟实时预览、还要给 NVR 或 Home Assistant 供流,那 go2rtc 就是那个承接一切的中间层。它的配置特别简单,核心就一个 YAML 文件,部署方式我推荐 Docker,干净、升级方便、备份就是 copy 一个配置文件。

这里提前给一个结论:go2rtc 最大的价值不是“转码”,而是“协议转换和统一出口”。它默认不做重编码,所以性能开销很低,树莓派 4 上同时转发四路 1080p 都没压力。真正需要转码的场景,它也会自动调用 FFmpeg 子进程兜底,后面我会专门讲这块的配置。

2. Docker 部署前的环境准备:网络模式、端口规划和镜像选择

部署之前,先花几分钟确认环境。go2rtc 官方提供 Docker 镜像,名字是 dgtlmoon/go2rtc 或者 ghcr.io/alexxp/go2rtc ,我一般直接用 Docker Hub 上的版本。安装 Docker 就不用多讲了,Linux 上一条 curl 脚本,Windows 上装 Docker Desktop,macOS 同样。需要注意的一点是,go2rtc 的 WebRTC 传输依赖 UDP 端口,如果你用的是 Docker Desktop 或远程服务器,端口映射要额外留心,我后面会展开。

2.1 网络模式:为什么我建议用 host

go2rtc 官方文档里,Docker 部署示例基本都是 network_mode: host 。原因很简单,go2rtc 涉及 RTSP(默认 8554)、WebRTC(默认 8555,TCP/UDP)、HTTP API(默认 1984)等多个端口,如果用 bridge 模式做端口映射,WebRTC 的 UDP 端口范围很难全部映射出去,而且 RTSP 回源时有些摄像头会校验源 IP,host 模式可以避免很多奇怪的问题。

Linux 下直接用 host 模式基本没毛病。Windows 和 macOS 的 Docker Desktop 对 host 网络支持不好,这种情况我建议你在虚拟机里跑一个 Linux 容器,或者直接把 go2rtc 安装在宿主机上,也就是用 release 二进制文件运行,一样很省事。

如果你坚持用 bridge 模式,至少要把这几个端口映射出来: 1984 (Web 管理界面和 API)、 8554 (RTSP 服务端口,如果你需要对外提供 RTSP 源)、 8555 (TCP,用于 WebRTC over TCP 的兜底)。UDP 的 8555 以及后续动态端口范围,在 bridge 模式下很难做干净,所以能 host 就 host。

2.2 端口规划:提前想好不打架

我自己的习惯是固定这么一套端口规划:

用途 端口 说明
Web 管理界面 / API 1984 浏览器访问 http://IP:1984 ,也是播放器页面的入口
RTSP 输出服务 8554 把 go2rtc 里的流地址以 rtsp://IP:8554/stream名 对外提供
WebRTC 8555 包括 TCP 和 UDP,浏览器低延迟播放主要靠它
HTTP-FLV / HLS 1984 自带 同一个端口下通过路径区分,例如 /hls/stream名.m3u8

如果服务器上已经跑了其他占用这些端口的服务,比如 Mediamtx 默认 8554,注意错开。我喜欢把 go2rtc 作为主力,让其他服务去换端口,因为 go2rtc 对外输出能力更灵活。

2.3 拉取镜像:加速源问题先解决

国内拉 Docker 镜像经常遇到超时,尤其是 ghcr.io 的镜像。go2rtc 在 Docker Hub 也有镜像,优先用 Docker Hub 上的,速度会好一些。如果还是慢,常见的做法是配置国内镜像加速源,或者通过代理环境变量拉取。拉取命令:

docker pull dgtlmoon/go2rtc:latest

不过时。这几年来这个镜像体积一直很小,也就几十 MB,里面包含了编译好的 go2rtc 二进制,以及默认集成的最小化 FFmpeg 静态编译版本,这个设计很关键——后面讲 FFmpeg 源和自动转码时会用到它。

3. go2rtc 核心配置文件:YAML 里藏着所有玩法

go2rtc 的配置在一个 YAML 文件里,默认路径是 go2rtc.yaml 。你要是之前配过 Frigate,会发现习惯很相似,但 go2rtc 的配置更精简。我用了这么久,最核心的就这么几块: streams (定义所有输入源)、 log (日志级别)、 rtsp (RTSP 服务端口)、 webrtc (WebRTC 参数,尤其是 ICE 服务器和端口范围)。

3.1 streams:把所有摄像头源统一注册

streams 是一个 map,key 是你给这条流起的名字,value 是源地址。举例说明最直观:

streams:
  front_door: rtsp://admin:password@192.168.1.64:554/h264/ch1/main/av_stream
  backyard: rtsp://admin:password@192.168.1.65:554/Streaming/Channels/101
  pi_cam: rtsp://192.168.1.100:8554/camera
  esp32_mjpeg: http://192.168.1.120:8080/?action=snapshot

这里我故意写了四个不同来源:海康的 RTSP 路径、大华的 RTSP 路径、树莓派上面另一个流媒体服务的输出、以及 ESP32 摄像头的 MJPEG HTTP 流。go2rtc 会自动探测源协议,不需要你专门声明,它内部有一套探测逻辑。比如看到 rtsp:// 就知道走 RTSP 客户端,看到 http:// 会先尝试 MJPEG,再尝试 HLS 或其他。

源地址之所以五花八门,是因为厂商的“标准路径”根本没统一。海康常见的是 /h264/ch1/main/av_stream ,大华常见的是 /cam/realmonitor?channel=1&subtype=0 或者我上面写的 /Streaming/Channels/101 ,TP-Link 又是另一套。go2rtc 的优势是你不必记住这些,直接在 streams 里配上,反正它只是个转发器,源怎么连是 FFmpeg 或内置 RTSP 客户端的事。

如果你有摄像头需要用户名密码,直接写在 URL 里是最省事的做法。但注意,明文密码会出现在 go2rtc 的日志和 API 响应中,如果在意安全,可以用环境变量或者直接通过 Web 界面配置,不过我实测中直接写在 YAML 里最简单,内网环境基本无所谓。

3.2 一个完整的推荐配置模板

我给你看一个我目前在用的配置,基本可以照抄:

log:
  level: info

rtsp:
  listen: ":8554"

webrtc:
  listen: ":8555"
  ice_servers:
    - urls:
        - "stun:stun.l.google.com:19302"

streams:
  front_door: rtsp://admin:password@192.168.1.64:554/h264/ch1/main/av_stream
  backyard: rtsp://admin:password@192.168.1.65:554/Streaming/Channels/101
  garage:
    - rtsp://admin:password@192.168.1.66:554/h264/ch1/main/av_stream
    - ffmpeg:rtsp://admin:password@192.168.1.66:554/h264/ch1/sub/av_stream
  pi_cam: rtsp://192.168.1.100:8554/camera
  esp32_mjpeg: http://192.168.1.120:8080/?action=snapshot

稍微解释一下 garage 这种写法。它给同一条流名配了两个源地址,go2rtc 会把它们当一个流来处理,默认优先用第一个源,如果断流了会自动切换到第二个备用源。第二个源我加了 ffmpeg: 前缀,意思是让 go2rtc 把这条源交给 FFmpeg 子进程去拉取,专门对付那些不支持标准 RTSP 握手、或者格式很怪的摄像头。这是 go2rtc 非常实用的一个小技巧,后面我会专门讲 FFmpeg 源的使用场景。

webrtc.ice_servers 这一段,如果你只是局域网内访问,其实不配也能用,但配了 STUN 之后,跨网段、或者你想在外面通过 Tailscale 之类的组网访问,WebRTC 协商会更稳。我这里用的是 Google 的公共 STUN,你也可以换成自建的 coturn,但内网场景不是必须。

3.3 配置热加载:不用重启容器

go2rtc 默认会监听配置文件的变化,你修改 YAML 保存后,它会自动重新加载,不用重启容器。这个特性在调试摄像头的时候太重要了。我经常先在 Web 界面里试各种源地址,确认能出画面了再写进配置文件,改完几秒钟就能生效。

不过有一个小坑:自动热加载的时候,内存里可能残留旧连接,个别情况下 WebRTC 播放会卡一下,重新刷新页面就好。如果你的配置改了之后没生效,可以先手动重启一下容器排障。

4. 完整部署实操:从 docker run 到 docker compose

部署方式其实就两种, docker run 一条命令跑起来,或者用 docker compose 管理。我强烈建议用 compose,因为升级、备份、改端口都比命令行为方便,而且配置可以写成代码,换机器直接拷贝整个目录就能复现。

4.1 快速跑通的 docker run 方式

如果你只想先试一下,直接用这条命令:

mkdir -p /opt/go2rtc
docker run -d --name go2rtc --restart unless-stopped --network host \
  -v /opt/go2rtc:/config \
  -e GO2RTC_CONFIG=/config/go2rtc.yaml \
  dgtlmoon/go2rtc:latest

说几个参数的含义。 --network host 上面解释过了,是为了让 RTSP 和 WebRTC 端口直通。 -v /opt/go2rtc:/config 把配置目录挂载出来,你改 YAML 就不需要进入容器了。 GO2RTC_CONFIG 这个环境变量指定配置文件路径,注意容器内默认路径和目录名称要对得上。启动后访问 http://服务器IP:1984 ,能看到一个简单的播放器页面,里面会列出你配置的 streams,直接点就能预览。

4.2 更推荐的 docker compose 方案

我实际在用的 compose 文件长这样:

services:
  go2rtc:
    image: dgtlmoon/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    network_mode: host
    environment:
      - GO2RTC_CONFIG=/config/go2rtc.yaml
    volumes:
      - /opt/go2rtc:/config

存成 /opt/go2rtc/docker-compose.yml ,然后:

cd /opt/go2rtc && docker compose up -d

升级的时候更简单:

docker compose pull && docker compose up -d

整个 go2rtc 的所有持久化状态就是一个 YAML 文件加几个临时文件,所以备份、迁移极其容易。我经常在不同服务器之间搬家,直接把 /opt/go2rtc 这个目录 rsync 过去,再拉起容器就完事。

4.3 验证部署是否成功

启动之后,第一步先看日志:

docker logs go2rtc

正常的日志会打印出它监听在哪些端口,以及发现配置文件里的 streams。如果没有报错,浏览器打开 http://IP:1984 ,就能看到播放器页面。

第二步,用 VLC 或 ffplay 验证 RTSP 输出是否正常。go2rtc 会自动把每个 stream 对外输出为 rtsp://IP:8554/stream名 ,比如:

ffplay -rtsp_transport tcp rtsp://127.0.0.1:8554/front_door

能出画面,说明整个链路已经通了。这时候你在浏览器里用 WebRTC 模式播放,延迟可以做到 200 毫秒以内,基本是肉眼无感。

5. 接入真实摄像头:海康、大华、树莓派、USB 摄像头全攻略

这一节是实操重点。配置 streams 看似简单,但实际接摄像头时坑非常多,因为不同厂商的 RTSP 实现不完全一样,有些还带鉴权、有些需要 onvif 探测、有些甚至只允许一个客户端连接。我把我接过的几类设备挨个讲一遍。

5.1 海康摄像头:RTSP 路径和鉴权注意点

海康的 RTSP 地址格式一般是:

rtsp://用户名:密码@IP:554/h264/ch1/main/av_stream

main 对应主码流, sub 对应子码流。如果你只需要预览,建议用子码流,带宽占用小很多,go2rtc 转发压力也小。主码流一般留给录像。海康的摄像头默认开启了 RTSP 服务,但有些老固件需要去 Web 管理界面里确认一下“标准 RTSP”是否勾选。

常见的一个坑是海康摄像头限制了同时建立的 RTSP 连接数,不同型号从 1 到 6 路不等。如果你同时让 go2rtc、VLC、手机 App 都去拉同一路流,可能会把连接占满,导致一方黑屏。我的对策是,凡是 go2rtc 要长期盯着的流,其他工具尽量通过 go2rtc 对外转发来取流,不要在 go2rtc 之外再直接连摄像头。

5.2 大华摄像头:路径风格完全不同

大华的 RTSP 地址常见格式是:

rtsp://用户名:密码@IP:554/cam/realmonitor?channel=1&subtype=0

或者简化形式:

rtsp://用户名:密码@IP:554/Streaming/Channels/101

两种我都遇到过,取决于固件版本。 subtype=0 是主码流, subtype=1 是子码流。大华摄像头有一个很容易踩的坑:修改过 IP 或恢复出厂后,ONVIF 和 RTSP 服务可能默认是关闭的,需要在 Web 界面激活设备后手动打开,否则 go2rtc 连不上。

另外大华的新固件对于弱密码策略很严格,如果你用简单密码,RTSP 鉴权可能直接拒绝。这种情况先在浏览器里登录官方 Web 管理界面,把账号密码改成强密码,再回来配 go2rtc。

5.3 树莓派 CSI 摄像头和 USB 摄像头:先把画面变成 RTSP 流

树莓派的 CSI 摄像头(比如 OV5647)本身不是网络摄像头,它输出的是 MJPEG 或 RAW 帧。要让 go2rtc 能采集,常见做法是先用 libcamera-vid 把它推成 RTSP 流,或者用 GStreamer 的 nvarguscamerasrc 在 Jetson 平台上采集后再推流,但对大部分树莓派用户来说,更直接的方案是用 go2rtc 自带的 FFmpeg 源来调用。

在 go2rtc 的 streams 里可以这样写:

streams:
  pizero_cam: ffmpeg:libcamera-vid --inline --nopreview -c libh264 -b 2000000 -f rtsp rtsp://127.0.0.1:8554/pizero_cam

这个写法我解释一下。 ffmpeg: 前缀后面跟的参数,是交给 FFmpeg 子进程执行的命令行。它会让 FFmpeg 启动一个 RTSP 客户端,把自己编码好的 H.264 推送到 go2rtc 的内部地址,go2rtc 再对外发布。这样一来,你就在 go2rtc 里拥有了一条名为 pizero_cam 的标准流,其他设备都能拉取,非常方便。

USB 摄像头类似。比如一个普通的 UVC 摄像头,接入树莓派或小主机后,设备名通常是 /dev/video0 。在 go2rtc 里可以这样配:

streams:
  usb_cam: ffmpeg:v4l2:/dev/video0

go2rtc 会调用 FFmpeg 读取 v4l2 设备,编码成 H.264 后对外输出。这种写法特别适合那些本身不支持网络功能的 USB 摄像头,配合一个几十块钱的小主机,就能化身网络摄像头。

ESP32-S3 这类设备,如果刷的是摄像头固件,一般输出 MJPEG over HTTP,直接在 streams 里写 http 地址就行,go2rtc 能自动识别 MJPEG 流。如果设备支持 WebRTC 或其他协议,只要地址格式符合 go2rtc 的源探测规则,通常都能直接接入。

5.4 私有化源和特殊流:FFmpeg 源兜底一切

go2rtc 内置的 RTSP 客户端和 HTTP 抓取,已经能覆盖绝大多数场景。但有些情况下源特别不听话,比如:

  • 摄像头的 RTSP 实现不标准,握手方式怪异
  • 源是 HLS 的 .m3u8 地址
  • 源是需要先经过某种认证页面才能拿到的流
  • 源是本地文件或 RTMP 推流

这时候就用万能兜底:在 streams 的值前面加 ffmpeg: 前缀,让 go2rtc 把整个取流过程交给 FFmpeg。例如:

streams:
  m3u8_source: ffmpeg:https://example.com/live/stream.m3u8
  local_file: ffmpeg:/media/video.mp4
  rtmp_source: ffmpeg:rtmp://192.168.1.200/live/cam

FFmpeg 拉到的流,会以标准格式回传给 go2rtc,go2rtc 保持对外统一输出接口。使用 ffmpeg: 源时,注意 CPU 占用会稍微高一些,毕竟多了一次解码和重新编码的开销,但实际体验中,普通四核小主机同时拉两三路 1080p 问题不大。

6. 前端播放与多端拉流的正确姿势

部署好之后,接下来是使用体验。go2rtc 最爽的一点,是它把“同一路流给不同终端看”这件事做得很顺滑。浏览器、手机、VLC、OBS、Home Assistant,各用各的协议,互不影响。

6.1 浏览器低延迟播放:WebRTC 优先

go2rtc 的 Web 管理界面 http://IP:1984 本身就是一个播放器聚合页。你不需要装任何插件,打开页面,点一下流名称,就能通过 WebRTC 实时预览,延迟极低。WebRTC 的核心优势是走 UDP,配合摄像头自身的低延迟编码,局域网环境下基本做到“所见即所得”。

如果需要自己嵌播放器页面,go2rtc 提供了很简单的播放器嵌入代码,只需要把 stream 名称替换进去。我常用的方式是:

<iframe src="http://IP:1984/?stream=front_door" width="640" height="360"></iframe>

当然你也可以直接用 HLS 或 HTTP-FLV 给其他前端用。go2rtc 会根据 URL 后缀自动区分。比如:

  • http://IP:1984/api/stream.m3u8?src=front_door 拉取的是 HLS 版本
  • http://IP:1984/api/stream.flv?src=front_door 拉取的是 HTTP-FLV 版本

用的时候把 src 参数换成你定义的 stream 名称即可。

6.2 VLC 和 ffplay 拉 RTSP

桌面端看监控,我还是习惯用 VLC 或 ffplay,因为它可以同时打开多路,并且支持录制。go2rtc 对外输出的 RTSP 地址是:

rtsp://IP:8554/stream名

在 VLC 里直接打开网络串流,输入这个地址就能看。如果你发现画面花屏或者延迟很大,可以把传输协议改成 TCP。ffplay 的命令是:

ffplay -rtsp_transport tcp rtsp://IP:8554/front_door

注意, -rtsp_transport tcp 只影响 ffplay 到 go2rtc 的传输方式,不影响 go2rtc 到摄像头的连接方式,两者是独立链路。go2rtc 到自己管理的每个流也可以用 TCP 或 UDP 发起连接,默认它会自动协商。

6.3 OBS 抓流作为虚拟摄像头

很多玩直播的朋友会有这个需求:把摄像头的画面抓进 OBS,然后通过虚拟摄像头给腾讯会议、钉钉课堂或者其他软件用。go2rtc 和 OBS 的搭配非常省心。

在 OBS 里添加“媒体源”,输入 go2rtc 输出的 RTSP 地址或 HLS 地址,画面就进来了。我更推荐用 VLC 插件方式或者直接“媒体源”填 HLS 地址,因为 OBS 的内置播放器对 RTSP 支持不太稳定。HLS 地址格式是:

http://IP:1984/api/stream.m3u8?src=front_door

填进 OBS 媒体源,延迟大约 3 到 5 秒,但十分稳定,不会像 RTSP 那样偶发断流。如果你要低延迟,也可以试试 OBS 的“浏览器源”加载 go2rtc 的 Web 播放器页面,通过 WebRTC 抓画面,延迟能降到毫秒级,不过抓取方式要稍微用点技巧。

6.4 给 Home Assistant 和 Frigate 供流

go2rtc 最常见的实际用途之一,就是作为 Free(群晖的监控套件)或 Home Assistant 的流媒体后端。Home Assistant 默认的摄像头集成可以直接取 go2rtc 的 RTSP 地址,Frigate 甚至可以配置成直接把 go2rtc 作为其内部流服务器,这样所有摄像头源统一由 go2rtc 管理,Frigate 只管 AI 识别。

配置思路很简单:在 Home Assistant 中添加 RTSP 摄像头,URL 填:

rtsp://go2rtc的IP:8554/stream名

由于 go2rtc 对外协议的丰富性,你在 Home Assistant 里还可以用 WebRTC 和 HLS 的卡片,不受 RTSP 限制。如果你要接入群晖的 Surveillance Station,同样把网络摄像头地址填成 go2rtc 的输出地址,相当于 go2rtc 变成了一个“代理摄像头”,帮群晖把各种乱七八糟的源统一成标准 RTSP,群晖就不会再去纠结大华和海康的路径差异。

7. 高可用和性能优化:多路并发、断线重连、自动转码

部署和接入做好之后,很多人会忽略性能调优,直到多路并发或者摄像头掉线之后才发现问题。我踩过不少坑,这里把几个关键优化点整理出来。

7.1 多路并发时的带宽和 CPU 预算

go2rtc 默认是“转发优先”的架构,也就是说,如果源和目的端都支持 H.264,它就只做解封装和再封装,不碰帧内容,CPU 占用可以忽略不计。但如果你把一路 4K 主码流同时给三个浏览器播放,三个浏览器可能都要求 H.264 或不同封装,这时候 go2rtc 不一定需要转码,因为它可以给每个客户端单独做封装,有时也能复用同一个编码流。

真正吃掉 CPU 的是跨协议转码,比如源是 H.265,浏览器不支持,go2rtc 就得把 H.265 转成 H.264。这种场景下,你需要给它配置转码参数。go2rtc 会在必要时自动调用 FFmpeg,但转码参数可以通过环境变量或配置项来控制。

我实测过,一个 4 核的小主机,用 go2rtc 转发四路 1080p H.264 流 + 同时对外出 RTSP 和 HLS,CPU 占用基本维持在 15% 到 25%。如果有一路需要转码 H.265 到 H.264,占用会跳到 40% 以上。所以建议尽量让摄像头输出 H.264,而不是 H.265,除非你完全不在乎 CPU。

7.2 断线重连和备用源配置

摄像头偶尔掉线是常态,尤其是 WiFi 摄像头。go2rtc 对断线重连有内置的处理机制,默认会周期性地重试连接。你不需要额外配置,但有一点要注意:如果源地址带参数,某些摄像头在断线后重连时,可能因为会话残留而拒绝连接,这种时候重启一下 go2rtc 或重启摄像头能解决。

更稳妥的做法是给关键流配置备用源。我在前面 3.2 节写过 garage 那个例子,就是给同一条流配了两个源。实际效果是:主源挂了,go2rtc 会自动切到备用源,播放端几乎感知不到。这个功能在有多路摄像头冗余的情况下特别好用。

7.3 HLS 分片和延迟取舍

HLS 天然会有几秒延迟,因为它要等切片生成和播放器拉片。go2rtc 默认的 HLS 配置是可以调整的,如果你用 HLS 给 OBS 或手机看,希望延迟尽量低,可以在配置里设置:

hls:
  segment_duration: 1
  segments: 3

segment_duration 是每个分片的时长,单位秒,设得越小延迟越低,但会增加服务器切片的 IO 压力。 segments 是保留的分片数量,设得越小,播放器追播的速度越快。实测中 segment_duration: 1, segments: 3 可以让 HLS 延迟压到 3 秒左右,已经接近 WebRTC 之外的体验了。

7.4 录像和 AI 识别:把 go2rtc 变成监控中枢

很多人搭 go2rtc 不只是为了看直播,还想录像。go2rtc 本身不负责录像,但你可以把它输出的 RTSP 地址直接接到 Frigate、Mediamtx、群晖 Surveillance Station 等录像系统里。我的典型架构是:

go2rtc 统一接入所有摄像头 -> 对外供流给 HAB(Home Assistant)和 Frigate -> Frigate 负责检测事件和录像。

这样设计的最大好处是,Frigate 不需要知道摄像头的具体品牌和路径,它只跟 go2rtc 打交道。摄像头掉线了,go2rtc 会自动重试和切换备用源,录像系统不会因为一次网络抖动就丢一堆视频。

对于不想上 Frigate 这么重方案的用户,也可以直接在宿主机上写个 cron 定时任务,通过 ffmpeg 定时录制 go2rtc 输出的 RTSP 流,命令大概长这样:

ffmpeg -rtsp_transport tcp -i rtsp://127.0.0.1:8554/front_door -t 60 -c copy /mnt/nas/front_door_$(date +%Y%m%d_%H%M%S).mp4

这种“go2rtc + ffmpeg 定时录制”的方案轻量、不折腾,几乎不占资源,很适合树莓派和小主机。

8. 常见问题与排查技巧实录

最后这部分,我把自己和身边朋友在实际使用中遇到的高频问题整理成一个速查表。很多问题看似诡异,实际上原因就那么几类。

现象 可能原因 解决办法
Web 页面打不开 容器没起来 / 1984 端口被占 docker logs go2rtc 看日志,用 ss -lntp 查端口
浏览器播放一直转圈 WebRTC UDP 端口没通 确认 host 网络模式,检查防火墙是否放行 8555/udp
VLC 能看,浏览器不能看 WebRTC 协商失败 在配置里加 STUN,或者强制播放端走 HLS
摄像头几分钟后掉线 摄像头单路连接数限制 减少直接连接摄像头的客户端,统一走 go2rtc 转发
画面延迟越来越大 传输用了 TCP 且丢包多 局域网内优先 UDP,跨网段再考虑 TCP
配置改了不生效 热加载有缓存 手动重启容器
CPU 占用突然飙高 某路流在转码 查看日志,找到转码流,调整摄像头码流类型为 H.264
RTSP 地址带密码,日志泄露 明文写在 URL 里 内网可忽略,公网或共享场景用环境变量代替
摄像头需要 ONVIF 探测才能连 固件默认关闭 RTSP 去 Web 界面激活设备并开启 RTSP

再说两个容易被忽略的坑。

第一个,很多海康和大华摄像头在默认配置下,RTSP 鉴权用的是 digest,go2rtc 是支持的。但如果你把密码写错了,它不会马上报错,而是每隔一段时间重试,日志里会反复出现 401 Unauthorized 。排查时不要只盯第一段日志,多看几行。

第二个,如果你在 go2rtc 里配了很多流,但某些流长时间没有人看,连接会处于闲置状态。有些摄像头检测到没有客户端拉流,会自动降低码流或休眠,导致你再次播放时前几秒很模糊。这种情况一般无法完全禁用,但你可以通过前端播放器设置“立刻请求主码流”来缓解,或者在录像系统里保持一路长期拉取,让摄像头不进入低功耗模式。

排查问题我还有一个习惯:先确认源本身能不能正常拉取,再去怀疑 go2rtc。可以用 ffprobe 直接探测摄像头:

ffprobe -rtsp_transport tcp -v error -show_entries format=duration -of default=noprint_wrappers=1 rtsp://admin:password@摄像IP:554/h264/ch1/main/av_stream

能打出时长,说明源是健康的,那问题大概率在 go2rtc 或网络链路上;打不出来,就先从摄像头端下手,检查 RTSP 开关、账号密码、网络连通性。

9. 进阶玩法:多流聚合监控墙、画面预览大屏和自定义 API

如果你已经能把摄像头全部接入 go2rtc,并且播放也正常了,接下来玩点进阶的。

go2rtc 的 Web 界面可以同时打开多个流,这一点很适合做“监控墙”。直接在 URL 后面加多个 ?stream= 参数,或者用 iframe 组合多个播放器,都能达到效果。我自己做过一个简单的监控大屏页面,用浏览器全屏打开,页面里嵌了八个 iframe,每个 iframe 指向 go2rtc 的不同流,整体效果和商业监控墙软件差别不大,但成本几乎为零。

如果你要做更复杂的自动化,go2rtc 还提供了一套 JSON API。比如获取所有流的列表:

curl http://IP:1984/api/streams

返回结果是 JSON 格式,包含每个流的状态、源地址和对外地址,方便你写脚本对接自己的系统。也可以用 API 动态增加临时流:

curl -X POST http://IP:1984/api/streams \
  -H "Content-Type: application/json" \
  -d '{"name":"temp_cam","url":"rtsp://admin:password@192.168.1.88:554/h264/ch1/main/av_stream"}'

这个功能在临时调试或者给某个活动临时接一路摄像头时特别好用,不用去改配置文件。

最后一个建议:版本升级要谨慎。go2rtc 更新频率不算慢,我遇到过几次升级后配置格式有细微变化,导致旧配置不兼容的情况。升级前最好先备份 go2rtc.yaml 和 docker-compose.yml,升级后第一时间看日志有没有警告。如果只是小版本升级,直接 pull 最新镜像即可;如果是跨大版本,建议先在一个测试环境跑通再上生产。

我个人在实际使用中最大的体会是,go2rtc 的精髓不是“多协议支持”这个标签,而是“把所有摄像头源统一抽象成一个简单的名字”,上层应用只需要知道流名字,不需要关心流从哪里来、什么格式、什么品牌。这种“一个出口对接所有”的思路,比单独给每个摄像头配转发要省心得多。如果你也在被各种摄像头协议折腾,不妨按这篇文章的步骤,花半小时把 go2rtc 跑起来,然后把摄像头一个个接进去,大概率你会回来感谢这个项目。

Logo

邀请您加入社区

更多推荐