news 2026/9/16 9:51:01

Docker部署go2rtc:从RTSP到WebRTC的摄像头流媒体网关

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker部署go2rtc:从RTSP到WebRTC的摄像头流媒体网关

1. 项目初衷与核心思路

1.1 go2rtc 到底是干什么的

如果你接触过摄像头接入,多半经历过这种破事:家里的老摄像头只支持 RTSP 协议,想在网页上看一眼画面,却发现浏览器原生根本不支持 RTSP;想接进 Home Assistant,插件告诉你要先转成 HLS;想在手机 App 上低延迟预览,又被告知需要 WebRTC。

go2rtc 就是解决这类“协议乱炖”问题的。它是一个用 Go 写成的轻量流媒体服务器,核心能力是把不同来源的流(RTSP、RTMP、HLS、MJPEG、文件等)拉进来,再以多种协议输出出去。你给它一个 RTSP 摄像头地址,它能自动给你生成 WebRTC、HLS、RTMP、MJPEG 等多种输出流。底层的 WebRTC 能力基于 Pion 实现,所以浏览器端不需要任何插件,打开网页就能低延迟看画面。

这个项目最初是给 Home Assistant 玩家做摄像头低延迟预览用的,后来因为简洁好用,逐渐被更多人拿来当独立的流媒体网关。它还会自动发现局域网内支持 ONVIF 的摄像头,省去手动逐个填写地址的功夫。

1.2 为什么非要套一层 Docker

有人会问,go2rtc 本身就是一个单个可执行文件,直接下载二进制放服务器上跑不就行了?确实可以,但用 Docker 部署有几个实实在在的好处,尤其适合家庭或小型工作室的场景。

第一是隔离干净。go2rtc 依赖 Go 运行时环境,以及可能存在的 FFmpeg 转码功能。用容器把所有依赖打包好,不污染宿主机,卸载时一条命令就能清理干净。第二是升级方便。官方镜像更新频繁,docker compose 里改一下 tag 再 up -d,几秒钟完成升级。第三是配置统一。把 go2rtc.yaml 和 docker-compose.yml 放在同一个目录里,整个服务可以随项目目录迁移,换机器时拷贝过去就能跑。

我自己的体会是,容器化最大的价值是“心智负担低”。你不用记得这个服务的可执行文件放在哪个目录、环境变量怎么配、日志在哪儿看,一切都被 Docker 的约定固化下来了。

2. 环境准备与基础概念

2.1 装好 Docker 基础环境

开始部署之前,先把 Docker 环境准备好。如果你是在 Linux 服务器上操作,以 Debian/Ubuntu 为例,安装命令一般是:

sudo apt update sudo apt install docker.io docker-compose-v2 sudo systemctl enable --now docker

如果是 Windows 或 macOS,直接安装 Docker Desktop,注意在设置里开启 WSL2 / Hyper-V 虚拟化支持。如果安装完提示 virtualization support not detected,基本就是 BIOS 里的虚拟化没打开,进 BIOS 开启 Intel VT-x 或 AMD-V 即可。

这里我建议直接把 Docker Compose 一起装上,因为后面我们会用 docker compose 来编排服务。别用旧版的 docker-compose(带横杠)了,v2 版本的 compose 插件功能更全,语法也更标准。

装完验证一下:

docker version docker compose version

能正常打印出版本信息,说明环境没问题。

2.2 镜像选型与版本策略

go2rtc 官方维护的容器镜像在 GitHub Container Registry,拉取地址是:

ghcr.io/alexxit/go2rtc:latest

如果你在国内服务器上拉取 GitHub 的镜像比较慢,可以配置 Docker 的 registry-mirrors 使用国内加速源,或者在 Docker Hub 上找第三方转存的镜像。不过我更推荐直接用 ghcr.io 官方源,毕竟第三方镜像的更新时效性和安全性都没法完全保证。

版本策略上,家庭自用场景我通常直接拉 latest。这个项目发版很活跃,latest 基本等于最新的稳定版。如果你对稳定性要求高,可以指定具体版本号,比如 ghcr.io/alexxit/go2rtc:v1.9.8,升级时手动改版本号。我个人习惯在 compose 文件里写成 latest 或大版本 tag,方便自动拿到新功能。

有一个小坑值得提一下:不同版本的 go2rtc 配置文件字段有过调整。如果你是老版本升级上来,建议升级后先看一眼 go2rtc 的 docs,确认配置项没有被废弃,避免启动时报错。

3. 实操部署:docker compose 搭建 go2rtc 服务

3.1 端口和目录规划

go2rtc 主要监听三个端口:

  • 1984/TCP:Web 管理界面和 API,浏览器访问 http://IP:1984 就能看到所有流的状态和画面。
  • 8554/TCP:内置的 RTSP 服务端口,用于对外提供 RTSP 输出流。
  • 8555/TCP+UDP:WebRTC 的媒体端口,浏览器和服务器之间通过它传输音视频数据。

如果你还需要把流推给其他 RTMP 服务,或者接收 RTMP 输入,可以额外映射 1935 端口。不过默认场景下,这三个端口就够了。

目录结构我习惯这样规划:

/home/ubuntu/go2rtc/ ├── docker-compose.yml └── config/ └── go2rtc.yaml

把配置文件放在宿主机上,容器内挂载到 /config 目录。这样备份配置、修改配置都非常直观,不用进容器操作。

3.2 编写 docker-compose.yml

直接上完整配置:

services: go2rtc: image: ghcr.io/alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped network_mode: bridge ports: - "1984:1984/tcp" - "8554:8554/tcp" - "8555:8555/tcp" - "8555:8555/udp" volumes: - ./config:/config command: - -c - /config/go2rtc.yaml environment: - TZ=Asia/Shanghai

几个关键点我逐一说明。

restart: unless-stopped保证服务器重启后服务自动拉起,摄像头流掉线后也会自动重连。network_mode: bridge是默认模式,配合显式的端口映射来暴露服务。.config目录挂载到/config,然后在 command 里用-c参数指定配置文件路径,这是最稳妥的方式——go2rtc 在某些环境下默认配置文件查找路径很玄学,直接在启动参数里指定,就完全不会有找不到配置的问题。

TZ=Asia/Shanghai设置时区,主要影响日志时间戳和后续可能的定时功能。看日志时如果发现时间对不上,大概率就是忘了设置时区。

3.3 启动、验证与开机自启

配置写好后,启动服务:

cd /home/ubuntu/go2rtc docker compose up -d

第一次启动会拉取镜像,耐心等一会儿。启动完成后看容器状态:

docker compose ps docker logs go2rtc

正常情况下日志末尾会出现类似这样的内容:

INFO go2rtc: starting config=/config/go2rtc.yaml INFO api: listen=0.0.0.0:1984 INFO rtsp: listen=0.0.0.0:8554 INFO webrtc: listen=0.0.0.0:8555

此时浏览器访问http://服务器IP:1984,应该能看到 go2rtc 的管理界面。界面上会显示当前的流列表、摄像头在线状态,以及各种协议的播放地址。这一步能正常打开,说明容器跑通了。

开机自启不用额外配置,因为restart: unless-stopped已经覆盖了这个场景。你只需要确保 Docker 服务本身是开机自启的,也就是前面提到的systemctl enable docker

4. 摄像头接入配置:从 RTSP 到多协议输出

4.1 RTSP 摄像头源配置

go2rtc 的摄像头源配置写在 go2rtc.yaml 里。最基本的配置是定义一个流名,然后指向摄像头的 RTSP 地址:

streams: cam_backyard: rtsp://admin:password@192.168.1.50:554/Streaming/Channels/101

这里有几个容易踩坑的地方。第一,不同品牌的摄像头 RTSP 地址路径完全不同。海康威视一般是/Streaming/Channels/101,大华是/cam/realmonitor?channel=1&subtype=0,TP-Link 又是一种。建议先用 VLC 或 ffprobe 在电脑上测试一下,确认地址能出画面再写到配置里,否则就是来回试错。

第二,用户名密码中如果包含特殊字符,比如@:#,必须做 URL 编码。比如密码是admin:123,RTSP 地址里的密码部分要写成admin%3A123,否则地址解析会出错,表现为一直连接不上或者反复提示认证失败。

配置多个摄像头时,逐个添加即可:

streams: cam_backyard: rtsp://admin:password@192.168.1.50:554/Streaming/Channels/101 cam_front: rtsp://admin:password@192.168.1.51:554/cam/realmonitor?channel=1&subtype=0

配置修改后不需要重启容器,go2rtc 会自动热加载配置文件。这一点非常方便,我经常改一行配置,然后刷新页面就能看到效果。

4.2 多协议输出链路

摄像头源配置好之后,go2rtc 会自动生成多种协议的播放地址。在 Web 管理界面的流详情里,你可以看到该流对应的 WebRTC、HLS、RTSP、MJPEG 等地址。

以名为cam_backyard的流为例,输出地址分别是这样:

  • WebRTC:http://服务器IP:1984/api/webrtc?src=cam_backyard
  • HLS:http://服务器IP:1984/api/hls/cam_backyard.m3u8
  • MJPEG:http://服务器IP:1984/api/mjpeg?src=cam_backyard
  • RTSP:rtsp://服务器IP:8554/cam_backyard
  • RTMP:rtmp://服务器IP:1935/cam_backyard

这套多协议输出是整个方案的核心价值。摄像头本身只支持 RTSP,但经过 go2rtc 中转后,iOS 端可以走 HLS,浏览器可以走 WebRTC 低延迟预览,NVR 录制可以走 RTSP 或 RTMP,老旧系统可以走 MJPEG。一个流源头,多种消费场景,完全不用为每种场景单独搭一套转码服务。

实际测试下来,WebRTC 模式在局域网内端到端延迟一般能控制在 100 到 300 毫秒以内,这是看云台控制或实时门铃的理想效果。HLS 延迟一般在 2 到 5 秒,适合手机端的稳定预览。

4.3 特殊源:FFmpeg 拉流、本地文件和音频流

go2rtc 不只是支持 RTSP 摄像头,还能接入一些特殊来源。

遇到某些格式刁钻的摄像头,比如私有协议或者编码格式是 MJPEG over RTSP,go2rtc 原生解析可能不干净。这时可以用 FFmpeg 方式拉流,只要在源前面加ffmpeg:前缀:

streams: cam_weird: ffmpeg:rtsp://admin:password@192.168.1.60:554/stream1#video=copy#audio=copy

#video=copy#audio=copy的含义是视频和音频都不要重新编码,只做封装转换,CPU 开销非常小。只有当源格式和目标格式确实不兼容时才去掉 copy 参数交给 FFmpeg 转码,那会比较吃 CPU。

本地视频文件也可以作为输入源,适合做测试:

streams: test_loop: file:///media/test.mp4

甚至麦克风、音频流也可以通过 go2rtc 转发。总之,只要能拿到流,go2rtc 都能帮你转发成需要的输出协议。

5. 常见问题与排查实录

5.1 端口被占用导致服务启动失败

新部署时最常遇到的是端口冲突。如果你服务器上已经有其他服务占用了 1984 或 8554,go2rtc 会启动失败,日志里会明确提示address already in use

排查方法很简单:

sudo lsof -i :1984 sudo netstat -tlnp | grep 8554

找到占用端口的进程后,要么停掉旧服务,要么修改 go2rtc 的监听端口。改端口时要注意,go2rtc.yaml 里可以这样覆盖默认端口:

api: listen: ":1985" rtsp: listen: ":8555" webrtc: listen: ":8556"

改完之后,docker-compose.yml 里的端口映射也要同步修改。

5.2 摄像头认证失败或拉流超时

这一类问题在日志里的表现是unauthorizedconnection timeout。我的排查顺序是这样的:

先用 VLC 或者命令行工具在宿主机上直接测试源地址。注意是从宿主机测试,不是从容器内测试,这样可以快速定位是摄像头拒绝认证,还是容器网络访问不到摄像头。

ffprobe -rtsp_transport tcp -i "rtsp://admin:password@192.168.1.50:554/Streaming/Channels/101"

如果 ffprobe 能出流信息,说明地址没问题,问题出在容器到摄像头的网络上。最常见的原因是 Docker 容器默认用的 bridge 网络,如果摄像头限制来源 IP,或者摄像头在一个单独的 VLAN 里,容器就可能访问不到。解决方式是把容器网络改成 host 模式,或者把摄像头网段接入到 Docker 网络中。

如果 ffprobe 直接提示认证失败,那就是用户名密码或路径的问题。密码里带特殊字符的话,优先检查 URL 编码是否做了。

5.3 WebRTC 画面黑屏、一直转圈

这是 Docker 部署 go2rtc 最经典的坑。现象是 Web 管理界面能看到流,点击播放后黑屏或一直转圈,控制台报 ICE 连接失败。

原因在于 WebRTC 的候选地址机制。go2rtc 在容器内运行时,默认会向浏览器报告容器内部 IP 作为媒体传输候选地址,但浏览器访问不了容器的内网 IP,连接自然建立不起来。

解决方法是显式配置 WebRTC 候选地址,指向宿主机局域网 IP 或公网 IP:

webrtc: listen: ":8555" candidates: - 192.168.1.10:8555

这里的 192.168.1.10 是你的宿主机局域网 IP。如果服务器有公网 IP,且端口已映射到公网,也可以写公网 IP。配置完成后重启服务,再测试 WebRTC 播放就能秒开。

这个坑我踩过一次之后深刻理解了 WebRTC 的 NAT 穿透逻辑。docker compose 的端口映射对普通 TCP 服务没问题,但 WebRTC 需要的是“媒体地址要能被对端直接访问”,所以必须手动手写候选地址。

5.4 CPU 和内存占用突然飙高

go2rtc 本身非常轻量,纯转发模式下 CPU 占用几乎可以忽略。如果你发现 CPU 占用异常,大概率是某个流在转码。转码的原因通常是源编码格式和目标协议不匹配,比如篮球场摄像头输出的是 H.265,但某些浏览器不支持 H.265 解码,go2rtc 就会自动转成 H.264。

我的建议是尽量让摄像头输出 H.264。如果摄像头只支持 H.265,再考虑在 go2rtc 里开启转码。转码时可以设置合理的分辨率和码率:

streams: cam_265: ffmpeg:rtsp://admin:password@192.168.1.70:554/stream1#video=h264#width=1280#height=720#bitrate=2500

内存方面,如果同时预览的路数很多,go2rtc 缓存区会占用一定内存。一般家庭十几个摄像头,1G 内存绰绰有余。

下面表格汇总这几个常见问题:

现象常见原因排查方向
启动失败报 address in use端口被占用lsof / netstat 查端口
拉流失败 unauthorized用户名密码或地址编码错误用 ffprobe 单独测试源地址
拉流超时 timeout容器无法访问摄像头网段检查 docker 网络,必要时改 host 模式
WebRTC 黑屏转圈缺少有效的 ICE 候选地址配置 webrtc.candidates 指向宿主机 IP
CPU 飙高发生不必要转码检查源编码格式,优先 H.264 copy

6. 进阶扩展与个人心得

6.1 接入 Home Assistant 的快捷方式

如果你在用 Home Assistant,go2rtc 接入其实是零成本的,因为 Home Assistant 的 Stream 组件本身就推荐搭配 go2rtc 使用。你可以在 Home Assistant 的 configuration.yaml 里配置:

stream: source: rtsp://192.168.1.10:8554/cam_backyard

这样 HA 所有依赖 stream 组件的功能,比如手机 App 推送、录音、自动化触发,都会通过 go2rtc 来取流,延迟和稳定性比直接连摄像头 RTSP 地址好得多。

6.2 配合 Frigate 做 NVR

go2rtc 和 Frigate 的关系更紧密。新版本 Frigate 内嵌了 go2rtc,作为摄像头接入和预览的前置层。如果你已经有独立的 go2rtc 实例,可以直接让 Frigate 连你的 go2rtc,避免重复拉流:

go2rtc: host: 192.168.1.10 port: 1984

这种架构的好处是,Frigate 做 AI 检测和录制,go2rtc 负责低延迟预览,各司其职,还能省掉摄像头侧的多路并发压力。

6.3 我自己用的这套方案的体会

我在家里部署这套 go2rtc 已经跑了两年多,期间经历过一次 Docker 版本大升级,也换过两三次摄像头,总体体验非常稳定。最初我直接用 docker run 跑,后来改成 docker compose 管理,再后来把配置拆出来挂载到宿主机,整个管理变得越来越顺手。

有一点感触比较深:这套方案最值钱的地方不在多协议转换本身,而在于它把摄像头接入的“最后一公里”统一了。以前每接一个新摄像头,都要重新折腾一遍协议对接,现在只需要把 RTSP 地址加进 yaml,然后所有下游应用都自动能用了。这种“一次接入,处处可用”的体验,是 go2rtc 最吸引我的地方。

如果你刚开始搭摄像头流媒体平台,或者正在被哈苏、WebRTC、HLS 各种协议兼容问题搞到头大,建议直接照着上面的配置试一遍。从 docker compose 启动到第一个摄像头出画面,整个过程半小时就能搞定,剩下的就是根据自己的场景慢慢调优参数了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 9:49:59

2026年1至9月扬州市房地产价格深度分析报告

一、报告背景与数据说明本报告基于2026年1月至9月扬州市房地产市场的实际成交案例,从成交价格、区域分布、物业类型、价格走势等维度进行深度分析。数据来源包括扬州市不动产登记中心备案数据、主要中介机构成交记录及典型楼盘网签信息,覆盖新房与二手房…

作者头像 李华
网站建设 2026/9/16 9:49:38

Android视频播放器内核选型:GSYVideoPlayer的IJKplayer与ExoPlayer实战

简介:一份面向安卓开发者的多功能视频播放器完整源码工程,集成ijk、系统与exo三种可切换的播放内核,支持多种网络协议,实现了边播边缓存、数十种滤镜、水印、GIF截图、弹幕、外挂字幕、列表播放、重力旋转与手动旋转同步、多分辨率…

作者头像 李华
网站建设 2026/9/16 9:46:31

Colibri:面向MoE架构的C语言轻量级推理引擎

1. 项目概述:Colibri 是什么,它解决的到底是什么问题?Colibri 这个名字乍一听像某种蜂鸟——轻盈、敏捷、高代谢。放在当前大模型推理的语境下,它恰恰就是这个名字的具象化:一个用C 语言实现的、专为MoE(Mi…

作者头像 李华
网站建设 2026/9/16 9:45:19

TVA具身架构驱动的机器行为与人类价值对齐机制

前沿技术探索:TVA智能体(简称TVA) TVA智能体(亦称“AI智能体视觉”或“TVA视觉智能体”)是依托Transformer架构与“因式智能体”理论构建的通用视觉技术体系。它有机融合深度强化学习(DRL)、卷积神经网络(CNN)与因式分解算法(FRA),构成了具身智能的核心视觉中枢(…

作者头像 李华
网站建设 2026/9/16 9:44:52

结构动力学仿真技术:原理、应用与发展趋势

1. 结构动力学仿真技术概述结构动力学仿真作为工程分析领域的重要分支,主要研究结构在动态载荷作用下的响应特性。这项技术通过建立数学模型,模拟结构在实际工况中的振动、冲击、疲劳等行为,为工程设计提供关键数据支持。现代结构动力学仿真已…

作者头像 李华
网站建设 2026/9/16 9:43:19

【2016-02-23】Linux无线模块WEXT简单分析

[历史归档] 本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。 发布时间: 2016-02-23 | 标题:Linux无线模块WEXT简单分析 | 分类: 操作系统 / linux / kernel &#xff…

作者头像 李华