家里摄像头牌子一多,事情就变得很烦:海康的用海康的App,TP-Link的用TP-Link的App,大华的又要单独装一个客户端,电脑上想同时看几路画面,得开好几个窗口。后来我接触到 go2rtc 这个项目,才算真正把这个问题解决掉。它就是一个用 Go 写的多协议流媒体网关,配合 Docker 部署,一条命令就能把 RTSP、RTMP、HLS、WebRTC 这些协议统一起来,内置网页管理界面,还能把视频流直接喂给 Home Assistant、Frigate 这些智能家居平台。这篇文章我会从 Docker 部署讲到摄像头接入、多协议播放、常见问题排查,尽量把每一步都讲透。无论你是刚接触 Docker 的新手,还是已经折腾过一阵子智能家居的老玩家,这篇应该都能帮到你。

1. 为什么偏偏选 go2rtc

1.1 它的核心能力:把视频源和播放端解耦

先理解一下 go2rtc 到底解决了什么问题。摄像头这东西,协议五花八门,很多安防设备只支持 RTSP,但浏览器播放 RTSP 又很费劲,手机 App 也未必直接支持。你可能会想:那我直接用 FFmpeg 转一下不就行了?确实可以,但 FFmpeg 是命令行工具,转出来的流单路用还行,多路管理、动态切换、按需拉流、失败自动重试这些需求,它就很难受。

go2rtc 的定位是一个中间层,它把“视频源”和“播放端”彻底解耦。视频源可以是 RTSP、RTMP、HTTP-FLV、MJPEG,甚至是某些只支持私有协议的摄像头;播放端可以是浏览器里的 WebRTC、HLS、MJPEG,也可以是 VLC 里的 RTSP、RTMP 地址,还可以是 Home Assistant 的 stream 组件。go2rtc 在你配置好之后,会自动做协议转换,你不需要关心每个播放端到底支持什么格式,只要给它一个统一的入口就行。

举个例子。我家里有一台老海康摄像头,只支持 RTSP 和 H.264,另外一台新买的支持 H.265。用 go2rtc 之后,我可以在 Web 界面里同时预览这两路画面,浏览器不需要装任何插件,因为 go2rtc 会把 H.265 的流自动转成浏览器能解码的格式。这种“屏蔽底层差异”的能力,正是它叫“gateway”的原因。

1.2 和其他方案对比,为什么它更适合家庭场景

有段时间我也折腾过别的方案,踩了不少坑,简单说一下对比结果:

方案 优点 缺点 适合人群
FFmpeg 命令行 万能,几乎所有格式都能处理 多路管理繁琐、没有 Web UI、状态不可视 临时转一下流的人
Nginx + RTMP 模块 稳定、可以对外分发 RTMP/FLV 配置复杂、HLS 切片要自己调、调试痛苦 有服务器运维经验的人
Zoneminder 功能完整、有检测和录像 太重了,配置学习曲线陡峭 需要完整 NVR 方案的人
Frigate AI 检测很强、Docker 化程度高 本身不是流媒体网关,需要外接视频源 主要做检测、录像的人
go2rtc 轻量、配置简单、有 Web UI、HA/Frigate 深度兼容 不能替代 NVR 做长时间录像 想把摄像头流统一管理和分发的人

我自己最后选择 go2rtc,核心原因就三个:第一,镜像很小,Docker 部署一条命令搞定;第二,内置 Web 界面,所见即所得;第三,如果你玩 Home Assistant 或者 Frigate,它几乎是它们最顺手的流媒体源,官方都默认推荐用它。它不是万能的,不会做录像存储和事件检测,但“把各路摄像头协议理顺”这件事,它做得极其漂亮。

2. Docker 装好之前,先把这些坑填了

2.1 Windows 上 Docker Desktop 装不上或者启动失败怎么办

不少人是在 Windows 上玩 Docker,装 Docker Desktop 的时候经常卡壳,最常见的报错就是:

Docker Desktop failed to start because virtualization support was not detected.

看到这个不用慌,本质就是虚拟化没开或者没开全。先按 Ctrl + Shift + Esc 打开任务管理器,切到“性能”标签页,看右下角“虚拟化”这一栏是不是“已启用”。如果是“已禁用”,那就需要进 BIOS/UEFI 开启 Intel VT-x 或者 AMD-V ,具体入口每个主板不一样,但名称通常带 Virtualization、VT-x、SVM 之类的字样。开机按 Del 或 F2 进 BIOS,找到后改成 Enabled,保存重启。

如果虚拟化已经开了,但还是报错,那就是 Windows 功能没启用。去“控制面板 -> 程序 -> 启用或关闭 Windows 功能”,把这三样都勾上:

  • Hyper-V
  • 适用于 Linux 的 Windows 子系统
  • 虚拟机平台

勾完后重启系统,再打开 Docker Desktop。还有一个坑是旧版本 WSL1 和 WSL2 混用导致 Docker 起不来,这时在管理员 PowerShell 里执行 wsl --update 、 wsl --set-default-version 2 基本能解决。实测下来,Windows 下 Docker 启动失败九成以上逃不出这几个原因。

2.2 Linux 下装 Docker 和镜像拉取慢的问题

如果你用的是 Ubuntu 或 Debian,装 Docker 最省事的方式是用官方脚本:

curl -fsSL https://get.docker.com | sh

CentOS 的话可以看官方文档装 docker-ce 源。装完记得把当前用户加入 docker 组,不然每条命令都要 sudo:

sudo usermod -aG docker $USER
newgrp docker

另一个高频问题是拉镜像特别慢。Docker Hub 在国内访问经常抽风,解决方案是给 Docker 配置镜像源。编辑 /etc/docker/daemon.json :

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

改完之后重启 Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

如果你用的是群晖 NAS、树莓派这类 ARM 设备,记得拉镜像的时候看清楚架构。go2rtc 的官方镜像 alexxit/go2rtc 是同时发布 amd64 和 arm64 版本的,树莓派 4、群晖大部分型号都能直接跑,不需要手动编二进制。

3. Docker 部署 go2rtc,两种方式都给你

3.1 docker run 一条命令快速跑起来

验证环境没问题之后,最快的方式是一条 docker run 把它拉起来:

docker run -d \
  --name go2rtc \
  --restart unless-stopped \
  -p 1984:1984 \
  -v /opt/go2rtc/config:/config \
  alexxit/go2rtc:latest

几个参数挨个说明一下:

  • -d :后台运行。
  • --name go2rtc :给容器起名字,方便后面 docker logs go2rtc 。
  • --restart unless-stopped :容器崩溃或服务器重启后自动拉起。这个参数很重要,不然无故退出后你都不知道它什么时候挂的。
  • -p 1984:1984 :把容器里的 1984 端口映射到宿主机,这是 go2rtc 的 Web 界面和 API 端口。
  • -v /opt/go2rtc/config:/config :挂载配置目录。go2rtc 的配置文件是 go2rtc.yaml ,放在挂载目录里,容器内外都能改。

跑起来之后,浏览器直接访问 http://服务器IP:1984 ,如果看到 go2rtc 的 Web 界面,说明部署成功了。这个界面自带播放器和配置功能,后面接入摄像头时非常直观。

3.2 用 docker compose 部署,升级更舒服

如果你觉得 docker run 参数太长,或者想固定版本方便回滚,我更推荐用 docker compose。先建目录:

mkdir -p /opt/go2rtc/config
cd /opt/go2rtc

然后创建 docker-compose.yml :

services:
  go2rtc:
    image: alexxit/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    ports:
      - "1984:1984"
      # 如果需要对外提供 RTSP 回放,解除下面这行的注释
      # - "8554:8554"
    volumes:
      - ./config:/config
    environment:
      - TZ=Asia/Shanghai

启动命令:

docker compose up -d

后续版本升级也简单:

docker compose pull
docker compose up -d

顺带一提,如果你的服务器上跑了很多容器,建议在 compose 里给 go2rtc 加一个资源限制,防止它转发高码流时把宿主机内存吃满:

    deploy:
      resources:
        limits:
          memory: 512M

这里多说一句:go2rtc 的 1984 端口是主入口,默认就够用了。RTSP 对外端口默认没有开启,如果你需要让 VLC 等外部播放器通过 RTSP 拉流,再在配置里写上 rtsp.listen: ":8554" ,然后在 compose 里把 8554 映射出去就行。

3.3 配置目录挂载和日志排错

很多人第一次部署完,修改 go2rtc.yaml 后重启容器,发现配置没生效,然后开始怀疑人生。多半是因为挂载目录不对。我自己习惯把配置放在 /opt/go2rtc/config 下,容器里统一映射到 /config ,然后 go2rtc 默认读取 /config/go2rtc.yaml 。这样直接编辑宿主机的文件就行,改完执行 docker restart go2rtc 即可。

如果容器一直重启,最快的方式是看日志:

docker logs go2rtc --tail 50

yaml 配置格式稍微写错一点,go2rtc 可能直接退出,但日志里一般有明确的报错行,比如 yaml: line 5: mapping values are not allowed in this context 。看到这种提示,去配置文件对应行附近检查缩进和冒号就行。

4. 让摄像头“开口”:接入与转码

4.1 手写配置文件,接入 RTSP/RTMP/MJPEG 摄像头

go2rtc 的配置文件结构非常简单,核心就是 streams 这个字典,key 是流的名字,value 是源地址。下面是一份实际可用的 go2rtc.yaml :

log:
  level: info

streams:
  living_room:
    - rtsp://admin:123456@192.168.1.64:554/Streaming/Channels/101
  garage:
    - rtsp://admin:123456@192.168.1.65:554/cam/realmonitor?channel=1&subtype=0
  front_door:
    - rtmp://192.168.1.66/live/door
  old_camera:
    - mjpeg://admin:123456@192.168.1.67/video.mjpg

这里有几个要注意的点:

第一,RTSP 地址里的用户名密码如果包含特殊字符,比如 @ 、 : 、 / 等,必须做 URL 编码。比如密码是 abc@123 ,要写成 abc%40123 ,否则 go2rtc 会把地址解析错。第二,海康和大华的 RTSP 路径不一样,海康常见的是 /Streaming/Channels/101 ,大华常见的是 /cam/realmonitor?channel=1&subtype=0 。别问我怎么知道的,都是一个个试出来的。第三,配置里如果用多个源,go2rtc 会按顺序尝试连接,相当于做个简单的故障切换。

4.2 不想写配置文件,Web 界面点点点就行

如果你觉得手写 yaml 还是麻烦,go2rtc 的 Web 界面其实已经内置了流管理功能。打开 http://IP:1984 ,页面上方有 Streams 区域,可以直接点击“Add”按钮添加。输入流名称和源地址,它会立即尝试连接,并且在浏览器里开始预览。

这个 Web 界面还有一个很实用的功能:自动发现局域网里的 ONVIF 摄像头。在页面上的 Discovery 区域,go2rtc 会自动扫描并列出支持 ONVIF 的设备,你只需要点击摄像头名字,它就会自动帮你生成配置并添加。我之前那台只记得 IP 不记得 RTSP 路径的摄像头,就是靠这个自动发现功能找回来的,省了翻手册的功夫。

不过需要提醒一句:自动发现依赖网络环境,如果你的摄像头 VLAN 隔离了,或者 go2rtc 容器用了 bridge 网络连不到摄像头网段,就发现不了。这时候还是老老实实手写地址,或者把容器网络改成 host 模式再试。

4.3 ffmpeg 前缀:兼容老旧摄像头和 H.265 转 H.264

不是所有摄像头都能被 go2rtc 直接拉流。某些老设备对 RTSP 的实现不太标准,go2rtc 内置的 RTSP 客户端连不上;还有一些摄像头只输出 H.265 编码,而浏览器不支持时就会出现黑屏。解决这个问题,go2rtc 提供了 ffmpeg: 前缀,启用后会自动调用内置的 FFmpeg 来做拉流和转码。

streams:
  old_camera_h265:
    - ffmpeg:rtsp://admin:123456@192.168.1.68:554/stream1

加上 ffmpeg: 前缀之后,go2rtc 会把 FFmpeg 当成一个“万能拉流器”,兼容性会好很多。如果你的浏览器播放 H.265 卡顿或者黑屏,也可以在 ffmpeg 参数里强制转成 H.264。go2rtc 支持通过环境变量或者配置给 ffmpeg 传附加参数,比如:

ffmpeg:
  bin: ffmpeg
  parameters:
    - -c:v
    - libx264
    - -preset
    - veryfast
    - -c:a
    - aac

这段配置的意思是:所有走 FFmpeg 的流转码成 H.264 + AAC。代价是 CPU 占用会上去,但换来的兼容性提升非常明显,老浏览器、低性能终端基本都能看。

5. 多协议播放与对外共享

5.1 Web 播放器:WebRTC 低延迟、HLS 兼容性最好、MJPEG 兜底

go2rtc 的 Web 界面里,每添加一条流,它都会自动生成对应的播放地址。对着不同场景,选择也不同。

协议 延迟 兼容性 适用场景
WebRTC 极低,300ms 左右 需要浏览器支持,内网最佳 自用实时监控
HLS 2-5 秒 所有浏览器、VLC 都能播 跨网络分享、嵌入网页
MJPEG 较高且体积大 几乎所有老旧设备都支持 特殊环境兜底

我自用的时候优先 WebRTC,因为内网延迟极低,画面几乎是实时的。看门铃按铃、看宠物动态,这种对实时性要求高的场景,WebRTC 体验最好。如果要把流分享给不在同一局域网的人看,HLS 是最省心的,直接发一个 .m3u8 地址,对方用浏览器打开就能看,没有插件依赖。

实际用法上,打开 http://IP:1984 后选择一条流,Web 界面右下角会给出不同协议的地址。比如 HLS 地址通常是:

http://IP:1984/api/stream/living_room.m3u8

RTSP 地址如果你开了 rtsp.listen ,则是:

rtsp://IP:8554/living_room

这个“一次接入,四处可用”的感觉,正是它作为流媒体网关的核心价值。

5.2 把视频流接到 Home Assistant 和 Frigate

很多玩 go2rtc 的人,最终目标都是把它接到 Home Assistant 或者 Frigate 里做统一监控和 AI 识别。这一步其实相当顺滑。

在 Home Assistant 里,如果要用 generic camera 或 stream 组件,视频源地址直接填 go2rtc 的 RTSP 或 HLS 地址即可。比如在 configuration.yaml 里:

camera:
  - platform: generic
    name: Living Room
    stream_source: rtsp://127.0.0.1:8554/living_room

如果你用的是 Frigate,Frigate 官方很推荐 go2rtc 作为统一流媒体入口。你可以在 Frigate 的 config.yml 里把摄像头源指向 go2rtc:

go2rtc:
  streams:
    living_room: rtsp://127.0.0.1:8554/living_room

cameras:
  living_room:
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/living_room
          roles:
            - detect
            - record

这样做的最大好处是,Frigate 不再需要直接面对各种不同品牌的摄像头协议,而是统一从 go2rtc 拿流,同时 go2rtc 还能给 Web 界面、Home Assistant 提供实时预览,一套配置多处复用,省心太多。

5.3 外网访问时必须做的安全措施

go2rtc 的 Web 界面本身是不带登录认证的,一旦把 1984 端口直接暴露到公网,等于把家里的实时监控画面免费送给所有人。我强烈不建议这么做。如果确实需要外网访问,比较稳妥的做法是套一层反向代理并加鉴权。

Nginx 的反向代理配置可以参考:

server {
    listen 443 ssl;
    server_name your.domain.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://127.0.0.1:1984;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

.htpasswd 文件可以用 openssl passwd -apr1 生成。需要提醒的是,WebRTC 的 UDP 端口经过反向代理转发时会比较麻烦,如果你外网主要用 HLS,那这套 Nginx 方案完全够用;如果你外网也想用 WebRTC 低延迟,那就要把对应的 UDP 端口也一并考虑进去,这属于更进阶的网络配置了。

6. 常见问题排查和避坑记录

6.1 Docker 容器层面

现象 可能原因 解决办法
容器一直重启 go2rtc.yaml 格式错误 docker logs go2rtc --tail 50 查看报错
浏览器打不开 1984 端口没映射或者被防火墙拦了 检查 docker ps 确认映射,放行宿主机 1984 端口
改了配置重启后没变化 挂载目录不对 确认容器内 /config 对应宿主机哪个目录
服务器重启后容器没了 缺 --restart 参数 重新创建容器并加上 unless-stopped

我在刚开始用 Docker 的时候,最常犯的错就是把配置文件的挂载路径写错,导致改了宿主机文件但容器里读的还是旧配置。后来我统一用 /opt/go2rtc 作为项目根目录, config 子目录挂载,再没出过这种问题。

6.2 go2rtc 连接摄像头失败

现象 可能原因 解决办法
画面黑屏 H.265 编码浏览器不支持 用 ffmpeg: 前缀转码成 H.264
连接超时 RTSP 地址里的密码含特殊字符 对特殊字符做 URL 编码
画面卡顿 无线网络质量差或者码流过高 换成有线连接,或者使用摄像头的子码流
只有画面没有声音 摄像头的音频编码格式兼容性差 尝试用 FFmpeg 转成 AAC
无法自动发现摄像头 容器网络隔离了摄像头网段 改用 host 网络模式,或手动填写 ONVIF 地址

这里特别想分享一个小经验:很多摄像头默认的主码流是 4K 甚至更高,在局域网预览时看起来流畅,但一旦走 WebRTC 或者 HLS 播放到外网,带宽不够就会出现持续卡顿。go2rtc 本身不做录像,只是实时转发,这时最有效的解法是接入摄像头的子码流,比如海康的 /Streaming/Channels/102 就是子码流,分辨率低不少,用于预览足够,带宽压力也会小很多。

6.3 资源占用与控制

go2rtc 本身作为 Go 程序,内存占用很低,通常几十 MB 就够。但如果你给很多路流都加了 ffmpeg: 转码,CPU 占用会跟着涨,因为 FFmpeg 转码非常吃计算资源。如果你在群晖这类设备上部署,建议给容器加上资源限制,并在 go2rtc 配置里合理选择是否转码。H.265 转 H.264 只在你确实需要浏览器播放时再做,不需要强行全局转码。

日志方面,go2rtc 默认会写不少访问日志,跑久了也会占磁盘空间。可以在配置里把日志级别调成 warn ,减少磁盘写入:

log:
  level: warn

最后再分享一个小技巧

部署 go2rtc 这件事,其实逻辑上并不复杂:Docker 负责跑起来,go2rtc 负责把各路摄像头协议理顺,Web 界面负责可视化,Home Assistant 和 Frigate 负责后续的自动化与检测。我个人的建议是,刚开始不用急着上 compose,先 docker run 一条命令跑起来,在 Web 界面里把摄像头添加好,确认能预览、能播放、能转码,再逐步迁移到 compose 固定下来。等你真正把配置稳定住,你会发现统一的流媒体入口带来的便利远超预期,家里再多几个摄像头,也只是在 yaml 里加几行的事情。

Logo

邀请您加入社区

更多推荐