资讯动态

基于go2rtc的Docker部署实践:统一接入多协议摄像头的低延迟流媒体方案

发布时间:2026/9/19 10:38:38 来源:尧图企业网站定制
做监控项目集成最头疼的是什么不同品牌的摄像头协议五花八门。海康走 RTSP萤石优先私有云小米摄像头第一次配置要扫码配对再加上项目里偶尔冒出来的 USB 摄像头、树莓派 CSI 模块想把它们统一接到一个平台里光适配就能耗掉好几天。go2rtc 就是为解决这个场景出现的一个用 Go 写的轻量级多协议流媒体服务器能把 RTSP、RTMP、HLS、WebRTC、MJPEG、SRT 这些主流流媒体协议串起来还可以把多路摄像头源聚合成统一地址在浏览器里低延迟播放。这几天我把工作室里的几台摄像头用 Docker 部署的 go2rtc 统一管理起来从写配置到出画面全程没走什么弯路这篇文章就把完整流程和细节整理出来。如果你正准备接入海康、萤石、大华这类 IP 摄像头或者想把手头的树莓派摄像头模块、USB 摄像头也变成网络可访问的流甚至想把多路流交给上层平台统一处理那这篇文章很适合你。1. go2rtc 本来就不是个新东西但真的好用1.1 摄像头流协议的乱象做摄像头接入第一个要面对的就是协议碎片化。海康威视的摄像头默认支持 RTSP但 RTSP 的路径各家都不一样有的在端口 554有的躲在高位端口得先用 ONVIF 探测才能拿到真正可用的媒体地址。萤石摄像头优先走私有云本地 RTSP 还要在手机 App 里手动开启很多人第一次接就被这一步卡住。小米、海雀这类家用摄像头很多型号本地连 RTSP 都不开放流只能从云端转想在自己服务器上拿到画面要么走官方开放平台接口要么就得想办法抓包复杂度直线上升。就算所有摄像头都支持 RTSP浏览器也不能直接播放 RTSP 协议。VLC 能播但你要是想做一个 Web 端的监控页面就卡在这一步了。常规做法是搭一个流媒体服务把 RTSP 或者裸视频源转成浏览器能播的 HLS 或者 WebRTC 流。过去很多人会自己拼一套方案Nginx-RTMP 拉流转推、FFmpeg 转 HLS、前端再用播放器去拉流配置繁琐不说稳定性还得看运气。go2rtc 就是把这个过程压缩到一个单文件、一条配置里。1.2 go2rtc 的核心能力和适用场景go2rtc 的核心能力可以归纳成三块第一多协议接入。它不只是个 RTSP 代理还能接 RTMP、HLS、WebRTC、MJPEG、SRT甚至可以读本地文件、HTTP 链接、UDP 推流也可以通过 exec 调起 ffmpeg 处理特殊源。第二多协议输出。同一个摄像头源go2rtc 可以同时输出 RTSP、HLS、WebRTC、MJPEG客户端按需选择协议不用为每种协议单独布一套服务。第三浏览器低延迟播放。它能在服务端做 WebRTC 信令桥接浏览器端通过 WebRTC 拉流实测在同一局域网内延迟能稳定在 500ms 以内体感上基本接近实时监控。适用场景很明确家庭和工作室的监控整合、树莓派或边缘设备上的本地视频流、要做 Web 端低延迟播放的项目以及想把各种摄像头源统一成标准流交给上层业务平台的中间层。go2rtc 很轻量一个二进制文件搞定跑在树莓派上毫无压力这也是我选它的重要原因。1.3 为什么不直接用 FFmpeg 或 Nginx-RTMP不少朋友会问FFmpeg 也能拉流转发Nginx-RTMP 也能做流媒体服务器为什么多引入一个 go2rtc首先是接入效率。用 FFmpeg 拉 RTSP 再推给 Nginx要写一长串命令行参数摄像头一多就要写脚本去批量管理进程每个进程的崩溃重连都得自己处理。go2rtc 把拉流、断线重连、协议转换、服务出口全部内置了配置里声明一条流就行断了它会自动重连不用写守护脚本。其次是协议转换的灵活性。go2rtc 的流是“逻辑流”概念你可以对同一条流挂多种输出。同一路摄像头Web 端用 WebRTC 看NVR 用 RTSP 拉手机端用 HLS 看不需要为每个出口单独起一套 FFmpeg 进程。再一个是资源占用。Go 写的服务内存占用很低。FFmpeg 每条流转码进程可能吃掉几百兆内存go2rtc 如果只做包转发不重编码开销小一个数量级。需要特别说明的是go2rtc 本身不重编码需要转码时会调用外部 ffmpeg但只在你明确需要转码的流上才拉起这个进程。所以说go2rtc 和 FFmpeg 不是对立关系它在需要转码时反而把 FFmpeg 当作一个可选的子进程来使用。这样的设计让工具链变得很轻也方便集中管理和扩展。2. Docker 部署 go2rtc三分钟先跑起来2.1 Docker 环境准备部署 go2rtc 最简单的方式是用 Docker省掉编译安装的麻烦也方便在 NAS、服务器、树莓派之间迁移。Docker 的安装步骤这里不展开只提醒几个容易踩的点。Linux 服务器上用发行版自带的包管理器或者官方脚本装 Docker 都行。装完记得把当前用户加进 docker 组否则每次执行 docker 命令都要加 sudosudo usermod -aG docker $USER newgrp dockerWindows 上用 Docker Desktop注意把 WSL 2 后端配置好。macOS 同样用 Docker Desktop。部署在树莓派上的话建议装 64 位系统然后直接 apt install docker.io 或者用官方脚本安装。树莓派的内存卡容量不用太大go2rtc 镜像本身很小。容器网络这里提醒一句go2rtc 容器需要连接局域网里的摄像头默认桥接网络就行只要宿主机能访问摄像头容器一般也能访问。如果摄像头在别的 VLAN 或者需要跨网段访问先测试一下宿主机到摄像头的连通性别一上来就怀疑 go2rtc 的问题。2.2 准备目录和配置文件先规划一个目录我习惯放在 /opt/go2rtc你们按自己的习惯来。这个目录里放两个文件docker-compose.yml 和 go2rtc.yaml。go2rtc.yaml 是核心配置先写一个最小的模板log: level: info api: listen: :1984 webrtc: listen: :8555 ice_servers: - urls: - stun:stun.l.google.com:19302这段配置的含义是日志级别 infoAPI 和 Web 界面监听 1984 端口WebRTC 使用 8555 端口做 UDP 传输并指定一个公共 STUN 服务器用于浏览器端网络穿透。STUN 服务器不是必须的但加上之后外网访问时的 WebRTC 连接成功率会明显更高。如果只是局域网内使用不加 ICE 配置也可以跑。2.3 用 Docker Compose 启动服务接下来写 docker-compose.yml。我建议直接使用 host 网络模式services: go2rtc: image: alexx2000/go2rtc:latest container_name: go2rtc restart: unless-stopped network_mode: host volumes: - ./go2rtc.yaml:/config/go2rtc.yaml command: -config /config/go2rtc.yaml有人不习惯 host 模式想用标准的端口映射也可以services: go2rtc: image: alexx2000/go2rtc:latest container_name: go2rtc restart: unless-stopped ports: - 1984:1984 - 8555:8555/udp volumes: - ./go2rtc.yaml:/config/go2rtc.yaml command: -config /config/go2rtc.yaml两种写法都能跑。区别在于host 模式下容器直接使用宿主机网络WebRTC 的 UDP 端口分配更简单不会出现容器内 NAT 导致的手握失败。在 NAS、树莓派这类单机场景下我更推荐 host 模式可以少踩很多 WebRTC 的坑。端口映射模式的好处是更干净适合多个容器共存、端口冲突明显的场景。启动服务cd /opt/go2rtc docker compose up -d docker compose logs -f日志里看到 api listening 之类的输出说明服务已经起来了。如果日志滚动太快直接按 CtrlC 退出日志查看容器会在后台继续运行。2.4 验证服务是否正常启动完成后浏览器访问 http://服务器IP:1984 能看到 go2rtc 的 Web 界面就说明服务起来了。界面上会有 API 信息、流列表当前还是空的以及内置的播放器页面。也可以直接调 API 验证curl http://127.0.0.1:1984/api/streams返回空列表或者空对象都正常因为还没有配置任何流。接下来就是本文的重点把摄像头加进去。3. 把摄像头都接进来多协议接入实操3.1 streams 配置语法速览go2rtc 的流配置都在 streams 节点下基本格式是streams: 流名称: 流地址流名称自定义用于在 Web 界面和 API 中定位这条流。流地址可以是一个协议 URL也可以是多个源的组合。go2rtc 支持把多个源指向同一个流名称它会自动做故障切换。如果第一个源连不上会尝试第二个这个特性在摄像头偶尔抖动时很有用。常见写法streams: cam1: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 cam2: rtsp://admin:password192.168.1.65:554/h264/ch1/main/av_stream如果一台摄像头有主码流、子码流可以分别定义成两条流也可以利用源顺序配置优先源第一条挂了自动切第二条streams: cam_main: - rtsp://admin:password192.168.1.66:554/Streaming/Channels/101 - rtsp://admin:password192.168.1.66:554/Streaming/Channels/102这种配置在做主备切换时非常实用实测摄像头主码流卡住时go2rtc 会自动切到子码流视频不至于完全黑屏。3.2 海康、萤石、大华 RTSP 接入示例以几种常见摄像头为例给出可以直接套用的 RTSP 地址格式。海康威视streams: hik_main: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 hik_sub: rtsp://admin:password192.168.1.64:554/Streaming/Channels/102101 是主码流102 是子码流。有些新固件路径可能不同先用 ONVIF Device Manager 扫描一下摄像头拿到实际的媒体地址更稳妥。萤石streams: ezviz: rtsp://admin:password192.168.1.65:554/h264/ch1/main/av_stream萤石部分新固件路径会有变化同样建议先扫描。大华streams: dahua: rtsp://admin:password192.168.1.66:554/cam/realmonitor?channel1subtype0subtype0 是主码流subtype1 是子码流。这里有个坑如果账号密码里含有 : / 这类特殊字符写在 URL 里会被解析错。我在项目里就碰到过密码带 的情况RTSP 地址被截断折腾了半天。最简单的解决办法是对特殊字符做 URL 编码把 写成 %40: 写成 %3A。编码之后的地址看起来没那么直观但至少不会莫名奇妙连不上。3.3 USB 摄像头和树莓派摄像头接入除了 IP 摄像头go2rtc 也能接入宿主机上的本地视频设备。USB 摄像头UVC接在宿主机上可以通过 ffmpeg 源暴露streams: usbcam: ffmpeg:video/dev/video0#videoh264#hardware这里的含义是调用 ffmpeg 读取 /dev/video0 设备输出 H264 编码的视频并尽量使用硬件编码。go2rtc 官方镜像里带了 ffmpeg实测树莓派上接 USB 摄像头直接就能出流。如果摄像头只输出 MJPEG需要转码成 H264可以省略 #hardware让 ffmpeg 用 CPU 软编码代价是 CPU 占用会高一些。树莓派 CSI 摄像头模块比如 ov5647接入方式类似streams: rpi_cam: ffmpeg:rpi#videoh264#hardwarego2rtc 针对树莓派有 rpi 源类型底层调用树莓派官方的 v4l2 或 raspivid 方案。使用硬件编码时 CPU 占用很低跑 720P 的流很稳定。需要注意的是这些特性依赖宿主机的设备映射。用 Docker 部署时要把视频设备挂载进容器services: go2rtc: ... devices: - /dev/video0:/dev/video0如果是树莓派 CSI 摄像头可能还需要挂载额外的设备节点和库目录这块比较进阶有需要可以单独研究驱动和权限问题。3.4 检查流的状态和画面配置完之后重新加载。go2rtc 支持通过 API 动态添加流不必重启容器curl -X POST http://127.0.0.1:1984/api/streams \ -H Content-Type: application/json \ -d {cam3: rtsp://...}不过最省事的还是改好 go2rtc.yaml 后重启容器docker compose restart go2rtc然后打开 http://服务器IP:1984 会看到所有配置的流。点击流名称可以直接在浏览器里播放。如果流是通的画面几秒内就会出现如果失败界面上一般会显示错误信息。也可以查 APIcurl http://127.0.0.1:1984/api/streams返回结果里每个流会有 source、producers、consumers 这些信息。简单判断如果 source 显示 error说明拉流地址有问题如果一直 connecting说明地址通但协议或者账号不对。我目前最常用的流程就是ONVIF 探测摄像头 RTSP 地址 - 填进 streams - 刷新 Web 页面看画面。熟练之后接一台新摄像头从探测到出画面基本在三五分钟内。4. 浏览器低延迟看监控WebRTC 播放与前端集成4.1 WebRTC 为什么能做到低延迟监控场景最关心的就是延迟。RTSP 直接播放延迟低但浏览器不支持HLS 兼容性好但切片式传输天生有几秒甚至十几秒的延迟RTMP 需要插件已经逐步被浏览器淘汰。WebRTC 走 UDP 传输浏览器原生支持延迟能压到几百毫秒。go2rtc 在浏览器和摄像头中间做了一个桥接。摄像头走 RTSP 进入 go2rtc浏览器通过 WebRTC 连接 go2rtc 拉流。go2rtc 拿到 RTSP 数据包之后尽可能不转码、不缓冲直接封装成 WebRTC 媒体包推给浏览器所以延迟控制得很好。这个架构里有个关键点是 UDP 端口。WebRTC 默认用 UDP 作为传输层go2rtc 的 8555 端口就是用来接收和发送媒体数据的。如果这个 UDP 端口不通浏览器会一直转圈表现是“连接中”。排查思路一般是先看宿主机防火墙有没有放行 8555/udp如果是 Docker 端口映射模式还要确认容器内能正常收到 UDP 包。这也是我推荐 host 网络模式的原因少一层 NATWebRTC 连接稳定很多。4.2 go2rtc 自带的 Web 界面go2rtc 自带一个简洁的 Web 界面打开 http://服务器IP:1984 就能看到。界面主要分两块左侧是配置好的流列表右侧是根据所选流生成的播放器。这个播放器默认走 WebRTC点击播放后几秒内出画面而且支持声音。实测同一局域网里从点击到画面出现大概 1 到 2 秒画面和声音基本同步延迟体感很低。如果只是自己看看监控这个界面完全够用不需要写任何前端代码。但如果你想把这些流嵌入到自己的业务系统里go2rtc 的页面可以直接用 iframe 嵌进去也可以走它的 API 单独做前端。4.3 通过 API 把流嵌入自己的页面go2rtc 暴露了不少 HTTP API常用的有GET /api/streams 获取所有流状态GET /api/stream/{streamName} 获取单条流详情GET /stream/{streamName}/hls 获取 HLS 播放地址GET /stream/{streamName}/mjpeg 获取 MJPEG 播放地址POST /api/webrtc 用于 WebRTC 信令交换前端要做 WebRTC 播放流程大概是构造一个 RTCPeerConnection拿到本地 SDP 后 POST 给 /api/webrtc服务端返回应答 SDP再 setRemoteDescription最后把媒体流挂到 video 标签上。这里贴一段简化的核心逻辑const pc new RTCPeerConnection({ iceServers: [{ urls: stun:stun.l.google.com:19302 }] }); pc.addTransceiver(video, { direction: recvonly }); pc.addTransceiver(audio, { direction: recvonly }); const offer await pc.createOffer(); await pc.setLocalDescription(offer); const resp await fetch(/api/webrtc?srccam1, { method: POST, body: offer.sdp }); const answer await resp.text(); await pc.setRemoteDescription({ type: answer, sdp: answer }); const video document.getElementById(player); pc.ontrack (event) { video.srcObject event.streams[0]; video.play(); };这只是一个最简示例实际项目中要注意信令地址、跨域、WebRTC 协商超时这些问题。如果不想自己写前端go2rtc 的 Web 界面也支持以 iframe 方式嵌入把 src 指向 go2rtc 页面即可iframe srchttp://server:1984/?srccam1autoplay1 stylewidth:100%;height:100%;border:0;/iframe常见参数还有 volume、muted、mic 等具体以版本文档为准。这个方案适合快速交付、不追求完全定制化 UI 的场景。5. 进阶玩法多端观看、录像和推流5.1 一路流多端复用避免摄像头被拉爆很多摄像头对外提供的 RTSP 并发路数有限民用级摄像头通常只支持一到两路并发。如果多个客户端同时直连摄像头拉流摄像头会把多余的连接踢掉表现就是画面断断续续甚至直接掉线。go2rtc 在这里的价值是“消费者复用”它只从摄像头拉一路流然后由它分发给所有下游客户端。也就是说无论是浏览器、手机、NVR 还是其它平台最终都从 go2rtc 取流摄像头只面对一个 RTSP 连接。这样做既能保护摄像头也大大降低了对摄像头性能的要求。我用一个场景举例工作室有一台海康摄像头以前我把 RTSP 地址同时给了 VLC、手机 App 和 NVR结果经常出现几秒钟断流。后来统一让 go2rtc 拉流三个端都从 go2rtc 拿流问题就消失了。5.2 配合 NVR / NAS 做长期录像go2rtc 自身不做 7x24 小时的录像存储和回放管理它更擅长做“出流端”。如果要做长时间录像建议把 go2rtc 输出的 RTSP 地址交给专业的 NVR 或者 NAS 上的录像软件。具体操作上在 go2rtc 配置里开启 RTSP 输入服务rtsp: listen: :8554重启容器后go2rtc 就会监听 8554 端口。对于某条名为 cam1 的流NVR 添加摄像头时地址填rtsp://go2rtc-ip:8554/cam1NVR 会把这条路当作一个普通网络摄像头去拉流。这样做的意义在于即使原始摄像头是 USB 设备、树莓派模块或者协议不标准的家用摄像头只要 go2rtc 能把它变成一路 RTSP 流NVR 就能把它当成标准摄像头来录像。像飞牛NAS、EasyNVR 这类平台本质上都是通过添加自定义 RTSP 源来接入摄像头go2rtc 正好充当了协议适配层。5.3 把 go2rtc 的流推到公网或直播平台如果你想在公网看家里的监控但又不想在路由器上做端口映射可以考虑让 go2rtc 把流“推出去”而不是让公网主动“拉进来”。go2rtc 可以通过 exec 源调用外部 ffmpeg 推流到 RTMP 直播平台配置示例大致如下streams: cam1: rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 cam1_push: - rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 - exec:ffmpeg -re -i {input} -c copy -f flv rtmp://your-rtmp-server/live/stream_key这种模式下推流连接是从 go2rtc 所在机器主动发起的不需要暴露 8555 端口到公网安全性和可维护性都更好。需要注意的是直播平台一般都会要求填写推流地址和串流密钥务必保密。串流密钥泄露意味着任何人都可以向你的直播间推流要小心保管。实际使用中我建议先把这路推流在本地验证一遍比如推到本地用 VLC 能拉的 RTMP 服务器再切到直播平台降低因为地址填写错误导致的排查成本。6. 实战中踩过的坑问题排查与调优6.1 摄像头频繁断流这是接入 RTSP 摄像头最常遇到的问题。表现是画面看一阵就断过一会又自动恢复日志里能看到 source 连接被重置。排查思路分三步。第一步确认摄像头的 RTSP 并发路数。很多民用摄像头只支持一路或两路并发如果同时让 go2rtc、录像机、手机 App 都在拉同一路流摄像头可能直接把其中一路踢掉。解决办法是只在 go2rtc 保留一路拉流其它端全部从 go2rtc 中转。第二步检查网络稳定性。WiFi 摄像头尤其容易出现 RTP 包丢失导致的断流可以先直接用 VLC 拉摄像头 RTSP 地址测试如果 VLC 也掉基本是摄像头或网络的问题不是 go2rtc 的责任。第三步go2rtc 的自动重连机制一般能自己恢复如果发现恢复不及时可以更新到最新版本旧版本的重连逻辑在某些场景下确实会慢一点。6.2 画面延迟高、卡顿延迟高的主要原因是协议链路太长。比如用 HLS 播放切片时长通常是 2 到 4 秒天然就有好几秒的延迟。想低延迟就优先用 WebRTC 或 RTSPHLS 只适合兼容性要求高但不太在意延迟的场景。卡顿则要分场景处理。多路同时观看时先看宿主机的 CPU 和内存流不需要转码时开销很小但如果有几路走了 ffmpeg 软转码CPU 就会吃紧。无线网络环境下稳定性优先于带宽建议摄像头优先接有线或者把视频码率调低。公网访问时上行带宽往往是瓶颈720P 的 H264 码率大约 2 到 4 Mbps上行只有 10 Mbps 的话同时看两路就可能卡。调优思路是让 go2rtc 只做包转发不要轻易引入转码多个客户端访问同一路流时go2rtc 会复用上游连接这个特性对摄像头并发压力非常友好。6.3 部分摄像头协议不兼容怎么办市面上的摄像头总有协议不标准的情况。要么 RTSP 路径不对要么干脆不提供 RTSP。遇到这种情况我的处理优先级是第一用 ONVIF 协议探测。很多摄像头虽然客户端里不显示 RTSP但底层支持 ONVIF 标准。用 ONVIF Device Manager 扫一下能看到媒体流地址很可能就是一个标准 RTSP 路径。第二用 go2rtc 的 ffmpeg 源兜底。如果摄像头只支持 RTMP 或 HLS 输出可以让 go2rtc 调用 ffmpeg 去拉streams: weird_cam: ffmpeg:rtmp://192.168.1.67/live/0#videocopy#audiocopy第三如果是海雀这类纯云端家用摄像头本地不开放 RTSP就需要先通过官方开放平台或抓包拿到流地址再接入 go2rtc这部分超出了 go2rtc 本身的能力范围。第四对于完全没有网络协议能力的裸摄像头比如普通 USB 摄像头、树莓派 CSI 模块用前面提到的 ffmpeg:v4l2 或 rpi 源解决把它们变成网络流。6.4 Docker 镜像拉取慢第一次部署时如果发现 docker pull 很慢或者一直卡住大概率是默认镜像源访问不稳定。国内通常的解决方式是把默认源换成云服务商提供的公共镜像源。操作方式是在 /etc/docker/daemon.json 里设置 registry-mirrors然后重启 Docker{ registry-mirrors: [https://your-mirror-source] }改完重启 Docker再重新拉取。不同云服务商的镜像源地址不一样选一个自己用起来稳定的就行。这只是一个镜像下载层面的调优改完实际上就能明显感受到拉取速度的提升。改之前记得备份原 daemon.json防止把 Docker 配置改坏。6.5 关于安全的几点提醒最后讲安全这部分容易忽略但非常重要。第一go2rtc 的 Web 界面默认没有认证。如果把它暴露到公网等于任何人访问 你的IP:1984 都能看到所有摄像头画面。建议至少用反向代理加一层认证比如 Nginx 的 auth_basic或者更严格的身份验证方案。第二摄像头 RTSP 的账号密码会明文写在 go2rtc.yaml 里注意配置文件权限不要让普通用户可读。我习惯把配置目录权限设定为 700chmod -R 700 /opt/go2rtc第三WebRTC 的 8555 端口是 UDP 端口如果不需要公网访问 WebRTC尽量把端口限定在内网用防火墙规则只允许可信网段访问。第四如果 go2rtc 实例暴露在公网建议定期看访问日志很多扫描工具会主动探测 1984 这类端口被扫到不奇怪重要的是别把完全开放的页面暴露出去。把这些基础安全措施都做了再谈把摄像头流接入业务系统心里才踏实。我用 Docker 部署 go2rtc 的完整流程基本就是这样。这工具最让我满意的地方就是它真的把一个曾经很碎的体系收拢成了一条简单的逻辑摄像头进来协议出去浏览器直接看。无论你是在树莓派上折腾 ov5647还是把海康、萤石接到飞牛NAS 做录像或者是想给自己的 Web 项目加一路低延迟监控画面go2rtc 几乎都能覆盖。最后分享一个小技巧当你新接入某台不熟悉的摄像头时先不要急着上 go2rtc先用 ONVIF Device Manager 扫一遍拿到准确的 RTSP 地址和编码格式再填进 streams。这一步能替你省下至少半小时的瞎试。遇到过太多项目里摄像头接不进来最后发现只是 RTSP 路径少写了一个斜杠。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价