ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Docker部署go2rtc:打通多品牌摄像头的多协议流媒体网关

用Docker部署go2rtc:打通多品牌摄像头的多协议流媒体网关 如果你正在为家里一堆摄像头想办法——海康走RTSP大华的走ONVIF还有USB摄像头只能出MJPEG浏览器打开却要一个个试插件——那么把go2rtc装进Docker等于在一台机器上给所有摄像头修通了一条多协议流媒体转发的立交桥。go2rtc是一个极轻量的流媒体网关它能接入不同品牌、不同协议、不同格式的摄像头再统一输出成WebRTC、HLS、MSE、RTSP、RTMP等协议让电脑、手机、电视、NVR各取所需。这篇文章适合三类人一是家里有七八个杂牌摄像头想统一管理又不想买昂贵的NVR二是正在玩Home Assistant、Frigate这类智能家居项目需要低延迟视频流三是做弱电或安防调试经常被设备协议不统一折磨需要一个能快速验证现场的中间层。当然如果你只是想把树莓派上的CSI/USB摄像头变成局域网内一个标准RTSP流go2rtc同样合适。我先把结论放在前面go2rtc单二进制文件通过Docker跑起来之后常驻内存占用通常只有几十MB却能同时管理上百路流配合Docker的restart: unless-stopped它可以像服务一样稳定运行。下面我会从方案选型、核心配置、完整部署、问题排查到扩展玩法把整个实操过程一次讲透。1. 核心思路与部署方案选型1.1 多摄像头多协议到底乱在哪先说一个真实的场景。我手里有一台海康网络摄像机RTSP地址是rtsp://admin:密码192.168.1.108:554/Streaming/Channels/101另一台大华摄像机RTSP路径却是/cam/realmonitor?channel1subtype0还有一块树莓派上的OV5647摄像头通过CSI口连的出来的流还需要先经过libcamera-vid处理再加上一个USB免驱摄像头插上主机以后只有/dev/video0设备节点直接给MJPEG。这四样设备协议、端口、路径、编码格式全都不一样。想把它们统一接到一个浏览器页面里早期我只能给每台设备装对应的ActiveX插件或者客户端软件后来用FFmpeg逐路拉流转发HLSCPU直接拉满。真正的问题不是单路流拉不出来而是“多路多协议多终端”同时满足时靠手工拼方案很容易变成灾难。go2rtc解决的就是这个上游接入、下游输出的中间层问题。它把各种来源的流统一抽象成“命名流”你给它一个名字它负责从摄像头拉流、缓存、转协议你只需要在客户端按名字取流。这样做的好处是摄像头厂商、协议差异、编码格式都被隔离在配置层上层应用不再关心每家设备的细节。1.2 为什么是go2rtc而不是FFmpegNginx拼方案我看很多人一说到流媒体转发第一反应就是FFmpeg推流到Nginx-RTMP模块再走HLS切片。这个方案不是不行但维护成本高FFmpeg命令需要为每路摄像头单独维护进程Nginx要配置RTMP和HLS模块切片文件还要定时清理。一旦摄像头断流FFmpeg进程退出恢复机制全靠自己写脚本很容易“一夜回到解放前”。go2rtc的定位完全不同。它本身就是一个流媒体中间件内置了RTSP、RTMP、HLS、WebRTC、MSE、HTTP-FLV等多种协议的推拉与转换能力。选它的主要原因有三个协议覆盖广上游能接RTSP、RTMP、HTTP(S)、MJPEG、文件、设备节点下游能出WebRTC、HLS、RTSP、RTMP等基本覆盖常见的监控浏览器播放和智能家居接入需求。自动断线重连摄像头掉线后go2rtc会按后台逻辑自动重新拉流不需要人工干预这一点对长期运行的监控系统极关键。轻量、零插件播放它内置WebRTC信令现代浏览器不需要装插件就能看低延迟实时流。这是NginxHLS方案很难做到的。遇到非要转码的场景go2rtc也可以把流丢给内置的FFmpeg去处理而不是自己死扛。它会把转码任务交给外部进程主程序始终保持轻量。这种“默认不通吃需要时再叫外援”的设计让它更适合做多路接入的网关。1.3 为什么用Docker来封装部署go2rtc本身是一个Go写的单二进制直接下载扔到机器上也能跑。但用Docker封装带来几个实打实的好处。第一隔离依赖。go2rtc有时候需要调用FFmpeg做转码还可能读写设备节点/dev/video0。直接在宿主机上装你得自己处理好二进制版本、系统库权限、用户权限一堆问题。Docker镜像里这些依赖已经装好我只需要选择“带FFmpeg的镜像”或者加一个FFmpeg容器就能把转码能力补上。第二升级回滚非常方便。go2rtc迭代速度不慢隔段时间就有新版本。用Docker的话我只需要docker pull新镜像再docker-compose up -d瞬间完成升级如果新版本有问题一条命令切回旧镜像。相比直接替换宿主机二进制风险小很多。第三资源隔离和自启动管理。通过--restart unless-stopped宿主机重启后go2rtc自动跟着启动不用写systemd服务。配合cgroup的内存限制也不会出现某个异常摄像头流把整机内存吃光的情况。2. go2rtc核心机制与配置拆解2.1 配置文件结构全景go2rtc默认读取go2rtc.yaml核心结构非常简单就是一个streams字段下面挂多个命名流。初次接触的人可能被各种环境变量和API吓到但主心骨就是配置文件里的流定义。一个典型的配置文件长这样streams: front_door: - rtsp://admin:password192.168.1.108:554/Streaming/Channels/101 back_yard: - http://192.168.1.110:8080/video.jpg usb_webcam: - device: /dev/video0 living_room: - ffmpeg:rtsp://192.168.1.120:554/ch1每个顶层键例如front_door就是一条“命名流”。值是一个列表原因在于go2rtc允许为同一条命名流配置多个来源当主源不可用时自动切换备用源。这一条设计对监控场景非常友好比如白天用一个高清主码流晚上自动切到低码率子码流。配置文件还支持全局参数比如rtsp: listen: :554 username: admin password: secret hls: listen: :8888意思是我可以同时开启RTSP服务端口和HLS服务端口。默认情况下go2rtc在HTTP端口1984上同时提供HLS和WebRTC访问入口如果不需要自定义这些全局配置甚至可以不写。对于新手我建议先别急着折腾全局参数先把streams写对让画面能在浏览器里跑通再回过头研究端口和鉴权。2.2 摄像头源怎么填RTSP与HTTP抓图实战大多数IP摄像头的标准接入方式是RTSP。海康摄像头常用的RTSP地址格式是rtsp://用户名:密码IP地址:554/Streaming/Channels/101其中101表示主码流的第一路102表示子码流的第一路。如果你只有一路摄像头通常101就是你要的那个高清流。大华摄像头的一般格式则长这样rtsp://用户名:密码IP地址:554/cam/realmonitor?channel1subtype0不同的摄像头路径差异很大最好的办法是用电脑上的VLC播放器先把原始RTSP地址跑通确认能出画面再填到go2rtc里。这一步能排除掉70%的“接不进去”问题。除了RTSPgo2rtc还支持直接拉取HTTP抓图作为视频源。部分老款摄像头或者USB摄像头没有RTSP能力只提供一个不断刷新的JPG地址例如streams: old_cam: - http://192.168.1.110:8080/cgi-bin/snapshot.cgi?channel1go2rtc会把静态图片地址按一定帧率拉取并合成为视频流虽然流畅度不如RTSP但至少保留了接入能力。这个功能在应急调试时特别好用。USB摄像头的接入方式则是通过设备节点。在Linux宿主机上插好USB摄像头查看设备节点是/dev/video0还是video1后在Docker启动命令里加上--device /dev/video0:/dev/video0配置里写streams: usb_cam: - device: /dev/video0需要注意容器里必须有访问设备的权限。我一般会在docker run命令里加--group-add video避免权限不足。2.3 对外输出都有哪些口子go2rtc的厉害之处在于输入源接入后天然可以通过多个协议对外输出。我整理了一个常用的输出协议表输出协议访问示例适用场景WebRTChttp://IP:1984/api/webrtc?srcfront_door浏览器低延迟播放速度最快HLShttp://IP:1984/hls/front_door.m3u8手机、电脑通用播放兼容性好MSEhttp://IP:1984/mse/front_door浏览器内嵌播放器使用RTSPrtsp://IP:1984/front_door供NVR、VLC等RTSP客户端拉流MPEG-TShttp://IP:1984/api/stream/front_door.mpegts局域网内其他程序取流默认情况下go2rtc启动后监听1984端口网页界面和API都在这个端口上提供。直接用浏览器打开http://服务器IP:1984就能看到go2rtc自带的Web UI里面列出了所有配置好的命名流点击一下就能播放。如果要低延迟首选WebRTC。它的延迟通常在几百毫秒以内而HLS因切片机制一般会有2到5秒的延迟所以“实时监控”和“录像回放”这两个需求建议用不同协议去满足。2.4 内置API与Web界面怎么用go2rtc内置的Web界面虽然简洁但对调试非常有用。打开首页会显示所有配置的流点击任意一条流它会自动选择一个适合当前浏览器的播放协议大部分情况下是WebRTC。如果画面没出来界面右下角会显示错误信息方便快速定位是URL错了、认证失败还是网络不通。除了界面API也是自动化集成的重点。常用的几个接口# 查看所有流信息 curl http://127.0.0.1:1984/api/streams # 查看某一条流的详细信息 curl http://127.0.0.1:1984/api/stream/front_door # 手动停止并重启某条流 curl -X DELETE http://127.0.0.1:1984/api/stream/front_door这些API在智能家居自动化里很有用。比如我通过Home Assistant检测到有人移动时调用go2rtc的API把对应摄像头流强制重连一次确保录像不会因长时间空闲而断流。go2rtc本身对外提供的是一套REST风格接口非常简单直接不需要额外装SDK。3. 从零到一的完整部署实战3.1 部署前的环境准备在动手之前先把环境理清楚。go2rtc对Docker版本没有太苛刻的要求Docker Engine 20.10以上就足够。如果在Windows上使用Docker Desktop注意CPU虚拟化要开启否则启动Docker会直接报错。我建议把go2rtc部署在一台局域网内的Linux小主机上比如NUC、二手迷你主机或者树莓派4B以上都行。摄像头最好和它处于同一个二层网络避免跨三层带来的组播发现等问题。部署前先做两件事确认摄像头的RTSP地址能用VLC打开确认服务器能直接访问摄像头地址不通的话先排查网络策略和防火墙。先创建一个目录用于保存配置和录像文件mkdir -p /opt/go2rtc cd /opt/go2rtc touch go2rtc.yamlgo2rtc.yaml先留空也没有关系go2rtc会以一个空配置正常启动UI页面也能打开。我习惯先把空配置传上去确认容器能跑起来再逐步往配置里加摄像头避免一次写错太多找不着北。3.2 快速启动一条命令跑起来如果你只是想快速验证go2rtc能不能用直接用下面的命令启动docker run -d \ --name go2rtc \ --restart unless-stopped \ -p 1984:1984 \ -v /opt/go2rtc/go2rtc.yaml:/config/go2rtc.yaml \ go2rtc/go2rtc这里有一个细节go2rtc默认会读取容器内/config目录下的go2rtc.yaml所以我把宿主机配置文件挂载到这个路径。如果你在别的操作系统上目录换成你自己的路径即可。启动后检查容器状态docker ps | grep go2rtc docker logs -f go2rtc看到类似Starting server的日志后打开浏览器访问http://服务器IP:1984。如果页面能打开说明go2rtc已经跑起来了。此时可以往go2rtc.yaml里加一条摄像头配置再重启容器画面出来了就说明接入成功。不过这种docker run方式适合测试不建议长期使用。因为参数都写在命令行里后续改配置不直观而且很容易忘记端口映射和挂载目录。正式使用还是推荐下面用docker-compose的方式。3.3 完整部署docker-compose管理多路摄像头使用Docker Compose可以让配置项清晰可见后续增加摄像头、调整端口、统一管理都方便。在/opt/go2rtc目录下创建docker-compose.ymlversion: 3.8 services: go2rtc: image: go2rtc/go2rtc container_name: go2rtc restart: unless-stopped network_mode: host volumes: - ./go2rtc.yaml:/config/go2rtc.yaml - ./recordings:/recordings environment: - TZAsia/Shanghai我在这里选择了network_mode: host。这是Linux部署下的一个实用选择因为Docker默认的bridge网络在转发WebRTC的UDP包时容易出问题而且摄像头如果在同一局域网host模式下go2rtc直接使用宿主机网络访问摄像头不走任何NAT延迟更低、更稳定。使用host模式后不需要ports映射因为容器直接共享宿主机网络端口。TZ设置为Asia/Shanghai让日志时间和后续录像文件的时间戳保持一致。启动命令docker compose up -d如果你是旧版Docker没有docker compose插件可能需要用docker-compose up -d启动后再次访问1984端口确认正常。然后把摄像头配置写入go2rtc.yaml比如streams: front_door: - rtsp://admin:password192.168.1.108:554/Streaming/Channels/101 back_yard: - rtsp://admin:password192.168.1.109:554/cam/realmonitor?channel1subtype0 usb_cam: - device: /dev/video0保存后执行docker compose restart go2rtc在UI页面里确认三条流都能播放。需要注意如果用到设备节点/dev/video0在docker-compose.yml里要额外加devices映射devices: - /dev/video0:/dev/video0同时加上group_addgroup_add: - video这样USB摄像头才能在容器内被正常访问。3.4 从手机、电脑、浏览器拉流验证部署完以后验证方式最好覆盖多个客户端。我一般会这样测试电脑浏览器打开http://服务器IP:1984点击对应的流默认走WebRTC画面出来之后看延迟基本在0.3秒以内。iPhone/Android手机打开浏览器访问同一个地址如果WebRTC不行会自动降级到HLS。手机上用VLC应用直接打开http://服务器IP:1984/hls/front_door.m3u8也可以秒开。电脑VLC打开网络串流地址填rtsp://服务器IP:1984/front_door能够正常播放说明RTSP对外服务也没问题。我遇到过一种情况浏览器里画面卡在马赛克状态但VLC拉RTSP却非常流畅。这种一般不是go2rtc本身的问题而是WebRTC传输时被路由器或者防火墙丢包。此时我会先强制走HLS播放如果HLS也卡再从摄像头源端排查码率是否过高。3.5 集成Home Assistant与其他系统如果你的智能家居中枢是Home Assistantgo2rtc的集成价值会放大一倍。Home Assistant自带stream组件在拉取RTSP流时偶尔不稳定尤其在多路摄像头的场景下会反复重启。换成go2rtc之后Home Assistant只需要对接rtsp://go2rtc地址:1984/流名称这一个稳定入口底层重连和协议转换全部交给go2rtc处理。在Home Assistant里可以在集成页面直接搜索“go2rtc”添加集成填写go2rtc服务器地址。之后在卡片里添加摄像头实体播放源地址直接使用go2rtc输出的HLS或WebRTC流。实测下来WebRTC源在Home Assistant面板上的加载速度明显比传统“camera.stream”快切换摄像头时的起播时间也短得多。不仅是Home AssistantFrigate这类智能安防系统同样能把go2rtc作为上游。Frigate只负责录像和探测RTSP拉流交给go2rtc可以有效减少画面断流导致的漏报。4. 实战中的坑与排查技巧4.1 摄像头连不上、RTSP握手失败这是最常遇到的问题。排查时我建议先看一眼go2rtc的日志docker logs go2rtc --tail 50如果看到类似failed to connect或者401 Unauthorized多半是RTSP地址或账号密码的问题。先用VLC在电脑上测试原始RTSP地址如果VLC能打开而go2rtc不行检查用户名密码是否包含特殊字符比如、:、#之类的符号需要URL编码。比如密码是Abc123RTSP地址里直接写admin:Abc123192.168.1.108会把当成分隔符导致解析错误。此时需要把编码为%40rtsp://admin:Abc%40123192.168.1.108:554/Streaming/Channels/101还有一个常见情况是RTSP握手卡住一直没有画面。这通常是摄像头对RTP传输模式有限制。go2rtc默认会自动协商但如果遇到老设备可以在源URL后面加上?tcp之类的传输参数试试。不过我不建议一上来就乱加参数先用VLC裸地址测试是最快的判断方法。4.2 画面延迟大、卡顿、花屏画面延迟和卡顿是两个不同的问题。延迟大优先考虑使用WebRTC或者LL-HLS不要用普通HLS做实时监控。HLS天然有几秒切片延迟这是协议特性不是go2rtc能完全消除的。卡顿和花屏则要重点关注带宽和码率。摄像头的子码流通常只有几百Kbps主码流可能到4Mbps甚至更高。如果同一时间有多个终端同时拉主码流交换机和无线AP的压力会很大。我的做法是UI预览和手机端查看统一走子码流需要录像归档时才单独拉主码流。如果go2rtc做了转码CPU占用会明显升高。此时要确认是否真的需要转码很多场景下浏览器已经能直接解码H.264不需要硬转。只有在老摄像头输出MJPEG或者格式不兼容时才用ffmpeg:前缀强制转码streams: old_cam: - ffmpeg:rtsp://192.168.1.120:554/ch1转码会额外消耗CPU我在一台J4125小主机上实测单路1080P转码能占掉一个核心。所以尽量保持直通不要把go2rtc当成万能转码器。4.3 端口冲突、防火墙与Docker网络go2rtc默认占用1984端口如果和已有服务冲突可以在配置里修改HTTP监听端口。另外如果你启用了RTSP输出或者自定义HLS端口记得在防火墙放行这些端口。Docker运行模式下有个容易踩的坑使用bridge网络并映射端口时WebRTC的UDP连接经常失败因为WebRTC默认会尝试建立P2P通道NAT和端口映射配置稍不对浏览器就只能降级到HLS。如果你需要极致低延迟最简单的办法是像我前面说的那样在Linux服务器上直接使用network_mode: host让UDP和TCP端口都直通避免Docker NAT层造成额外延迟。Windows和Mac的Docker Desktop不支持真正的host网络此时WebRTC可能不太稳定优先使用HLS或MSE或者直接反馈给用户的浏览器让其采用TCP候选。这个限制需要提前心里有数。4.4 容器重启与配置失效的应急处理go2rtc本身设计得比较健壮但配置写错、镜像升级失败仍会导致容器启动异常。我建议每次修改go2rtc.yaml之前先备份cp go2rtc.yaml go2rtc.yaml.bak如果修改后容器不停重启先查看日志docker logs go2rtc --tail 30日志会明确提示YAML解析错误或者不认识的前缀。此时把备份文件恢复回去再docker compose restart即可。另一个常见问题是容器内存占用逐渐涨高。新版go2rtc对内存做了很多优化但某些摄像头流异常或者转码任务堆积时内存仍可能上涨。我习惯在Docker Compose里加一个内存限制deploy: resources: limits: memory: 512M这样即使单路流异常也不会拖垮整个宿主机。4.5 多路并发性能调优当接入路数超过20路时性能调优就要提上日程了。第一点是尽量使用摄像头子码流因为预览场景根本不需要主码流的清晰度。第二点是为每条流设置合理的缓存。go2rtc默认会给每条流缓存一定时长的数据但如果你只做预览不做录像可以关闭或减小缓存降低内存压力。第三点是合理使用备用源。比如白天用主码流晚上自动切到子码流这需要在外部联动脚本里修改配置并重启。如果你不想这么麻烦可以直接在一条命名流里配置多个源go2rtc会在源断线后自动切换。最后如果服务器性能实在有限可以考虑把go2rtc拆成多个实例按摄像头分组分流。比如一台主机跑两个容器一个管室内摄像头一个管室外摄像头互不干扰。5. 扩展玩法从“能看”到“好用”5.1 多摄像头统一命名与分组流一多命名管理就很重要。我建议不要用cam1、cam2这种没有含义的名字而是用front_door、back_yard、garage这种语义化名字。这样在API调用、Home Assistant配置、日志排查时一眼就能知道是哪台设备。go2rtc还支持给同一个命名流配置多个来源适合做不同码率的切换。比如streams: front_door: - rtsp://admin:password192.168.1.108:554/Streaming/Channels/101 - rtsp://admin:password192.168.1.108:554/Streaming/Channels/102实际播放时go2rtc会优先使用第一个源当第一个源不可用时自动切换到第二个。这种机制保证了稳定性尤其适用于摄像头偶尔重启或网络不稳定的场景。5.2 录像与回放场景落地go2rtc本身是一个实时流媒体网关主要职责是协议转换和分发并不自带录像管理。如果你需要存储录像有几个方向可以扩展。方案一是配合Frigate使用。Frigate专门做NVR和物体检测它天然支持go2rtc作为RTSP网关通过rtsp://go2rtc:1984/流名称拉流后做录像和事件存储。我在实际项目中就是用Frigate接收go2rtc转发的流再按检测到人形/车辆的时间段保存录像比起传统NVR每天连续录像存储成本低很多。方案二是用FFmpeg定时录像。如果你不想引入Frigate可以在宿主机上写一个简单的cron任务定时执行ffmpeg -rtsp_transport tcp -i rtsp://127.0.0.1:1984/front_door \ -c copy -f segment -segment_time 3600 \ /recordings/front_%Y%m%d_%H%M.mp4这样就把go2rtc当成了一个稳定的中间源录像任务不直接对接摄像头可靠性更高。5.3 公网访问与安全加固如果你希望通过公网查看家里的摄像头我不建议把go2rtc的1984端口直接暴露到公网。go2rtc本身提供了基础的登录认证但公网环境里强烈建议在前面加一层反向代理比如Caddy或者Nginx用HTTPS和Basic Auth保护Web界面。Caddy配置里加一段反向代理即可stream.example.com { reverse_proxy 127.0.0.1:1984 basicauth { user $2a$14$hash } }启用HTTPS后浏览器里的WebRTC播放也能顺利通过加密通道不被运营商拦截或干扰。此外尽量不要把摄像头的RTSP原始地址暴露在公网所有公网访问都走go2rtc这一层既统一了协议也隐藏了内网拓扑。安全上还有个小细节如果使用network_mode: host1984端口和RTSP端口会直接暴露在局域网里建议在宿主机防火墙里限制来源IP只允许内网网段和外网代理服务器IP访问。在我自己的部署经验里最满意的一次是把办公室的16路杂牌摄像头统一接到go2rtc再对接Home Assistant和Frigate整个过程只花了一个下午。中间踩得最深的坑反而是那些看起来特别基础的问题RTSP地址里特殊字符没编码、host网络模式和Docker Desktop不兼容、摄像头子码流和主码流切换时导致的卡顿。这些坑单看都不难但没人提醒的话能折腾你一晚上。如果你正准备动手我的建议很直接先拉一条裸RTSP流跑通最简链路再逐步加摄像头配置改动前备份YAML升级镜像前先看更新日志能用子码流预览就别用主码流能直通就别转码。go2rtc不难难的是把它的轻巧和“实时”真正用好。最后分享一个小技巧在go2rtc的Web UI播放页面按F12打开开发者工具能看到当前播放用的具体协议是WebRTC还是MSE还是HLS。每次调优后用这个方式确认实际走的路线比看文档猜行为靠谱得多。
返回列表