MiBeeNvr:我写的一个轻量级家用 NVR 系统

家里有好几个摄像头,两台小米,一台自己用 ESP32 搭的,还有一台树莓派 CSI 摄像头。之前一直在用一些云存储方案,但总觉得不放心:厂商绑定、依赖网络、费用还不少。索性自己动手,写了个 NVR 系统,就叫 MiBeeNvr。

为什么做 MiBeeNvr

说实话,我对现有的云存储方案一直不太满意。先说小米摄像头吧,默认只能通过米家 app 查看,录像存储要么靠 SD 卡(容量有限,还得经常插拔),要么就得上云。云存储每个月几十块,而且最关键的是隐私问题——你不知道厂商什么时候会把你的视频数据拿去训练 AI,或者干脆就把数据卖给了第三方。更别提厂商绑定了,想换平台都难。

ESP32 摄像头也是类似的情况。自己用 ESP32 搭了几个摄像头,录像是存 SD 卡,但查看和回放都不方便。需要一个统一的管理平台。

市面上其他开源方案我也试过:ZoneMinder 需要 LAMP 堆栈,安装部署比我整个项目还复杂;Shinobi 配置复杂得一塌糊涂;还有些小众项目基本上都没维护了。Frigate 虽然不错,但主要是针对 AI 检测,而且依赖 Docker,太重了。

说白了就是想要一个:

  • 单文件二进制,下载就能跑
  • 资源占用要少,树莓派都能跑
  • 支持多种摄像头,特别是小米私有协议
  • Web 界面要清爽,不用折腾前端
  • 自动清理旧录像,不会占满硬盘

折腾了一圈发现,现成方案都不太合适,那就自己写一个吧。

MiBeeNvr 是什么

MiBeeNvr 是一个用 Go 写的轻量级 NVR 系统,专门解决家用摄像头的本地存储问题。

整体架构

先看一张全局架构图,整个系统长这样:

mermaid
graph TB
    subgraph 摄像头端
        CAM1["📷 小米摄像头<br/>miss 协议"]
        CAM2["📷 ESP32 摄像头<br/>HTTP JPEG"]
        CAM3["📷 树莓派 CSI<br/>RTSP H.264"]
    end

    subgraph 协议桥接
        G2RTC["go2rtc<br/>miss → RTSP"]
        MTX["MediaMTX<br/>CSI → RTSP"]
    end

    subgraph MiBeeNvr
        direction TB
        API["REST API"] --> REC["录制引擎"]
        REC --> STORE["SQLite 存储"]
        STORE --> CLEAN["自动清理"]
        API --> HLS["HLS 直播"]
    end

    subgraph 接入方式
        direction TB
        WEB["Web UI"]
        WEBDAV["WebDAV / FTP"]
        PROM["Prometheus"]
    end

    CAM1 -->|"miss"| G2RTC -->|"RTSP"| API
    CAM2 -->|"HTTP"| API
    CAM3 -->|"RTSP"| MTX -->|"RTSP"| API

    API --> WEB
    API --> WEBDAV
    API --> PROM

三层结构:摄像头端负责采集,中间有协议桥接层处理私有协议,MiBeeNvr 核心做录制和存储,上面再挂各种访问方式。

录制流水线

视频流进入 MiBeeNvr 之后的处理流程:

mermaid
flowchart LR
    subgraph 输入
        RTSP["RTSP 连接<br/>gortsplib"]
        HTTP["HTTP JPEG<br/>定时抓帧"]
    end

    subgraph 解码 & 封装
        RTP["RTP 解包<br/>pion/rtp"]
        MP4["MP4 封装<br/>go-mp4"]
    end

    subgraph 存储
        SEG["分段文件<br/>30s / 10m"]
        DB["SQLite 元数据"]
        DISK["磁盘"]
    end

    RTSP --> RTP --> MP4
    HTTP --> MP4
    MP4 --> SEG --> DISK
    MP4 --> DB

说白了就是:RTSP → RTP 解包 → MP4 封装 → 分段存储。前端用的是 Svelte 5,整个 SPA 直接编译成静态资源然后 embed 到 Go 二进制里,这样部署时只需要一个文件,连 Web 服务器都省了。

后端技术栈:

  • Go 1.26 + modernc.org/sqlite(纯 Go 实现,无 CGO 依赖)
  • chi 路由库,简洁高效
  • gortsplib 处理 RTSP/RTP 协议
  • pion/rtp 处理实时流媒体

选 SQLite 是因为它单文件、纯 Go 实现、性能对家用场景完全够用、支持并发访问,最重要的是不需要额外装数据库。

设计理念

整个项目的设计哲学就是「简单粗暴」:

  • 单个二进制文件,无任何外部依赖
  • 支持交叉编译,AMD64/ARM64 都能跑
  • 配置文件用 YAML,简单直观
  • 内置 Web 界面,打开浏览器就能用
  • 资源占用极少,树莓派 4 也能流畅运行

主要特性

  • 支持多种摄像头协议:RTSP(H.264/H.265)、HTTP JPEG
  • 内置 Web 界面,支持明暗主题切换
  • 中文/英文双语支持
  • WebDAV(可读写)、FTP、REST API
  • MQTT 触发录制,适合智能家居集成
  • Prometheus 监控指标
  • 每个摄像头独立的保留策略
  • MP4 分段录制,自动清理旧文件
  • 支持 HLS 直播流(最多 4 个并发)

我的实际部署

我这边是用一台 ARM64 小主机跑的,512MB 内存,2GB 存储。整个系统跑得很稳定,基本上是设置完就不管了。

接了 4 个摄像头,各有特点:

  1. 树莓派 CSI 摄像头 - 通过 MediaMTX 做 RTSP 桥接,把 CSI 接口的视频流转换成标准 RTSP。配置 rtsp_h264
  2. ESP32-S3 摄像头 - 自己搭的,跑 MJPEG 流,通过 HTTP 协议接入。配置 http_jpeg
  3. 小米摄像头(阳台) - 通过 go2rtc 把小米私有协议转成 RTSP,2K 分辨率,配置 rtsp_h265
  4. 小米摄像头(客厅) - 同上,1080P。

配置是 30 秒分段录制,保留 1 天。这个时间间隔是个权衡:太短了文件太多,太长了万一出事查起来不方便。开了 WebDAV(可读写)和 FTP,方便手机查看和备份。

摄像头管理页面

Web 界面挺清爽的,摄像头管理、录像列表都有。

录像列表(暗色)

设置页面:

设置页面

配置文件

完整的配置文件长这样,YAML 格式,一目了然:

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
server:
  listen: ":9090"

storage:
  root_dir: "/mnt/data/nvr"
  segment_duration: "30s"

cameras:
  - id: "rpi-csi-cam"
    name: "RPi CSI Camera"
    protocol: "rtsp_h264"
    url: "rtsp://10.0.1.100:8554/stream"
    enabled: true

  - id: "esp32-cam"
    name: "ESP32-S3 Camera"
    protocol: "http_jpeg"
    url: "http://10.0.1.101/capture"
    enabled: true

  - id: "xiaomi-balcony"
    name: "Xiaomi Camera"
    protocol: "rtsp_h265"
    url: "rtsp://10.0.1.102:8554/xiaomi_stream"
    enabled: true

cleanup:
  retention_days: 30
  disk_threshold_percent: 95

auth:
  username: "admin"
  password_hash: "用 mibee-nvr hash-password 命令生成"

webdav:
  enabled: true
  path_prefix: "/dav"
  read_write: true

ftp:
  enabled: true
  port: 2121

小米摄像头接入

小米摄像头的协议问题是个大坑。它用自己私有的 “miss”(Mi Secure Streaming)协议,做了多层加密,不开放标准 RTSP 接口。也就是说,你就算知道摄像头的 IP,也没法用 VLC 直接拉流。

不过好在有 go2rtc 这个神器。整个接入链路是这样的:

mermaid
sequenceDiagram
    participant APP as 米家 App
    participant CLOUD as 小米云
    participant CAM as 小米摄像头
    participant G2 as go2rtc
    participant NVR as MiBeeNvr

    Note over G2,CLOUD: 1. 账号认证 & 密钥交换
    G2->>CLOUD: 用小米账号登录
    CLOUD-->>G2: 返回设备列表 & 加密密钥

    Note over G2,CAM: 2. 建立 P2P 连接
    G2->>CAM: 发起 miss 协议握手
    CAM-->>G2: P2P 连接建立

    Note over G2,NVR: 3. 视频流转发
    loop 持续录制
        CAM->>G2: miss 加密视频流
        G2->>G2: 解密 & 转码
        G2->>NVR: 标准 RTSP 流(H.265)
        NVR->>NVR: MP4 分段录制
    end

    Note over APP,NVR: 4. 不再依赖米家 App
    NVR->>NVR: Web UI / WebDAV / FTP 查看录像

整个过程不需要刷机、不需要拆摄像头、也不需要小米官方的云存储。go2rtc 帮你搞定了所有协议转换的事情。

go2rtc 部署

go2rtc 用 Docker 部署最省事:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 创建配置文件
cat > go2rtc.yaml << EOF
streams:
  xiaomi_balcony:
    - xiaomi://your_account:[email protected]?did=your_camera_did&model=isa.camera.hlc7
  xiaomi_living_room:
    - xiaomi://your_account:[email protected]?did=your_camera_did&model=isa.camera.mj200

rtsp:
  listen: ":8554"
EOF

# 运行容器
docker run -d --name go2rtc \
  -p 8554:8554 \
  -p 1984:1984 \
  -v $(pwd)/go2rtc.yaml:/config.yaml \
  alexxit/go2rtc

关键点:

  • xiaomi:// 协议需要小米账号和密码认证
  • did 是设备的唯一标识,model 是设备型号(在米家 app 里能找到)
  • go2rtc 会自动处理 P2P 连接和 miss 协议解密
  • 最终在 8554 端口暴露标准的 RTSP 流,MiBeeNvr 当普通摄像头接入就行

然后在 MiBeeNvr 的配置里指向 go2rtc:

yaml
1
2
3
4
5
6
cameras:
  - id: "xiaomi-balcony"
    name: "小米摄像头"
    protocol: "rtsp_h265"
    url: "rtsp://localhost:8554/xiaomi_balcony"
    enabled: true

踩坑记录

小米摄像头的接入有不少坑:

  1. 首次联网:小米摄像头必须能连外网,因为需要和小米服务器做密钥交换。建立连接后,后续传输是局域网内的。
  2. 设备 ID 获取:每个摄像头的 did 都不同,需要用 go2rtc 的 WebUI(端口 1984)自动发现,或者在米家 app 里翻。
  3. 不是所有型号都支持:go2rtc 维护了一个兼容列表,买摄像头之前最好先查一下。
  4. H.265 vs H.264:新款小米摄像头基本都是 H.265,MiBeeNvr 对两种编码都支持,但 H.265 省存储空间。

ESP32 摄像头项目

搞 MiBeeNvr 的时候,顺带做了几款 ESP32 摄像头固件。ESP32 摄像头这个坑踩了不少,但也挺有意思的。

三款固件定位不同:

mermaid
graph LR
    subgraph ESP32 摄像头固件
        A["MiBeeCam<br/>ESP32-S3-A10<br/>入门首选"]
        B["AI_Thinker CAM<br/>ESP32-CAM<br/>性价比之王"]
        C["MiBeeHomeCam<br/>XIAO ESP32-S3<br/>功能最强"]
    end

    subgraph NVR 统一管理
        NVR["MiBeeNvr"]
    end

    A -->|"HTTP JPEG"| NVR
    B -->|"HTTP JPEG / WebDAV"| NVR
    C -->|"HTTP JPEG / FTP"| NVR

所有固件都设计成 MiBeeNvr 的上游采集端:摄像头负责采集视频,MiBeeNvr 负责统一存储和管理。

MiBeeCam — ESP32-S3-A10 方案

GitHub · MIT 许可证

这个是最成功的方案。ESP32-S3-A10 开发板 + OV2640 摄像头(8225N 模块),16MB Flash,ESP-IDF v5.4.3 开发。功能上 MJPEG 流、帧差法移动检测、Web 配置界面、Prometheus 指标都有。说实话有块 LCD 屏幕调试方便很多。

AI_Thinker ESP32-CAM — 经典方案

GitHub · MIT 许可证

入门级选择,AI_Thinker ESP32-CAM 开发板到处都能买到,十几块钱一块。4MB Flash + 4MB PSRAM,跑 MJPEG 流没问题。亮点是支持 SD 卡存储和 NAS 上传(WebDAV/HTTP),还做了自适应暗场景检测——晚上会自动切换红外模式。缺点是没有屏幕调试不方便,Flash 只有 4MB。

MiBeeHomeCam — XIAO ESP32-S3 Sense

GitHub · GPL v3.0

最高级的方案。XIAO ESP32-S3 Sense 板子小巧精致,带 OV2640/OV3660 双摄像头支持,8MB Octal PSRAM 充分利用。亮点是 AVI 分段录制(不是简单的截图,是真的视频录制)、FTP/WebDAV 双协议上传、看门狗防死机、芯片温度监控、批量文件管理。适合长期稳定运行的场景。

选择建议

  • 新手入门:选 AI_Thinker ESP32-CAM,便宜且资料多
  • 日常使用:选 MiBeeCam,有屏幕调试方便
  • 追求极致:选 XIAO ESP32-S3 Sense,功能最强

系统服务配置

为了稳定运行,用 systemd 管理 MiBeeNvr:

ini
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[Unit]
Description=MiBee NVR
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=nvr
ExecStart=/mnt/data/nvr/bin/mibee-nvr -config /mnt/data/nvr/mibee-nvr.yaml
WorkingDirectory=/mnt/data/nvr
Restart=on-failure
RestartSec=5

# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/mnt/data/nvr
PrivateTmp=true

[Install]
WantedBy=multi-user.target

保存到 /etc/systemd/system/mibee-nvr.service,然后 systemctl enable --now mibee-nvr 就完事了。自动开机启动,挂了也会自动重启。

开源地址

MiBeeNvr 已经开源,欢迎 star 和贡献:

  • MiBeeNvr:https://github.com/Mi-Bee-Studio/MiBeeNvr (MIT 许可证)
  • MiBeeCam:https://github.com/Mi-Bee-Studio/luatos-esp32s3-a10-camera (MIT)
  • AI_Thinker ESP32-CAM:https://github.com/Mi-Bee-Studio/ai-thinker-esp32-cam (MIT)
  • MiBeeHomeCam:https://github.com/Mi-Bee-Studio/seeed-esp32s3-cam (GPL v3.0)

文档比较全,部署和配置都有详细说明。

写在最后

说实话,折腾这个项目主要是因为对现有方案都不满意。云存储太贵,开源方案太重,商业产品又太封闭。自己写一个刚好:轻量、免费、可控。

对了,项目取名 MiBeeNvr,“Mi” 代表我(Mickey),“Bee” 代表?保密,“Nvr” 自然是网络录像机了。简洁好记,又有点意思。

如果你也有家用摄像头的需求,或者对 NVR 系统有什么想法,欢迎交流。有问题可以在 GitHub 上提 issue。