Docker部署go2rtc:统一RTSP/WebRTC/HLS多协议流媒体网关指南
1. 为什么先把 go2rtc 拉进 Docker多协议拆解1.1 RTSP、RTMP、HLS、WebRTC、MJPEG 到底各管什么如果你和我一样同时接触过安防摄像头、直播推流、Web 播放器这三类东西会很快意识到一个尴尬摄像头最擅长的是 RTSP但浏览器不直接支持 RTSP直播平台习惯收 RTMP 推流但监控头又不会自己推 RTMP手机浏览器里 HLS 能放延迟却又高得离谱至于 MJPEG虽然老设备喜欢用但码率爆炸、画质一般。于是你不得不在各个协议之间来回搭桥。go2rtc 解决的就是这件事它把这些协议的“翻译”工作集中到一个轻量进程里。它本身不是转码工具而是一个多协议流媒体网关——往里看可以接 RTSP 摄像头、RTMP 直播流、HTTP-FLV、HLS、MJPEG、WebRTC 源往外看又能以任意一种你想要的协议把同一路视频分发出去。这一点非常关键去看 go2rtc 的文档时会发现它的核心概念只有一个stream。一个 stream 就是一路视频源你给它取个名字比如front_door之后所有客户端都通过这个名字来拿流至于拿的时候想要 RTSP 还是 HLS 还是 WebRTC由 go2rtc 处理。我在部署之前最担心的是“协议转换会不会要 CPU 重编码”实际跑起来才发现大部分情况它只做 remux转封装也就是把 H.264/H.265 的裸流从 RTSP 容器里拆出来再装进 WebRTC 或 HLS 的容器里视频编码不做二次压缩。摄像头是什么编码输出就是什么编码所以 CPU 占用低得不像一个实时转码服务。只有当浏览器不支持原始编码比如某些安卓 WebView 不认 H.265时才需要走 ffmpeg 转码这个你可以选择在 go2rtc 里让它自动调用也可以自己单独开一个 ffmpeg 进程处理灵活度很高。1.2 和 ffmpeg / mediamtx / 成品NVR 有什么本质区别网上讲流媒体服务器经常会被几个名字绕晕ffmpeg、mediamtx原 rtsp-simple-server、gstreamer、ZoneMinder、Frigate、go2rtc。我刚开始也以为它们都是同类其实分工差别很大。ffmpeg 是“瑞士军刀”几乎什么都能做但它是命令行工具本身不常驻监听端口也缺少给客户端动态分配流的能力。你用它做流媒体服务需要自己写一大堆进程管理、流状态判断、端口分配逻辑复杂度一下子上去了。mediamtx 更接近“纯 RTSP/RTMP 服务器”它能收流、能分发但有协议覆盖没有 go2rtc 广尤其在 WebRTC 和 HLS 上需要配合其他组件才能做到低延迟浏览器播放。NVR比如成品录像机、Synology Surveillance Station解决的是录像、回放、告警、用户权限管理它更重也更偏存储而不是灵活输出。你要把摄像头流接给 Home Assistant 或自研 Web 页面NVR 往往给不出一个干净的rtsp://地址让你随便消费。go2rtc 的定位正好卡在中间不抢 NVR 的录像饭碗不做重转码但它把“摄取多路源”和“多协议分发”这件事做到极简。举个例子我有一只摄像头同时要被 Home Assistant 抓实时流、被一个老旧的 NVR 拉存储流、被家里人用手机浏览器看放在以前得分别配置三套路径在 go2rtc 里只需要配置一个流名称三种消费端自己去选协议。这个“一份源、多协议分发”的模型是我最终留下来的主要原因。1.3 go2rtc 不适合干的事我也要泼一点冷水它不是万能的。第一它默认不带录像回放 UI你可以拿它配合 ffmpeg 落地录像文件但没有漂亮的时间轴回放界面第二它没有用户权限体系登录、多用户、摄像头权限隔离这些都得靠外部的反向代理或认证网关来做第三如果需要广电级大规模分发、多节点调度它同样不是这个方向。所以比较恰当的使用姿势是go2rtc 做流媒体接入与协议汇聚的那一层录像、告警、权限交给其他更专业的系统。把这个边界想清楚后面才不会出现“装了 go2rtc 之后发现少了一堆功能”的落差感。2. 部署前的规划网络模式、端口和摄像头选型一起说2.1 摄像头侧最常见的前置条件我踩过最大的坑其实发生在部署 go2rtc 之前有一台家用摄像头压根不提供 RTSP 地址只给手机 App 看问客服也只丢过来一个“云平台专用”的说法。这种设备无论用什么流媒体服务器都接不进去所以选摄像头时第一条原则就是确认支持 RTSP/ONVIF 标准协议。海康、大华、TP-Link、宇视这些安防品牌的主流型号基本都支持但一些纯互联网品牌的低成本云台摄像头很可能不支持主动拉流。另一件容易被忽略的事是摄像头是否需要先“激活”。新买的海康/大华设备第一次通电后必须用官方工具或浏览器激活并设置密码否则 RTSP 地址就算写对了也会返回 401 或连接被拒绝。很多刚接触的人卡在“扫到设备但拉不到流”其实不是 go2rtc 的问题而是摄像头还没有完成初始化。先把摄像头通过官方 App 或浏览器后台激活再进到“网络-高级设置”里打开 RTSP 服务接下来才有得谈。2.2 端口规划与 network_mode: host 的原因go2rtc 默认监听这几个端口端口协议用途1984/TCPHTTPWeb 管理界面、API、HLS、MJPEG 输出8554/TCPRTSPRTSP 拉流端口客户端用rtsp://IP:8554/流名称访问8555/UDPWebRTCWebRTC 信令与媒体传输的默认入口我在 Docker 里给 go2rtc 用的网络模式是network_mode: host而不是默认的 bridge。原因很直接WebRTC 要同时走 UDP 和 TCP端口协商比较活跃如果用桥接网络再手动映射端口遇到 ICE 候选地址取不到正确 IP 的情况会非常痛苦。host 模式下容器直接复用宿主机网络栈go2rtc 能拿到真正的局域网 IPWebRTC 通话建立的稳定性高很多。代价就是端口隔离没了宿主机上其他服务要避开这三个端口尤其是 8555 这种 UDP 端口冲突时不太容易察觉。如果是在 Windows 上用 Docker Desktophost 网络的支持有限那就只能老老实实用端口映射把1984/TCP、8554/TCP、8555/UDP映射出去同时确认 Windows 防火墙放行对应端口。局域网内自用没问题但不要指望 WebRTC 在所有复杂网络环境里都一次成功。2.3 资源你想多了其实省得很我用一台只有 2 核 CPU、2GB 内存的老迷你主机跑了 8 路 1080p 摄像头全部走 H.264 直通、不做转码的情况下内存占用长期在 200MB 上下CPU 占用在 5% 到 15% 之间波动主要消耗在 WebRTC 打包和 HTTP 分发。真正吃 CPU 的是 ffmpeg 转码尤其是 H.265 转 H.264那个开销能瞬间吃满好几个核。所以如果预算有限路线很明确优先保证摄像头输出 H.264go2rtc 只做 remux资源问题基本不存在。3. Docker Compose 部署 go2rtc 的完整过程3.1 目录结构与 compose 文件我的宿主机上专门建了一个目录/opt/go2rtc所有文件都放在里面方便后续备份配置和查看日志。目录结构很简单mkdir -p /opt/go2rtc cd /opt/go2rtc touch go2rtc.yamlgo2rtc.yaml是它的核心配置文件。初次部署时不需要写太多先留一个最小配置让它跑起来确认服务正常后再慢慢加摄像头流。最小配置大概长这样log: level: info api: listen: :1984 rtsp: listen: :8554 webrtc: listen: :8555如果你用的是 Docker Composedocker-compose.yml可以这样写services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped network_mode: host volumes: - ./go2rtc.yaml:/config/go2rtc.yaml注意这里的挂载路径是/config/go2rtc.yaml不是随意指定的go2rtc 镜像默认从/config目录读取配置。漏掉这个路径启动后你会发现自己写的配置完全没生效。另外官方镜像名是alexxit/go2rtc在 Docker Hub 上可以直接拉取如果网络拉镜像慢设置一下 Docker 的镜像加速器会省心很多。3.2 启动与验证一切都准备好后执行docker compose up -d docker compose logs -f go2rtc日志里没有报错后先验证 HTTP 服务是否正常curl http://localhost:1984/api/streams刚启动时没有配置任何流这条命令会返回一个空数组或空的 JSON 对象那就说明服务已经在跑了。接着打开浏览器访问http://宿主机IP:1984会看到 go2rtc 自带的 Web UI。这个界面虽然朴素但非常实用既能实时预览流也能直接在里面测试添加摄像头。到这一步已经完成了 80%剩下的时间基本都花在配摄像头上。如果你是第一次跑建议先别急着加很多路流就加一路最熟悉的摄像头把“从 Web UI 点击预览”这条链路打通再批量接入剩余设备。3.3 Windows/macOS 用 Docker Desktop 时的桥接替代如果你没有 Linux 服务器只能用 Docker Desktophost 网络方案在 Windows 上并不好用。这时候改为端口映射模式services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped ports: - 1984:1984 - 8554:8554 - 8555:8555/udp volumes: - ./go2rtc.yaml:/config/go2rtc.yaml这种模式下Web UI 访问http://localhost:1984没问题RTSP 播放一般也能通WebRTC 预览则可能因为 NAT 和防火墙问题出现“一直连接中”。我的建议是Windows 上如果只是临时调试用 bridge 模式足够要长期当家庭流媒体中心用还是弄一台 Linux 小主机或者直接用 WSL2 里的 Docker 更省事。4. 接入摄像头的正确姿势从已知 URL 到可用流4.1 主流摄像头 RTSP 地址规则写 go2rtc 配置之前最关键的是搞到摄像头的真实 RTSP 地址。这个地址不是随便猜的不同品牌有固定的 URL 风格。海康威视系设备常见格式rtsp://用户名:密码摄像头IP:554/Streaming/Channels/101其中101表示第一通道的主码流102是第一通道的子码流。如果摄像头是多通道的依次是201、202这样。萤石云设备如果已经开放了 RTSP也沿用类似的路径。大华系设备常见格式rtsp://用户名:密码摄像头IP:554/cam/realmonitor?channel1subtype0这里的channel1是通道号subtype0表示主码流subtype1表示子码流。子码流通常只有 720p 甚至更低码率也小适合多人预览和网页低延迟播放主码流则留给录像或需要在电视上全屏看高清的场景。还有一部分摄像头支持 ONVIF 标准但 ONVIF 自己并不定义绝对的 RTSP 路径它只告诉你的客户端“RTSP URL 在哪个地址”。用 ONVIF Device Tool 或 VLC 的“打开网络串流”功能输入设备的 IP 和账号密码有时候能直接探测到。但要注意扫到 URL 不代表能一直稳定用很多固件会把 RTSP URL 动态调整所以我还是建议进入摄像头后台的“媒体设置”页面里查看官方给出的地址示例那个最靠谱。4.2 把多路摄像头写进 streams 配置有了地址后在go2rtc.yaml里的streams段中逐条写入即可streams: gate: - rtsp://admin:密码192.168.1.64:554/Streaming/Channels/102 yard: - rtsp://admin:密码192.168.1.65:554/cam/realmonitor?channel1subtype1 workshop: - rtsp://admin:密码192.168.1.66:554/Streaming/Channels/102这里我给gate、yard、workshop分别起了容易记的名字后面所有客户端都按名字取流不用再记一大串带账号密码的 RTSP URL。注意我特意用了子码流海康102大华subtype1来做日常预览因为这几路只是需要“能看”子码流已经足够还能大幅节省带宽和并发压力。改完配置后重启容器让配置生效docker compose restart go2rtc然后回到 Web UI你应该能看到这三条流出现在列表里点击任意一条就能实时播放。如果播放失败先别急着改配置用后面的 ffprobe 方法验证一下原始地址究竟通不通。4.3 临时摸流Web UI 和 ffprobe 配合go2rtc 的 Web UI 上有一个输入框可以直接临时添加流不需要编辑配置。这个功能对调试特别友好你可以先在 UI 上写一条test: rtsp://...点击连接看看能不能出画面。但临时加的流不会保存到配置文件里重启后会消失确认没问题后还是要写进go2rtc.yaml。如果想从命令行侧确认摄像头地址是否正确用 ffprobe 是最快的ffprobe -rtsp_transport tcp rtsp://admin:密码192.168.1.64:554/Streaming/Channels/102如果能够打印出视频流信息说明地址和账号密码都没问题。如果卡住不动或报错大概率是地址格式错了、账号密码不对、摄像头没有启用 RTSP或者摄像头和 go2rtc 之间跨了网段导致 RTSP 握手失败。我遇到过一台摄像头在 VLAN 的另一个网段里VLC 能看go2rtc 却连不上最后发现是交换机上限制了该网段的 RTP 媒体端口放行后立刻就好了。像这种跨网段问题最好先临时把 go2rtc 网段和摄像头网段打通或者通过交换机出口放行 UDP/TCP 端口不要指望纯软件层能解决。5. 多协议分发实战一部流给所有端5.1 浏览器低延迟预览靠 WebRTCgo2rtc 最惊艳我的部分是它内置的 WebRTC 能力。以前要在 Web 页面上看 RTSP 监控常见方案是后端用 ffmpeg 转 HLS延迟随随便便两三秒而且每增加一个观看者就要多开一路转码进程。go2rtc 的浏览器预览走的是 WebRTC协商好后可以直接点对点传媒体流延迟通常在几百毫秒左右而且不需要额外装插件。在 Web UI 里点击某个流名称go2rtc 会尝试用 WebRTC 建立连接。如果几秒内出画面说明端口和网络都正常。如果一直转圈优先排查 8555/UDP 端口是否被防火墙拦截、宿主机的网络是否为 NAT 多层环境。WebRTC 对 NAT 穿透的要求比较苛刻在公司网络或手机热点环境下偶尔会因为 UDP 被限制而失败这时可以退回 HLS 模式看只是延迟会变高。5.2 统一输出给 ffplay、VLC、HLS、MJPEG摄像头接入 go2rtc 后一个流名称就可以对应多种播放方式。比如我在客厅的电视盒子上用 VLC 看门口摄像头VLC 里填的就是rtsp://go2rtc主机IP:8554/gate如果是在公司电脑上临时网页查看直接访问http://go2rtc主机IP:1984/gate.m3u8go2rtc 会根据路径后缀自动把流转成对应协议。gate.m3u8就是 HLS 播放列表gate.mjpeg就输出 MJPEG 流一些老的嵌入式设备或低版本浏览器可以通过 MJPEG 方式拿到画面。命令行下想快速验证流是否正常可以ffplay -rtsp_transport tcp rtsp://127.0.0.1:8554/gate这种“同一个流名称后缀决定协议”的设计几乎不需要学习成本对集成开发特别友好。我在给自己的小面板写监控页面时只需要把http://go2rtc主机IP:1984/gate.mjpeg塞进img标签页面立刻就有画面了。5.3 一份输入多份输出推到 RTMP/其他系统go2rtc 的 streams 配置支持把同一个源同时输出到多个目标。写法是把源地址和输出地址写成一个数组streams: gate: - rtsp://admin:密码192.168.1.64:554/Streaming/Channels/102 - rtmp://127.0.0.1:1935/live/gate这样 go2rtc 会先拉取摄像头 RTSP再以 RTMP 客户端身份推给本地另一个流媒体服务或直播平台。以前要做这种“一边本地看、一边推直播平台”的操作得自己写一个 ffmpeg 循环脚本还要处理断流重连go2rtc 这里把输入和输出当成同一个流断流后会自动重连省了很多事。如果你有多个下游系统继续往数组里加即可。不过要提醒一点每个输出目标都会增加一路会话虽然 go2rtc 不转码但网络带宽和连接数还是会线性增加。我的经验是下游在局域网内就优先用 RTSP 或 WebRTC跨公网就用 RTMP 或 HLS不要所有目标全都用同一种协议。5.4 想要录像文件go2rtc 不做 NVR 但能配合go2rtc 自身不带录像管理界面但它输出的 RTSP 流非常适合被外部工具消费。我自己用了一个很轻量的方案写一个定时 ffmpeg 命令从 go2rtc 拉一路 RTSP按小时切片保存成 MP4。ffmpeg -rtsp_transport tcp -i rtsp://127.0.0.1:8554/gate -c copy \ -f segment -segment_time 3600 \ /video/gate_%Y%m%d_%H%M%S.mp4这段命令的关键是-c copy不转码只做封装切片CPU 占用几乎为零。只要磁盘够大就能按小时生成一段录像。配合 cron 定期清理过期文件一个轻量录像系统就搭起来了。回放体验虽然不如专业 NVR但对家庭用、只求留痕来说是性价比极高的方案。如果家里有 Frigate 这类 AI 检测系统也可以让它直接连 go2rtc 的rtsp://127.0.0.1:8554/流名称这样前端预览统一走 go2rtc 的 WebRTC后端检测分析走 Frigate两不耽误。6. 跑了一段时间后踩到的坑能少走就少走6.1 花屏/延迟高先怀疑传输而不是转码我刚开始接入某台 TP-Link 摄像头时Web UI 预览花屏严重画面还经常卡住。第一反应是 go2rtc 转码不行后来用 ffprobe 直接连原始 RTSP 地址也同样花屏才意识到是摄像头和主机之间的网络传输问题。这种花屏大概率是 RTSP 默认使用 UDP 传输跨交换机或 WiFi 环境下丢包导致的。解决方法有几个方向一是到摄像头后台把 RTSP 传输方式改成 TCP很多摄像头默认是 UDP二是降低码率主码流如果设成 8Mbps 以上普通 WiFi 的稳定性很难扛住三是尽量用有线连接摄像头和 go2rtc 主机。go2rtc 这边的配置也可以加参数强制走 TCP但不同品牌对参数的支持不太一样最稳妥还是在摄像头后台直接设置。还有一个小技巧同一路摄像头日常预览用子码流需要截图或录像时才切主码流这样花屏概率会小很多。我用海康的102子码流和主码流做过对比子码流在弱网下的表现明显稳定画面清晰度也足够手机观看。6.2 并发连接多了之后 UDP 端口不够用go2rtc 的 WebRTC 默认监听 8555/udp但实际的媒体流离不开 RTP 端口分配。当多个客户端同时通过 WebRTC 看不同摄像头或者一个页面开了多个画面时偶尔会出现“连接建立成功但画面一直黑”的情况看日志会发现是 UDP 端口资源不足。这个问题在 host 网络模式下相对少见但如果你用的是 bridge 映射模式Docker 的端口隔离会加剧这个现象。我的处理方式是如果 WebRTC 并发要求高优先回到 host 网络模式在宿主机防火墙里放行 8555/udp以及可能需要的高位 UDP 端口段。因为 go2rtc 在不同版本里对 WebRTC 端口的使用策略有调整最直接的办法是启动后查看日志里有没有 “Failed to listen” 之类的报错根据日志提示去放行对应端口比照着旧文档猜省力得多。6.3 私有协议摄像头“扫到地址也拉不动”的真实原因有些摄像头虽然可以被 ONVIF 工具扫到甚至显示出一个 RTSP 地址但填到 go2rtc 里就是连不上。我遇到过两种典型情况一种是通过 ONVIF 扫出来的地址里带的是 ONVIF 服务端口而非 RTSP 媒体端口需要手动改成554另一种是摄像头固件限制只有特定来源 IP 可以发起 RTSP 拉流比如只在“可信 IP”里加白名单go2rtc 所在主机的 IP 不在名单里就会被静默拒绝。这类问题排查起来很费时间我的建议是直接进摄像头后台找“RTSP 认证”或“访问策略”相关选项把 go2rtc 主机 IP 加进允许列表。另外如果你手头有“只支持主动推流的云摄像头”也就是设备只会定时往厂商云平台推流不支持外部主动拉 RTSP那么任何流媒体服务器都拉不到它。这种情况只能放弃接入或者换支持 RTSP 推送的设备。不要被“扫到端口”这个表象迷惑连接不上就是连接不上别再耗时间。6.4 默认零认证千万别把 1984 直接暴露到公网go2rtc 默认没有任何认证机制这是它保持轻量的一部分原因但也意味着只要你的 1984 端口暴露在公网任何能访问到的人都可以看到全部摄像头的画面甚至可以往里面添加自己的流的地址完全失控。这个安全隐患比“录像被偷看”更严重因为攻击者可以利用它做跳板。我的安全原则很明确go2rtc 的端口只允许局域网或受信任的内网网段访问公网防火墙直接丢弃外部到 1984 的请求如果确实需要远程观看前面要套一个带身份认证的反向代理用 Basic Auth 或更复杂的登录认证都行不要在 go2rtc 本身上面裸奔不要为了方便直接做端口转发修改 go2rtc 默认端口也改变不了什么攻破扫描器对常见端口的探测易如反掌真正要解决的问题是“谁能访问它”而不是“它在哪个端口”。最后分享一个我的使用习惯我会把所有摄像头的子码流都以流名称_sub的形式接入浏览器和 Home Assistant 预览统一用子码流只有需要回看细节时才切到主码流。这样既保证了多端并发的流畅性又不会因为带宽压力影响整个网络。go2rtc 这套东西不复杂但它把“多协议流媒体平台”的门槛降低了非常多如果你手上正好有一堆规格参差不齐的摄像头很值得花一个晚上部署起来。