几个月前,朋友让我帮忙把他家仓库里的四路海康摄像头接到一个统一页面里,手机、电脑、大屏都能看,最好还能录像。我第一反应是上 NVR,但朋友要求“尽量少花钱、不折腾硬件、最好一台小主机搞定”。于是我翻出了吃灰的迷你主机,装好 Docker,直接跑 go2rtc。实测下来,这个轻量级流媒体网关确实是摄像头多协议接入场景里最省心的方案之一,配置极简、协议支持广、还有 Web 界面可以直接预览。如果你也被各种摄像头协议、播放格式折磨过,这篇实战记录应该能帮你少走不少弯路。

先说结论:go2rtc 是一个开源的多协议流媒体网关,支持 RTSP、RTMP、HLS、WebRTC、MJPEG、SIP 等协议,可以把它理解为“视频流翻译官”——把不同品牌摄像头的私有流统一转换成你需要的形式。配合 Docker 部署,一台普通小主机就能轻松搞定多路摄像头的接入和分发。这篇文章会从方案选型讲起,逐步拆解部署过程、配置细节、踩坑记录,适合有一定 Docker 基础、但还没摸过 go2rtc 的朋友。

1. 为什么我最终选了 go2rtc:项目整体思路与方案选型

1.1 从“摄像头接入难”说起:协议不统一的尴尬

做过监控接入的人都知道,最痛的不是摄像头画质差,而是协议五花八门。同一个局域网里,海康走 RTSP,大华走 RTSP 但端口可能不一样,萤石某些型号只给你私有 SDK,老式 IPC 甚至只出 MJPEG。你还没开始做业务功能,光是把这些流拉通就已经耗掉大半天。

更麻烦的是输出端。你的 Web 页面不能直接播放 RTSP,浏览器只认 HTTP 协议,而 HLS 会有几秒延迟,WebRTC 延迟低但配置复杂。如果用 FFmpeg 自行转码,CPU 占用高不说,命令行还特别长。这时候 go2rtc 的价值就体现出来了:它把“各种输入协议”统一映射成一套内部媒体源,再由它对外输出成 RTSP、HLS、WebRTC、MJPEG 等等。输入侧和输出侧完全解耦。

1.2 go2rtc 的三大核心优势:协议转换、WebRTC 低延迟、配置极简

选型的时候我对比过 Mediamtx(原 RTSP-Simple-Server)、Janus、SRS 这类方案。Mediamtx 很轻,但主要专注于 RTSP 的重新分发;Janus 功能强,但部署和配置门槛高,对小白不太友好;SRS 更适合大规模直播分发,单机接几路摄像头属于杀鸡用牛刀。

go2rtc 最打动我的三点是:

  • 协议转换能力极强 。不仅能拉 RTSP,还直接支持 RTMP、HLS、MJPEG、SIP、HTTP 拉流,甚至能从某些私有协议的摄像头上直接取流。输入和输出协议可以自由交叉组合,比如把海康 RTSP 拉进来,输出成 WebRTC 给浏览器低延迟播放。
  • WebRTC 低延迟做得非常好 。要理解,浏览器原生播放 RTSP 基本不可能,传统做法是走 HLS,延迟 3~10 秒。而 go2rtc 通过 WebRTC 可以把延迟做到 500 毫秒以内,这个体验差距是质的飞跃。
  • 配置极简 。核心就是一个 YAML 文件,不需要数据库,不需要额外服务,启动参数也少得可怜。对比 Janus 那套复杂的插件配置,go2rtc 可以说是一看就会。

1.3 为什么用 Docker 跑 go2rtc(对比裸装)

其实 go2rtc 本身就是一个编译好的二进制文件,裸装一点也不复杂。但我之所以强烈推荐 Docker 方式,原因有几个:

第一, 清爽 。go2rtc 运行时会占用一个端口范围(默认 1984 起),还可能需要访问摄像头所在网段。用 Docker 可以把它和宿主机隔离,配置文件挂载到外部,升级时直接换镜像,无需担心环境污染。

第二, 批量部署一致性好 。如果你有多台设备需要部署,写一份 docker-compose.yml 就够了,拷贝到哪都能跑。裸装的话,每台机器的环境、依赖、目录结构都可能出差错。

第三, 快速回滚 。镜像 tag 固定后,升级出问题就回退到旧 tag,几秒钟搞定。裸装的二进制回滚需要手动备份,容易忘。

2. 部署前的准备:环境、端口与关键概念

2.1 Docker 环境检查与安装(附常用命令)

如果你机器上还没装 Docker,可以先用这条命令确认一下:

docker --version
docker compose version

如果提示找不到命令,那就先装 Docker。Ubuntu / Debian 系可以用官方脚本:

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

CentOS / RHEL 系建议走 yum 源安装 docker-ce 和 docker-compose-plugin 。Windows 和 macOS 用户直接用 Docker Desktop 即可,但要注意 Docker Desktop 在部分老机器上需要开启虚拟化支持,具体去看 BIOS 里的 VT-x 或 AMD-V。

装完以后,最好跑一个测试容器验证环境是否正常:

docker run --rm hello-world

看到 “Hello from Docker!” 就说明环境没问题。这一步看似多余,但能帮你提前排除网络拉镜像失败、权限不足等基础问题。

2.2 go2rtc 的端口规划与网络模式选择

go2rtc 默认监听 1984 端口,Web 管理界面和 API 都走这个端口。除了主端口,WebRTC 需要额外的端口用于媒体传输,默认是 8555 开始的 UDP/TCP 端口范围。如果你的前置环境有防火墙,必须把这两个端口同时放开。

这里有个容易踩的坑:如果你用 Docker 的 bridge 网络模式,容器内端口映射到宿主机后,WebRTC 的 UDP 端口经常会出现协商失败的情况,因为 go2rtc 返回给客户端的 IP 是容器 IP,而不是宿主机 IP。所以我的建议是:

如果 go2rtc 要接入摄像头并给局域网用户看流,网络模式直接选 host 。这样 go2rtc 和摄像头处在同一网络视图下,RTSP 拉流不会绕 NAT,WebRTC 也能正确拿到网卡 IP,少很多妖蛾子。

用 host 模式的副作用是端口没法用 -p 映射了,9184 就是 9184,1984 就是 1984,端口冲突需要自己注意。

2.3 搞懂 RTSP、RTMP、HLS、WebRTC 这几个词再动手

配置 go2rtc 之前,这几个协议术语最好先有个概念,不然看配置文档会一头雾水:

  • RTSP :专门用来控制流媒体会话的协议,摄像头领域最常见。海康、大华、宇视等品牌的 IPC 基本都支持。默认端口一般 554,但也可以改成自定义端口。
  • RTMP :Adobe 推流协议,直播行业的老牌标准。很多旧平台或编码器支持,但浏览器不原生支持,一般需要转成 HLS 或 WebRTC 播放。
  • HLS :苹果主导的基于 HTTP 的流媒体协议,兼容性极好,几乎所有浏览器都能播。缺点是切片带来延迟,通常 3~10 秒,不适合需要实时互动的场景。
  • WebRTC :浏览器原生支持的实时通信协议,延迟可以做到 1 秒以内,是 go2rtc 的最大卖点。但需要额外的信令和端口协商,对网络环境要求较高。

一句话总结:源端往往是 RTSP,观看端如果要低延迟就用 WebRTC,如果不介意延迟就用 HLS。

3. 实操:使用 Docker 部署 go2rtc 并接入第一路摄像头

3.1 通过 docker run 快速跑起来

先不走 compose,我们用一条最简单的命令把 go2rtc 拉起来测试连通性:

docker run -d --name go2rtc \
  --network host \
  --restart unless-stopped \
  -v /opt/go2rtc:/config \
  -e TZ=Asia/Shanghai \
  docker.io/alexxit/go2rtc:latest

拆开解释一下这些参数:

  • --network host :使用宿主机网络,解决 WebRTC 协商和摄像头回连问题。
  • --restart unless-stopped :异常退出或重启机器后自动拉起容器,这是跑服务的基础配置。
  • -v /opt/go2rtc:/config :把配置文件目录挂载到宿主机。go2rtc 默认在 /config 下读取 go2rtc.yaml ,如果你没挂载,容器重建后配置就全没了。
  • -e TZ=Asia/Shanghai :设置时区,避免日志和录像时间显示成 UTC。

启动后打开 http://你的主机IP:1984 ,看到 go2rtc 的 Web 界面就算成功了。首次打开时配置为空,页面会提示你添加流或编辑配置。

3.2 编写 docker-compose.yml 固化配置

docker run 适合快速验证,但长期跑服务建议用 compose 把配置固化下来。下面是我在生产环境里实际使用的一份 docker-compose.yml :

services:
  go2rtc:
    image: docker.io/alexxit/go2rtc:latest
    container_name: go2rtc
    network_mode: host
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /opt/go2rtc:/config

这套配置极其简单,没有任何多余参数。我把文件放在 /opt/go2rtc/docker-compose.yml ,然后执行:

cd /opt/go2rtc
docker compose up -d

用 docker compose logs -f 可以实时查看日志,排查问题非常方便。

为什么要专门建一个目录而不是直接挂 /config ?因为 go2rtc 的配置文件和录像/日志文件都放在同一个配置目录下更方便备份。目录权限记得给足,否则容器内写不进去配置文件,启动时会有报错。

3.3 在 Web 界面添加摄像头并查看流(RTSP 示例)

启动后打开 Web 界面,点击配置按钮,会看到一个 YAML 编辑框。最简单的配置格式如下:

streams:
  kitchen:
    - rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101

kitchen 是你给这路流起的自定义名称,随便起,但要有辨识度。后面是摄像头的 RTSP 地址,注意格式是 rtsp://用户名:密码@IP:端口/路径 。不同品牌的路径不一样:

品牌 常见 RTSP 路径示例
海康威视 /Streaming/Channels/101 (101 表示主码流、第一路)
大华 /cam/realmonitor?channel=1&subtype=0
宇视 /media/video1
TP-LINK /stream1

保存配置后,回到主界面就能看到 kitchen 这一路画面。如果画面没有自动出,点击一下卡片上的播放按钮即可。go2rtc 默认会生成一个可供浏览器直接播放的 WebRTC 流,这也是它最大的亮点——不用再开 VLC 或者转 HLS 才能预览。

3.4 配置多协议输出:同一路源同时出 RTSP/HLS/WebRTC

go2rtc 的“多协议输出”不是靠写多个输出地址实现的,而是内置了一套按需转协议的逻辑。你只要定义好源,go2rtc 会自动生成以下访问地址:

  • WebRTC 播放地址: http://IP:1984/api/webrtc?src=kitchen
  • HLS 播放地址: http://IP:1984/api/hls/kitchen.m3u8
  • MJPEG 快照/视频流: http://IP:1984/api/mjpeg?src=kitchen
  • RTSP 重新分发: rtsp://IP:1984/kitchen

也就是说,同一个 kitchen 源,既可以被 Web 页面 WebRTC 播放,也可以被 VLC 用 RTSP 拉流,还可以接入 ffmpeg 做录像。你不需要为每种输出协议单独配置一个任务,这是 go2rtc 最省心的地方。

这个“按需转协议”机制背后的原理是:go2rtc 在请求到达时才创建对应的输出会话,内部通过一个通用媒体管线做协议转换。所以即使你配置了十路摄像头,只要没人观看,CPU 基本零占用;一旦有人拉流,才真正开始消耗资源。

4. 高阶玩法:多摄像头接入、ONVIF 自动发现与 API 调用

4.1 多路摄像头批量接入的方案

很多人的第一反应是在 streams 下写十几条配置,但摄像头一多,这种方式立刻变得难以维护。我自己的做法是分两层:

第一层,保持 go2rtc 配置简单,只定义业务逻辑上需要的“逻辑流”:

streams:
  front_gate:
    - rtsp://admin:pass@192.168.1.101:554/Streaming/Channels/101
  back_yard:
    - rtsp://admin:pass@192.168.1.102:554/Streaming/Channels/101
  parking_lot:
    - rtsp://admin:pass@192.168.1.103:554/Streaming/Channels/101

第二层,用 watchtower 或 cron 脚本定期探测摄像头在线状态,把离线、在线情况汇总到一个监控页面。go2rtc 本身不负责状态监控,但它提供 REST API,你可以方便地获取每一路流的连接信息、在线状态和比特率。

如果你的摄像头很多而且支持 ONVIF,可以考虑直接使用 go2rtc 的 ONVIF 能力自动发现设备,避免手写几十条 RTSP 地址。

4.2 用 ONVIF 让 go2rtc 自动找到摄像头

ONVIF 是安防设备的标准协议之一,很多摄像头都支持。go2rtc 内置了 ONVIF 客户端能力,配置方式非常简单:

onvif:
  # 启动时自动发现局域网内的 ONVIF 设备
  discover: true
  # 如果设备需要认证,可以预设全局用户名密码
  username: admin
  password: password

配置好后,go2rtc 会在启动时向局域网广播 ONVIF 探测报文,支持 ONVIF 的摄像机会回应。这样你在 Web 界面就能直接看到发现的设备,勾选即可添加流,不需要再手查 RTSP 地址。

需要注意的是,很多老设备虽然标称支持 ONVIF,但只实现了一部分 Profile,不一定能自动出视频流。常见现象是设备能被发现,但拉流失败。这时还是得回到厂商的 RTSP 地址,手动配置最稳妥。

另外,ONVIF 发现功能依赖 UDP 广播,如果你把 go2rtc 跑在 Docker bridge 模式里,广播探测很可能失败。这就是我前面强调要用 host 网络的另一个原因。

4.3 通过 API 和前端播放器实现自定义集成

go2rtc 提供了一套非常友好的 HTTP API,比较常用的几个:

接口 作用
GET /api/streams 列出所有可用流及其状态
GET /api/streams/{name} 查看单路流的详情,包括连接数、协议、码率
GET /api/webrtc?src=kitchen 获取 WebRTC 播放所需的 SDP 信息
GET /api/hls/{name}.m3u8 获取 HLS 播放列表
GET /api/mjpeg?src=kitchen 获取 MJPEG 视频流(适合嵌入 img 标签)

我最常用的是 /api/streams ,可以直接拿到所有摄像头的在线状态、分辨率和码率,方便做自定义监控页面。前端播放器方面,go2rtc 官方提供了配套的 WebRTC 播放器组件,如果你是自己写前端,也可以用 html5 播放 HLS,或者直接嵌 <img> 显示 MJPEG 流。

这里有个小技巧:如果你要做一个“九宫格监控墙”,不需要自己写复杂播放器,直接用 MJPEG 输出到 <img> 标签即可。虽然 MJPEG 延迟不低、带宽占用也偏大,但对于不要求实时的监控墙场景,实现成本最低。

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

5.1 画面打不开?先按这个顺序查

有一次朋友反馈说摄像头接入后画面全是黑的,我在远程帮他排查,最后发现是密码里有特殊字符没做 URL 编码。这里总结一套我自己的排查顺序:

第一,确认 RTSP 地址对不对。先用 VLC 在电脑上拉一下同一个地址,如果能出画面,说明地址没问题,问题出在 go2rtc 或网络上。

第二,看 go2rtc 日志。使用 docker logs go2rtc 查看最近日志,如果出现 dial tcp ... connect: no route to host ,说明网络不通,检查摄像头网段和防火墙。如果出现 401 Unauthorized ,说明用户名或密码错误。

第三,确认端口可达。在运行 go2rtc 的机器上执行 telnet 摄像头IP 554 ,看 554 端口是否通。摄像头虽然和 go2rtc 在同一局域网,但一些单位网络做了端口隔离,光看 IP 通并不能代表 RTSP 端口也通。

第四,检查码流类型。有些摄像头主码流是 4K,转 WebRTC 时对性能要求高,如果卡顿,先切到子码流测试。子码流的 RTSP 路径通常是 .../Streaming/Channels/102 ,把最后的 101 改成 102 即可。

5.2 WebRTC 连不上:端口和 ICE 配置问题

WebRTC 是 go2rtc 最方便也是最容易出问题的输出方式。症状通常是:Web 页面显示正在连接,但迟迟不出画面,或持续黑屏。

最常见的坑有两个。第一个是 UDP 端口范围没放行。WebRTC 媒体传输默认走 8555 端口开始的 UDP,如果你的环境有防火墙,需要放行,比如:

ufw allow 1984/tcp
ufw allow 8555/udp
ufw allow 8555/tcp

第二个是 ICE 候选地址配置错误。当 go2rtc 跑在 Docker bridge 模式或跨网段访问时,它会向客户端返回一个内网或容器 IP,导致客户端无法连接。解决办法有两个:一是直接用 network_mode: host ;二是在 go2rtc 配置里手动指定 rtsp 或 webrtc 的公开 IP 地址。具体配置可以参考官方文档的 webrtc 段,形如:

webrtc:
  candidates:
    - 192.168.1.10:8555

如果你只是局域网内使用, host 模式基本能解决 99% 的问题,不需要手动指定候选地址。

5.3 容器重启后配置丢失怎么办?

这个场景我见得太多了。有人把 go2rtc 容器跑起来后,在 Web 界面里添加了一堆摄像头,用着很爽。结果某天机器重启,容器虽然起来了,但配置全没了,界面空空如也。

原因很简单:你没有挂载配置目录。go2rtc 的配置写在容器内的 /config/go2rtc.yaml ,如果容器被删除重建,数据就丢了。解决办法就是把 /config 挂载到宿主机。

如果已经发生了配置丢失,也不用太慌。如果你还记得摄像头的 RTSP 地址,重建一遍也很快。但更推荐的做法是养成“配置文件版本管理”的习惯,把 go2rtc.yaml 纳入 git 或至少定期备份到另一台机器。

5.4 实战避坑清单:我踩过的那些坑

最后分享几个我实测中踩过、但网上资料很少提及的坑:

第一, 不要在 streams 里给同名流配多个源 。go2rtc 支持多个源做故障切换,但如果你没有正确设置切换顺序,一个源挂掉时会出现几秒到十几秒的黑屏等待,体验很差。单摄像头场景就直接配一个源,不要画蛇添足。

第二, 注意码流规格差异 。不同品牌摄像头对 RTSP 的 SDP 描述细节不一样,go2rtc 对某些设备的兼容性比 ffmpeg 略差,偶尔会遇到有画面但声音不出,或者分辨率识别错误。遇到这类问题,可以用 ffmpeg 先转一遍再喂给 go2rtc,但这样会引入额外 CPU 开销,非必要不建议。

第三, 日志里出现 no compatible tracks 时先查音频。go2rtc 默认会保留视频和音频轨道,但有些老摄像头音频编码格式比较冷门(比如 G.711 变体),go2rtc 可能不支持。如果你只需要画面,可以在流配置里手动关闭音频:

streams:
  camera1:
    - rtsp://admin:pass@192.168.1.64:554/Streaming/Channels/101
    # 如果音频有问题,可以加一个 audio: false 的控制参数
    # 但具体看版本支持,旧版本可能需要通过 API 忽略音频

第四, 磁盘占用问题 。如果你用 go2rtc 做录像,要特别注意磁盘空间。很多摄像头主码流 4K 下码率能到 8~12 Mbps,一路一天就是 80~120 GB,多录几路不到一周磁盘就爆了。建议录像时主动切成子码流,或控制保留时间定期清理。

第五, 不要同时用多个平台竞争同一路 RTSP 流 。部分老摄像头同一路 RTSP 只允许 1~2 个连接,如果你既在 go2rtc 里拉了流,又在其他软件里直接连同一个摄像头,摄像头可能会主动断开其中一个连接。这种问题表现非常诡异,查了很久才发现是连接数限制。

在我实际使用的这几个月里,go2rtc 给我最大的感受就是“轻”。它不像一些重量级流媒体服务器那样需要一堆配置和依赖,一个 20MB 左右的镜像,配上十几行 YAML,就能稳定输出多路摄像头的多协议流。如果你只是在局域网里做摄像头统一接入、或者给监控系统加一个低延迟看流能力,go2rtc 完全够用,没必要上重型平台。后面我还在计划把 go2rtc 接入 Home Assistant,让安防和自动化联动起来,等跑通了再写一篇记录。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐