用Docker部署go2rtc:统一接入RTSP/RTMP/WebRTC摄像头的低延迟流媒体平台
家里装了两个海康摄像头,一个在大门口,一个在阳台,原来各自用厂家 App 看也就算了,后来想在一个浏览器页面里同时看两路画面,顺便接到自己写的智能家居面板上,结果发现事情没那么简单:海康走 RTSP,杂牌摄像头走 RTMP,还有一个树莓派上的 USB 摄像头只出 MJPEG,想让它们在同一个界面里低延迟播放,用传统方案折腾了一晚上都没搞定。后来换成 go2rtc,配合 Docker 部署,前后不到半小时就把所有摄像头统一接进来了,浏览器直接打开就能看,延迟压到了几百毫秒级别。这篇文章就把我完整搭这套“摄像头多协议流媒体平台”的过程、配置、踩过的坑全部整理出来。如果你手里正好有海康、大华或者其他杂牌摄像头,想用 Docker 快速搭一个能统一接入、浏览器直连、低延迟预览的流媒体平台,这篇文章可以直接照抄。
1. 为什么是 go2rtc + Docker:这套方案到底解决了什么
先解释一个很多人一开始没想明白的问题:摄像头明明都有画面,为什么想让它们在网页里“直接”跑起来这么麻烦?因为摄像头和浏览器说的根本不是同一种语言。搞清楚了这一点,你才能真正理解 go2rtc 在这套方案里扮演的角色。
1.1 摄像头协议生态有多乱:先看清你手里的流是什么
市面上绝大多数 IP 摄像头出厂默认走的都是 RTSP 协议,这是安防行业的老标准,专门用来传输音视频流。RTSP 本身只负责“会话控制”,真正的视频数据是靠 RTP 包传出去的,所以你会看到类似
rtsp://192.168.1.100:554/Streaming/Channels/101
这种地址,它跟
http://
开头的网页地址完全不是一回事,浏览器地址栏直接输入是打不开的。这也是很多人第一次接触监控流时最容易踩的坑:以为拿到一个 rtsp 地址就能像看视频网站一样直接播。
除了 RTSP,还有一堆其他协议:RTMP 是直播行业的老古董,Flash 时代就流行了,现在一些杂牌摄像头、物联网设备还在用;HLS 是苹果推的,兼容性最好,但延迟动不动就三四秒甚至更高;MJPEG 是一帧一帧的 JPEG 图片连续播放,兼容性无敌但带宽占用高;WebRTC 是实时通信领域的明星,延迟能做到几百毫秒,但信令和 NAT 穿透都挺复杂。
| 协议 | 延迟 | 浏览器原生支持 | 典型场景 |
|---|---|---|---|
| RTSP | 低 | 不支持 | 监控摄像头、NVR |
| RTMP | 中等 | 不支持 | 直播推流、老旧设备 |
| HLS | 高 | 支持 | 视频网站、回放 |
| MJPEG | 中等 | 部分支持 | 嵌入式设备、安防老设备 |
| WebRTC | 极低 | 支持 | 视频通话、低延迟监控 |
打个比方,RTSP 就像是厨房里切好的半成品食材,专业厨师(NVR、VLC)拿到可以直接做菜,但普通客人(浏览器)进不了厨房。HLS 就像做好的标准套餐,谁都能吃,但出餐要等半天。WebRTC 则像直达包间的专属传菜通道,快是快,但需要专门安排服务员(信令服务)去对接。go2rtc 干的事,就是把这些不同形态的“食材”,统一加工成浏览器能直接消费的菜品,而且默认给出的是延迟最低的那种。
1.2 go2rtc 的拿手绝活:多协议互转 + 浏览器低延迟直连
go2rtc 是一个用 Go 语言写的开源流媒体网关,作者是 AlexxIT,最早是给 Home Assistant 做摄像头接入用的,后来发现单拿出来当独立流媒体服务器用也非常顺手。它的核心能力可以概括成三点。
第一,多协议接入。RTSP、RTMP、HLS、MJPEG、HTTP-FLV 这些常见的流媒体协议,它都能直接拉流或者推流。这意味着你不需要关心摄像头到底支持什么协议,统一交给 go2rtc 处理就行。第二,多协议输出。同一个输入源,它可以同时以 WebRTC、HLS、MJPEG、RTSP 等格式输出给不同的消费端。这一点在做多端适配时特别省事:电脑浏览器走 WebRTC,手机网页走 HLS,老旧小程序走 MJPEG,一套配置全搞定。第三,原生 WebRTC。这是它跟传统流媒体服务器最大的区别。传统方案一般是 FFmpeg 拉 RTSP 推到 RTMP 服务器,再转成 HLS 播放,链路长,延迟居高不下。go2rtc 直接在内部把 RTSP 流转成 WebRTC,中间不经过 FFmpeg 重编码,延迟可以打到 200 到 500 毫秒,看监控画面的体验接近本地直连。
而且 go2rtc 本身就是一个单二进制文件,CPU 占用很低,对内存的消耗也几乎可以忽略。我自己跑过三路 1080P 主码流加两路子码流,在树莓派 4B 上都毫无压力,这在传统 FFmpeg 转码方案里是不可想象的。
1.3 为什么偏要用 Docker:环境隔离、快速复制、多机器迁移
go2rtc 虽然可以直接在宿主机上跑,但我在实际使用中强烈推荐加上 Docker 这一层。理由很实在。
首先是环境隔离。go2rtc 本身依赖很少,但它经常要跟 FFmpeg、外部录制脚本、Home Assistant 这些组件配合使用,直接装在宿主机上,时间一长依赖就乱成一锅粥。用 Docker 容器打包之后,宿主机干干净净,升级回滚都只在容器层面操作,不会污染系统环境。
其次是快速复制。go2rtc 的所有配置都集中在一个 YAML 文件里,加上 Docker Compose 文件,整个服务栈就变成了“代码”。我有一台家庭服务器和一台办公室工控机,需要同时跑同一套流媒体配置,直接把 compose 文件和 yaml 文件拷贝过去,
docker compose up -d
一条命令就全部搞定,两台机器的行为完全一致。
最后是嵌入式场景友好。go2rtc 官方镜像支持多架构,x86_64、ARM64、ARMv7 都有对应版本,所以树莓派、电视盒子、开发板这些设备都可以跑同一套 Docker 部署流程。后面我会专门讲树莓派和智能车摄像头的接入经验,Docker 在这里帮了大忙。
2. 部署前的准备:环境、摄像头与端口规划
很多人看到“Docker 部署”就觉得要先装一堆东西,其实整个准备过程比想象中简单,核心就三件事:准备一个能跑 Docker 的环境,拿到摄像头的流地址,规划好几个端口。这三件事各有一点小坑,我按顺序说。
2.1 Docker 环境准备:桌面端、服务器和树莓派各有什么讲究
如果你用的是 Linux 服务器或者工控机,安装 Docker 基本上就是几条命令的事。以 Ubuntu/Debian 系为例,常见做法是先更新索引、安装依赖,然后添加 Docker 官方仓库,最后安装 docker-ce 和 docker-compose-plugin。安装完记得把自己用户加入
docker
组,不然每条命令都要加 sudo,很烦。
sudo apt update
sudo apt install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-compose-plugin
sudo usermod -aG docker $USER
Windows 用户一般是装 Docker Desktop,这时候最容易出问题的是 Windows 的虚拟化功能没开。Docker Desktop 依赖 Hyper-V 或者 WSL2 后端,如果你在安装或者启动时报错提示“virtualization support not detected”或者 “Docker Desktop failed to start”,多半就是 BIOS 里的虚拟化被我的一堆前置依赖挡住了。解决办法是进 BIOS 开启 VT-x/AMD-V,然后在 Windows 功能里启用“适用于 Linux 的 Windows 子系统”和“虚拟机平台”,装好之后重启再开 Docker Desktop。另外,默认的镜像拉取可能比较慢,建议在 Docker Desktop 的配置里加一个国内镜像源地址,或者拉取时多等一会儿。
树莓派这类 ARM 设备跟普通 Linux 服务器的区别在于架构。好在 go2rtc 官方镜像有多架构支持,拉取的时候 Docker 会自动拉对应架构的版本,不需要手动指定,这一点非常省心。我自己在树莓派 4B 上用官方镜像跑得很稳,但注意不要用树莓派跑太多路 4K 主码流,毕竟 SoC 的编解码能力有限。
2.2 拿到摄像头的流地址:海康、大华和其他杂牌一次讲清
这一步是整个部署的核心前置条件,也是很多人卡住的地方。go2rtc 只是一个流媒体网关,它自己不产生视频流,它需要知道“去哪里拉流”。所以你必须先拿到摄像头的流媒体地址。
以最常见的海康威视为例,默认 RTSP 地址格式是:
rtsp://用户名:密码@摄像头IP:554/Streaming/Channels/101
末尾的
101
表示第 1 通道的主码流,
102
表示第 1 通道的子码流。主码流分辨率高、码率大,适合录存储;子码流分辨率低、码率小,适合预览和手机端。如果你在本地局域网内做预览,一般建议用子码流,流畅度会好很多。我把地址写成这样方便理解,实际填到配置文件里时,密码里有特殊字符的话需要做 URL 编码,比如密码是
abc@123
,要写成
abc%40123
,这一步漏了会直接导致拉流失败。
大华摄像头的 RTSP 地址跟海康不太一样,常见格式是:
rtsp://用户名:密码@摄像头IP:554/cam/realmonitor?channel=1&subtype=0
这里的
subtype=0
是主码流,
subtype=1
是子码流。如果你用的是大华并且希望走 RTMP,部分新固件的摄像头还支持直接把流推到指定的 RTMP 服务器上,这个后面可以配合 go2rtc 一起用。
还有一些杂牌摄像头,尤其是智能家居类的,可能压根不开放 RTSP 功能。这种设备很多只能通过厂家 App 看,或者通过云平台转发。遇到这种情况,你就当它是“半个摄像头”,go2rtc 也无能为力。但如果你手里有一个能输出 RTMP 或者 MJPEG 的源,哪怕是一个网络摄像头或者电脑摄像头软件,go2rtc 都可以接。
2.3 端口规划与网络安全思路:默认别急着漂到公网
go2rtc 默认监听 1984 端口,这个端口同时承担 Web 管理界面、WebSocket 信令、REST API 的功能,是最核心的入口。除此之外,如果你要用 RTSP 输出,会用到 8554 端口,WebRTC 通信则依赖 8555 端口。
| 端口 | 协议 | 用途 |
|---|---|---|
| 1984 | TCP | Web 界面、API、WebSocket 信令 |
| 8554 | TCP | RTSP 输出 |
| 8555 | TCP/UDP | WebRTC 音视频数据 |
使用 Docker 部署时,最省心的网络模式其实是
host
模式,直接把容器的网络跟宿主机共享,所有端口自动暴露,也少了一层 NAT 映射,对 WebRTC 这种需要打洞的协议特别友好。但 host 模式会让容器跟宿主机网络完全打通,隔离性弱一些。如果你只想做标准的端口映射,那么 TCP 的 1984、8554 还有 8555 的 TCP/UDP 都要映射出来,少一个都会导致 WebRTC 打通失败。
关于远程访问,我得说一句很实在的忠告:不要把 go2rtc 的 1984 端口直接映射到公网。它默认是没有访问认证的,任何知道你 IP 和端口的人都能直接看到你的摄像头画面,这几乎等于把家门钥匙挂在门口。如果确实需要在外网看,更稳妥的方式是把它放到 Home Assistant 这类有完善用户认证的智能家居平台后面统一转发,或者在你自己的安全网关后面加一层访问控制。平时纯局域网使用,就完全不需要考虑这些,体验好得很。
3. 容器化部署 go2rtc 的完整实操
环境准备好、流地址拿到手、端口也规划好了,接下来就是真正的容器化部署流程。我会从一条命令的最简启动开始,逐步过渡到多路摄像头 + docker-compose 的正式方案。
3.1 最简启动:一条命令先把服务跑起来
先用最直接的方式验证整个链路是否畅通。拉取镜像并启动容器:
docker run -d --name go2rtc --restart unless-stopped -p 1984:1984 ghcr.io/alexxit/go2rtc
等几秒钟,浏览器访问
http://宿主机IP:1984
,能看到一个简洁的 Web 界面,说明 go2rtc 容器已经正常启动了。这个界面左侧是配置文件的导航,右侧是视频预览区域,在没有配置任何流源的情况下,主界面是空的,这很正常。
接下来直接在 Web 界面的右上角输入一个摄像头地址测试拉流。比如输入
rtsp://user:pass@192.168.1.100:554/Streaming/Channels/102
,如果一切正常,界面上会直接出现该摄像头的实时画面,而且延迟非常低。这一步如果通了,说明你的摄像头、网络、go2rtc 三者之间没有任何问题,后面就是把这些地址固化到配置文件里的事。
3.2 写一份多路摄像头配置文件:YAML 里到底该放什么
直接在 Web 界面输入地址适合临时测试,正式使用必须把流源固化到配置文件中。go2rtc 使用 YAML 格式的配置文件,默认路径是当前工作目录下的
go2rtc.yaml
。
创建一个配置文件
go2rtc.yaml
,内容如下:
log:
level: info
streams:
front_door:
- rtsp://user:pass@192.168.1.100:554/Streaming/Channels/102
backyard:
- rtsp://admin:pass@192.168.1.101:554/cam/realmonitor?channel=1&subtype=1
living_room:
- rtmp://192.168.1.200/live/cam1
每个流源的名字可以自定义,前端展示时就会看到这个名字。同一个名字下面可以写多个地址,go2rtc 会尝试按顺序连接,第一个不可用时自动切换下一个。这个特性在某些场景下非常有用,比如一个摄像头同时提供 RTSP 和 RTMP 双协议源,你可以都写上当作冗余备份。
写完之后,删除之前的测试容器,改用挂载配置文件的方式重新启动:
docker rm -f go2rtc
docker run -d --name go2rtc --restart unless-stopped -p 1984:1984 -p 8554:8554 -p 8555:8555/tcp -p 8555:8555/udp -v /path/to/go2rtc.yaml:/go2rtc.yaml ghcr.io/alexxit/go2rtc
再访问 Web 界面,左侧应该能看到你配置的三个流源名称,点击任意一个就能直接预览画面。到这里,一个最基础的多路摄像头流媒体平台已经搭建完成了。
3.3 用 docker-compose 固化整套服务:一键启动、一键迁移
如果你的摄像头数量不止一路两路,或者你还打算同时跑 FFmpeg、录制脚本、反代服务,我建议你直接上 docker-compose。把整个服务栈写成一份
docker-compose.yml
,以后不管在哪台机器上,都能用同样的命令一键拉起。
version: "3.8"
services:
go2rtc:
image: ghcr.io/alexxit/go2rtc:latest
container_name: go2rtc
restart: unless-stopped
network_mode: host
volumes:
- ./go2rtc.yaml:/go2rtc.yaml
- ./data:/data
environment:
- TZ=Asia/Shanghai
这里我特别用了
network_mode: host
,原因前面提过:WebRTC 涉及端口打洞和多路 UDP 通信,走桥接模式容易遇到端口映射不全或者 NAT 穿透失败的问题,host 模式直接共享宿主机网络,少了中间层干扰,是我实测下来最省心的方式。
启动命令很简单:
docker compose up -d
查看日志确认没有报错:
docker compose logs -f go2rtc
看到类似
config loaded
、
stream started
之类的日志,说明服务已经正常工作了。以后要升级镜像,只需要
docker compose pull && docker compose up -d
两条命令,配置和数据全部保留。
这套方式的好处是,整个流媒体服务栈的“状态”都被固化在了这两个文件里。不管是一台新服务器、一个树莓派,还是一台 ARM 开发板,把
docker-compose.yml
和
go2rtc.yaml
拷贝过去,一条命令就能复现一套完全相同的环境。对于多节点部署监控系统的场景,这种复制成本几乎为零。
3.4 把视频流推给其他平台:Home Assistant、NVR 与二次推流
go2rtc 最爽的地方在于,接入摄像头之后,它不只是给你一个网页看画面,它还把流“转发”的能力开放给了外围系统。
如果你在用 Home Assistant,go2rtc 可以说是它的天然搭档。HA 官方推荐的通用流媒体方案就是 go2rtc,在 HA 的
configuration.yaml
里配置
stream
组件指向 go2rtc,然后在 Lovelace 界面上添加“相机”卡片,填上
rtsp://127.0.0.1:8554/front_door
,就能在 HA 的界面上低延迟查看摄像头画面,比 HA 自带的流插件稳定得多。
如果你希望把 go2rtc 收到的流再推送到自己的 NVR 或者别的平台,可以通过它内置的 go2rtc API 来二次开发。最常用的一个接口是:
http://127.0.0.1:1984/api/stream?src=front_door&format=mpegts
这个接口可以直接拿到一路 MPEG-TS 流。你可以用 FFmpeg 定时拉取这个流做录像存储,或者再推给其他平台。我自己的做法是写了一个循环录制脚本,每过 30 分钟启动一次 FFmpeg,用这个接口拉流保存为 MP4 文件,实现轻量级 NVR 功能。go2rtc 本身不负责录像存储,这个定位要清楚,它擅长的是“实时转发”,而不是“长期记录”,把存储交给 FFmpeg 或者专业 NVR 是更合理的分工。
4. 从“能跑”到“好用”:常见问题与排查技巧实录
这一部分我把自己实际踩过的坑,和帮朋友排查时遇到的问题都整理出来,按排查优先级排序。如果你部署过程中遇到黑屏、卡顿、连不上,大概率能在这里找到答案。
4.1 画面黑屏、无法拉流,按这几个顺序排查
画面黑屏是最常见的问题,但原因可能有一堆。我的习惯是先去验证源头:用 VLC 或者在宿主机上装个 ffprobe,直接拉一下摄像头地址,看这个流本身能不能正常解析。命令行验证方式很直接:
ffprobe -rtsp_transport tcp -i "rtsp://user:pass@192.168.1.100:554/Streaming/Channels/102"
如果 ffprobe 都拉不到流,那问题大概率出在摄像头侧,go2rtc 这边怎么调都没用。
海康摄像头尤其要注意“非法登录锁定”这个功能。如果你密码输错几次,或者连续拉流失败,摄像头会直接把这个 IP 列入黑名单,之后你从该 IP 发起的任何连接都会被拒绝,表现就是原本正常的地址突然拉不通了。解决办法是登录摄像头 Web 管理界面,关闭非法登录锁定,或者把当前主机 IP 加入白名单。这个坑很隐蔽,因为它不是你配置错了,而是摄像头主动“拉黑”了你。
还有一个很典型的坑是 H.265 编码的摄像头。go2rtc 默认转发视频流,浏览器端是否能播放取决于浏览器是否支持对应编码。H.264 基本所有浏览器都支持,但 H.265 在 Chrome、Firefox 里默认是不支持的,表现就是画面黑屏或者只有声音没图像。解决办法有几个:一是把摄像头编码改成 H.264,二是改接子码流(部分摄像头的子码流是 H.264),三是给 go2rtc 开启 FFmpeg 转码功能,让它在服务端把 H.265 转成 H.264。前两种是我最推荐的,代价最低。
4.2 延迟高、画面卡顿的调优思路
很多人在浏览器里看直播流时发现延迟好几秒,第一反应是 go2rtc 性能不行,实际上是你用的播放协议不对。如果你在 Web 界面里看到的是 HLS 流的预览,那延迟三四秒是正常的,因为 HLS 切片机制决定了它天然不适合低延迟监控。想要低延迟必须在 Web 界面的设置里切换为 WebRTC,或者在前端直接用 WebRTC 协议拉流。
还有一个非常影响流畅度的参数是码流选择。我之前图清晰度,一直用主码流做预览,结果就是来回拖动页面时画面经常卡住,后来改成子码流预览,主码流留给录制和回放,流畅度立刻上了一个档次。监控场景跟视频点播不一样,它的核心诉求是“看得清的同时要连续”,1080P 已 经足够,没必要在满屏码流的边缘试探。
如果你是走 Docker 桥接模式部署的,遇到 WebRTC 打洞失败、预览画面转圈的情况,优先考虑切到 host 网络模式。我一开始在 NAS 上部署时用端口映射,手机上 WebRTC 经常连不上,切到 host 模式之后问题直接消失。如果确实因为网络规划原因不想用 host 模式,那就务必检查 8555 的 TCP/UDP 是否都映射全了,少一个都会导致 WebRTC 通信失败。
4.3 远程访问:别把 1984 端口直接裸奔到公网
前面我已经不只一次强调过这个问题,但还是要单独开一小节再说一遍。go2rtc 默认不要求认证,只要有网络访问权限,任何人都能打开你的摄像头画面,这等于把监控系统的大门敞开着。所以我强烈不建议在路由器上直接做端口映射把 1984 端口暴露到公网。
如果你真的需要在外网看家里的摄像头,我的经验是走“带认证的平台 + 安全网关”这条路线。最简单的方式是把 go2rtc 接入 Home Assistant,通过 HA 自带的有完善登录认证的界面来远程预览;或者在你的网关设备上配置一层反向代理访问控制,先校验了访问者身份再放行到 go2rtc。这样既能远程看监控,又不会把摄像头画面暴露给未知的人。
当然,如果你只是临时调试,比如出门前在手机上看一下摄像头画面是否正常,用 go2rtc 自身的临时分享链接或者短时间手动开放端口也能接受,但用完必须马上关闭。安全这事,宁可麻烦一点,也不要心存侥幸。
4.4 嵌入式与特殊场景:树莓派 CSI 摄像头和智能车怎么接
这部分是给玩树莓派、智能车、巡检机器人等嵌入式场景的朋友单独准备的。大家经常会遇到一个问题:手里的树莓派摄像头模块(比如 OV5647)或者 ESP32-S3 上的 USB 摄像头,不是标准的 IP 摄像头,没有 RTSP 地址,怎么接到 go2rtc 里?
方法其实不复杂。go2rtc 支持的源协议里有 MJPEG 和 RTMP,嵌入式设备上常见的做法是先把摄像头图像输出成一路 MJPEG 或 RTMP 流,然后再交给 go2rtc 做协议转换。比如树莓派上可以用 GStreamer 把 CSI 摄像头的数据推成 RTMP 流,ESP32-S3 这种板子直接输出 MJPEG over HTTP,go2rtc 都能直接当源拉取。
streams:
robot_cam:
- mjpeg://192.168.1.50:8080/?action=stream
像上面这样,只要设备暴露了一个 HTTP MJPEG 地址,go2rtc 就能自动接管,转换成 WebRTC 或者 HLS,让浏览器端和手机端都能低延迟观看。
智能车和巡检机器人这类移动场景,我个人的经验是要特别关注网络质量。移动机器人用 Wi-Fi 连接时,信号波动会导致视频流频繁卡顿甚至断流。解决方案一般是两个方向:一是降低摄像头输出码率,以 MJPEG 或者低码率 H.264 输出;二是在 go2rtc 配置中加一个备用低清晰度源,主码流不稳时自动切换子码流。这两个技巧配合好了,移动端摄像头画面基本能保持连续稳定。
最后再分享一个小技巧。go2rtc 的配置文件支持环境变量替换吗?我测试过,官方镜像不直接支持在 YAML 里用
${ENV}
这种写法,但是你可以用
sed
命令在容器启动前动态替换配置文件中的占位符,这样就能实现在同一个镜像里通过环境变量控制不同的摄像头地址。虽然有点绕,但如果你有批量部署多套环境的需求,这招还是能省下不少重复配置的时间。测了这么多轮下来,整体性价比最高的组合还是“Docker + go2rtc + 子码流 + WebRTC 预览”,稳定、低延迟,还省资源,至少在我家里这三路摄像头上已经稳定跑了半年多没出过问题。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)