家里摄像头一多,问题就来了:海康的走RTSP、大华的走私有SDK、树莓派那边可能还在出MJPEG,手机上看个实时画面还得装三四个App,浏览器里根本没法直接打开RTSP流。我折腾过Mediamtx,也试过ZLMediaKit,最后固定下来用的是go2rtc——一个能把RTSP、RTMP、HLS、WebRTC、MJPEG这些协议全部打通的小工具,配合Docker跑起来,一台普通小主机就能把所有摄像头统一成一个流媒体出口,浏览器直接看、Home Assistant直接接、手机端不用装额外播放器。这篇就完整记录我怎么用Docker部署go2rtc,把手头各路摄像头接进来,以及这几个月踩过的坑。

1. 方案选型:为什么是go2rtc而不是其他流媒体服务

1.1 流媒体中间层的核心矛盾

摄像头本身不会输出浏览器能直接播放的协议。RTSP是监控领域事实标准,但浏览器不认,手机浏览器也不认;HLS延迟太高,看监控画面会感觉像在看“延时摄影”;MJPEG虽然万能但带宽浪费严重。这就需要一个中间层把摄像头原有的协议转换成浏览器、App、智能家居平台都能消费的格式。

我最早用的Mediamtx(前身是RTSP-Simple-Server),功能纯粹,但配置摄像头流时得手动给每路流指定协议和端口,摄像头一多配置就冗长。ZLMediaKit功能强,但部署和调优成本高,对只想把摄像头“接进来就能看”的场景有点杀鸡用牛刀。

go2rtc的定位很明确:不重复造轮子,而是做协议转换和流分发的中枢。它原生支持RTSP、RTMP、HLS、MJPEG、WebRTC、SIP,还能直接拉取ONVIF设备,甚至可以反向把流推给其他服务。最关键是它对WebRTC的支持做得非常顺滑——浏览器里看监控画面,延迟能压到几百毫秒,这才叫“实时监控”。

1.2 go2rtc的差异化优势

它的核心设计是“会话中按需拉流”。传统流媒体服务器会一直从摄像头拉流再分发,占带宽也占摄像头连接数;go2rtc可以配置成按需启动,有人看的时候才去拉摄像头流,没人看就断开,对摄像头和网络的压力都小很多。实测下来,家里3路摄像头全天挂着,路由器后台看流量,只有在打开网页看监控的时候才会出现持续带宽占用,这点非常友好。

另一个让它胜出的点是配置格式。go2rtc的配置直接用YAML定义流名称和源地址,支持通配符、模板变量,还能通过API动态添加流。比如你有一台NVR,下面挂了几十路摄像头,go2rtc可以直接对接NVR的RTSP端口,也可以用 [name]:[password]@[ip] 的格式统一拼接地址,不用每路都手写一行完整URL。

1.3 选型时的对比清单

方案 部署复杂度 浏览器播放支持 延迟表现 配置管理
go2rtc 低(单容器/单二进制) 原生WebRTC+HLS+MSE 极低(百毫秒级) YAML+API,动态管理
Mediamtx 低 需配合HLS或WebRTC相关配置 中低 配置较繁琐,适合轻量
ZLMediaKit 高 需配合前端播放器 低 接入复杂,运维成本高
传统NVR/平台 取决于硬件 基本靠专用App或插件 中 封闭生态,扩展差

如果你的需求是“我有一堆杂牌摄像头,想在一个网页里全部实时看到,还希望接入Home Assistant之类的智能家居中枢”,go2rtc就是性价比最高的一条路。如果只是单纯要把一路RTSP转成HLS推送,那Mediamtx就够了,没必要上go2rtc。选型的本质是先分清自己的核心需求是“协议转换”还是“规模化管理”,go2rtc两者兼备,但它的优势场景明显偏向后者。

2. Docker部署go2rtc:从安装到Web界面

2.1 安装前的准备:几个必须要确认的点

部署之前先确认三件事:Docker环境是否正常、端口是否可用、以及摄像头是否支持RTSP或ONVIF。

Docker环境不多说,Linux上用包管理器装docker-ce,Windows上装Docker Desktop,需要确保能正常拉取镜像。端口方面,go2rtc默认用 1984 端口同时提供Web管理界面和API,WebRTC还需要一组UDP端口用于媒体传输。这里有个容易踩的坑:很多人只改了Web端口,忘了WebRTC端口也需要映射到宿主机,结果网页能看到画面列表,但一点“播放”就卡住转圈。

摄像头那边,尽量先去摄像头管理后台把RTSP功能打开。海康、大华、TP-LINK这类主流牌子一般都有RTSP开关,有的需要设置RTSP认证账号密码。如果你的摄像头不支持RTSP但支持ONVIF,go2rtc也能通过ONVIF自动发现设备并拉流。注意ONVIF只是“设备发现和管理协议”,真正的视频流还是走RTSP,所以设备至少要能提供RTSP流。

2.2 docker run快速部署

官方镜像名是 alexxit/go2rtc ,带平台标签,x86和ARM架构都能跑。我最开始是在一台x86小主机上跑的,部署命令如下:

docker run -d \
  --name go2rtc \
  --restart unless-stopped \
  -p 1984:1984 \
  -p 50000-50010:50000-50010/udp \
  -v /opt/go2rtc/go2rtc.yaml:/config/go2rtc.yaml \
  alexxit/go2rtc:latest

这里给几个参数解释:

  • -p 1984:1984 :Web界面和API端口,浏览器访问 http://宿主机IP:1984 就是管理界面。
  • -p 50000-50010:50000-50010/udp :WebRTC媒体传输端口范围。这个范围不是固定的,可以自己定,但一定要和后面容器内配置保持一致。如果防火墙开着,记得同时放行这段UDP端口。
  • -v /opt/go2rtc/go2rtc.yaml:/config/go2rtc.yaml :把配置文件挂载出来。挂载的好处是改配置不用进容器,直接编辑宿主机文件,然后重启容器即可生效。

启动之后,先浏览器打开 http://192.168.1.100:1984 ,看到go2rtc自带的“Streams”页面就说明部署成功了。页面上现在应该空空如也,因为还没添加任何流。

2.3 docker compose方式:适合一次配置长期复用

如果机器上同时跑了很多容器,我更推荐用docker compose管理。下面是我实际在用的 docker-compose.yml :

version: "3.8"

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

注意我这儿用了 network_mode: host ,也就是主机网络模式。这个改动很重要:go2rtc需要动态创建WebRTC端口,如果用bridge模式,得把所有可能的UDP端口都映射出来,容易漏;host模式下容器直接共享宿主机网络,端口不用映射,WebRTC端口也能随意分配,省心很多。代价是容器不再有独立网络栈,端口占用直接打在宿主机上,适合一台机器只跑一个实例的场景。如果你有多个容器需要做端口隔离,或者跑在群晖这类NAS的Docker里,还是老老实实用bridge模式加端口映射。

2.4 配置文件格式:go2rtc.yaml从零开始

go2rtc的配置是一个YAML文件,最核心的 streams 段定义所有摄像头流。先给个最简可用的示例:

log:
  level: info

api:
  listen: ":1984"

webrtc:
  listen: ":50000"

streams:
  bedroom:
    - rtsp://admin:password@192.168.1.20:554/Streaming/Channels/101
  living_room:
    - rtsp://admin:password@192.168.1.21:554/cam/realmonitor?channel=1&subtype=0

每个流名称底下跟一个或多个源地址,多个源可以写成一串列表,go2rtc会自动做故障切换或负载均衡。比如你有一台摄像头支持主码流和子码流两个地址,可以配两个源,让go2rtc在播放时根据客户端能力选择,或者主码流断了自动切子码流。

2.5 在Home Assistant里直接使用go2rtc

如果你的智能家居中枢是Home Assistant,go2rtc可以无缝对接。在HA的配置里加入go2rtc的地址,HA就能把go2rtc里的每一路流直接识别为摄像头实体,然后你就能在HA的仪表盘里看到实时画面、录制、人脸识别都可以接。我在配置文件中加了一行:

homeassistant:
  url: "http://192.168.1.100:8123"
  token: "你的长期访问令牌"

这让go2rtc主动连接HA,功能上等价于把go2rtc当作HA的“摄像头网关”。如果你不想让go2rtc主动连HA,也可以反过来在HA的 configuration.yaml 里用 camera 和 stream 平台指向go2rtc的API地址。

3. 多协议摄像头接入实操:从RTSP到树莓派

3.1 RTSP直连:海康/大华/TP-LINK的通用配置方法

绝大多数IP摄像头都支持RTSP协议,只是URL路径各家不一样。海康威视常见的RTSP地址格式是:

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

其中 101 表示第1通道的主码流, 102 是第1通道的子码流。大华的RTSP地址一般是:

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

channel 是通道号, subtype=0 主码流, subtype=1 子码流。TP-LINK的较新固件地址类似 rtsp://用户名:密码@IP:554/stream1 。

在go2rtc的 streams 里加流时,建议不要直接写明文密码。YAML配置里可以用环境变量替换,比如:

streams:
  front_door:
    - rtsp://${CAM_USER}:${CAM_PASS}@192.168.1.20:554/Streaming/Channels/101

然后在启动容器时通过 -e CAM_USER=admin -e CAM_PASS=xxx 传入环境变量。这样配置文件即使被传到网上,也不会泄露摄像头密码。

接入之后,在go2rtc网页的Streams页面,点击流名称下的“play”按钮,浏览器会通过WebRTC直接播放,此时观察延迟,走局域网的RTSP源一般延迟在300毫秒以内,这是普通HLS方案很难做到的。

3.2 ONVIF自动发现:适合“不知道地址”的摄像头

有些摄像头是二手收来的,账号密码不知道,或者压根不清楚厂商默认的RTSP路径。go2rtc支持ONVIF自动发现和拉流,你只需要提供摄像头的IP和ONVIF账号密码,go2rtc就能自动探测设备的媒体地址。

配置方式很简单:

streams:
  new_cam:
    - onvif:admin:password@192.168.1.30

go2rtc会去设备支持的ONVIF服务里查找Media2或Media1接口,拿到Profile信息后组装RTSP地址。这个功能我实际试过几次,成功率很高,不过前提是设备确实开启了ONVIF服务,并且摄像头固件没那么魔改。

如果你不知道ONVIF账号密码,可以先去摄像头Web管理后台重置,或者查出厂默认账号。大部分摄像头默认的ONVIF账号和Web登录账号是同一套,个别设备需要单独开启“ONVIF用户”开关。

3.3 树莓派上跑go2rtc:把OV5647摄像头模块变成网络摄像头

树莓派加OV5647摄像头模块是很多折腾玩家的标配组合,但树莓派自带摄像头输出的是CSI接口信号,不是RTSP流。想接入go2rtc,得先在树莓派上跑一个RTSP推流服务,把摄像头画面包装成RTSP协议。

我的做法是用树莓派上的 libcamera-vid 把CSI摄像头数据流转成H.264,然后通过 mediamtx (或者go2rtc自带的 rtsp 模块)把流转成RTSP服务。实测下来,树莓派4B上跑一条 libcamera-vid 命令就能稳定输出主流RTSP:

libcamera-vid -t 0 --codec h264 --width 1280 --height 720 --framerate 15 --bitrate 1500000 -o - | ffmpeg -fflags nobuffer -f h264 -i - -c:v copy -f rtsp rtsp://localhost:8554/pi

这里 ffmpeg 的作用是把stdin输入的H.264裸流封转成RTSP推给Mediamtx的8554端口。树莓派上跑Mediamtx也很轻,实测CPU占用在20%左右,不影响其他任务。

然后在go2rtc的配置里添加:

streams:
  raspberry_pi:
    - rtsp://192.168.1.31:8554/pi

这样就实现了“树莓派摄像头 -> RTSP -> go2rtc -> 浏览器WebRTC播放”的全链路。这套方案比直接买一个网络摄像头便宜很多,代价是要折腾一下系统服务和开机自启,我用systemd服务把上面那条命令行封装成开机自动运行,一直很稳定。

3.4 USB摄像头和v4l2设备的接入思路

普通USB摄像头,或者像ESP32-S3摄像头这类自带UVC输出的设备,常见做法也一样:先借助v4l2抓帧,封装成RTSP推给go2rtc。关键是要保证长时间稳定推流,USB摄像头和树莓派官方摄像头不同,有的会在低光照条件下自动调整帧率,导致推流抖动。

我自己用OBS加虚拟摄像头插件做过一个“电脑屏幕流”的场景:开个OBS虚拟摄像头,然后用OBS的RTSP推流功能推到go2rtc,再把go2rtc接到Home Assistant。结果我发现,电脑性能不好时推流会掉帧,但go2rtc对掉帧容忍度很高,播放端只是稍微卡一下,不会整个断流。

3.5 萤石/小米这类云摄像头如何接入

很多消费级摄像头,比如萤石、小米的云台版,默认不支持RTSP,它们优先走自己的云平台。想接入go2rtc,第一件事是去摄像头的App里打开“局域网RTSP”或“开发者模式”。

小米摄像头在米家App里打开“局域网RTSP”后,会显示RTSP地址和账号密码,地址一般是 rtsp://用户名:密码@IP:554/stream1 。萤石则需要设备开启“RTSP认证”后,使用SDK或者直接通过 rtsp://用户名:密码@IP:554/h264/ch1/main/av_stream 之类的路径。

麻烦的是这些消费级设备时不时会通过固件更新调整RTSP行为,可能出现昨天能拉流今天断流的情况。我的经验是,这类摄像头只适合做“非关键”监控,比如看猫主子、看快递,不适合做安防关键路径。关键路径上,老老实实用支持标准RTSP/ONVIF的摄像头更靠谱。

3.6 多路并发推流:go2rtc接到NVR或录像机

如果你已经有海康录像机(NVR),录像机的RTSP端口通常以 554 对外提供服务。go2rtc可以直接把NVR当作一个大的RTSP源来拉,比如:

streams:
  nvr_camera1:
    - rtsp://admin:password@192.168.1.240:554/Streaming/Channels/101
  nvr_camera2:
    - rtsp://admin:password@192.168.1.240:554/Streaming/Channels/201

这里 101 、 201 分别是NVR第一、第二通道。有个细节:NVR的码流路径不一定每个牌子都一样,但大部分兼容海康的通用路径规则。如果拉不通,去NVR的Web管理后台,找到“网络服务 -> 平台接入”或“RTSP配置”里的默认地址模板,通常能照抄。

如果你只是想统一用go2rtc做在线播放层,录像仍然让NVR自己干,也完全没问题。go2rtc不负责录像,它只做转发和协议转换。录像持久化建议还是交给NVR或者另外挂一个Frigate之类的NVR软件。

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

4.1 网页能看列表但播放黑屏/转圈

这是最普遍的坑。播放黑屏九成是WebRTC的UDP端口没有正确映射或防火墙没放行。我排查的固定套路是:

  1. 先确认容器日志里有没有 webrtc: listen on :50000 这类信息。
  2. 在宿主机上测试UDP端口是否通: nc -u -z 宿主机IP 50000-50010 。不通就查防火墙。
  3. 确认浏览器是否支持WebRTC。Chrome、Edge、Firefox都没问题,Safari老版本可能对WebRTC支持不完整。
  4. 如果用了Nginx之类的反代,记得配置HTTP/2和WebSocket支持,WebRTC的信令走HTTPS和WS,不能用普通HTTP反代替代。

4.2 摄像头连接“闪断”

症状是画面能出,但过一会儿就断,然后自动重连。排查下来最常见的原因是摄像头端最大连接数限制。很多家用摄像头限制同时只能有2路或4路RTSP连接,如果有NVR、手机App、go2rtc同时连接,就会触发上限。

解决思路也很直接:

  • 给go2rtc配置子码流拉流,而不是主码流。子码流码率低,对连接数和带宽压力都小,go2rtc负责把子码流转发给客户端,主码流留给NVR或者录像用途。
  • 开启go2rtc的 rtsp.transport 设置。有的摄像头对TCP/UDP传输方式敏感,这边配置成 tcp 可以降低半开连接导致的断流:
streams:
  main_cam:
    - rtsp_transport: tcp
      url: rtsp://admin:password@192.168.1.20:554/Streaming/Channels/101

4.3 浏览器里没有音频

go2rtc会按需转码音频,但有些浏览器对AAC、PCMU等编码支持有限。摄像头原始音频如果是G.711,浏览器不一定直接支持,go2rtc会自动做音频转码为AAC或Opus。

如果发现“有画面没声音”,先确认摄像头那边打开了音频开关——很多摄像头默认连音频采集都是关闭的。然后在go2rtc的流详情里查看音频媒体信息,确认是否有音频轨。如果确认有但播放无声,试试在流配置里强制指定音频编码:

streams:
  office_cam:
    - ffmpeg:rtsp://admin:password@192.168.1.40:554/Streaming/Channels/101#audio=aac

这个 ffmpeg: 前缀是go2rtc内置的FFmpeg转码模块,可以把源流的音频转成AAC再输出。注意转码会增加CPU负载,但单路音频转码基本可以忽略。

4.4 播放画面是“花屏”或者马赛克

这个通常不是go2rtc的问题,更多是摄像头码流传输过程中丢包。排查顺序是:

  • 看摄像头到go2rtc之间是有线还是Wi-Fi,Wi-Fi容易出现码流不稳定。
  • 在go2rtc日志里搜索 lost packets 或 discard ,如果有,把RTSP传输改成TCP。
  • 有些老摄像头主码流是MJPEG或者H.265,浏览器不支持会花屏或黑屏。go2rtc支持转码,但转H.265到H.264会消耗CPU,建议直接用子码流(通常是H.264)。

4.5 延迟越来越高

go2rtc默认WebRTC播放延迟很低,但如果中间走了HLS,延迟就会高起来。HLS通常有几秒到十几秒的延迟,这是切片机制决定的,不是故障。

如果想把延迟控制在1秒内,优先用WebRTC播放;如果客户端是iPhone的Safari,Safari对WebRTC支持没那么完善,可以改用MSE模式,go2rtc会通过Media Source Extensions提供接近WebRTC的延迟体验。

4.6 go2rtc容器日志刷屏,都是“connection refused”

日志高频出现 connection refused 基本就是配置里的RTSP地址不对、端口不通、或账号密码错了。拿配置里的URL单独在宿主机上用FFmpeg拉一下: ffmpeg -i rtsp://xxx ,能通就说明源没问题,不通就一个个排查IP、端口、路径、协议。

4.7 内存泄漏还是普通缓存增长?

go2rtc连续跑一周,内存占用有波动是正常的,因为要缓冲媒体数据。但如果持续攀升不回落,看下是不是有播放会话没断开。可以调用它的API接口查看活跃会话数:

curl http://localhost:1984/api/streams

返回的JSON里有 clients 字段,如果长时间不为0且有异常IP,检查是不是有人或某个自动化脚本一直挂着一个没释放的连接。正常使用下,go2rtc的常驻内存一般稳定在100~200MB左右,按需拉流模式下空闲时内存会降下来。

5. 进阶玩法:go2rtc还能干什么

5.1 把go2rtc变成“推流分发枢纽”

除了从摄像头拉流,go2rtc也能直接接流再转推给其他地方。比如把一路RTSP同时推给HLS切片器、WebRTC客户以及另一个NVR。这在做直播或者跨平台分发时很好用。

YAML里的写法大致是:

streams:
  mixed:
    - rtsp://192.168.1.20:554/Streaming/Channels/101
    - ffmpeg:rtsp://192.168.1.40:554/Streaming/Channels/201#video=copy#audio=copy

两路源放在同一个流名下,go2rtc会把它们合并成一个聚合流输出。这种“混流”一般需要借助FFmpeg,具体编码参数按需求调。

5.2 接入动态发现,自动添加海量摄像头

go2rtc支持从外部API动态管理流,你可以用脚本扫描局域网的摄像头,然后调用 /api/streams 接口把发现到的新设备自动加入配置。爬虫式扫描可以用Nmap先找出开放554端口的设备,再逐个用ONVIF探测,把拿到的媒体地址POST到go2rtc的API。

这样管理几十路摄像头的场景就轻松很多,不用每加一台摄像头都去手改YAML然后重启容器。

5.3 配合Home Assistant做“有人检测”

如果想让摄像头流具备简单的人形检测能力,可以搭配Frigate使用。Frigate负责从go2rtc拉取RTSP进行AI识别,识别结果回传给Home Assistant触发自动化。go2rtc在这条链路里做的事情只是“提供一个统一的、稳定的RTSP源”,但正是这个统一的源,让多路摄像头接入Frigate时不用每个都单独配地址和密码,管理成本大幅下降。

5.4 简易录像方案

go2rtc本身不负责录像,但它可以把任意一路流转推到支持录像的服务。组合组常见的是:go2rtc转推RTSP给Mediamtx,Mediamtx按天切片存储文件;或者直接用go2rtc的FFmpeg模块定时落盘。

这里我更推荐让“录像”这种事交给专门的NVR软件做,go2rtc专注做它擅长的“连接”和“转化”。架构上职责分离,后续排查问题也更有头绪。

6. 实操体验总结与经验建议

6.1 一些部署上的“卫生习惯”

我在多次部署和重装中总结出几个能避免很多破事的小习惯:

  • 挂载配置时,先用一个临时配置启动容器确认能正常读取,再替换成正式配置,避免一次写错整个配置导致容器启动失败。
  • 配置文件里不要写明文密码,用环境变量注入,至少能在配置泄漏时少一层直接伤害。
  • 给Web界面加个访问控制,最简单的是在前面挂一层带基础认证的反代。go2rtc本身就设计成内网服务,裸奔在公网上风险太大,容易被扫描器拿去当跳板。
  • 升级go2rtc之前先备份YAML和容器配置,虽然新版一般向后兼容,但个别大版本对配置的字段有过调整。

6.2 我最满意的一个使用场景

我现在家里有三路摄像头:海康的门口机、大华的庭院机、树莓派做的阳台摄像头。它们三个的观看方式在以前是三个App,现在全部接到了go2rtc,浏览器打开一个页面就能同时看到三路实时画面。另外我用Home Assistant做了个简单联动——树莓派摄像头检测到有人移动,自动把对应画面推送到手机通知。

这套方案最大的价值不是某个单独的功能,而是把所有东西统一到了一起。摄像头厂商的App一更新就可能改变交互逻辑,但go2rtc只关心你出不出RTSP流,只要底层协议不变,上层怎么折腾都不影响我的统一入口。

以后如果再添摄像头,我的第一反应也不再是“这牌子能不能用”,而是看它支不支持RTSP或ONVIF。只要支持,go2rtc就能接;不支持,我就用树莓派加个USB摄像头自己造一个。这种“不被厂商绑定”的自由度,才是这套方案让我最上瘾的地方。

Logo

邀请您加入社区

更多推荐