
先说个场景家里那台海康摄像头手机App看一眼没问题可一旦你想把视频流接进 Home Assistant、Frigate或者推给网页端实时预览就会遇到协议不通、延迟高、格式不兼容一堆破事。go2rtc 就是专门来解这个死结的。它是一个轻量、开源的多协议流媒体网关通过 Docker 几分钟就能跑起来把 RTSP、RTMP、HTTP-FLV、HLS、WebRTC 这些协议串在一起帮你把摄像头流统一成一套平台。这篇文章我从零讲清楚为什么选 go2rtc、Docker 怎么部署、摄像头怎么接入、配完以后怎么验证最后还有一堆我踩过的坑。适合刚接触流媒体的小白也适合正在给智能家居做视频集成的老玩家。1. 为什么是 go2rtc先弄懂它到底解决了什么问题很多朋友第一次听到 go2rtc 的反应是“我直接用 ffmpeg 转一下不就行了为什么要多一个服务”这个想法我也曾有过但真正把摄像头接入系统之后你会发现情况远不止“转一下”这么简单。1.1 摄像头协议乱局RTSP、ONVIF、私有协议谁也听不懂谁摄像头厂商最“擅长”的事之一就是把 RTSP 地址做得千奇百怪。海康常见的路径是/Streaming/Channels/101大华是/cam/realmonitor?channel1subtype0TP-Link 是/stream1还有一些杂牌设备直接给你一个 RTMP 地址。更麻烦的是不少设备虽然支持 ONVIF但 ONVIF 只解决“发现设备”和“控制云台”的问题真正拿视频流还是要靠 RTSP 或者厂商私有协议。浏览器这边也很尴尬原生不支持 RTSP也不支持 RTMPHTTPS 页面里甚至不允许直接加载 http 的非安全流。所以你要是想在网页上看监控画面就得先把 RTSP 转成 HTTP-FLV、HLS或者等浏览器原生支持的 WebRTC。单独为了某一路流写一个 ffmpeg 命令行临时用可以一旦摄像头数量多、协议杂脚本和进程管理会迅速失控。go2rtc 把这一层收口了它主动去连各种来源的流然后对外统一提供多种协议出口。你不需要关心每路摄像头到底是从哪来的只要告诉 go2rtc“一路流叫什么名字、源地址是什么”剩下的 RTSP、HLS、WebRTC、FLV、MJPEG 输出全由它接管。1.2 go2rtc 是怎么把“多协议”揉成一张网的go2rtc 的作者是 AlexxIT最早是为了给 Home Assistant 做低延迟摄像头集成而写的后来慢慢变成了一个比较通用的流媒体网关。它最大的特点是“一个二进制收所有”进程里内置了 RTSP Server/Client、RTMP Client、WebRTC、HLS、MJPEG、HTTP-FLV 这些能力不需要像传统方案那样再单独部署 Nginx-RTMP、ZLMediaKit 或者 SRS。优势主要体现在这几点WebRTC 低延迟浏览器打开控制台就能看到接近实时的画面延迟通常在 200-500ms比 HLS 那种动辄 2-5 秒的体验强太多。这个特性对门铃、云台控制这类场景很关键。零配置协议输出接入一路 RTSP 后HLS、HTTP-FLV、WebRTC、MJPEG 这些输出是自动生成的不需要每个协议单独写规则。自动发现 ONVIF 设备在一个局域网里它能通过 ONVIF 扫描到摄像头省去挨个查 IP 的麻烦。FFmpeg 转码兜底如果摄像头输出 H.265而你的浏览器不支持go2rtc 可以调用 FFmpeg 转成 H.264画面尺寸、帧率、码率也都能调。小巧且有 Web UI容器镜像很小自带一个管理页面能看到所有流、所有连接状态调试非常直观。从家庭监控到小型工作室的多路直播它都能兜住。你也可以把它理解成一个轻量“流媒体交换机”摄像头只管把流送过来消费端想用什么协议拿流由 go2rtc 负责分发。1.3 为什么我把部署方式选成了 Dockergo2rtc 本身也提供 Linux 二进制直接跑也不是不行但我还是建议用 Docker。原因倒不是什么“容器化高大上”而是实际维护省心第一依赖干净。go2rtc 在 Ubuntu 上跑可能还要装 ffmpeg、配 webp、设置 service 自启用容器的话镜像把这些都打包好了换一台机器docker run一遍就恢复。第二升级方便。官方镜像新版本发布后一条命令拉新镜像、重建容器即可不用担心旧文件残留。第三和 Home Assistant、Frigate 联动方便。这类智能家居服务本身就是容器部署go2rtc 也容器化之后网络和文件挂载都好协调。当然Docker 也引入了一些需要注意的地方比如端口映射、网络模式的选择。这些我在后面会详细讲算是本文最值钱的部分之一。2. 部署前准备Docker 环境、镜像与网络规划在正式启动容器之前先把基础环境理顺。这里的坑比较多处处都想当然的话后面排查起来非常痛苦。2.1 检查 Docker 环境顺便解决镜像拉取慢的问题先在服务器或者开发机上确认 Docker 是否已经装好。终端执行docker --version docker compose version如果提示找不到命令就需要先安装 Docker。Linux 上一般用官方脚本安装Windows 和 macOS 可以用 Docker Desktop。装完 Docker Desktop 后有些朋友会遇到启动失败、提示“virtualization support not detected”之类的问题这个通常是 BIOS 里没开虚拟化或者没有启用 Windows 的虚拟机平台功能去 BIOS 打开 Intel VT-x/AMD-V然后在 Windows 功能里勾选“虚拟机平台”和“Hyper-V”看 Docker Desktop 版本要求基本就能解决。镜像拉取是另一个高频痛点。go2rtc 镜像本身不大但国内网络直连 Docker Hub 确实慢有时还会超时。解决办法是配置镜像加速器。Linux 下修改/etc/docker/daemon.json{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com ] }改完重启 Dockersudo systemctl restart dockerDocker Desktop 用户可以直接在 Settings - Docker Engine 里改也可以点击界面上的镜像加速源设置。这里说的只是正常加速优化如果你在特殊网络环境里请遵守相关网络管理规定。镜像加速生效后拉取 go2rtcdocker pull dgtlmoon/go2rtc如果你更信任作者仓库也可以拉ghcr.io/alexxit/go2rtc。两者核心功能一致选一个作为默认镜像即可。2.2 准备目录、端口和网络模式这些细节决定了你后面少踩坑先建一个目录放配置mkdir -p ~/go2rtc cd ~/go2rtcgo2rtc 主要有两个端口需要关注1984Web UI 和 API 端口。浏览器访问、流输出都走这个端口。8555WebRTC 专用端口既需要 TCP 也需要 UDP。如果你的 go2rtc 只是在本机给 Home Assistant 用端口暴露范围可以限制得严一点如果想让手机在外网也能看还要考虑端口映射和防火墙规则。网络模式上我做了一个重要决策优先使用 host 网络模式。原因很简单WebRTC 要协商 UDP 通道如果用 bridge 模式只映射了 TCP 端口或者 UDP 映射不完整外部浏览器可能会一直卡在连接中。host 模式下go2rtc 直接使用宿主机网络8555 端口的 UDP 报文天然可达省掉很多 NAT 穿透的麻烦。当然 host 模式也有代价它不再有独立的容器 IP所有端口都直接暴露在宿主机上。不过对家庭网关来说这通常不是问题反而更好理解。3. 写配置把摄像头流接进 go2rtcgo2rtc 的魅力在于接入一路流只需要在配置文件里写几行。配置文件默认挂载到容器内的/config/go2rtc.yaml你只需要在宿主机上准备这个 YAML 文件即可。3.1 go2rtc.yaml 基础结构与三种常用模式先看一个最简配置log: level: info api: listen: :1984 webrtc: listen: :8555 streams: frontdoor: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 backyard: ffmpeg:rtsp://192.168.1.65:554/cam/realmonitor?channel1subtype0我习惯把streams这个字段理解成一个“流字典”key 是自己起的流名称value 是源地址。value 有三种常见写法直接写 RTSP 或者 RTMP 地址go2rtc 自己拉流原样输出。这种方式延迟最低适合 H.264 摄像头。加ffmpeg:前缀让 go2rtc 调起内置 FFmpeg 去拉流或转码。摄像头是 H.265、浏览器不支持时或者需要对画面裁剪、改分辨率时用这个。写一个列表数组给同一路流配多个备用源。当第一个源断流时go2rtc 会自动切换到第二个源。这个我很喜欢比自己在外面套一层 watchdog 靠谱得多。3.2 主流摄像头 RTSP 地址写法与参数细节不同品牌摄像头接入 go2rtc最大的坑就是 RTSP 地址路径。下面是我实测过的常见格式品牌RTSP 地址示例说明海康威视rtsp://user:passip:554/Streaming/Channels/101101 是主码流102 是子码流大华rtsp://user:passip:554/cam/realmonitor?channel1subtype0subtype0 主码流1 子码流TP-Linkrtsp://user:passip:554/stream1部分型号是 stream1/stream2Reolinkrtsp://user:passip:554/h264Preview_01_main主码流h264Preview_01_sub是子码流杂牌 ONVIFrtsp://user:passip:554/onvif1看厂商规格路径五花八门写地址时有三个细节值得留意密码里的特殊字符要 URL 编码。比如密码是Abc123要写成%40否则解析会出错。我甚至遇到过密码里有:的那还要把冒号编码成%3A。先用一个简单密码测试通过再上复杂密码能省很多排查时间。主码流和子码流的认知要清楚。主码流分辨率高、码率高适合录像子码流分辨率低、码率低适合实时预览和窄带环境。go2rtc 里可以同时接入两路给 UI 用不同的流名。刚开始调试时我建议先接子码流因为传输压力小画面出来快验证通了之后再决定要不要切换主码流。同一路流不要在多个工具里反复拉取。如果 Frigate 也在拉这路流、手机 App 也在拉摄像头并发数有限go2rtc 可能只拿到其中一路其他工具会卡。go2rtc 作为统一出口后所有下游客户端只和 go2rtc 通信摄像头只和 go2rtc 建立一条连接压力小很多。3.3 从浏览器到播放器多协议输出与不同使用场景配置写入流之后不需要为每种协议写额外规则go2rtc 会自动生成 URL。假设你给摄像头起的流名是frontdoor服务地址是192.168.1.100:1984那么浏览器实时低延迟预览打开http://192.168.1.100:1984在 Web UI 里选中frontdoor底层自动走 WebRTC。HLS 输出http://192.168.1.100:1984/api/stream/frontdoor/hlsHTTP-FLV 输出http://192.168.1.100:1984/api/stream/frontdoor/flvMJPEG 输出http://192.168.1.100:1984/api/stream/frontdoor/mjpegRTSP 输出rtsp://192.168.1.100:1984/frontdoor这些协议各有适用场景我整理了一张对比表方便你按需选择协议延迟兼容性典型场景WebRTC低约 200-500ms浏览器原生支持但 NAT 穿透依赖 UDP实时预览、云台控制HLS高约 2-5 秒几乎所有播放器都支持走 HTTP录像回放、跨公网分享HTTP-FLV低约 1 秒浏览器需要 flv.js 支持移动端兼容一般网页低延迟直播MJPEG中极老设备也能播但带宽大低清预览、兼容旧系统RTSP极低只能专业播放器/系统使用对接 Frigate、VLC、NVR实际项目里我最常用的是 WebRTC 做预览HLS 做回放。WebRTC 的低延迟体验确实好但如果你要在微信公众号或者某些原生 App 里嵌套网页HLS 的兼容性更稳。4. 容器启动与集成docker run、docker-compose、验证与联动配置文件准备好之后启动 go2rtc 反而是最简单的一步。但启动方式的选择会影响后续维护这里推荐用 docker-compose 固定下来。4.1 docker run 快速启动推荐 host 网络最直接的启动方式是这样docker run -d \ --name go2rtc \ --restart always \ --network host \ -v $(pwd)/go2rtc.yaml:/config/go2rtc.yaml \ dgtlmoon/go2rtc解释一下参数--network host让 go2rtc 使用宿主机网络WebRTC 的 UDP 协商最省心。-v $(pwd)/go2rtc.yaml:/config/go2rtc.yaml把宿主机当前目录下的配置挂载进容器。以后改配置只需重启容器不用重建镜像。--restart always服务器重启后自动拉起容器真实环境里非常重要。如果你的 Docker 环境不支持 host 模式比如 Docker Desktop 的部分后端那就退而求其次用 bridge 模式docker run -d \ --name go2rtc \ --restart always \ -p 1984:1984 \ -p 8555:8555/tcp \ -p 8555:8555/udp \ -v $(pwd)/go2rtc.yaml:/config/go2rtc.yaml \ dgtlmoon/go2rtcbridge 模式下记得把 8555 的 TCP 和 UDP 都映射出来少一个都可能导致 WebRTC 连接一直“卡正在连接”。4.2 用 docker-compose 固定配置方便以后改docker run 敲一两次没问题但时间一长容易忘参数。我建议在~/go2rtc目录下写一个docker-compose.ymlversion: 3.8 services: go2rtc: image: dgtlmoon/go2rtc container_name: go2rtc restart: always network_mode: host volumes: - ./go2rtc.yaml:/config/go2rtc.yaml如果你需要限制资源占用还可以加cpus: 1.0 mem_limit: 512m然后在同一目录运行docker compose up -d升级 go2rtc 时一句话就行docker compose pull docker compose up -d这种流程的好处是配置和编排文件都在一个目录里换服务器时直接把整个文件夹拷过去一键拉起不用再回忆“当时到底怎么启动的”。4.3 启动后怎么验证流是否真的通了容器启动后先看日志有没有报错docker logs -f go2rtc日志里出现stream frontdoor started类似的信息说明它已经成功连上摄像头源。如果日志一直报EOF或者unauthorized大概率是 RTSP 地址或账号密码有问题。然后打开浏览器访问http://你的IP:1984。Web UI 的左侧会列出所有配置好的流点一下流名如果画面出来说明 webRTC 链路也通了。我习惯再用命令行做一次协议验证比如测试 HLSffplay http://127.0.0.1:1984/api/stream/frontdoor/hls如果你装了 VLC直接打开rtsp://127.0.0.1:1984/frontdoor也是一个办法。这个 RTSP 出口多用于 Frigate 等后端拉流验证它能通非常关键。注意如果你用 bridge 模式127.0.0.1要换成宿主机 IP。4.4 与 Home Assistant、Frigate 集成的几个坑go2rtc 和智能家居设备联动是很多人的核心需求。Frigate 的最新版本本身也内置了 go2rtc但如果你想统一管理家庭里的摄像头流完全可以独立跑一个 go2rtc然后用 Frigate 去拉它输出的 RTSP。在 Frigate 的config.yml中你可以把检测源直接写成 go2rtc 的 RTSP 地址go2rtc: external: - rtsp://192.168.1.100:1984/frontdoor cameras: frontdoor: ffmpeg: inputs: - path: rtsp://127.0.0.1:1984/frontdoor roles: - detect这里有个很容易犯的错如果你的 Frigate 也是容器那么它里的127.0.0.1指向的是 Frigate 容器自身而不是宿主机。要让 Frigate 容器访问宿主机上的 go2rtc要么用宿主机在 Docker 网络中的桥接 IP例如172.17.0.1要么把 Frigate 也设置为 host 网络。如果你用的是 Home Assistant 里的 Frigate 插件一般可以直接用宿主机地址。Home Assistant 集成 go2rtc 就更直接了。把 go2rtc 添加为流源后在 Lovelace 卡片里使用 WebRTC Camera 卡片地址填rtsp://宿主机IP:1984/frontdoor画面延迟非常低云台控制的手感也明显好过 HLS。这个方案我实际用了很久稳定性很满意。5. 常见问题与排查技巧实录下面的内容基本都是在我搭建和长期运行过程中踩过的坑你大概率也会遇到一部分。5.1 摄像头连不上先查地址、网络、日志最典型的“连不上”表现是 Web UI 里点流名一直转圈或者直接黑屏。排查路径我建议按这个顺序看日志docker logs go2rtc关键字unauthorized、EOF、connection timed out会直接指出方向。unauthorized是账号密码或 RTSP 鉴权模式不对timed out是网络不通。确认 go2rtc 和摄像头网络是通的docker exec go2rtc ping 摄像头IP不通就检查 VLAN、防火墙、网段。用 VLC/ffprobe 直接拉原始 RTSP 地址如果 VLC 也拉不通说明是摄像头侧的问题和 go2rtc 无关VLC 能拉通而 go2rtc 不行重点检查 URL 编码和路径参数。检查摄像头是否限流有些摄像头默认限制并发连接数如果你同时开着厂商 App、VLC、go2rtc会出现“go2rtc 能发现设备但拉不出流”的怪象。把 App 退出再试通常能定位。另外如果摄像头开启了 RTSP 认证但 go2rtc 日志里反复报错可以试试把 URL 里的user:pass去掉然后在 go2rtc.yaml 里通过单独参数配置go2rtc 官方并不推荐这种方式还是建议直接放 URL。所以URL 编码还是最可靠的办法。我自己遇到最难忘的一次是海康摄像头的 RTSP 路径里多了一个空格VLC 容错性强照常播放go2rtc 解析严格就一直报失败。后来我把路径复制到十六进制编辑器里才看到空格编码成%20后秒通。这类问题不常见但遇到了就要往“细节”里查。5.2 延迟卡顿区分“转码”和“传输”瓶颈画面上出来但延迟大或者反复缓冲这是另一个高频问题。先把“延迟大”和“卡顿”分开来说。延迟大画面出来慢但连续如果是 HLS2-5 秒延迟是正常现象主要受切片和播放器 buffer 影响不是 bug。如果 WebRTC 也有明显延迟可能视频流经过了 FFmpeg 转码。尽量让 go2rtc 原生拉取 H.264 流浏览器解码压力小延迟就能拉低。卡顿画面反复转圈、掉帧先看 CPU 占用。H.265 摄像头在浏览器播放时go2rtc 调用 FFmpeg 转 H.264CPU 一高就卡。这时候两个优化方向一是让摄像头直接输出 H.264不要用自带转码硬扛二是给 go2rtc 容器挂载硬件加速设备比如 Intel 核显启用 h264_qsv/h264_vaapi。配置里可以给转码参数加video_codech264之类的但不同平台参数不一样建议先跑ffmpeg -encoders看一下容器里可用的编码器。如果是无线摄像头卡顿很可能出在 Wi-Fi 信号上。子码流延迟低但清晰度差主码流清晰但 Wi-Fi 扛不住。我的取舍是预览用子码流录像用主码流两边各走各的路稳定性好很多。5.3 多路并发与资源限制长期稳定运行的经验当你接了超过 4-5 路摄像头后资源管理就变得重要起来。首先我不建议在树莓派那种小机器上全跑主码流 WebRTC内存和带宽都顶不住。合理做法是接 flv 或 HLS 的客户端并发多时让 go2rtc 只对每路源维持一条 RTSP 连接转发给多个下游下游订阅多的话HLS 的分片会让 go2rtc 的内存涨得比较快。你在 compose 里可以加mem_limit至少给 256m如果有转码任务再往上加。日志里如果出现too many connections多半是 go2rtc 同时收到大量播放请求摄像头源瞬间并发超过设备上限。此时可以给视频源增加备用地址数组让 go2rtc 自动负载切换。而且我们要理解一件事go2rtc 不是硬转码服务器它更适合做“协议网关”。真要大规模转码或开公网直播后端还得靠更强的媒体服务go2rtc 负责把摄像头流统一收进来再往下分发已经足够家庭和小型工作室用了。最后再分享一个小技巧我个人最看重的 go2rtc 功能反而是它内置的 ONVIF 自动发现。在局域网里打开 Web UI点击添加设备它会扫描出所有支持 ONVIF 的摄像头填个账号密码就能自动生成 RTSP 地址不用再翻设备说明书。这个功能对新手特别友好。如果你手头有一些杂牌摄像头找不到标准 RTSP 路径先试 ONVIF 扫描往往比你猜路径节省大量时间。go2rtc 这个项目还在快速迭代很多细节版本之间会调整但核心思路很稳用一套配置管住所有摄像头用一个入口提供所有协议。先用一台摄像头跑通再加第二台、第三台你会发现这个容器越来越像你家里视频系统的“总闸”所有流都从它这里过后台不再是一堆乱糟糟的 ffmpeg 进程。希望这篇总结能帮你少走点弯路顺利把自己的监控流管起来。