MiBeeNvr v0.3.1: 多协议流媒体与小米摄像头内置支持

v0.2.0 发布之后又折腾了不少,这次 v0.3.x 带来了几个重量级更新:小米摄像头内置支持、归档录像功能、多协议流媒体架构(WebRTC/HTTP-FLV/RTMP/SRT/LL-HLS),以及一大波安全加固。从外部依赖到内置实现、从单协议到全协议支持的架构演进过程,比我想象的要曲折得多。

上一篇介绍了 v0.2.0 的 15 个新功能(v0.2.0 更新),没看过第一篇的可以先看 MiBeeNvr 介绍。v0.3.0 聚焦小米摄像头深度集成,v0.3.1 在此基础上带来了完整的多协议流媒体架构。完整更新列表见 GitHub Release Notes

架构演变:从外部依赖到内置实现的曲折之路

小米摄像头支持的开发过程,是我做过最有趣的架构实验之一。整个过程经历了三个截然不同的阶段,每个阶段都有其独特的教训。

第一阶段:go2rtc 外部依赖 (v0.1.0 时代)

早期的 MiBeeNvr 完全依赖 go2rtc 来处理小米摄像头的协议转换:

mermaid
graph TB
    subgraph "小米摄像头"
        A[Camera]
    end
    
    subgraph "外部容器"
        B[go2rtc]
    end
    
    subgraph "MiBeeNvr"
        C[RTSP 客户端]
        D[HLS 编码]
        E[存储管理]
    end
    
    A -->|"miss 协议"| B
    B -->|"RTSP"| C
    C -->|HLS| D
    D -->|存储| E
    
    classDef hardware fill:#e3f2fd,stroke:#1976d2
    classDef process fill:#e8f5e9,stroke:#4caf50
    classDef storage fill:#fff3e0,stroke:#ff9800
    classDef network fill:#f3e5f5,stroke:#9c27b0
    
    class A hardware
    class B process
    class C,D,E storage

说实话,这个方案一开始看起来很优雅:用一个现成的 Docker 容器解决协议转换问题,主程序专心做 NVR 功能。但很快问题就来了:

  1. CS2 P2P 连接不稳定:小米摄像头的 P2P 连接大约 20 分钟就会超时卡死
  2. 调试困难:go2rtc 是独立项目,出了问题很难定位
  3. 运维复杂:需要管理两个 Docker 容器,端口冲突、资源争抢问题不断

归根结底,把核心功能依赖到外部容器上,本身就是个设计上的失误。

第二阶段:插件架构实验 (v0.2.0 到 v0.3.0 之间)

吸取教训之后,我决定把 go2rtc 的 Xiaomi 代码移植到 MiBeeNvr 里,做成插件系统。当时的想法很好:

  • 通过 Go 接口注册实现协议解耦
  • 使用 init() 基础的插件机制
  • 甚至实验了 gRPC 进程隔离

听起来很美好对吧?让架构变得可扩展,牺牲一点性能换来开发便利性。结果现实给了我一记响亮的耳光:

mermaid
flowchart LR
    subgraph "小米摄像头"
        A[Camera]
    end
    
    subgraph "插件进程"
        B[Plugin Process]
        C[gRPC 通信]
    end
    
    subgraph "MiBeeNvr 主程序"
        D[主 NVR 引擎]
        E[HLS 编码]
        F[存储管理]
    end
    
    A -->|"miss 协议"| B
    B -->|gRPC| C
    C -->|处理| D
    D -->|HLS| E
    E -->|存储| F
    
    classDef hardware fill:#e3f2fd,stroke:#1976d2
    classDef process fill:#e8f5e9,stroke:#4caf50
    classDef storage fill:#fff3e0,stroke:#ff9800
    classDef network fill:#f3e5f5,stroke:#9c27b0
    
    class A hardware
    class B,C process
    class D,E,F storage

实际使用中发现的问题:

  1. 架构过度复杂:实时预览多了好几层中间环节,性能下降明显
  2. 资源消耗增加:额外的进程和通信开销
  3. 新人门槛高:代码库变得难以理解,一个简单的功能需要跨进程通信

折腾了一圈才发现,有时候最简单的方案反而是最好的。

第三阶段:回归简洁 (v0.3.0)

最终在 v0.3.0 中,我彻底移除了插件系统,将 Xiaomi 代码迁移到内置的 internal/xiaomi/ 包中:

mermaid
graph TB
    subgraph "小米摄像头"
        A[Camera]
    end
    
    subgraph "MiBeeNvr 核心"
        B[internal/xiaomi/ 包]
        C[核心 NVR 引擎]
        D[HLS 编码]
        E[存储管理]
    end
    
    A -->|"miss 协议"| B
    B -->|直接处理| C
    C -->|HLS| D
    D -->|存储| E
    
    classDef hardware fill:#e3f2fd,stroke:#1976d2
    classDef process fill:#e8f5e9,stroke:#4caf50
    classDef storage fill:#fff3e0,stroke:#ff9800
    
    class A hardware
    class B,C,D,E process

正如 Release Notes 中所说:

“Plugin System Removed — Xiaomi migrated from gRPC plugin to built-in internal/xiaomi/ package”

“Full Xiaomi IP camera integration — no plugins, no external dependencies”

现在的架构就是一个单二进制文件,无需外部进程,无需 Docker 依赖。有时候,对于这个阶段的项目,简洁性比扩展性更重要。

小米摄像头功能详解

内置 CS2 P2P 协议

现在 MiBeeNvr 直接支持小米摄像头的 CS2 P2P 协议,不再需要 go2rtc 这个中间人。

云端认证流程

完整的认证流程包括:

  1. 使用小米账号登录
  2. 获取 passToken
  3. 自动发现设备列表
  4. 建立直连

支持的设备类型包括:

  • .camera. - 普通摄像头
  • .cateye. - 猫眼摄像头
  • .feeder. - 喂养器(HLC8 型号)

配置示例

mibeenvr.yaml 中添加小米摄像头配置:

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
xiaomi:
  user_id: "123456789"
  token: "your_passToken"
  region: "cn"

cameras:
  - id: "xiaomi-living"
    name: "客厅小米"
    protocol: "xiaomi"
    encoding: "h264"
    did: "device_id_here"
    vendor: "cs2"
    enabled: true

Web 界面

Web 界面提供了完整的账号管理功能:

  • 小米账号登录页面
  • 自动发现摄像头
  • 一键添加功能

说实话,这个认证流程比我想象的要复杂,但最终实现出来的用户体验还算不错。

实际界面是这样的:

小米设备自动发现界面

归档录像:独特的摄像机管理方式

归档录像功能是这次 v0.3.0 的一大亮点。这个需求的场景很简单:当你需要移除一个摄像头(坏了、搬家、升级)但又想保留历史录像时,传统 NVR 系统通常的做法是直接删除所有相关数据。

MiBeeNvr 提供了更人性化的解决方案:

mermaid
flowchart LR
    A[活跃摄像头] --> B[停止录制]
    B --> C[合并片段]
    C --> D[标记归档]
    D --> E[从配置移除]
    E --> F[录像保留]
    
    classDef process fill:#e8f5e9,stroke:#4caf50
    classDef storage fill:#fff3e0,stroke:#ff9800
    classDef network fill:#f3e5f5,stroke:#9c27b0
    
    class A,B,C,E process
    class D,F storage

三态摄像头管理

摄像头现在有三种状态:

  • Active - 正在录制
  • Disabled - 已暂停
  • Archived - 只读(历史记录)

归档流程

归档操作很直观:

  1. 在摄像头列表中点击"归档"
  2. 弹出确认对话框
  3. 输入 “DELETE” 确认
  4. 系统自动合并片段并标记归档
  5. 摄像头从实时列表移除,但录像完整保留

归档后管理

归档后的录像支持:

  • 播放历史录像
  • 下载单个录像文件
  • 删除指定录像
  • 设置归档组保留期限

实际效果如下:

归档摄像头管理界面
说实话,这个功能是我在使用其他 NVR 系统时最想要的。大多数系统在移除摄像头时直接一刀切删除,完全没有保留历史数据的意识。

多协议流媒体:从 HLS 到全协议支持

v0.3.1 最重量级的变化是引入了完整的多协议流媒体架构。v0.3.0 时代只有 HLS 一种播放协议,延迟在 5-15 秒,对于实时监控来说体验不算好。这次直接上了 5 种协议,延迟最低可以到亚秒级。

StreamHub 多消费者扇出

核心架构变化是引入了 StreamHub —— 一个多消费者帧分发器。之前每路摄像头只能对接一个消费者(HLS 编码器),现在可以同时扇出到多个协议输出:

mermaid
flowchart LR
    A[摄像头源] --> B[StreamHub]
    B --> C[HLS]
    B --> D[WebRTC]
    B --> E[HTTP-FLV]
    B --> F[RTMP]
    B --> G[SRT]
    
    classDef input fill:#E3F2FD,stroke:#1565C0,color:#1565C0
    classDef hub fill:#FFF3E0,stroke:#E65100,color:#BF360C
    classDef output fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
    
    class A input
    class B hub
    class C,D,E,F,G output

StreamHub 的设计很简单:每个录制器把帧广播到 StreamHub,消费者(HLS/WebRTC/FLV 管理器)各自订阅感兴趣的流。这样添加新协议只需要实现一个新的消费者,不需要动录制器的代码。

协议对比

五种协议各有适用场景:

协议延迟编码方向适用场景
WebRTC (WHEP)<1sH.264拉流实时预览、对讲
HTTP-FLV1-3sH.264/H.265拉流Web 低延迟监控
LL-HLS2-5sH.264/H.265拉流低延迟兼容方案
HLS5-15sH.264/H.265拉流最大兼容性
RTMPH.264推流OBS 等推流工具
SRTH.264推流远程/不稳定网络推流

协议切换器

前端新增了 ProtocolSwitcher 组件,用户可以在播放界面一键切换协议。下拉菜单显示当前可用的协议和预估延迟,H.265 摄像头会自动排除不支持的协议(如 WebRTC)。

WebRTC WHEP 实现

WebRTC 通过 WHEP (WebRTC-HTTP Egress Protocol) 标准实现,后端用 pion/webrtc v4:

  • 每个 H.264 摄像头最多 2 个 peer
  • 空闲超时自动驱逐
  • SDP 音频拒绝(纯视频场景)
  • 僵尸连接检测 + 自动重连

说实话,WebRTC 的信令交换比我想象的简单多了,WHEP 协议把 SDP 交换简化成了一个 POST 请求。

RTMP 推流与 SRT 接收

除了拉流协议的丰富,v0.3.1 还增加了推流接入能力:

  • RTMP:基于 gortmplib 实现,支持 stream key 认证,H.264 流直接进入 StreamHub
  • SRT:基于 gosrt 实现,MPEG-TS 解复用后提取 H.264 NALU 送入 StreamHub

这意味着你可以用 OBS 推流到 MiBeeNvr,或者通过 SRT 在不稳定网络环境下远程推送视频流。

初始化向导:首次运行体验

v0.3.1 新增了 Setup Wizard,第一次打开应用时会自动进入配置向导:

  1. 自动检测浏览器支持的流媒体协议
  2. 推荐最佳协议(优先级:LL-HLS > WebRTC > HTTP-FLV > HLS)
  3. 设置管理员密码
  4. 创建初始配置

这修复了 v0.3.0 的一个痛点:Docker 部署时初始化页面不会自动显示,需要先尝试登录才会跳转。现在 /api/health 接口新增了 setup_required 字段,前端可以在页面加载时主动检测未初始化状态并自动跳转到 #/setup

v0.3.1 体验优化

Dashboard 多协议支持

Dashboard 网格现在支持多协议播放。每个摄像头格子右上角显示协议徽章,颜色区分不同协议:WebRTC 绿色、FLV 橙色、HLS 蓝色、JPEG 灰色。默认协议在设置页面配置,不可用时自动降级到 HLS → JPEG。

Settings 页面重构

设置页面拆分成了 General 和 Advanced 两个标签页:

  • General:清理策略、前端偏好、默认协议选择、协议指南
  • Advanced:合并策略、WebDAV、流媒体配置(WebRTC/FLV/RTMP/SRT)、Feature Toggles

之前所有设置堆在一页里,现在分类清晰多了。

摄像头品牌兼容指南

新增了 20+ 摄像头品牌的兼容指南文档,包括海康、大华、宇视、Axis、TP-Link、小米等品牌的 RTSP 地址示例和配置方法。

v0.3.0 + v0.3.1 亮点汇总

安全加固(v0.3.0)

v0.3.0 包含了 15+ 个安全修复:

  • 路径验证加强
  • 认证机制硬化
  • 速率限制优化
  • 安全头设置
  • SQL 注入防护

前端全面升级(v0.3.0)

前端完成了 Svelte 5 的完整迁移:

  • 运行模式 (runes) 语法
  • 移除所有遗留语法
  • 国际化 500+ 个键值

HLS 改进(v0.3.0)

HLS 播放器优化:

  • 指数退避自动重试
  • 僵尸播放器检测
  • 子流降级支持

v0.3.1 Bug 修复

  • Docker 初始化页面修复:新增 setup_required 字段到 health 接口,主动检测并跳转
  • LL-HLS 优先级修正:协议推荐顺序改为 LL-HLS > WebRTC > HTTP-FLV > HLS
  • 小米摄像头 PTS 修复:PTS 基准在重连时被重置导致 HLS 时间戳重启,移至 Start() 只设一次
  • ONVIF 错误传播:发现错误不再静默吞掉,返回结构化错误信息(NETWORK/TIMEOUT/NO_DEVICES/PARSE_ERROR)
  • WebRTC 僵尸检测:Dashboard 多格子快速挂载/销毁时的 null 检查修复
  • 统计图表单位:修复 GB 级数据显示为 MB 的阈值偏移问题

总结 + 开源链接

从 v0.2.0 到 v0.3.1,总共 160+ 个提交。v0.3.0 带来了小米摄像头内置支持和归档录像(141 commits, 45 features, 48 fixes),v0.3.1 在此基础上引入了完整的多协议流媒体架构(WebRTC/HTTP-FLV/RTMP/SRT/LL-HLS)、Setup Wizard 和一系列体验优化。

从外部依赖到插件实验,再到内置简洁实现;从单协议 HLS 到 StreamHub 扇出的全协议支持。两次架构演进让我深刻体会到:

有时候,最"正确"的架构并不是最灵活的那个。但对于流媒体来说,协议可扩展性确实是必须的。

开源地址:

折腾了这么久,从小米摄像头到全协议流媒体,MiBeeNvr 终于从一个简单的 NVR 走向了更通用的视频流平台。如果你也在折腾智能家居或者需要轻量级 NVR 方案,不妨试试看。