
一句话总结这是 MiBeeNvr 自 0.8.0 以来最大的一次发版主题不是加功能而是把底层清干净——H.265 全链路打通、Timelapse 重写到 v3、MJPEG 低延迟化、认证安全现代化以及一轮贯穿 config / DI 根 / API 路由 / 前端播放器的架构清理。这一切都是为后续sidecar AI 路线铺路。⚠️这是 Preview 版Release Candidate不建议用于生产。含破坏性变更升级前务必阅读文末破坏性变更一节与官方升级指南。本次发版包含54 次提交、273 个文件变动量27,246 / −12,719行feat 9 / fix 29 / refactor 9 / test 2 / cleanup 2 / docs 2 / perf 1。为什么是这个时候做重构v0.9 把累积的工程债一次还清 之后NVR 主线要往两个方向走引入更强的 AI 能力、接入更多摄像头。这两件事都要求主进程结构清晰、协议可插拔、AI 推理不阻塞视频管线。前面几篇调研里讨论的取舍在这版开始落地H.264/H.265/H.266 Web 前端播放选型流媒体传输协议全景NVR 远程访问 P2P 技术方案全面调研P2P 信令与中继服务器技术调研SD/TF 卡选购分析先说清楚一件事v0.9 帖里预告过——v0.9.x 是自 v0.1.0 以来最后一个稳定兼容版本下一个大版本会引入破坏性变更并精确告知迁移路径。这版兑现了那个承诺所有破坏性变更都列在文末给了 grep 命令和迁移步骤没有静默破坏。一、H.265 全链路打通直播 录制 合并 回放0.9.x 之前 H.265 是个半成品状态直播依赖 WebCodecs而 WebCodecs 需要 HTTPS secure context局域网裸 HTTP 直接放不出来合并产物在 Edge 上经常黑屏延时摄影的合并根本不工作。这版把整条 H.265 链路——直播、录制、合并、回放——端到端修通了。1.1 直播侧libde265 WASM 解码器最大的变化是引入了yume-chan/libde265这个 WASM 解码器。纯 HTTP 的局域网也能播 H.265 直播不再绑死 HTTPS。前端做了一个自动探测HTTPS 环境优先走 WebCodecs拿到硬件加速不可用时回退 WASM 软解。是否 / 不支持浏览器请求 H.265 直播是否 HTTPS secure context?WebCodecs 硬件加速解码libde265 WASM 软解Player Orchestrator播放器渲染WebCodecs 是最优解但有 secure context 门槛WASM 是兜底这版把两者串成了一条带降级的链。1.2 前端播放器重构Player Orchestrator 状态机光有解码器还不够。多路 H.265 网格grid在浏览器里会触发一种很恶心的**“冻结风暴”**——一路解码卡住整个 grid 的 frame 轮询全被拖死。这版把前端播放器抽象成了一个可测试的状态机——Player Orchestrator代码在web/src/lib/player/。状态机有三个状态ok稳定运行 30 秒以上degraded检测到解码异常但不立刻动手先观察8 秒防抖避免网络抖一下就切协议failed确认挂了立刻降级到候选链里的下一个协议。检测到解码异常8s 内恢复8s 防抖后仍异常降级到下一协议并恢复okdegradedfailedwebrtc → flv → hls → mjpeg全部失败 → 静态快照webrtc → flv → hls → mjpeg全部降级失败就退化为静态快照。这个8s 防抖窗口是踩坑踩出来的——太短会被瞬时网络抖动误触发太长用户盯着卡画面干等。Player Orchestrator 被设计成 DOM 无关的纯逻辑模块可以在不启动 jsdom 的情况下做单元测试dispatch.ts里详细记录了 Svelte 5 同步发射的陷阱和绕法。1.3 链路剩下的几块时序与数据一致性编码持久化——“probe once, persist forever”。ONVIF 相机的编码探测结果以前每次启动都要重探探测结果和实际流的编码不一致时会出现黑屏和协议风暴。现在探测一次双写到 YAML DB后续启动直接读。参数集时序修复——SPS/PPS/VPS 的捕获挪到了RecordEnabledgate 之前。之前的时序是先判断是否启用录制、再抓参数集结果 live-only不录制模式下参数集永远抓不到回放黑屏。回放路由——按运行时编解码 权威的 MSE 探测来决定路由H.265 强制走 WASM 而不是错误地走 MSE 路径。延时合并可播放——修正了 hvcC box 的numNalus用保守的 Main-tier 默认值不去解析可能自相矛盾的 SPS这一步解决了 Edge HEVC 扩展拒绝播放的问题周期合并现在正确区分 H.265/H.264 帧——之前只收集 JPEGH.265 相机的延时合并必然失败。二、Timelapse v3 重写延时摄影系统这版整体重写到了 v3。v0.7 那次重写 把合并路径从 JPEG 序列改成了纯 Go 的 NAL→MP4 muxer但合并窗口还是有个1h 硬上限——8h/12h/24h/7d/30d/natural-day这些值在代码里被静默 clamp 到 1h。v3 把这个限制真正去掉了。v3 的几条主线合并窗口解禁。natural-day/8h/12h/24h/7d/30d现在真正生效不再 clamp。默认值从1h改成natural-day本地午夜对齐的 24h 窗口。natural-day的时区对齐也修了——之前Local时区在某些配置下会让窗口偏移。周期合并 DB 记录。新增timelapse_merges表schema v28→v29每个长窗口的合并输出都有完整的 DB 记录相机、窗口、时长、codec、帧数、状态。前端通过/api/timelapse/merges*端点发现、播放、删除合并产物。以前这些中间产物是游离的文件没有结构化元数据。codec 感知播放。合并端点响应里带X-Timelapse-Codec头前端据此选择播放器——H.264/H.265 用video元素MJPEG 用 JPEG 帧轮播。连续合并链。周期合并会折叠掉之前的中间产物避免无限累积。如果要留中间文件做调试有可选的retain_intermediate_mp4配置默认 false每天能省 ~1.5GB/相机。repair remerge-h265CLI。专门修历史上 Edge 播不了的 H.265 延时合并。三、MJPEG 低延迟化 WebRTC 跨网ESP32 MiBeeCam 这类 MJPEG 相机以前走 HTTP 轮询每帧一次请求延迟和连接数都下不来。这版改成了WebSocket 流式传输wsstream单连接持续推帧延迟和连接开销同时降下来。WebRTC 这边加了streaming.webrtc.ice_servers配置——STUN/TURN 服务器列表。以前 WebRTC 只能局域网用现在配上 ICE 服务器就能跨网访问了。远程访问的具体方案选型自建信令、商用 P2P 方案、白标 P2P 的安全红线之前在 NVR 远程访问 P2P 调研 和 P2P 信令与中继服务器调研 里详细对比过这版把 ICE 配置层先接上。四、架构大清理真正的主线这一节是这次发版的真正主线也是标题里为 sidecar AI 铺路的落点。重构集中在三个地方config 按子系统拆分。以前所有配置挤在一个文件里1946 行camera / merge / timelapse / transcoding / streaming / health / ingest / ai / server 这些子系统的配置互相穿插。现在每个子系统独立成文件入口只剩 155 行 13 个子系统文件入口只负责加载和拼装。public API 全部保留运行时行为零变化。DI 组合根拆分。pkg/app/run.go是依赖注入的组装点以前把 helper 逻辑和路由组装混在一起。现在拆出独立的helpers.go和router.go组合根只做把谁注入给谁这一件事。路由按领域注册。以前所有 API 端点在一个Routes()方法里集中注册685 行的 god-method。现在拆成 14 个按领域分的注册器——摄像头、录像、延时、转码、流媒体、设置各有自己的注册函数入口循环调用它们。端点零丢失。架构清理重构后领域拆分13 个子系统配置文件helpers.go router.go14 个领域路由注册器重构前god-methodconfig.go 1946 行run.go 混合组装Routes 685 行集中注册拆分本身不改变运行时行为但改变了加新东西要动哪里这个问题的答案。以前加一个新协议、新 AI 能力要在那一个巨大的 config 里找位置、在 god-method 里塞端点现在每个领域是独立文件改动收敛在自己的子系统里。这是 sidecar 路线能推进的前提——主进程内部结构清晰外部能力才能干净地挂上去。配套的工程改动API 带宽优化gzip 压缩实测体积下降一个数量级、ETagIf-None-Match→304、viewsummary轻量端点、录像列表 limit clamp默认 50 / 最大 500、CameraRow字段omitempty。前端 grid 轮询的流量大幅下降。Settings 信息架构重写左侧边栏导航 统一保存 破坏性操作确认。修了切换分类丢未保存更改的回归。前端 merge-utils 提取合并工具函数从路由组件挪到lib/路由组件不再背业务逻辑。录像页移除 gallery 视图这是 v0.9 预告过的——默认时间轴聚焦核心场景。Surveillance 网格拖拽排序监控网格支持拖拽重新排列摄像头单元顺序持久化。清理里两个值得单独拎出来的4.1 TUTK dtls 死代码删除一套完整的 ChaCha20Poly1305 DTLS 实现1420 行确认零引用后整段删掉。TUTK 协议本身的 DTLS 用的是另一个路径这套实现是早期实验遗留。死代码删干净后面接 sidecar 协议适配层时就不会踩到这些地雷。4.2 AIINVALID_PROTOBUF根因这个 bug 花了最多时间。最初的怀疑方向是 CDN 截断了模型下载但真正的 root cause 是streaming-gzip 中间件给二进制响应追加了错误的 gzip trailer。AI 模型文件是二进制 protobuf经过 gzip 中间件时被错误地补全了 trailer 字节导致 ONNX Runtime 解析失败。修复是4 层防御中间件 root cause——二进制 content-type 不进 gzip。下载层——retry Range 请求 原子写。Docker 镜像——预置 AI 模型运行时不再下载。前端缓存自愈——检测到损坏自动重下。详细 postmortem 在docs/known-issues-ai-onnx-gzip-trailer.md。这个修复对 sidecar 路线有直接意义——AI 推理挪到 sidecar 之后模型分发和完整性校验就是核心问题这版的 4 层防御是基础。4.3 CI flake 根治goroutine 生命周期管理startup-bg service backfillWg service-layer goroutine joins消除了 TempDir 清理竞态全量go test ./...第一次做到一次性 0 失败。CI 升级到 Node 24 actions。这些看着不起眼但 sidecar 架构会引入更多并发进程和异步生命周期CI 不稳根本没法迭代。五、关键稳定性修复挑几个跨模块的修复单独列合并 MP4 泄漏——以前删录像只删 DB 行和源帧合并产物 MP4 留在磁盘上不管。这版删录像时同步删合并产物并新增repair reclaim-orphan-mergesCLI 清理历史泄漏。升级后建议跑一次--dry-run看看能回收多少空间默认 20ms 删除间隔对 USB-HDD 友好。录像时间线截断——新增轻量级/api/recordings/timeline端点避免全量加载。Xiaomi 频繁重连一天能产生 5000 碎片全量加载直接卡死。autodiscover 稳定化——IP 漫游去重、未变端点的 recorder 风暴、endpoint URL 规范化scheme/host/port/trailing-slash经过 4 轮修复后趋于稳定。启动扫描阻塞——异步启动扫描 有界 reconcile8 分钟阻塞 → 8 秒启动。合并回填积压——退役历史 singleton 解除回填循环阻塞8500 pending 排空。ONVIFrecording_enabledfalse静默录制、MJPEG 迁移 OOM限制迁移帧数、平台感知 AVI 分段时长≤2GB RAM→30s2GB→5min。六、破坏性变更升级前必读完整迁移步骤见 官方升级指南。挑关键的1. 配置组合协议串被拒硬错误阻塞启动0.10.0 启动时直接拒绝旧格式的组合协议串。先 grep 检查grep-nEprotocol:\s*.*_(h264|h265|mjpeg|jpeg)/path/to/mibee-nvr.yaml命中的行必须拆开# ❌ 旧格式0.9.x 接受0.10.0 拒绝-id:front-doorprotocol:rtsp_h264# ✅ 新格式-id:front-doorprotocol:rtspencoding:h264适用所有带下划线的 protocol 值rtsp_h264/rtsp_h265/rtsp_mjpeg/http_jpeg/onvif_jpeg等。2. DB schema v28 → v29recordings.merged列被删除迁移前自动VACUUM INTO备份到db.pre-v29-backupmerged1的行会先更新到merge_statusmerged作为安全网。 v0.9.x 不支持直升 0.10.0必须先升到 0.9.x。3. API 端点移除GET /api/timelapse/{id}/thumbnail和/preview返回 404替换为/api/timelapse/merges系列。外部脚本需要迁移NVR 自己的前端已经切到新端点。4. 默认值变化cleanup.disk_threshold_percent95→85避免 HDD 在 90% 满之后的性能悬崖timelapse.merge_duration默认1h→natural-day注意滚动窗口合并merge.rolling_window仍 cap 在 1h。5. 升级后建议跑一次mibee-nvr repair reclaim-orphan-merges --dry-run检查历史泄漏的合并 MP4。七、Sidecar AI 架构愿景这一节讲方向不展开实现——具体设计会在后续专题里写。前面那些架构清理不是为清理而清理。NVR 往下走有两个明确方向引入更强的 AI 能力更复杂的检测模型、多模态推理、事件触发、接入更多摄像头更多协议、更多厂商、更多边缘场景。这两个方向有一个共同约束AI 推理和协议适配都不能拖累主进程的视频管线——录像、直播、合并这些核心功能必须保持稳定低延迟。sidecar 模式的核心思路是主进程保持轻量和稳定把可变的能力AI 推理、协议适配、数据 enrichment以独立进程/容器的方式挂在旁边通过明确定义的接口通信。这种结构在服务网格里被验证过——Envoy、Linkerd 这些都是 sidecar 模式。NVR 场景的特殊性在于视频帧是高吞吐数据流sidecar 和主进程之间的数据通道要专门设计。帧总线高吞吐数据流共享内存 / UDS控制/健康通道配置下发 指标上报Sidecar独立进程/容器AI 推理协议适配主进程保持轻量录像直播合并这张草图说明三件事主进程保持轻量——录像、直播、合并这些核心视频管线不动AI 推理挪出去。AI 和协议适配是独立 sidecar——可以独立升级、独立扩缩、独立崩溃不影响主进程。新接一个厂商的私有协议加一个协议适配 sidecar 就行主进程代码不碰。两条通道分离——帧总线走高吞吐数据流共享内存或 Unix Domain Socket控制/健康通道走配置下发和指标上报。两条通道的 QoS 要求完全不同物理上分开。为什么这版的架构清理是前置条件因为 sidecar 要能挂上去主进程内部必须先做到领域边界清晰——config 按子系统拆分、路由按领域注册、认证无状态化HMAC token 不占主进程内存。这些做完sidecar 的接入点才干净。AI 模型分发的 4 层防御gzip 中间件 root cause 那个也是同一件事的基础——sidecar 场景下模型完整性校验会更关键。具体 sidecar 的进程间通信协议、部署形态同机进程 vs 容器 vs Pod、AI 模型分发机制会在后续专题里展开。这版先把主进程的底子打牢。八、升级# 一键安装amd64 / arm64 / armv7curl-fsSLhttps://raw.githubusercontent.com/Mi-Bee-Studio/MiBeeNvr/main/install.sh|sudobash# Docker镜像已内置 AI 模型无需运行时下载dockercompose --project-directory.-fdeploy/docker/docker-compose.yml up-d这是 preview 版不建议用于生产。升级前务必备份 DBgrep 检查组合协议串读一遍 升级指南。长期稳定部署建议继续锁定 v0.9.x等 0.10.0 正式版出来后再升级。相关链接MiBeeNvr GitHubv0.10.0-preview.1 Release Notes升级指南中英双语MiBeeNvr v0.9.0 → v0.9.1 发布帖MiBeeNvr v0.7.0 发布帖H.264 / H.265 / H.266 Web 前端播放选型流媒体传输协议全景NVR 远程访问 P2P 技术方案全面调研P2P 信令与中继服务器技术调研SD/TF 卡选购分析本文由 MiBee 开源项目实践系列整理原文发布于 MiBee BlogRelease Notes 同步于 GitHub。转载请注明出处。