
家里摄像头一多问题就来了海康的走RTSP、大华的走私有SDK、树莓派那边可能还在出MJPEG手机上看个实时画面还得装三四个App浏览器里根本没法直接打开RTSP流。我折腾过Mediamtx也试过ZLMediaKit最后固定下来用的是go2rtc——一个能把RTSP、RTMP、HLS、WebRTC、MJPEG这些协议全部打通的小工具配合Docker跑起来一台普通小主机就能把所有摄像头统一成一个流媒体出口浏览器直接看、Home Assistant直接接、手机端不用装额外播放器。这篇就完整记录我怎么用Docker部署go2rtc把手头各路摄像头接进来以及这几个月踩过的坑。1. 方案选型为什么是go2rtc而不是其他流媒体服务1.1 流媒体中间层的核心矛盾摄像头本身不会输出浏览器能直接播放的协议。RTSP是监控领域事实标准但浏览器不认手机浏览器也不认HLS延迟太高看监控画面会感觉像在看“延时摄影”MJPEG虽然万能但带宽浪费严重。这就需要一个中间层把摄像头原有的协议转换成浏览器、App、智能家居平台都能消费的格式。我最早用的Mediamtx前身是RTSP-Simple-Server功能纯粹但配置摄像头流时得手动给每路流指定协议和端口摄像头一多配置就冗长。ZLMediaKit功能强但部署和调优成本高对只想把摄像头“接进来就能看”的场景有点杀鸡用牛刀。go2rtc的定位很明确不重复造轮子而是做协议转换和流分发的中枢。它原生支持RTSP、RTMP、HLS、MJPEG、WebRTC、SIP还能直接拉取ONVIF设备甚至可以反向把流推给其他服务。最关键是它对WebRTC的支持做得非常顺滑——浏览器里看监控画面延迟能压到几百毫秒这才叫“实时监控”。1.2 go2rtc的差异化优势它的核心设计是“会话中按需拉流”。传统流媒体服务器会一直从摄像头拉流再分发占带宽也占摄像头连接数go2rtc可以配置成按需启动有人看的时候才去拉摄像头流没人看就断开对摄像头和网络的压力都小很多。实测下来家里3路摄像头全天挂着路由器后台看流量只有在打开网页看监控的时候才会出现持续带宽占用这点非常友好。另一个让它胜出的点是配置格式。go2rtc的配置直接用YAML定义流名称和源地址支持通配符、模板变量还能通过API动态添加流。比如你有一台NVR下面挂了几十路摄像头go2rtc可以直接对接NVR的RTSP端口也可以用[name]:[password][ip]的格式统一拼接地址不用每路都手写一行完整URL。1.3 选型时的对比清单方案部署复杂度浏览器播放支持延迟表现配置管理go2rtc低单容器/单二进制原生WebRTCHLSMSE极低百毫秒级YAMLAPI动态管理Mediamtx低需配合HLS或WebRTC相关配置中低配置较繁琐适合轻量ZLMediaKit高需配合前端播放器低接入复杂运维成本高传统NVR/平台取决于硬件基本靠专用App或插件中封闭生态扩展差如果你的需求是“我有一堆杂牌摄像头想在一个网页里全部实时看到还希望接入Home Assistant之类的智能家居中枢”go2rtc就是性价比最高的一条路。如果只是单纯要把一路RTSP转成HLS推送那Mediamtx就够了没必要上go2rtc。选型的本质是先分清自己的核心需求是“协议转换”还是“规模化管理”go2rtc两者兼备但它的优势场景明显偏向后者。2. Docker部署go2rtc从安装到Web界面2.1 安装前的准备几个必须要确认的点部署之前先确认三件事Docker环境是否正常、端口是否可用、以及摄像头是否支持RTSP或ONVIF。Docker环境不多说Linux上用包管理器装docker-ceWindows上装Docker Desktop需要确保能正常拉取镜像。端口方面go2rtc默认用1984端口同时提供Web管理界面和APIWebRTC还需要一组UDP端口用于媒体传输。这里有个容易踩的坑很多人只改了Web端口忘了WebRTC端口也需要映射到宿主机结果网页能看到画面列表但一点“播放”就卡住转圈。摄像头那边尽量先去摄像头管理后台把RTSP功能打开。海康、大华、TP-LINK这类主流牌子一般都有RTSP开关有的需要设置RTSP认证账号密码。如果你的摄像头不支持RTSP但支持ONVIFgo2rtc也能通过ONVIF自动发现设备并拉流。注意ONVIF只是“设备发现和管理协议”真正的视频流还是走RTSP所以设备至少要能提供RTSP流。2.2 docker run快速部署官方镜像名是alexxit/go2rtc带平台标签x86和ARM架构都能跑。我最开始是在一台x86小主机上跑的部署命令如下docker run -d \ --name go2rtc \ --restart unless-stopped \ -p 1984:1984 \ -p 50000-50010:50000-50010/udp \ -v /opt/go2rtc/go2rtc.yaml:/config/go2rtc.yaml \ alexxit/go2rtc:latest这里给几个参数解释-p 1984:1984Web界面和API端口浏览器访问http://宿主机IP:1984就是管理界面。-p 50000-50010:50000-50010/udpWebRTC媒体传输端口范围。这个范围不是固定的可以自己定但一定要和后面容器内配置保持一致。如果防火墙开着记得同时放行这段UDP端口。-v /opt/go2rtc/go2rtc.yaml:/config/go2rtc.yaml把配置文件挂载出来。挂载的好处是改配置不用进容器直接编辑宿主机文件然后重启容器即可生效。启动之后先浏览器打开http://192.168.1.100:1984看到go2rtc自带的“Streams”页面就说明部署成功了。页面上现在应该空空如也因为还没添加任何流。2.3 docker compose方式适合一次配置长期复用如果机器上同时跑了很多容器我更推荐用docker compose管理。下面是我实际在用的docker-compose.ymlversion: 3.8 services: go2rtc: image: alexxit/go2rtc:latest container_name: go2rtc restart: unless-stopped network_mode: host volumes: - ./go2rtc.yaml:/config/go2rtc.yaml注意我这儿用了network_mode: host也就是主机网络模式。这个改动很重要go2rtc需要动态创建WebRTC端口如果用bridge模式得把所有可能的UDP端口都映射出来容易漏host模式下容器直接共享宿主机网络端口不用映射WebRTC端口也能随意分配省心很多。代价是容器不再有独立网络栈端口占用直接打在宿主机上适合一台机器只跑一个实例的场景。如果你有多个容器需要做端口隔离或者跑在群晖这类NAS的Docker里还是老老实实用bridge模式加端口映射。2.4 配置文件格式go2rtc.yaml从零开始go2rtc的配置是一个YAML文件最核心的streams段定义所有摄像头流。先给个最简可用的示例log: level: info api: listen: :1984 webrtc: listen: :50000 streams: bedroom: - rtsp://admin:password192.168.1.20:554/Streaming/Channels/101 living_room: - rtsp://admin:password192.168.1.21:554/cam/realmonitor?channel1subtype0每个流名称底下跟一个或多个源地址多个源可以写成一串列表go2rtc会自动做故障切换或负载均衡。比如你有一台摄像头支持主码流和子码流两个地址可以配两个源让go2rtc在播放时根据客户端能力选择或者主码流断了自动切子码流。2.5 在Home Assistant里直接使用go2rtc如果你的智能家居中枢是Home Assistantgo2rtc可以无缝对接。在HA的配置里加入go2rtc的地址HA就能把go2rtc里的每一路流直接识别为摄像头实体然后你就能在HA的仪表盘里看到实时画面、录制、人脸识别都可以接。我在配置文件中加了一行homeassistant: url: http://192.168.1.100:8123 token: 你的长期访问令牌这让go2rtc主动连接HA功能上等价于把go2rtc当作HA的“摄像头网关”。如果你不想让go2rtc主动连HA也可以反过来在HA的configuration.yaml里用camera和stream平台指向go2rtc的API地址。3. 多协议摄像头接入实操从RTSP到树莓派3.1 RTSP直连海康/大华/TP-LINK的通用配置方法绝大多数IP摄像头都支持RTSP协议只是URL路径各家不一样。海康威视常见的RTSP地址格式是rtsp://用户名:密码IP:554/Streaming/Channels/101其中101表示第1通道的主码流102是第1通道的子码流。大华的RTSP地址一般是rtsp://用户名:密码IP:554/cam/realmonitor?channel1subtype0channel是通道号subtype0主码流subtype1子码流。TP-LINK的较新固件地址类似rtsp://用户名:密码IP:554/stream1。在go2rtc的streams里加流时建议不要直接写明文密码。YAML配置里可以用环境变量替换比如streams: front_door: - rtsp://${CAM_USER}:${CAM_PASS}192.168.1.20:554/Streaming/Channels/101然后在启动容器时通过-e CAM_USERadmin -e CAM_PASSxxx传入环境变量。这样配置文件即使被传到网上也不会泄露摄像头密码。接入之后在go2rtc网页的Streams页面点击流名称下的“play”按钮浏览器会通过WebRTC直接播放此时观察延迟走局域网的RTSP源一般延迟在300毫秒以内这是普通HLS方案很难做到的。3.2 ONVIF自动发现适合“不知道地址”的摄像头有些摄像头是二手收来的账号密码不知道或者压根不清楚厂商默认的RTSP路径。go2rtc支持ONVIF自动发现和拉流你只需要提供摄像头的IP和ONVIF账号密码go2rtc就能自动探测设备的媒体地址。配置方式很简单streams: new_cam: - onvif:admin:password192.168.1.30go2rtc会去设备支持的ONVIF服务里查找Media2或Media1接口拿到Profile信息后组装RTSP地址。这个功能我实际试过几次成功率很高不过前提是设备确实开启了ONVIF服务并且摄像头固件没那么魔改。如果你不知道ONVIF账号密码可以先去摄像头Web管理后台重置或者查出厂默认账号。大部分摄像头默认的ONVIF账号和Web登录账号是同一套个别设备需要单独开启“ONVIF用户”开关。3.3 树莓派上跑go2rtc把OV5647摄像头模块变成网络摄像头树莓派加OV5647摄像头模块是很多折腾玩家的标配组合但树莓派自带摄像头输出的是CSI接口信号不是RTSP流。想接入go2rtc得先在树莓派上跑一个RTSP推流服务把摄像头画面包装成RTSP协议。我的做法是用树莓派上的libcamera-vid把CSI摄像头数据流转成H.264然后通过mediamtx或者go2rtc自带的rtsp模块把流转成RTSP服务。实测下来树莓派4B上跑一条libcamera-vid命令就能稳定输出主流RTSPlibcamera-vid -t 0 --codec h264 --width 1280 --height 720 --framerate 15 --bitrate 1500000 -o - | ffmpeg -fflags nobuffer -f h264 -i - -c:v copy -f rtsp rtsp://localhost:8554/pi这里ffmpeg的作用是把stdin输入的H.264裸流封转成RTSP推给Mediamtx的8554端口。树莓派上跑Mediamtx也很轻实测CPU占用在20%左右不影响其他任务。然后在go2rtc的配置里添加streams: raspberry_pi: - rtsp://192.168.1.31:8554/pi这样就实现了“树莓派摄像头 - RTSP - go2rtc - 浏览器WebRTC播放”的全链路。这套方案比直接买一个网络摄像头便宜很多代价是要折腾一下系统服务和开机自启我用systemd服务把上面那条命令行封装成开机自动运行一直很稳定。3.4 USB摄像头和v4l2设备的接入思路普通USB摄像头或者像ESP32-S3摄像头这类自带UVC输出的设备常见做法也一样先借助v4l2抓帧封装成RTSP推给go2rtc。关键是要保证长时间稳定推流USB摄像头和树莓派官方摄像头不同有的会在低光照条件下自动调整帧率导致推流抖动。我自己用OBS加虚拟摄像头插件做过一个“电脑屏幕流”的场景开个OBS虚拟摄像头然后用OBS的RTSP推流功能推到go2rtc再把go2rtc接到Home Assistant。结果我发现电脑性能不好时推流会掉帧但go2rtc对掉帧容忍度很高播放端只是稍微卡一下不会整个断流。3.5 萤石/小米这类云摄像头如何接入很多消费级摄像头比如萤石、小米的云台版默认不支持RTSP它们优先走自己的云平台。想接入go2rtc第一件事是去摄像头的App里打开“局域网RTSP”或“开发者模式”。小米摄像头在米家App里打开“局域网RTSP”后会显示RTSP地址和账号密码地址一般是rtsp://用户名:密码IP:554/stream1。萤石则需要设备开启“RTSP认证”后使用SDK或者直接通过rtsp://用户名:密码IP:554/h264/ch1/main/av_stream之类的路径。麻烦的是这些消费级设备时不时会通过固件更新调整RTSP行为可能出现昨天能拉流今天断流的情况。我的经验是这类摄像头只适合做“非关键”监控比如看猫主子、看快递不适合做安防关键路径。关键路径上老老实实用支持标准RTSP/ONVIF的摄像头更靠谱。3.6 多路并发推流go2rtc接到NVR或录像机如果你已经有海康录像机NVR录像机的RTSP端口通常以554对外提供服务。go2rtc可以直接把NVR当作一个大的RTSP源来拉比如streams: nvr_camera1: - rtsp://admin:password192.168.1.240:554/Streaming/Channels/101 nvr_camera2: - rtsp://admin:password192.168.1.240:554/Streaming/Channels/201这里101、201分别是NVR第一、第二通道。有个细节NVR的码流路径不一定每个牌子都一样但大部分兼容海康的通用路径规则。如果拉不通去NVR的Web管理后台找到“网络服务 - 平台接入”或“RTSP配置”里的默认地址模板通常能照抄。如果你只是想统一用go2rtc做在线播放层录像仍然让NVR自己干也完全没问题。go2rtc不负责录像它只做转发和协议转换。录像持久化建议还是交给NVR或者另外挂一个Frigate之类的NVR软件。4. 常见问题与排查技巧实录4.1 网页能看列表但播放黑屏/转圈这是最普遍的坑。播放黑屏九成是WebRTC的UDP端口没有正确映射或防火墙没放行。我排查的固定套路是先确认容器日志里有没有webrtc: listen on :50000这类信息。在宿主机上测试UDP端口是否通nc -u -z 宿主机IP 50000-50010。不通就查防火墙。确认浏览器是否支持WebRTC。Chrome、Edge、Firefox都没问题Safari老版本可能对WebRTC支持不完整。如果用了Nginx之类的反代记得配置HTTP/2和WebSocket支持WebRTC的信令走HTTPS和WS不能用普通HTTP反代替代。4.2 摄像头连接“闪断”症状是画面能出但过一会儿就断然后自动重连。排查下来最常见的原因是摄像头端最大连接数限制。很多家用摄像头限制同时只能有2路或4路RTSP连接如果有NVR、手机App、go2rtc同时连接就会触发上限。解决思路也很直接给go2rtc配置子码流拉流而不是主码流。子码流码率低对连接数和带宽压力都小go2rtc负责把子码流转发给客户端主码流留给NVR或者录像用途。开启go2rtc的rtsp.transport设置。有的摄像头对TCP/UDP传输方式敏感这边配置成tcp可以降低半开连接导致的断流streams: main_cam: - rtsp_transport: tcp url: rtsp://admin:password192.168.1.20:554/Streaming/Channels/1014.3 浏览器里没有音频go2rtc会按需转码音频但有些浏览器对AAC、PCMU等编码支持有限。摄像头原始音频如果是G.711浏览器不一定直接支持go2rtc会自动做音频转码为AAC或Opus。如果发现“有画面没声音”先确认摄像头那边打开了音频开关——很多摄像头默认连音频采集都是关闭的。然后在go2rtc的流详情里查看音频媒体信息确认是否有音频轨。如果确认有但播放无声试试在流配置里强制指定音频编码streams: office_cam: - ffmpeg:rtsp://admin:password192.168.1.40:554/Streaming/Channels/101#audioaac这个ffmpeg:前缀是go2rtc内置的FFmpeg转码模块可以把源流的音频转成AAC再输出。注意转码会增加CPU负载但单路音频转码基本可以忽略。4.4 播放画面是“花屏”或者马赛克这个通常不是go2rtc的问题更多是摄像头码流传输过程中丢包。排查顺序是看摄像头到go2rtc之间是有线还是Wi-FiWi-Fi容易出现码流不稳定。在go2rtc日志里搜索lost packets或discard如果有把RTSP传输改成TCP。有些老摄像头主码流是MJPEG或者H.265浏览器不支持会花屏或黑屏。go2rtc支持转码但转H.265到H.264会消耗CPU建议直接用子码流通常是H.264。4.5 延迟越来越高go2rtc默认WebRTC播放延迟很低但如果中间走了HLS延迟就会高起来。HLS通常有几秒到十几秒的延迟这是切片机制决定的不是故障。如果想把延迟控制在1秒内优先用WebRTC播放如果客户端是iPhone的SafariSafari对WebRTC支持没那么完善可以改用MSE模式go2rtc会通过Media Source Extensions提供接近WebRTC的延迟体验。4.6 go2rtc容器日志刷屏都是“connection refused”日志高频出现connection refused基本就是配置里的RTSP地址不对、端口不通、或账号密码错了。拿配置里的URL单独在宿主机上用FFmpeg拉一下ffmpeg -i rtsp://xxx能通就说明源没问题不通就一个个排查IP、端口、路径、协议。4.7 内存泄漏还是普通缓存增长go2rtc连续跑一周内存占用有波动是正常的因为要缓冲媒体数据。但如果持续攀升不回落看下是不是有播放会话没断开。可以调用它的API接口查看活跃会话数curl http://localhost:1984/api/streams返回的JSON里有clients字段如果长时间不为0且有异常IP检查是不是有人或某个自动化脚本一直挂着一个没释放的连接。正常使用下go2rtc的常驻内存一般稳定在100~200MB左右按需拉流模式下空闲时内存会降下来。5. 进阶玩法go2rtc还能干什么5.1 把go2rtc变成“推流分发枢纽”除了从摄像头拉流go2rtc也能直接接流再转推给其他地方。比如把一路RTSP同时推给HLS切片器、WebRTC客户以及另一个NVR。这在做直播或者跨平台分发时很好用。YAML里的写法大致是streams: mixed: - rtsp://192.168.1.20:554/Streaming/Channels/101 - ffmpeg:rtsp://192.168.1.40:554/Streaming/Channels/201#videocopy#audiocopy两路源放在同一个流名下go2rtc会把它们合并成一个聚合流输出。这种“混流”一般需要借助FFmpeg具体编码参数按需求调。5.2 接入动态发现自动添加海量摄像头go2rtc支持从外部API动态管理流你可以用脚本扫描局域网的摄像头然后调用/api/streams接口把发现到的新设备自动加入配置。爬虫式扫描可以用Nmap先找出开放554端口的设备再逐个用ONVIF探测把拿到的媒体地址POST到go2rtc的API。这样管理几十路摄像头的场景就轻松很多不用每加一台摄像头都去手改YAML然后重启容器。5.3 配合Home Assistant做“有人检测”如果想让摄像头流具备简单的人形检测能力可以搭配Frigate使用。Frigate负责从go2rtc拉取RTSP进行AI识别识别结果回传给Home Assistant触发自动化。go2rtc在这条链路里做的事情只是“提供一个统一的、稳定的RTSP源”但正是这个统一的源让多路摄像头接入Frigate时不用每个都单独配地址和密码管理成本大幅下降。5.4 简易录像方案go2rtc本身不负责录像但它可以把任意一路流转推到支持录像的服务。组合组常见的是go2rtc转推RTSP给MediamtxMediamtx按天切片存储文件或者直接用go2rtc的FFmpeg模块定时落盘。这里我更推荐让“录像”这种事交给专门的NVR软件做go2rtc专注做它擅长的“连接”和“转化”。架构上职责分离后续排查问题也更有头绪。6. 实操体验总结与经验建议6.1 一些部署上的“卫生习惯”我在多次部署和重装中总结出几个能避免很多破事的小习惯挂载配置时先用一个临时配置启动容器确认能正常读取再替换成正式配置避免一次写错整个配置导致容器启动失败。配置文件里不要写明文密码用环境变量注入至少能在配置泄漏时少一层直接伤害。给Web界面加个访问控制最简单的是在前面挂一层带基础认证的反代。go2rtc本身就设计成内网服务裸奔在公网上风险太大容易被扫描器拿去当跳板。升级go2rtc之前先备份YAML和容器配置虽然新版一般向后兼容但个别大版本对配置的字段有过调整。6.2 我最满意的一个使用场景我现在家里有三路摄像头海康的门口机、大华的庭院机、树莓派做的阳台摄像头。它们三个的观看方式在以前是三个App现在全部接到了go2rtc浏览器打开一个页面就能同时看到三路实时画面。另外我用Home Assistant做了个简单联动——树莓派摄像头检测到有人移动自动把对应画面推送到手机通知。这套方案最大的价值不是某个单独的功能而是把所有东西统一到了一起。摄像头厂商的App一更新就可能改变交互逻辑但go2rtc只关心你出不出RTSP流只要底层协议不变上层怎么折腾都不影响我的统一入口。以后如果再添摄像头我的第一反应也不再是“这牌子能不能用”而是看它支不支持RTSP或ONVIF。只要支持go2rtc就能接不支持我就用树莓派加个USB摄像头自己造一个。这种“不被厂商绑定”的自由度才是这套方案让我最上瘾的地方。