摆脱云存储绑架:用自建 NVR 掌控家里的每一路摄像头

2026-05-12 20 预计阅读时间:1 分钟
来源:oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:13 分钟

家里摄像头越来越多——几台小米、几个 ESP32 自制摄像头、还有树莓派挂的 CSI 模块。云存储用着总觉得不对劲:小米摄像头默认只能走米家,想拉 RTSP 流得刷固件或者买官方插件;其他品牌的云方案要么按月收费,要么断网就断录;最关键的是,视频数据躺在别人的服务器上,你不知道它被怎么处理、什么时候会被清理。

与其继续给各家云方案交月租,不如自己搭一个 NVR(Network Video Recorder),把所有路视频流收归本地。MiBeeNvr 就是作者在这种思路下写出来的轻量级家用 NVR 系统。

云存储的三个硬伤

厂商绑定。 小米摄像头默认封闭 RTSP,想拉流得走非官方路径;其他品牌各有各的 App,互不相通。你买了五个品牌的摄像头,就得装五个 App、开五个云存储账号。

网络依赖。 云录制的本质是:摄像头先把视频推到厂商服务器,你再从服务器拉回来看。家里的上行带宽有限,多路同时推流容易卡顿;一旦外网断了,录制直接中断。

持续费用。 一路摄像头一个月云存储几十块,几路加起来一年几百。硬盘买一块放家里,一次投入用几年。

自建 NVR 的思路很简单:摄像头在局域网内推 RTSP 流,NVR 在本地拉流、存盘、回放。数据不出家门,不依赖外网,不按月交钱。

让摄像头吐出 RTSP 流

自建 NVR 的前提是每路摄像头都能输出 RTSP 流。不同设备的开放程度差异很大,这里逐类说清楚。

小米摄像头。 原生固件不开放 RTSP,社区有两个主流路径:

  • 刷第三方固件(风险较高,可能变砖)
  • 用官方的"局域网直播"功能,部分型号支持通过米家 App 开启本地 RTSP,但稳定性参差

如果不想刷固件,一个折中方案是用小米的云直播地址做中转,但这又回到了网络依赖的老路。最干净的做法还是选支持 RTSP 的摄像头品牌。

ESP32 摄像头。 ESP32-CAM 模块天然支持 HTTP 快照和流,但原生不带 RTSP。可以用 Micro-RTSP 库给 ESP32 加上 RTSP Server:

// ESP32 Micro-RTSP 最小示例
// 依赖:https://github.com/geeksville/Micro-RTSP
#include "OV2640.h"
#include "MicroRTSPServer.h"

OV2640 cam;
RTSPServer rtspServer(8554); // RTSP 端口

void setup() {
    cam.init(esp32cam_aio_config); // 根据你的板子选配置
    rtspServer.begin(&cam);
}

void loop() {
    rtspServer.handleClient(); // 处理 RTSP 客户端请求
}

编译上传后,用 VLC 或 FFmpeg 验证流是否可用:

ffplay rtsp://192.168.1.105:8554/mjpeg/1

ESP32 性能有限,一般只能推 640×480 @ 15fps 的 MJPEG,画质别指望太高,但作为门铃、走廊监控够用。

树莓派 CSI 摄像头。 树莓派推 RTSP 流最简单的方案是用 libcamera + v4l2relay 或直接跑 FFmpeg:

# 树莓派 Bookworm 系统,使用 libcamera 采集 + FFmpeg 推 RTSP
libcamera-vid -t 0 --codec mjpeg --width 1280 --height 720 \
  --framerate 15 --output - | \
ffmpeg -re -i - -c:v copy -f rtsp \
  rtsp://0.0.0.0:8554/rpi_cam

这条命令让树莓派持续采集 CSI 摄像头画面,通过 FFmpeg 以 RTSP 协议发布到本地 8554 端口。局域网内任何设备都能拉流。

用 FFmpeg 搭一个最简 NVR 核心

在正式介绍 MiBeeNvr 之前,先理解 NVR 的核心逻辑:拉流 → 分段存储 → 索引回放。用 FFmpeg 就能实现最简版本:

# 单路摄像头持续录制,每段 15 分钟,按时间命名
ffmpeg -rtsp_transport tcp -i "rtsp://192.168.1.105:8554/mjpeg/1" \
  -c:v copy -f segment -segment_time 900 \
  -segment_format mp4 \
  -strftime 1 \
  "/mnt/nvr/esp32_cam/%Y%m%d_%H%M%S.mp4"

关键参数解释:

  • -rtsp_transport tcp:用 TCP 拉流,比 UDP 更稳定,丢包时不会花屏
  • -c:v copy:不重编码,直接拷贝视频流,省 CPU
  • -segment_time 900:每 900 秒(15 分钟)切一段文件
  • -strftime 1:文件名用时间格式化,方便按时间检索

多路摄像头就开多个 FFmpeg 进程,每路一个。用 systemd 管理可以保证进程挂掉后自动重启:

# /etc/systemd/system/nvr-esp32.service
[Unit]
Description=NVR Record - ESP32 Cam
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/ffmpeg -rtsp_transport tcp -i "rtsp://192.168.1.105:8554/mjpeg/1" -c:v copy -f segment -segment_time 900 -segment_format mp4 -strftime 1 "/mnt/nvr/esp32_cam/%Y%m%d_%H%M%S.mp4"
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl enable nvr-esp32
sudo systemctl start nvr-esp32

这套方案能跑,但缺点明显:没有 Web 界面、没有实时预览、没有移动检测触发录制、存储满了没有自动清理。这就是需要 MiBeeNvr 这类专用系统的原因。

MiBeeNvr 的设计思路

根据作者的描述,MiBeeNvr 针对的就是家用场景——几路到十几路摄像头、一台 Linux 小主机(或树莓派)当服务器、不需要企业级功能但要足够省心。

一个家用 NVR 需要解决的核心问题:

问题 FFmpeg 脫手方案的短板 NVR 系统该做的事
实时预览 只能 ffplay 单路 Web 端多路同看
录制管理 手动启停 systemd 服务 按配置自动启停,异常自动恢复
存储策略 磁盘满了就崩 自动清理旧文件,保留最近 N 天
回放检索 只能按文件名猜时间 时间轴拖拽、事件检索
移动检测 不支持 画面变化时触发录制或标记

MiBeeNvr 作为轻量级系统,大概率在架构上走的是:Go 或 Python 写后端,拉流用 FFmpeg 或 GStreamer,前端提供实时预览和回放,后台管理存储生命周期。作者没在摘要里披露具体技术栈,但这类项目的典型实现可以参考下面这个结构。

一个可改造的迷你 NVR 项目骨架

如果你想自己搭一个类似系统,或者给 MiBeeNvr 做二次开发,下面是一个最小可运行的 Python + FFmpeg NVR 骨架,包含拉流录制和存储清理:

#!/usr/bin/env python3
"""mini_nvr.py — 最简 NVR:拉流录制 + 自动清理旧文件"""

import subprocess, time, os, threading, schedule
from pathlib import Path
from datetime import datetime

# ── 配置 ──────────────────────────────────────────
CAMERAS = {
    "esp32_door":  "rtsp://192.168.1.105:8554/mjpeg/1",
    "rpi_garden":  "rtsp://192.168.1.110:8554/rpi_cam",
    "xiaomi_living": "rtsp://192.168.1.120:8554/live",
}
STORE_ROOT = Path("/mnt/nvr")
SEGMENT_SEC = 900          # 15 分钟一段
KEEP_DAYS   = 7            # 保留最近 7 天
FFMPEG      = "ffmpeg"

# ── 单路录制进程 ────────────────────────────────────
def record_stream(name: str, url: str):
    out_dir = STORE_ROOT / name
    out_dir.mkdir(parents=True, exist_ok=True)
    cmd = [
        FFMPEG, "-rtsp_transport", "tcp",
        "-i", url,
        "-c:v", "copy",
        "-f", "segment",
        "-segment_time", str(SEGMENT_SEC),
        "-segment_format", "mp4",
        "-strftime", "1",
        "-reset_timestamps", "1",
        str(out_dir / "%Y%m%d_%H%M%S.mp4"),
    ]
    while True:
        print(f"[{name}] 启动录制: {url}")
        proc = subprocess.Popen(cmd, stderr=subprocess.PIPE)
        proc.wait()
        print(f"[{name}] 进程退出 (code={proc.returncode}), 10秒后重启")
        time.sleep(10)

# ── 存储清理 ────────────────────────────────────────
def purge_old_files():
    cutoff = datetime.now().timestamp() - KEEP_DAYS * 86400
    for cam_dir in STORE_ROOT.iterdir():
        if not cam_dir.is_dir():
            continue
        for f in cam_dir.glob("*.mp4"):
            # 从文件名解析时间: 20240315_093000.mp4
            try:
                ts = datetime.strptime(f.stem, "%Y%m%d_%H%M%S").timestamp()
                if ts < cutoff:
                    f.unlink()
                    print(f"[清理] 删除 {f}")
            except ValueError:
                pass

# ── 主入口 ──────────────────────────────────────────
def main():
    # 为每路摄像头启动录制线程
    for name, url in CAMERAS.items():
        t = threading.Thread(target=record_stream, args=(name, url), daemon=True)
        t.start()

    # 每天凌晨 3 点清理旧文件
    schedule.every().day.at("03:00").do(purge_old_files)
    print("NVR 已启动,Ctrl+C 退出")
    while True:
        schedule.run_pending()
        time.sleep(60)

if __name__ == "__main__":
    main()

运行方式:

# 安装依赖
pip install schedule

# 前台运行(测试)
python3 mini_nvr.py

# 后台运行(生产)
nohup python3 mini_nvr.py >> /var/log/nvr.log 2>&1 &

修改 CAMERAS 字典加入你自己的摄像头 RTSP 地址,调整 KEEP_DAYS 控制存储保留天数。这个骨架没有 Web 界面和移动检测,但已经覆盖了 NVR 最核心的三个功能:多路并行录制、断流自动重启、存储自动清理。

自建 NVR 的硬件选择

跑 NVR 不需要很强力的硬件,关键看摄像头路数和编码格式:

  • 2-4 路 MJPEG 流:树莓派 4B 就够,CPU 占用低因为 -c:v copy 不重编码
  • 4-8 路 H.264 流:建议用 J4125/N5105 小主机,低功耗 x86,跑 Linux 稳定
  • 8 路以上:考虑 N100 或更强的小主机,或者用带硬件解码的板子

存储方面,一块 2TB 机械硬盘挂在小主机上,8 路 720p 影像存 7 天大概占 200-400GB(视编码和帧率而定),2TB 留两周绰绰有余。

上手建议

如果你也在纠结云存储的厂商绑定和持续费用,可以按这个顺序推进:

  1. 先让所有摄像头吐出 RTSP 流。ffplay 逐路验证,确保局域网内可拉流。小米摄像头是最麻烦的一环,提前调研你那个型号的 RTSP 开放路径。
  2. 用 FFmpeg 命令行手动录制几小时。 确认稳定性、估算存储量,再决定硬盘容量。
  3. 跑上面的 mini_nvr.py 骨架或直接试用 MiBeeNvr。 先接 1-2 路,跑稳后再逐步加入其余摄像头。
  4. 考虑网络和电力冗余。 NVR 机器接 UPS,摄像头尽量走有线以太网而不是 Wi-Fi——Wi-Fi 在多路推流时抖动明显。

自建 NVR 的投入是一次性的:一块硬盘、一台小主机、几根网线。换来的是数据完全自主、不按月交费、不受厂商政策变化影响。对于家里有多个摄像头的人来说,这笔账算下来很划算。


相关推荐