Docker部署go2rtc:统一RTSP/WebRTC摄像头流媒体网关实战
2026/9/14 17:31:26 网站建设 项目流程

家里摄像头一多,最头疼的往往不是画质清不清楚,而是怎么把它们放到同一个地方看。海康的球机、大华的枪机、树莓派上挂的OV5647模块,每台设备都有自己的App,每次想确认一下门口和阳台的画面,都像在玩App切换马拉松。直到我在Docker里把go2rtc跑起来之后,这个问题才算彻底解决。go2rtc是一个很轻量的多协议流媒体网关,它能把RTSP、RTMP、HLS、WebRTC这些协议统一到一个入口,再按需分发给网页、播放器、Home Assistant和手机端。这篇文章就围绕Docker部署go2rtc这件事,把全流程拆开讲清楚,适合想给家里或者工作室搭建统一摄像头流媒体平台的朋友参考。

1. 多品牌摄像头统一接入:go2rtc到底怎么解决碎片化问题

1.1 一个摄像头一个App,维护成本全堆在个人身上

我最早接触这个场景,是因为家里陆续添了几台不同品牌的摄像头。海康的NVR和摄像机自成一套体系,大华的枪机有自己的客户端,树莓派那块OV5647摄像头模块又是另外一套玩法。表面上每个设备都能看,实际上没有一块统一的屏幕把它们并排显示出来。更麻烦的是,给这些设备分配IP、维护端口、记住各自App的登录状态,还得时不时升级固件,这些琐事全压在一起,体验非常差。

后来我想过用VLC直接拉RTSP地址来看,但VLC一次只能打开一个地址,多个画面同时看还得手动开好几个播放器窗口。也试过一些商业监控软件,虽然能统一接入,但很多功能要付费,UI也偏重,改动一下布局就要研究半天。

这里的核心矛盾在于:摄像头只认识自家协议,而我需要的是一个“所有流都汇聚到一起,再按需输出”的服务层。go2rtc解决的就是这个中间层问题。

1.2 go2rtc与MediaMTX这类流媒体网关的差异

go2rtc用Go语言编写,整个程序是单个二进制文件,跑起来的内存占用很低,特别适合塞进软路由、NAS或者树莓派。它默认支持RTSP、RTMP、HLS、LLHLS、MJPEG、WebRTC、ONVIF,几乎把常见的摄像头和播放端协议都覆盖了。启动后,配置好的每一路摄像头都会变成一个统一流ID,比如living_roomback_yard。对上层来说,根本不用关心这个流来自哪个品牌,只要通过同一个HTTP接口拿播放地址就行。

不少人也会把go2rtc和MediaMTX放在一起对比。MediaMTX走的是“标准流媒体服务器”路线,接入和分发能力很稳,适合做一次中转把流分发给多个下游;go2rtc的侧重点则是协议转换和生态联动,比如web UI里直接预览、自动onvif发现摄像头、和Home Assistant深度集成。两个项目不冲突,我自己的环境里就是go2rtc负责接入和分发,部分场景再用MediaMTX做冗余。对绝大多数家用场景来说,先玩go2rtc就够了,它配置起来更快,也不需要引入额外的数据库和后台服务。

2. 部署前先定两件事:选择镜像tag与规划Docker运行方式

2.1 镜像选型:普通版与full版各解决什么问题

用Docker部署go2rtc有一个优势:不用去编译Go环境,镜像拉下来就能跑。Docker Hub上的官方仓库是alexxit/go2rtc,常规场景直接拉latest即可。但需要注意,普通镜像更强调轻量,一些依赖ffmpeg转码的功能不一定完整。如果你需要把H.265的摄像头画面转为H.264再推到浏览器、或者做一些复杂的音频转码,建议使用带完整ffmpeg的镜像,Docker Hub上可以看有没有带full标签的版本,或者自己去项目仓库里找full构建配置打一个镜像。

从我的实际体验看,一开始没太在意这个区别,拉了个精简版跑得很开心。后来要接一台只输出H.265的摄像头,浏览器里WebRTC播放一直黑屏,兜兜转转到最后才意识到是转码能力缺失。所以部署前先想清楚两个问题:现有的摄像头都输出什么编码?看画面的终端又是什么?如果只是局域网里的VLC和Home Assistant,latest就够;如果需要兼容各种浏览器,建议直接上full版。

2.2 网络模式:为什么很多老用户推荐host模式

部署go2rtc前,端口规划比选镜像更容易踩坑。go2rtc默认会用到几个端口:RTSP服务默认监听8554,HTTP API和Web UI默认走1985,WebRTC的UDP端口是8555,如果需要RTMP转发,还会用1935。如果是用docker run或者docker-compose以bridge网络方式部署,就需要把这些端口依次映射出来:

ports: - "8554:8554" - "1985:1985" - "8555:8555/udp" - "1935:1935"

但这里有个隐藏问题。WebRTC协商的时候,浏览器拿到的候选地址是容器内部的IP,而不是宿主机的IP。如果你用bridge模式但没有额外处理好映射关系,浏览器就找不到流媒体服务,表现就是Web UI能打开、RTSP地址用VLC也能播,唯独WebRTC一直在转圈。

所以很多有经验的人会直接把go2rtc的容器网络设为host模式,让容器共享宿主机网络栈,彻底绕开端口映射和IP不一致的问题:

network_mode: host

在Linux的Docker环境里,这种方案最省心。Windows和macOS的Docker Desktop对host网络支持没那么完整,这时候选择bridge模式,同时把上面几个端口显式映射出来,也是可行的。但要记住,WebRTC的UDP端口8555/udp必须映射,否则网页端低延迟播放一定会出问题。

3. 核心操作:写一份能直接跑的config.yaml

3.1 最小配置:一条RTSP流分出5种播放协议

go2rtc的配置文件非常直观,我的最小可用配置是这样:

log: level: info api: listen: ":1985" webrtc: listen: ":8555" rtsp: listen: ":8554" streams: back_yard: - rtsp://admin:your_password@192.168.1.64:554/Streaming/Channels/101 front_gate: - rtsp://admin:your_password@192.168.1.65:554/cam/realmonitor?channel=1&subtype=0 pi_cam: - rtsp://192.168.1.30:8554/pi

解释一下这段配置在做什么。streams下每个键就是一条流的独立ID,值是源列表。注意即使只有一路视频源,也要用列表形式,因为go2rtc允许给同一条流配置多个候选源,主源断开时能自动切换。海康的/Streaming/Channels/101是主码流,把结尾改成102就变成子码流,适合预览墙这种不追求极致清晰度的场景。大华的cam/realmonitor?channel=1&subtype=0是常见取流路径,subtype=0是主码流,subtype=1是子码流。

配置写好后,在项目目录下创建config文件夹,把文件保存为config/config.yaml,然后启动容器。启动成功后,打开http://宿主机IP:1985,就能看到一个简单的web UI,里面能看到配置里的每条流,点击即可直接预览。这一刻的感受还是很爽的——那些原本分散在各自App里的画面,终于全部集中到同一个页面里了。

3.2 多协议输出地址:哪些给网页、哪些给播放器、哪些给低延迟端

go2rtc最值钱的地方在这里:它接收的是RTSP源,对外提供的却不只是RTSP。只要配置好了一条流ID,go2rtc会自动帮你生成多个协议的播放地址。

播放场景地址格式说明
网页端低延迟播放http://IP:1985/api/webrtc?src=back_yard延迟最低,适合安防监控
HLS通用播放http://IP:1985/api/hls/back_yard.m3u8兼容性好,VLC/IINA都能播放
LLHLS低延迟播放http://IP:1985/api/llhls/back_yard.m3u8比普通HLS延迟低,适合直播类场景
MJPEG嵌入网页http://IP:1985/api/mjpeg?src=back_yard直接放在<img>标签里就能显示
RTSP转发rtsp://IP:8554/back_yard给支持RTSP的播放器和NVR
RTMP转发rtmp://IP:1935/back_yard给直播平台或其它推流端

这些地址不用死记硬背,go2rtc的web UI里每条流旁边都会给出可以直接复制的链接。我自己的使用习惯是:Home Assistant取流用RTSP地址,临时查看用web UI,手机上看用WebRTC,跨网段的慢速查看用HLS。多协议输出最大的价值,就是不用再为了不同终端反复搭不同协议的服务。

3.3 用onvif自动发现摄像头,告别手抄RTSP地址

手抄RTSP地址这一步,确实容易让人崩溃。不同品牌的设备,路径规则千奇百怪,同一品牌不同固件版本还会变。如果你家有支持ONVIF标准的摄像头,可以直接让go2rtc去自动发现:

streams: back_yard: - onvif://admin:your_password@192.168.1.64

go2rtc会连到设备上,通过ONVIF标准协议去查询设备的媒体能力,自动解析出可用的RTSP播放地址并建立流。对海康、大华、宇视这类主流安防品牌,成功率很高。

我专门试过用ONVIF接入宇视摄像头,效果很理想。因为宇视和其它小众品牌的RTSP路径在不同型号上差异较大,手写经常得到一张黑屏或错误日志。打开ONVIF自动发现后,设备自己告诉我它能提供什么编码、什么分辨率、RTSP服务在哪个路径,go2rtc直接就能拉通。这个功能建议优先用起来。

4. docker-compose固化整套流媒体环境

docker run一条命令启动go2rtc很直接,但当我加了配置目录、时区、日志限制之后,命令越来越长,后来干脆固定成docker-compose配置。这份compose文件我已经在NAS和软路由上跑了很长时间,可以直接抄:

services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped network_mode: host environment: - TZ=Asia/Shanghai volumes: - ./go2rtc/config:/config logging: driver: json-file options: max-size: "10m" max-file: "3"

network_mode: host前面说了,是为了让WebRTC能正常工作。restart: unless-stopped保证设备重启后服务自动恢复。日志大小限制值得额外说一句,go2rtc调试阶段经常输出大量日志,如果不限制,几个月下来日志文件可能占掉不少空间。

如果所在的Docker环境不支持host网络,可以把网络模式改成bridge,并加上端口映射:

services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped ports: - "8554:8554" - "1985:1985" - "8555:8555/udp" - "1935:1935" environment: - TZ=Asia/Shanghai volumes: - ./go2rtc/config:/config logging: driver: json-file options: max-size: "10m" max-file: "3"

启动命令很简单:

docker compose up -d

查看运行状态和日志:

docker ps | grep go2rtc docker logs -f go2rtc

我在树莓派4B和一台老x86软路由上都试过,go2rtc的CPU占用日常基本在个位数,内存只用几十MB,负担很小。

5. 让平台更完整的几个进阶玩法

5.1 ffmpeg转码:H.265摄像头在浏览器播放的解药

H.265(HEVC)压缩率高,很多新摄像头默认会用它,但浏览器生态对H.265的支持一直很糟糕。Chrome基本不能通过WebRTC正常解码H.265,Safari稍微好一点,也不稳定。如果你在web UI里点开摄像头画面黑屏,但VLC能播RTSP,大概率就是编码格式不兼容。

解决办法是让go2rtc调用ffmpeg做一次转码。在config.yaml里配置ffmpeg模块,指向Intel核显或AMD GPU的VAAPI设备:

ffmpeg: vaapi: /dev/dri/renderD128 streams: back_yard_h264: - "ffmpeg:rtsp://admin:your_password@192.168.1.64:554/Streaming/Channels/101#video=h264#audio=aac"

同时容器需要挂载显卡设备:

services: go2rtc: image: alexxit/go2rtc:latest network_mode: host volumes: - ./go2rtc/config:/config devices: - /dev/dri/renderD128:/dev/dri/renderD128

这样back_yard_h264这条流输出的就是H.264编码,浏览器播放不会再黑屏。注意,如果没有硬件解码条件,纯CPU转码也不是不行,只是同一时间看的路数越多CPU压力越大。我自己在树莓派上试过纯CPU转码,同时转两路720p就有点吃力了,最后把摄像头直接改为输出H.264子码流给WebRTC用,主码流保留给录制备查,这样更省资源。

5.2 接入Home Assistant,摄像头画面进入自动化体系

如果你已经在用Home Assistant,go2rtc几乎是天然的流媒体后端。把摄像头接入go2rtc之后,在Home Assistant里配置一个Generic Camera集成,填上go2rtc提供的RTSP地址即可:

camera: - platform: generic name: 后院监控 stream_source: rtsp://192.168.1.10:8554/back_yard still_image_url: http://192.168.1.10:1985/api/frame?src=back_yard

still_image_url用的这个api/frame接口是go2rtc提供的截图接口,Home Assistant的缩略图、自动化触发时抓拍都能用上。配置好之后,摄像头画面就能进到HA的仪表盘里,和门磁、人体传感器、灯光设备一起做联动。

我的实际场景是:门口有人体传感器触发时,HA自动把门口摄像头的画面推到电视上,同时手机收到带快照的推送。这里面的视频流线路就是传感器触发 -> HA调用go2rtc的流 -> 电视通过HLS拉流,整体延迟可以接受,链路也很清晰。

5.3 与OBS配合:从拉流到推流的灵活组合

go2rtc还能和OBS配合得很好。一种用法是OBS作为播放端,直接在OBS里添加“媒体源”,填入rtsp://IP:8554/back_yard,就能把摄像头画面拉进直播场景,不需要额外采集卡。另一种用法是反着来,把OBS的画面推给go2rtc,再由go2rtc分发给多个观众端:

ffmpeg -re -i input.mp4 -c copy -f flv rtmp://IP:1935/back_yard

这个方式的意义在于,OBS输出的是RTMP流,而go2rtc可以把RTMP转换成HLS和WebRTC,方便手机端和网页端同时观看。比如做一个简易的家庭直播或分享屏,不需要额外买云服务,一台小主机就够。

6. 部署与使用中踩过的坑:从URL解析到WebRTC不通

6.1 密码里的特殊字符把URL解析干崩了

摄像头RTSP地址里的密码如果包含@:?这类特殊字符,解析大概率会出问题。我遇到过最典型的一次是密码设成了abc@123,配置里写rtsp://admin:abc@123@192.168.1.64:554/...,go2rtc会把第二个@当作登录信息结束的位置,整个URL解析失败,日志里反复报认证错误。

正确做法是把特殊字符做URL编码。@编码成%40,密码abc@123写成abc%40123

streams: back_yard: - rtsp://admin:abc%40123@192.168.1.64:554/Streaming/Channels/101

如果密码里还有中文或空格,也建议先用在线工具或脚本做一次完整URL编码,再填到配置里。排查这类问题时,把配置里的地址复制出来,放到VLC里直接打开,能播放就是配置没问题,不能播放就逐步检查编码、端口、路径。

6.2 设备取流上限导致画面掉线

很多家用摄像头对同时建立的RTSP会话数有限制,海康一些型号默认只允许几路并发取流。之前我把RTSP地址同时配给了go2rtc、VLC、HA和手机App,结果摄像头反复掉线,过一会儿又恢复,日志里出现连接被拒绝的记录。

这个问题其实正好突显了go2rtc的价值。合理的方式是:所有消费端都去连go2rtc的地址,只有go2rtc自己往摄像头设备拉一路流。这样摄像头侧始终只维持一个或少数几个连接,无论下游有多少播放器,压力都不会传导到设备上。

所以部署完go2rtc之后,记得把其它App里直接访问设备RTSP地址的配置全部改掉,统一指向go2rtc的转发地址。这也是一个“平台化”的关键习惯。

6.3 WebRTC在Docker端口映射下协商失败

前面提到的WebRTC不通问题,我再展开讲一下排查思路。如果你的web UI能打开、HLS地址能播放,唯独WebRTC一直转圈,第一个要查的就是网络。在Linux Docker环境里,优先用host网络;用bridge的话,检查8555/udp端口是否映射到了宿主机,同时宿主机防火墙有没有放行UDP。

另外不要在Docker Desktop的Windows或macOS环境里强行依赖host网络,这两个平台的Docker运行在虚拟机里,host网络和你预期的宿主机网络并不完全一致。我有个朋友在macOS上折腾了一整天WebRTC,最后换到Linux小主机上,一切正常。如果你只能在Windows/macOS环境开发,先用HLS凑合预览,或者把go2rtc直接装到宿主机原生的Go环境里调试,会更顺利。

6.4 暴露公网之前必须想清楚的权限问题

go2rtc的设计思路是给内网使用,它本身没有账号体系,web UI只要能访问到,谁都能点开看所有摄像头画面。所以无论如何,不建议把19858554端口直接映射到公网。

如果你确实需要随时随地看家里的摄像头,比较稳妥的做法是:服务只监听在内网,远程访问通过带身份认证的反向代理来转发。代理层至少要加一层Basic Auth或更复杂的登录认证,并且限制能访问的路径。我之前还遇到过扫描器扫到了1985端口并尝试枚举流ID的情况,可见不加任何保护直接暴露在公网,风险是实打实的。

另外一个容易被忽略的点:反代转发1985给前端时,WebRTC和HLS在代理层可能因为缓冲、超时设置不当而失败。如果只是看不太实时的画面,HLS相对稳妥;追求低延迟,就得把WebSocket和UDP相关配置一并处理好。总体原则是——默认不暴露,必须暴露时加认证、限来源。

如果你现在正被家里或者办公室这些设备之间的协议折腾,我的建议是别一上来就规划一堆复杂功能。先把一条最常用的RTSP地址填进config.yaml,用docker compose up -d跑起来,到web UI上把玩一下那几种播放地址的区别。等这套跑顺了,再逐步把其它摄像头、H.265转码、Home Assistant联动加进去。go2rtc给我的感觉是越用到后面越顺,整个过程最值钱的可能不是某条命令,而是你终于明白自己每一路视频是从哪来、经过谁、又到哪去了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询