一个人在家里或工作室里折腾监控摄像头、智能家居、无人机图传、甚至是一堆树莓派自制采集设备时,最痛苦的事情其实不是设备买错,而是 协议全都不一样 。有的摄像头只出RTSP,有的只支持RTMP推流,有的走私有SDK,有的走ONVIF,还有的是USB摄像头、手机摄像头、甚至一个网页上的MJPEG流。你想把这些画面统一收进一个平台、随时用浏览器看、用手机看、或者喂给NVR录像,就得靠一个能把“多协议接入”这件事做干净的流媒体网关。go2rtc就是干这个的,而用Docker把它跑起来,几乎是目前最省心的部署方式,不用关心系统依赖、Python版本、编译环境,拉个镜像起来就能用。

这篇文章我会从方案选型开始讲起,再到Docker部署的具体配置、摄像头接入时常见的URL写法和参数细节,最后是一堆实测踩坑记录。适合正在做智能家居集成、监控系统改造、或者单纯想把家里几路摄像头视频统一管理的朋友参考,如果你还在被“这个设备只支持RTSP、那个只支持WebRTC”搞到头大,那这篇文章基本就是为你写的。

1. 为什么选go2rtc:与其他流媒体方案的对比

1.1 传统流媒体方案的根本痛点

很多第一次做摄像头接入的人,第一反应是“直接用FFmpeg转一下不就行了”。这话对了一半。FFmpeg确实是万能的转码工具,但它本质上是一个“一次性处理”的命令,你需要在后台手动维护进程、断线重连、多路并发管理、浏览器播放兼容性,这些都是额外的工作量。比如你用FFmpeg把RTSP转成HLS切片,得自己写循环脚本、处理IO错误、清理历史分片,摄像头一旦断线,进程就挂了,脚本还得重启。做一两路没问题,一旦超过五路,维护成本立刻失控。

另一类做法是上比较重的商业流媒体服务器,比如SRS、ZLMediaKit、Nginx-RTMP这类方案。这些平台功能确实强,但配置复杂度高,而且很多能力对普通玩家来说是过剩的。你只是想在内网里把几路摄像头画面统一起来、在浏览器里低延迟看,不是要做一个能扛百万并发直播的CDN。而且这类方案通常要求你先把所有输入转成统一协议,再对外分发,链路还是偏长。

这时候go2rtc就非常有意思了。它是直接用Go语言写的轻量级流媒体网关,单个二进制文件就能跑,核心卖点就是 多协议接入和多协议输出 。它原生支持RTSP、RTMP、HTTP-FLV、HLS、WebRTC、MJPEG等常见的流媒体协议,还支持直接对接ONVIF摄像头做设备自动发现,甚至可以发现自己接入Home Assistant、Frigate这些智能家居平台。它最大的价值是:你不用关心输入源是什么协议,go2rtc会帮你转成一个最适合播放端消费的输出,浏览器端可以走WebRTC看低延迟画面,手机端可以走HLS,老旧的网页可以直接用MJPEG。

1.2 go2rtc的核心能力与适用场景

go2rtc比较吸引人的一点是它不把转码作为默认手段。很多方案一遇到协议不匹配,第一反应就是“重新编码”,这是吃CPU的大户。go2rtc会尽量做“透传”,只在真正必要的时候(比如浏览器不支持H265、输入格式太特殊)才调用FFmpeg转码。这一点在实际部署中非常值钱,意味着你用一个瘦小的主机就能同时跑五六路摄像头,CPU占用率还相当低。

适用场景我总结下来有三类:

  • 监控/智能家居集成场景 :手头设备比较杂,有海康、大华、TP-LINK还有不知名的小厂摄像头,协议不统一,想归拢到一套体系里。
  • 低延迟远程观看场景 :在浏览器里用WebRTC看实时画面,延迟能压到一秒以内,比传统的HLS延迟从十几秒降到毫秒级体验差别非常明显。
  • 边缘AI/录像联动场景 :go2rtc可以把流直接推给Frigate、Home Assistant这类平台,作为视频源接入,不用再二次转码。

如果你有这些需求中的一个,那go2rtc就是很合适的中间层。接下来进入正题,用Docker把它跑起来。

2. Docker部署整体设计与环境准备

2.1 部署前的环境要求

go2rtc对Docker宿主机的性能要求很低,毕竟它本职是“转发”而不是“转码”。我自己实测在树莓派4B这种ARM架构的小主机上跑六路H264子码流转发,CPU占用率大概在20%-30%之间(不开转码的情况下)。如果你要开FFmpeg转码,那CPU占用会明显上去,ARM板子就比较吃力了,建议起码是x86的N100、J4125这档次的低功耗主机。

宿主机安装好Docker和docker-compose插件是前提,这一步不太赘述。需要注意的一点是,如果你的宿主机是Windows,直接用Docker Desktop跑go2rtc是可以的,但 网络性能会比Linux宿主机差一些 ,尤其在多路大码流转发场景下,还是建议用Linux系统比较稳。

建议的目录结构是这样的:

/home/you/go2rtc/
├── docker-compose.yml
├── go2rtc.yaml
└── data/
    └── recordings/   # 如果开启录像功能

2.2 docker-compose.yml完整配置

我贴一份自己实际在用的docker-compose配置,标记了每段的作用,方便你直接抄改:

version: "3.8"

services:
  go2rtc:
    image: ghcr.io/alexxito/go2rtc:latest
    container_name: go2rtc
    restart: unless-stopped
    network_mode: host
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ./go2rtc.yaml:/config/go2rtc.yaml
      - ./data:/data
    ports:
      # 注意:使用host网络模式时,ports配置可忽略,这里留着只是便于理解端口
      - "1984:1984"    # Web控制台
      - "8554:8554"    # RTSP入口
      - "8555:8555"    # WebRTC入口
      - "8555:8555/udp" # WebRTC UDP端口

这里我特意用了 network_mode: host ,这是我踩过几次坑之后的选择。一开始我用bridge模式,按常规映射端口,结果WebRTC那边总是连不上,排查半天发现是WebRTC需要大范围的UDP端口做音视频传输,在bridge模式下你得手动把成百上千个UDP端口都映射出来,非常反人类。改成host模式之后,go2rtc直接复用宿主机的网络栈,WebRTC的UDP端口分配就不用管了,省心很多。

注意:如果你的宿主机是macOS,Docker Desktop对host网络模式支持有限,建议还是用bridge模式并把必要的端口映射出来,或者直接跑原生二进制,别跟网络模式死磕。

2.3 配置文件的初始化

go2rtc支持把配置写在 go2rtc.yaml 里,启动时自动加载。最开始我们可以先放一个最简配置进去,验证服务能起来:

log:
  level: info
  format: text

api:
  listen: ":1984"

rtsp:
  listen: ":8554"

webrtc:
  listen: ":8555"

这个配置意思是:Web控制台监听1984端口,RTSP服务监听8554端口,WebRTC监听8555端口。填好之后启动容器:

docker compose up -d

启动后打开 http://宿主机IP:1984 ,你应该能看到go2rtc的管理界面。界面上会展示当前已经配置的流、连接的客户端数量、每路流的码率等统计信息。这个管理页本身就是一个很实用的调试工具,后面排查问题基本都靠它。

3. 摄像头接入:设备发现与手动配置实操

3.1 用ONVIF自动发现摄像头

go2rtc内置了ONVIF协议的支持,可以帮你自动扫描同一网段内的摄像头设备。在管理后台左侧点击“设备”或直接在配置里加一个onvif源,它就能通过广播发现局域网里支持ONVIF标准的摄像头。海康、大华、TP-LINK这类主流品牌基本都支持ONVIF。

自动发现到设备后,go2rtc会尝试用配置的凭证去拿流地址。这里有个细节:ONVIF只是帮你找到“设备在哪”,真正拿视频流还要靠RTSP或HTTP拉流,所以摄像头上填写ONVIF用户时,建议直接用摄像头的管理员账号,免得碰到权限不足导致拉不到流。

不过说实话,ONVIF自动发现在设备不多的时候挺方便,但如果你的网络里设备太多、或者摄像头厂家对ONVIF支持不完整,自动发现出来的流地址可能不是最优的(比如抓到的是MJPEG而不是H264)。所以更靠谱的方式还是手动配置RTSP地址。

3.2 主流品牌摄像头RTSP地址写法与参数计算

手动配置RTSP地址这事,网上很多教程写得含糊,但实际就那几个套路。以最常见的海康和大华为例:

海康威视的RTSP地址通常长这样:

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

末尾的 101 含义是“通道1的主码流”, 102 是“通道1的子码流”, 201 就是“通道2的主码流”,依次类推。主码流分辨率高、码率大,适合录像和后期分析;子码流分辨率低、码率小,适合多路同时预览和手机端观看。

大华的RTSP地址语法稍有不同:

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

其中 channel=1 表示通道1, subtype=0 表示主码流, subtype=1 表示子码流。

其他小厂或者杂牌摄像头的RTSP地址就五花八门了,最靠谱的办法是去摄像头的Web管理界面看“音视频/网络设置”里是否显示了流地址,或者搜索“品牌 + RTSP地址获取”。go2rtc配置里把地址写进streams段即可:

streams:
  drive_cam:
    - "rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101"
  yard_cam:
    - "rtsp://admin:password@192.168.1.101:554/cam/realmonitor?channel=1&subtype=1"

这里有个参数上的学问: 用户名或密码里如果包含特殊字符,必须要做URL编码 。比如密码是 abc@123 ,直接填进URL里会导致解析失败,因为 @ 是URL里用来分隔用户信息和地址的保留字符。需要把 @ 替换成 %40 。我见过好几个人头天晚上折腾半天接入不上,第二天发现就是密码里有个 # 没编码。这个细节很容易被忽略,但排查起来最耗时。

3.3 多路流合并与备份源配置

go2rtc的streams配置还支持一个很有用的特性:一个流名称下可以配置多个地址,用减号开头列出来。这样做的意义有两个:

  • 可以配置 故障切换 ,如果第一个地址拉流失败,自动尝试第二个。
  • 可以配置 同源不同清晰度 ,比如主码流断了自动切子码流,保证画面不中断。
streams:
  front_door:
    - "rtsp://admin:pass@192.168.1.10:554/Streaming/Channels/101"
    - "rtsp://admin:pass@192.168.1.10:554/Streaming/Channels/102"

go2rtc会按顺序尝试,第一个不通就切换往下走。这个功能在摄像头偶尔重启或者网络抖动的场景下非常有价值,画面不容易直接黑掉。

4. 浏览器低延迟播放与码流策略

4.1 WebRTC播放路径的正确理解

go2rtc配置好摄像头流之后,最直接的播放验证方式就是打开管理后台,点一下流名称旁边的播放按钮,它会自动用WebRTC把画面拉到浏览器里。

WebRTC是go2rtc的招牌功能,它的意义在于:浏览器原生支持WebRTC,不需要装插件、不需要Flash(这玩意已经死了很多年了)、不需要看HLS那种10秒以上的延迟。go2rtc内部会做一层转换,把RTSP的RTP包转换成WebRTC需要的形式,然后通过UDP直接推给浏览器播放。

实际使用中,WebRTC的延迟能做到几百毫秒,体感上非常“接近实时”。如果你有远程看门口、看宝宝房、看宠物这类需求,WebRTC的体验比HLS好太多。但要注意,WebRTC依赖UDP传输,所以你如果要在公网访问,必须在路由器或防火墙上放行UDP端口。这也是我一直推荐host网络模式的原因,UDP端口管理的问题会被绕开一大半。

4.2 浏览器黑屏与H265编码问题

WebRTC播放最常见的坑,就是 画面黑屏但声音正常,或者完全没有画面也没有报错 。这种情况十有八九是摄像头输出的视频编码格式是H265(HEVC)。

浏览器对H265的支持一直很尴尬。WebRTC规范里强制要求的视频编码是VP8,H264是主流补充,H265则是“看缘分”。Chrome、Edge这些基于Chromium的浏览器官方并没有稳定的H265硬解支持(部分新版本在特定硬件下能用,但不保证)。所以如果你摄像头的编码设成H265,go2rtc做WebRTC推流时会发现“浏览器没法解码”,表现就是黑屏,或者有时候首帧要等好久。

解决方式有两个:

  • 在摄像头后台把视频编码改成H264。这是最优解,成本最低,兼容性最好。
  • 如果摄像头只能出H265(比如某些较新的4K机型以H265为主),那就得在go2rtc里加转码配置,让它把H265转成H264再推流。

转码配置需要在go2rtc的配置里指定FFmpeg参数:

ffmpeg:
  h265_to_h264: "-analyzeduration 1000000 -probesize 1000000 -f lavfi -i anullsrc -c:v libx264 -preset ultrafast -tune zerolatency -c:a aac"

streams:
  living_room:
    - "rtsp://admin:pass@192.168.1.50:554/Streaming/Channels/101"
    - "ffmpeg:living_room#video=h265_to_h264"

注意,转码是CPU密集型操作。我实测过把一路1080P H265主码流转成H264,在J4125上CPU占用大概增加15%-25%。所以如果你有十路摄像头要转码,还是乖乖在摄像头上改编码格式最划算,别让主机白白烧CPU。

4.3 码流选择策略:主码流还是子码流

这个问题我多说一句,因为在多路预览场景下的经验很多人写博客没提。如果你只是想在浏览器里同时看四路、六路、甚至九路画面, 强烈建议在配置里使用子码流 。子码流通常分辨率是D1或720P级别,码率只有主码流的四分之一左右,多路同时跑时网络和CPU的压力都会小很多。

具体做法就是上面提到的,在RTSP地址里控制 101/102 或者 subtype=0/1 的参数。主码流留给录像或者需要放大看细节的场景。把“实时预览走子码流、录像走主码流”这套策略做好,你会发现设备多起来了系统依然跑得很轻快。

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

5.1 部署与接入阶段高频问题速查表

我把自己和身边朋友踩过的高频问题整理成了表格,按现象、原因、解决办法排列,方便你直接对号入座。

问题现象 根本原因 解决办法
Web控制台打不开 容器没起来或端口没映射 docker compose ps 查看状态,确认1984端口是否被占用
控制台能看到流,但画面一直转圈 RTSP鉴权失败或网络不通 先用VLC拉流验证地址是否可用,同时检查用户名密码特殊字符URL编码
浏览器黑屏无声音 H265编码,浏览器不支持 摄像头端改H264,或者配置FFmpeg转码参数
WebRTC连不上 UDP端口没放行 改用host网络模式,或在路由器放行WebRTC对应的UDP区间
画面卡顿严重 多路同时拉主码流,带宽/CPU瓶颈 切换到子码流,降低同时预览路数
摄像头画面每隔几秒断一次 摄像头侧RTSP会话超时配置过于严格 调整摄像头“RTSP会话超时/心跳时间”设置,或开启go2rtc的重连机制
局域网观看正常,公网无法访问 端口未做映射或运营商封端口 确认公网IP是真正的公网IP,映射1984/8554/8555,考虑DDNS加证书

5.2 独家避坑技巧

这里分享几个常规文档里基本不会写的坑。

第一个是 摄像头拉流不要用主码流作为默认预览源 。不止一次,有人跟我抱怨“为什么我的go2rtc画面延迟高、卡顿”,我跟他说你把URL里的 101 改成 102 或 subtype=1 ,瞬间就好了。根本原因就是摄像头主码流可能是4K/8Mbps的高码流,你浏览器窗口本身显示区域就小,拉一个8Mbps的流纯属浪费带宽和CPU。

第二个是 关于Docker容器重启后的流自动恢复问题 。go2rtc默认在流断开后会按一定的退避策略重连,但如果摄像头是DHCP分配IP,重启后IP变了,配置里的旧IP就拉不到流了。所以建议在摄像头后台把IP改成静态或者用DHCP保留绑定,省得go2rtc重启后还得手动改配置。

第三个是 配置文件的修改生效时机 。go2rtc默认不会自动热加载你修改的yaml文件,改完配置后需要重启容器或者调用API重载。如果你开发调试时频繁改配置,建议用 go2rtc -config 配合外部编辑器保存时自动重启进程的方式,省去手动重启的麻烦。

第四个是关于 多平台播放时的协议选择 。浏览器端我优先用WebRTC,移动端在微信内置浏览器里WebRTC支持不理想,我就用HLS。go2rtc会自动把每个流暴露成HLS的播放地址,格式是 http://宿主机IP:1984/api/stream/{流名称}/hls 。手机端的VLC、nPlayer、甚至网页里的 <video> 标签都能直接播HLS,只是延迟会高一些。多终端多场景切换时,记住“实时低延迟选WebRTC,兼容性优先选HLS”基本够用。

5.3 与Home Assistant和Frigate的联动

最后聊一个很多玩智能家居的朋友会关心的方向。go2rtc因为原生支持Home Assistant集成,你可以在HA里直接以“camera”实体形式添加go2rtc的流,不用再走一遍 generic camera 的模板配置。Frigate也一样,它的 go2rtc 配置段可以直接指向你已经配好的流名称,让Frigate从go2rtc拉流做物体识别检测,避免多个服务同时直连摄像头导致摄像头RTSP连接数爆掉。

这个架构思路非常实用: 摄像头只允许有限的RTSP连接数,但你的AI检测、录像、网页预览、手机App可能都想同时拉流 。让go2rtc做唯一与摄像头通信的网关,其他所有消费方都从go2rtc拿流,就不会出现“摄像头拒绝连接”的尴尬。我自己的环境就是这样,摄像头只与go2rtc建立一条RTSP连接,Frigate、Home Assistant、手机App全部从go2rtc分发,跑了一两个月,稳得很。

配置上只需要在HA的configuration.yaml里加上go2rtc的自定义源,或者在Frigate的配置里指定 go2rtc 的URL,之后就能直接在HA里看到摄像头画面。这类联动我建议你把go2rtc作为一个基础服务先跑起来,后面接AI、接录像都属于水到渠成的事。

这套Docker部署go2rtc的方案我已经推荐给好几个朋友了,从智能家居玩家到工作室做监控改造的都有,反馈都是“以前要凑齐一堆工具才能干的事,现在一个容器全搞定”。如果你也在被摄像头协议不统一折磨,不妨按这篇文章的步骤跑一遍,相信我,你会回来感谢自己的。

Logo

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

更多推荐