MiBeeNvr v0.5.0: ONVIF 全协议支持 + 硬件转码 + rpi-cam 协同开发

v0.4.0 发布不到一周,又肝了 31 个提交。v0.5.0 是一个功能密度很高的版本:ONVIF 全协议支持(Device/Media/PTZ/Imaging/Event 五大服务全覆盖)、硬件转码(H.265 → H.264)、录制器重连优化。127 个文件变更,+24,509 / -730 行。完整更新列表见 GitHub Release Notes

没看过之前版本的可以先看 MiBeeNvr 介绍v0.4.0 技术帖

ONVIF 全协议支持

v0.4.0 已经有了 ONVIF 设备发现和流地址获取,但那只是 ONVIF 协议的冰山一角。v0.5.0 补全了 ONVIF Profile S 的核心服务:

ONVIF 服务功能状态
Device ServiceWS-Discovery、能力查询、Profile 列表
Media Service流地址获取、媒体配置
PTZ Service连续/相对/绝对移动、预置位、归位✅ 新增
Imaging Service亮度/对比度/饱和度/锐度/聚焦(原始 SOAP)✅ 新增
Event ServicePullPoint 订阅,实时摄像头事件✅ 新增
SnapshotGetSnapshot JPEG 抓图✅ 新增

ONVIF 交互流程

一个典型的 ONVIF 摄像头从发现到控制的完整流程:

mermaid
sequenceDiagram
    participant NVR as MiBeeNvr
    participant CAM as ONVIF 摄像头

    Note over NVR,CAM: ① WS-Discovery 发现设备
    NVR->>CAM: Probe (Multicast)
    CAM-->>NVR: ProbeMatch (XAddr)

    Note over NVR,CAM: ② 获取设备能力
    NVR->>CAM: GetCapabilities
    CAM-->>NVR: Device/Media/PTZ/Imaging URI

    Note over NVR,CAM: ③ 获取媒体配置
    NVR->>CAM: GetProfiles
    CAM-->>NVR: 编码/分辨率/帧率

    Note over NVR,CAM: ④ 获取流地址
    NVR->>CAM: GetStreamUri
    CAM-->>NVR: rtsp://host:port/stream

    Note over NVR,CAM: ⑤ PTZ 控制
    NVR->>CAM: ContinuousMove / AbsoluteMove
    CAM-->>NVR: PTZ 响应

    Note over NVR,CAM: ⑥ 图像参数调节
    NVR->>CAM: GetImagingSettings / SetImagingSettings
    CAM-->>NVR: 当前/更新后的参数

    Note over NVR,CAM: ⑦ 事件订阅
    NVR->>CAM: PullPoint Subscribe
    CAM-->>NVR: 实时事件通知

前端集成

v0.5.0 在前端完整接入了 ONVIF 所有服务:

  • DeviceManagement — 设备信息展示、能力查询
  • ImagingPanel — 亮度、对比度、饱和度、锐度实时滑块调节
  • PresetManager — PTZ 预置位创建、调用、删除
  • ONVIFEvents — 实时事件订阅和展示
  • DeviceCapabilities — LiveView 中显示设备能力信息

UI 大改版

v0.5.0 的前端有了比较大的变化,ONVIF 相关功能全部融入了 LiveView 和设置页面:

  • ONVIF 设备管理面板 — 在摄像头详情页可以看到设备信息、能力查询结果,直接管理 ONVIF 连接

ONVIF PTZ 控制和预置位管理

  • PTZ 控制摇杆 + 预置位管理 — LiveView 中直接操作云台,创建和管理预置位,一键归位
  • 图像参数实时调节 — 滑块控制亮度、对比度、饱和度、锐度,调完即时生效
  • ONVIF 事件流 — 实时展示摄像头上报的事件(移动检测、报警等)
  • 转码状态监控 — 新增监控页面,展示转码任务状态和性能指标

转码状态监控页面

连接缓存

ONVIF 的 SOAP 请求开销不小——每次操作都要建立 HTTP 连接、解析 WSDL、组装 XML。v0.5.0 对每个摄像头维护了一个 ONVIF 客户端连接池,避免重复建连。对于需要频繁调 PTZ 或调参数的场景,响应速度明显提升。

为什么要造 rpi-cam

ONVIF 协议开发有一个很现实的问题:需要一个能跑的 ONVIF 服务端来调试。光看协议文档和抓包是不够的,客户端实现到底对不对,得有个对端实际交互才知道。

我手边有一台树莓派 3B + OV5647 摄像头,之前跑着 MediaMTX 推 RTSP 流。但 MediaMTX 没有 ONVIF 服务端模式(GitHub issue #1402),NVR 发现不了它。

找了一圈现成方案:

  • Python 写的 — 性能不行,内存占用高
  • 功能不完整 — 只支持 Device 服务,缺 Media/PTZ/Imaging
  • 需要 CGO — 交叉编译太麻烦

而且有个关键优势:MiBeeNvr 的 ONVIF 客户端用的是 0x524a/onvif-go 库,如果服务端也用同一个库,客户端和服务端就是完全兼容的,调试起来特别方便。

于是就造了 rpi-cam(也叫 raspberrypi-camera)。

rpi-cam 是什么

rpi-cam 是一个用 Go 写的轻量级树莓派 ONVIF 相机服务,专门为资源受限的 ARM 设备设计。核心特性:

  • 完整的 ONVIF Profile S — Device/Media/PTZ/Imaging 四大服务 + WS-Discovery
  • RTSP 流媒体 — 使用和 MediaMTX 同样的 gortsplib 库
  • RTMP 推流 — 支持推到阿里云、腾讯云等云服务
  • 相机参数控制 — 亮度、对比度、饱和度、锐度实时调节
  • 数字 PTZ — 通过软件裁剪实现平移、缩放
  • 零 CGO — 纯 Go,交叉编译无压力
  • 15-25 MB 内存 — 替换掉 MediaMTX 后内存占用降了一半
mermaid
flowchart LR
    subgraph "树莓派"
        CAM["CSI/USB 相机"]
        RPC["rpi-cam"]
    end

    subgraph "MiBeeNvr"
        ONVIF["ONVIF 客户端"]
        REC["录制器"]
    end

    subgraph "云服务"
        CLOUD["RTMP 推流"]
    end

    CAM --> RPC
    RPC -->|"ONVIF Profile S"| ONVIF
    RPC -->|"RTSP"| REC
    RPC -->|"RTMP"| CLOUD

    classDef cam fill:#E3F2FD,stroke:#1565C0,color:#1565C0
    classDef app fill:#FFF3E0,stroke:#E65100,color:#BF360C
    classDef ext fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20

    class CAM cam
    class RPC,ONVIF,REC app
    class CLOUD ext

客户端 + 服务端协同开发

rpi-cam 和 MiBeeNvr 的 ONVIF 实现使用的是同一个 onvif-go 库。这意味着:

  1. 协议兼容性零成本 — 客户端的 SOAP 请求格式和服务端的解析逻辑完全一致
  2. 双向验证 — 发现问题可以同时排查客户端和服务端
  3. 快速迭代 — 改完客户端立刻能在 rpi-cam 上验证,不需要等真实摄像头响应

实际开发过程中,ONVIF 的 PTZ、Imaging、Event 服务都是同时在 MiBeeNvr(客户端)和 rpi-cam(服务端)两端开发的。写完一个服务的客户端调用代码,立刻用 rpi-cam 验证,发现问题当场修。这种开发效率是用真实 IP 摄像头调试做不到的——真实设备可能不支持某些操作,返回的错误信息也不够明确。

实测对比

在树莓派 3B(905MB 内存)上,rpi-cam 替换 MediaMTX 之后的对比:

指标rpi-camMediaMTX
内存占用15-25 MB~45 MB
ONVIF 服务端✅ Profile S 完整支持❌ 不支持
相机参数控制✅ 亮度/对比度等❌ 无
RTMP 推流✅ 内置❌ 需额外配置
CGO 依赖需要

rpi-cam 已经在树莓派上稳定运行了几个月,没掉过线。感兴趣可以看 rpi-cam GitHub 和之前的 介绍文章

硬件转码系统

另一个重要特性是硬件加速转码(H.265 → H.264)。很多摄像头只输出 H.265,但浏览器直接播放 H.265 还是比较困难。v0.5.0 的转码系统可以自动检测输入编码,在需要时进行硬件加速转码:

  • 自动检测 — 识别摄像头编码格式,按需启动转码
  • 硬件加速 — 利用设备 GPU 进行转码,降低 CPU 开销
  • 冻帧防闪 — 转码过程中的冻帧防闪处理,流更平滑

配合新增的监控页面,可以实时查看转码状态和性能指标。

硬件转码对设备有要求

转码依赖硬件的视频编解码能力。当前的主力设备树莓派 3B 的 VideoCore IV GPU 不支持 H.265 硬件解码,遇到 H.265 摄像头只能靠 CPU 软解,905MB 内存 + Cortex-A53 @ 1.2GHz 的算力实在捉襟见肘。

接下来我会引入 Banana Pi BPI-M5 来完善转码和视频存储能力。BPI-M5 搭载的 Amlogic S905X3 芯片内置 Mali-G31 MP2 GPU,支持 H.265/HEVC 4K@60fps 硬件解码,更适合做 NVR 的转码节点。

参数树莓派 3BBanana Pi BPI-M5
SoCBroadcom BCM2837Amlogic S905X3
CPUCortex-A53 × 4 @ 1.2GHzCortex-A55 × 4 @ 1.8GHz
GPUVideoCore IVMali-G31 MP2
RAM1GB LPDDR24GB DDR4
H.265 解码❌ 不支持✅ 4K@60fps
H.264 解码✅ 1080p@30fps✅ 1080p@60fps
有线网络100Mbps1000Mbps
USB2.0 × 43.0 × 4
存储microSDeMMC + microSD

BPI-M5 在 CPU 性能、内存、H.265 硬解、千兆网口、USB 3.0 全面碾压树莓派 3B。NVR 场景下,千兆网口意味着更多摄像头并发不卡,4GB 内存可以缓存更多实时流,H.265 硬解让转码不再是瓶颈。后续转码功能的完善会在 BPI-M5 上进行。

录制器重连优化

v0.5.0 重构了录制器的重连策略。之前用指数退避,但实际场景中固定间隔的重试更可控。新的分级退避策略:

1
1s → 5s → 10s → 60s(带随机抖动)

每一级都有随机抖动,避免多个摄像头同时断连后同时重连造成雪崩。RTSP 连接超时也加入了配置项,可以根据网络环境调整。

Bug 修复

v0.5.0 修复了几个影响稳定性的问题:

  • CS2 缓冲区溢出 — 丢弃最旧帧而不是触发完整 P2P 重连,小米摄像头稳定性显著提升
  • Docker 首次启动崩溃 — autoInitConfig 调用 ApplyDefaults(),HLS 字段在验证前正确填充
  • 健康告警误报 — 修正阈值、增加防抖、支持每摄像头覆盖
  • UI 认证跳转 — 修复 hash 同步问题导致的导航死循环
  • ONVIF ContinuousMove — 提供默认超时
  • ONVIF PTZ 预置位 — 补全缺失的 HTTP 端点

社区反馈已解决

v0.4.x 和 v0.5.0 期间收到了不少社区反馈,以下是 GitHub Issues 中已关闭的问题:

Docker 部署问题

#6 数据库打开失败 — Docker 首次启动时 db open: unable to open database file (14)。原因是 DB 初始化在存储管理器创建目录之前执行。已在代码中增加 os.MkdirAll 确保目录存在。

#8 绿联云 NAS 权限错误 — 绿联云 NAS 上 Docker 部署权限不匹配。解决方案:Docker 设置 user:0:0,文件管理器将 data 目录权限设置为普通用户。

#13 Docker 首次启动配置缺失 — Docker 首次启动自动生成配置时,HLS 默认值未正确填入,导致 hls.segment_count must be between 3 and 10, got 0 错误。v0.5.0 的 autoInitConfig 调用了 ApplyDefaults() 确保 HLS 字段在验证前填充。

小米摄像头连接稳定性

#10 / #11 小米摄像头频繁断连 — 摄像头在"录制"和"重连中"之间循环,日志出现 cs2: pop buffer is fullcs2: EOF

v0.5.0 做了三层修复:

  1. 缓冲区溢出不再重连 — 满时丢弃最旧帧并推入新帧,而不是触发完整 P2P 重连。对视频流来说丢一帧远比重建整个会话代价小
  2. 重连策略改为分级退避 — 前 5 次 1s、5-9 次 5s、10-19 次 10s、20+ 次 60s,成功连接后重置计数
  3. CS2 连接挂起修复 — 修复了约 20 分钟后连接挂起的问题,消除了 UDP 写入的数据竞争

#5 重启容器后摄像头不可用 — 部分摄像头使用 TUTK 协议(而非 CS2),代码中仍向小米云请求了 TUTK 参数导致误导性报错。已从云请求中移除 TUTK,仅支持 TUTK 的摄像头在添加时会给出明确提示。

设备兼容性

#9 小米智能室外摄像机 4 Pro 三摄变焦版 — 该型号使用 TUTK 传输协议(非 CS2),需要商业授权,目前不在支持范围内。如果摄像头支持 RTSP,推荐用 RTSP 方式接入。


如果你遇到了类似问题,先升级到最新版本试试。如果问题仍然存在,欢迎到 GitHub Issues 提交并附上日志,我会跟进排查。

关于 ONVIF 兼容性

ONVIF 协议虽然号称标准,但实际上不同厂商的实现差异很大。rpi-cam 作为开发调试工具保证了 MiBeeNvr 自身的 ONVIF 实现是正确的,但对接真实的商用 IP 摄像头时,可能还会遇到各种边界情况。

ONVIF 的完整兼容性还需要时间验证。目前主要在 rpi-cam(自研服务端)和少量真实摄像头上测试通过,覆盖的品牌和型号还有限。如果你在用 MiBeeNvr 对接 ONVIF 摄像头时遇到问题——比如发现不了设备、PTZ 控制不响应、参数调节失败——请到 GitHub Issues 提交,附上摄像头型号和具体现象,我会跟进排查。

升级

bash
1
2
3
4
5
# 下载最新二进制
wget https://github.com/Mi-Bee-Studio/MiBeeNvr/releases/latest/download/mibee-nvr-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')

# 或 Docker
docker pull ghcr.io/mi-bee-studio/mibeenvr:latest

配置向后兼容,直接替换二进制即可。

相关链接