记录一下第 761 号实验项目:把本地局域网里的视频监控服务,改造成“本地化存储 + 局域网外访问”都能搞定的组合方案。整套链路用的是 Go2RTC 做流接入,EasyNVR 做通道管理和录像,最后用 cpolar 把 EasyNVR 的页面入口映射到公网。实验做完后我可以直接在手机浏览器里打开公网地址,看实时画面和录像回放,文件全部落在本地硬盘上,不依赖云存储,也没有额外的流量套餐。
别急着把“监控系统”想成多复杂的工程。这次实验里的环境,就是几台不同品牌的网络摄像头、一台装了 Linux 的小主机、一块普通机械硬盘。难点从来不在设备数量多,而在于三个问题要同时解决:一是品牌杂、取流协议不统一;二是录像要可靠地写到本地;三是人不在同一局域网时,还得能打开回放页面。这套方案特别适合不想买云存储服务、手头又有老旧杂牌摄像头的玩家,也适合在一个小办公室里做统一视频管理的小型项目。
下面按实战顺序来写,先把需求拆清楚,再讲部署、存储、远程访问和排坑,能直接用到的命令和配置都会给出来。
1. 这 761 号项目究竟做了什么:需求与链路设计
1.1 本地化存储和远程访问,卡在哪
先说最原始的问题:摄像头出厂自带的存储方式,要么插 TF 卡,要么依赖设备本地小容量存储。TF 卡方案最让人头疼的是容量撑不了几天,而且摄像头本身一旦出了问题,录像也跟着没了。想提高录像的可靠性,就得把数据单独放到一台常开的设备上,于是“本地化存储”这个需求就出现了。
如果用一台设备统一存录像,这台设备就得把摄像头的视频流都收下来。现实环境里摄像头品牌很杂,有些是常见的 RTSP 取流地址,有些设备甚至只给一个网页播放器,根本没开标准的取流协议。如果不做一层抽象,就要每台设备用不同软件、不同账号去登录,麻烦不说,后期维护也痛苦。我选择 Go2RTC 做第一层接入,核心原因就是让它去把不同协议的流统一拉回来,再统一转交给后面的视频管理软件。
远程访问是另一个独立问题。摄像头本身也有公网能力,但很多情况下要么没有公网 IP,要么运营商不开放端口,要么你不想直接在路由器上做端口映射把设备暴露出去。视频管理服务跑在局域网里,外面的人想访问,就需要一条可控的通道。cpolar 在这里的作用就是建立这条通道,把本地端口映射成一个公网地址,而且访问入口可以做认证,比裸暴露安全得多。
所以这个项目本质上要解决两件事:把录像收拢到本地,把访问扩展到外网。两者之间还需要一个中间层,就是 EasyNVR 这类视频管理软件,它负责把 Go2RTC 拿到的流真正管理起来、录下来,然后对外提供页面和播放接口。
1.2 三条链路的分工与整体架构
先列一条数据链路出来,后面所有配置都是围绕它展开的:
摄像头取流 → Go2RTC(统一拉流、自动重连、转出) → EasyNVR(通道管理、录像、播放) → 本地硬盘
外部访问链路是这样:手机/浏览器 → cpolar 公网地址 → 隧道 → 本地 EasyNVR 页面
第一段链路的关键词是“统一”。不同品牌摄像头的 RTSP 取流地址格式不一样,有些设备断流后还不会主动重连。Go2RTC 可以把所有摄像头配置在一个文件里统一管理,而且它对断流重连的处理很积极,实测下来比让 EasyNVR 直接去拉一堆杂牌摄像头要稳定得多。
第二段是“管理”。EasyNVR 作为服务端,把 Go2RTC 吐出来的流添加为通道,通道里面可以配置录像计划、存储位置,对外提供网页播放、HLS、HTTP-FLV 等直播输出。要注意的是,EasyNVR 本身也可以直接从摄像头取流,但我这个项目里故意加了一层 Go2RTC,原因后面展开说明。
第三段是“通道”。cpolar 做的事情不是把摄像头流直接推出去,而是把 EasyNVR 的网页服务端口映射出去。这样外面访问的是一个带认证的网页,而不是裸的 RTSP 端口,安全性好很多,可操作性也更强。整条链路设计清楚之后,剩下的就是按顺序部署、配置、验证了。
2. Go2RTC + EasyNVR 部署:先把本地链路打通
2.1 Go2RTC:异构摄像头的统一入口
Go2RTC 最让我喜欢的地方是轻量:单一可执行文件,没有复杂的依赖,下载下来就能跑。它支持把 RTSP 源转成 RTMP、HLS、HTTP-FLV 等输出,也能把流再提供给其它系统。这个项目的第一步,就是把摄像头流写进它的配置文件。
先创建一个 go2rtc.yaml:
streams: front_gate: - rtsp://admin:password123@192.168.1.10:554/Streaming/Channels/101 backyard: - rtsp://admin:password123@192.168.1.11:554/cam/realmonitor?channel=1&subtype=0上面是两个常见摄像头品牌的地址写法。海康系常见路径是 /Streaming/Channels/101,其中 101 是主码流、102 是子码流;大华系常见路径是 /cam/realmonitor?channel=1&subtype=0。如果不是这两个品牌,去设备后台找 RTSP 地址,或者查一下设备型号对应的取流路径就行。
密码里如果有 @、#、: 这类特殊字符,必须做 URL 编码,否则会因为地址解析错误而拉流失败。实验里那台老摄像头密码带了一个 @ 符号,第一次配置怎么都连不上,后来把 @ 改成 %40 就正常了。
配置文件写好之后直接启动:
./go2rtc -config go2rtc.yaml默认情况下 Go2RTC 的网页管理界面跑在 1984 端口。打开 http://本机IP:1984 就能看到通道列表和实时画面。这一步能通,说明拉流没问题。
建议在启动前检查一下摄像头是否允许同时建立多路 RTSP 连接。有些低端设备默认只允许两路并发,Go2RTC 和 EasyNVR 两边都拉的话,可能会互相把连接踢掉。如果遇到画面时断时续,优先检查设备端的连接数限制。
2.2 EasyNVR:接入、播放与录像的承载方
EasyNVR 负责视频管理和录像。我这次用的是容器方式部署,好处是隔离干净、目录挂载清晰,以后想迁移到另一台机器也方便。
docker run -d \ --name easynvr \ -p 1080:1080 \ -p 5900:5900 \ -v /mnt/recordings:/data \ your-easynvr-image:tag这里把容器内 1080 端口映射到了宿主机 1080,把录像目录挂载到了 /mnt/recordings。注意不同版本、不同平台的容器镜像,端口和目录名会有差异,以你实际拿到的那个版本说明为准。第一次启动后,浏览器打开 http://本机IP:1080 就能进 EasyNVR 的配置界面。
进去之后第一步是添加通道。我在这里选择“手动添加”,而不是自动发现,因为通道地址是 Go2RTC 转出来的统一流地址。手动添加时,填写 Go2RTC 输出的 RTSP 流地址:
rtsp://本机IP:1984/front_gate这里解释一个细节:Go2RTC 默认会把自己管理的流以 RTSP 形式重新暴露出来,地址就是 1984 端口加通道名。实测下来,从 Go2RTC 拉流比让 EasyNVR 直连摄像头更省心,因为重连逻辑统一在 Go2RTC 这一层处理,EasyNVR 只负责消费流,不用关心设备端各种奇怪的断流问题。
2.3 本地联调:确认实时流和回放都正常
Go2RTC 和 EasyNVR 都起来之后,先别急着配置存储和远程访问,应该做一次完整的本地联调。我在这个环节通常会做三件事。
第一,确认 EasyNVR 里的通道全部在线,画面正常。第二,用浏览器打开实时预览,确认 HTTP-FLV 或 HLS 能播。第三,手动触发一次录像,看录像文件能不能落盘、文件能不能正常回放。
如果这一步就发现画面黑屏、声音断续或者录像文件损坏,说明问题还在本地链路,先解决本地链路再往下做,不要带着隐患去配远程访问。这次联调时我遇到过一个情况:Go2RTC 输出的某个通道在 EasyNVR 里显示在线,但画面一直不出来,排查后发现是 Go2RTC 配置文件里写错了子码流地址,拉到一条无信号的码流。替换成正确地址后,画面立刻恢复。
本地链路完全调通后,下一步才轮到存储规划和远程访问。这两块没有先后依赖,但我会先做存储,因为录像稳定可靠是这套系统的底线。
3. 本地化存储方案:录像计划、容量与目录管理
3.1 先算容量:码率决定硬盘下限
录像存储最忌讳的事情,是装完软件之后才发现硬盘塞满了。所以先算容量,再规划硬盘和录像计划。计算录像容量的核心参数是码率,也就是视频流每秒产生的数据量。摄像头后台通常会标注主码流码率,比如 2Mbps、4Mbps、8Mbps。
换算公式很直接:
单个摄像头每天容量(GB)≈ 码率(Mbps)× 3600 秒 × 24 小时 ÷ 8 ÷ 1024
以一路 2Mbps 的摄像头为例:2 × 3600 × 24 ÷ 8 ÷ 1024 ≈ 21GB。如果码率是 4Mbps,那就是 42GB 左右。四路 2Mbps 的摄像头存七天,需要的容量大约是 21 × 4 × 7 = 588GB。这个数字直接决定了硬盘选多大。
所以在配置存储前,我会先统计一遍所有通道的实际码率,然后把每路的录像时长需求加起来。注意码率不是固定的,画面里动静大时码率会明显上升,尤其是 265 编码和可变码率模式下,实际容量可能比理论值高 20% 左右,预留空间是必须的。
3.2 录像计划与生命周期策略
存储规划好之后,进入 EasyNVR 的录像计划配置。一般录像模式有两种:全天连续录像和事件录像。
全天连续录像最省心,但耗硬盘;事件录像只检测到移动目标时录,省空间,但如果检测灵敏度设置不好,容易漏掉关键片段。我的建议是对关键通道做 7×24 连续录像,对非关键通道做事件录像,或者降低帧率。具体怎么取舍,取决于硬盘容量和场景的重要程度。
录像文件还需要一个生命周期策略。最简单实用的是“循环覆盖”,也就是存储空间达到阈值时,自动删除最早的录像。EasyNVR 的存储配置里一般有磁盘空间清理策略。不建议把录像文件放在系统盘上,系统盘一旦爆满,录像服务和其它软件都会跟着出问题。
还有一个容易忽略的编码参数:关键帧间隔,也就是 GOP 大小。录像回放时能否快速拖动定位,和关键帧间隔关系很大。GOP 太大,回放时拖动进度条后会出现长时间黑屏等待。我在项目里把摄像头的 GOP 调到 2 到 4 秒之间,回放体验好了很多。
3.3 存储目录、权限和时间同步的三个坑
这三个坑是实际使用中很容易踩到的,而且一旦发生问题都比较隐蔽。
第一个是文件系统格式。录像文件动辄几个 GB,FAT32 格式单文件不能超过 4GB,很容易导致录像写满后失败。存储盘建议直接用 ext4、NTFS 或 XFS,挂载前先确认分区格式没问题。
第二个是权限。用容器跑 EasyNVR 时,容器内的用户 ID 和宿主机不同,挂载的目录如果没有写权限,录像服务会静默失败。我这次挂载 /mnt/recordings 后第一次配置录像计划,过了一个小时回去看,录像目录里没有任何文件,日志也没有明显报错。后来检查发现就是目录权限不对。处理方式是把挂载目录的所有者改成容器内进程使用的 UID,或者在启动容器之前就 chown 好目录。
第三个是时间同步。录像文件命名和时间索引依赖系统时间,如果设备时间不准,录像回放时会出现时间错乱,录像计划也可能在错误的时间段执行。建议给跑 EasyNVR 的机器开启时间同步,让系统时间自动校准。这个很多人会忽略,等到回放时找不到某一段录像才后悔。
4. cpolar 实现局域网外访问:隧道原理与安全配置
4.1 隧道到底打通了什么
远程访问的第一步,是理解 cpolar 到底做了什么。简单说,它在你本地运行的客户端和远程服务端之间建立一条长期保持的连接;公网用户访问一个特殊网址时,请求先到达远程服务端,再经过这条连接转发到你本地的端口上。
用大白话讲:局域网里的设备相当于没有对外门牌的房间,cpolar 就像一个接待员,外面的人报出接待员的名字,接待员再把请求转交给你。这是一条受控制的通道,并不是把设备完全暴露到公网。
在这个项目里,我要暴露的是 EasyNVR 的网页服务端口,也就是宿主机上的 1080 端口。不直接暴露 Go2RTC 或摄像头自身的 RTSP 端口,是因为 RTSP 协议在公网环境里更容易被扫描和滥用,而网页服务可以加账号认证,行为更可控。
安装和绑定账号很简单。从 cpolar 官网下载对应平台的客户端后,用命令行绑定认证信息:
cpolar authtoken <你的Token>绑定一次之后,使用时就只需要指定要暴露的本地端口。
4.2 创建隧道并绑定认证
创建一条最简单的 HTTP 隧道:
cpolar http 1080执行之后,终端会输出一个公网访问地址。把这个地址在浏览器里打开,就能看到 EasyNVR 的登录页面。到这里,局域网外访问的链路已经通了,整个过程下来,其实就是一行命令的事。
不过实验归实验,正式用的时候不能只靠一条裸隧道。我强烈建议至少做两层保护。
第一层,EasyNVR 自身必须开启登录认证,页面不能匿名可访问。第二层,cpolar 支持认证隧道模式,可以在公网入口先加一道用户名密码验证。这两层叠加后,即使有人拿到公网地址,也进不了真实的服务页面。
固定地址也是正式使用时要考虑的点。免费模式下公网地址通常是随机分配的,重启后可能变化,不适合日常使用。cpolar 控制台支持绑定固定二级域名,配置好之后把这条隧道写进配置文件,以后启动时地址不变,分享给别人也方便。
不同版本的 cpolar 配置写法略有差异,功能名称也可能变化,具体以官方文档为准。但核心思路一致:先建隧道确认通,再开认证和固定地址保证长期可用。
4.3 访问体验与并发带宽取舍
隧道打通之后,还有个现实问题:外网访问流畅度取决于上行带宽。
家庭宽带的上行带宽通常远小于下行。如果同时有多个人在外面看实时画面,每一路都会占用上行带宽。比如家宽上行只有 20Mbps,外面同时看两路 4Mbps 码流,就会出现卡顿和花屏。
解决方案有三个方向:一是远程查看时切换子码流,通过 EasyNVR 的界面选择低码率播放;二是降低实时预览的码率限制,只在回放时使用原始画质;三是经常有多人访问的话,考虑升级上传带宽,或者部署一台配置更高的机器做转码压缩。
还有一点,公网访问时播协议的选择会影响即时性。HLS 协议在浏览器里兼容性最好,但延迟通常有 5 到 10 秒;HTTP-FLV 延迟低一些,更适合实时预览。EasyNVR 一般同时提供多种播放协议,远程查看时我习惯用 HTTP-FLV,回放时用 HLS 或 MP4 文件,体验比较平衡。
5. 常见问题与排查实录
5.1 拉流失败先分清是设备还是链路问题
Go2RTC 配置完成后,如果网页管理界面里通道状态异常,先不要急着怀疑 Go2RTC,应该先用独立工具直接测试摄像头源地址。最简单的测试方式是用 ffprobe 或 ffplay:
ffprobe -rtsp_transport tcp -i "rtsp://admin:password123@192.168.1.10:554/Streaming/Channels/101"如果直接拉摄像头地址也失败,那问题大概率在设备端:密码错误、协议被禁用、地址路径不对,或者摄像头只允许特定 IP 访问。如果直接测摄像头可以,但 Go2RTC 里不通,就查配置文件里的地址转义、特殊字符,以及摄像头的并发连接限制。
还有一种很常见的坑:主码流和子码流路径写反了。有些设备主码流是 4K 或超大码率,普通播放器拉起来很吃力;子码流清晰度低但更稳定。Go2RTC 拉到主码流后,如果 EasyNVR 里没开转码,后面的播放体验会很差。建议配置一条主码流对应录像,一条子码流对应预览,分别给到 EasyNVR 的不同通道。
5.2 录像中断、文件缺失与时间错乱
本地化存储最常见的故障,是录像计划明明开了,回放时却发现某段时间没有文件,或者文件存在于目录里但无法播放。
先检查存储盘剩余空间。空间写满是最常见原因,循环覆盖策略如果配置错了,新录像会持续失败。再检查文件系统格式,FAT32 无法写入超过 4GB 的单文件,录像文件跨过这个边界时会直接报错。最后检查挂载目录的权限,尤其是容器化部署时,文件虽然能创建,但可能写到了系统盘而不是数据盘,也会带来问题。
关于时间错乱,前面已经强调过系统时间同步的重要性。这里再补一个触发因素:如果 EasyNVR 所在机器长时间待机或休眠,唤醒后时间可能滞后,录像文件名的时间戳就会乱掉。把 BIOS 时间、系统时间和时间服务器校准统一,是根治办法。
还有一个偏门坑:多路录像同时写入同一块机械硬盘时,硬盘寻道压力变大,可能导致部分录像文件损坏。解决方案是按通道分目录、使用多盘位存储,或者在预算允许时换成监控级硬盘或 SSD。
5.3 外网访问常见问题速查表
我把远程访问环节遇到的典型问题整理成了一张速查表,方便照着排查:
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 本地能开 EasyNVR,外网地址打不开 | 隧道进程没运行,或本地端口写错 | 检查 cpolar 终端是否正常输出地址,确认映射的是宿主机 1080 端口 |
| 外网地址要反复刷新才出画面 | 上行带宽不足,或码流过大 | 远程端切换到子码流,或改用 HTTP-FLV |
| 地址能打开但提示无权限 | EasyNVR 登录未开启,或会话过期 | 进 EasyNVR 设置开启登录认证,重新登录 |
| 一段时间后地址失效 | 免费随机域名变了,或隧道断开 | 绑定固定二级域名,并让隧道支持自动重连 |
| 公网画面花屏、马赛克 | RTSP 传输用 UDP 丢包 | 在 Go2RTC 侧改用 TCP 传输模式 |
| 手机浏览器播放不出 HLS | 手机浏览器对 HLS 兼容差异 | 换用 HTTP-FLV,或使用播放器客户端 |
这张表的思路是:先判断问题发生在本地链路还是隧道链路,再判断是协议问题还是带宽问题。远程访问问题很少是无解的,耐心分步排查基本都能解决。
5.4 延迟高、卡顿该怎么调节
实时预览延迟高,需要从两个方向同时下手:编码端和播放端。
编码端的核心是 GOP 和码率。GOP 太长会导致播放器等待完整关键帧才能开始解码,直观感受就是打开画面慢、拖动回放大段黑屏。建议保持 2 到 4 秒的关键帧间隔。码率方面,外网访问时优先选子码流,不要固执地用 4K 主码流远程看画面,带宽是硬约束。
播放端,EasyNVR 页面里提供的 HLS 本身就是分段切片协议,天然存在延迟。局域网里用 HLS 的延迟大约 3 到 6 秒,公网经过隧道转发后还会再叠加链路延迟。如果对实时性要求高,远程访问优先选 HTTP-FLV。RTMP 在浏览器里支持不好,通常要配合播放器,所以我更推荐 HTTP-FLV。
关于 Go2RTC 的传输方式,默认走 UDP 拉流可能因为公网丢包导致花屏,可以改用 TCP。FFmpeg 和 Go2RTC 都支持指定 RTSP 传输模式,TCP 模式牺牲一点连接开销,换来更稳定的画面,远程场景下这个取舍是值得的。
6. 这套组合的后续扩展方向
6.1 多路并发时的调优思路
四路摄像头是这个实验的起步配置,如果通道数量继续增加,会有两个瓶颈先出现。
第一个是带宽。所有摄像头码流都汇聚到跑 Go2RTC 和 EasyNVR 的机器上,网卡流量和磁盘写入吞吐会明显上升。建议把存储盘和系统盘分开,让录像写盘不占用系统运行所需的 IO;有条件的话,用独立网口处理监控流量。
第二个是转码性能。EasyNVR 接入的通道如果需要转码输出,CPU 占用会随着并发观看人数增加。直播观看人数少时没问题,一旦外面有几个人同时看,转码压力就上来了。我的做法是:尽量不转码,让设备直接推原始编码,远程端按需选择子码流播放;确实需要转码的视频源才单独配置转码通道。
6.2 把录像和状态接进其它平台
EasyNVR 通常会开放状态查询和播放地址获取的接口,这些能力可以用来做很多事情。
比如在外部做一个简单的页面,展示各通道的在线状态、录像容量、今日录像时长;或者对接告警系统,当某个通道掉线或硬盘空间过低时自动提醒;再比如写脚本定期检查录像文件完整性,发现损坏文件及时处理。这些都不需要改动核心链路,只是在 EasyNVR 外面加一层自动化逻辑。
我自己习惯写一个定时脚本,每天统计前一天录像文件的数量和大小,与预期值对比。偏差超过一定比例就输出告警。这样能在硬盘出问题之前发现问题,而不是等到回放时才发现录制功能早断了。
6.3 一个值得保留的实验习惯
最后分享一个这次实验沉淀下来的习惯:每改动一次配置,先备份旧文件再动手。Go2RTC 的 yaml、EasyNVR 的通道设置、cpolar 的隧道配置,这些文件都很小,备份成本极低,但关键时刻能省下大量排查时间。
尤其是 Go2RTC 的配置文件,调整一个摄像头地址时格式稍微写错,整个流服务就可能启动失败。我在这次 761 号实验里就遇到过,因为注释符号写错导致服务一直起不来,后来养成了每次修改前先复制一份带时间戳的旧文件作为回退点的习惯。如果你是第一次搭这套东西,建议先把本地链路完全调通,再考虑存储和远程访问。这个顺序不是规定,但按这个顺序做,出问题时边界最清晰,排查效率最高。