家里装了两个海康摄像头,一个在大门口,一个在阳台,原来各自用厂家 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 预览”,稳定、低延迟,还省资源,至少在我家里这三路摄像头上已经稳定跑了半年多没出过问题。

Logo

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

更多推荐