297 lines
11 KiB
Markdown
297 lines
11 KiB
Markdown
|
||
---
|
||
|
||
## 总体思路
|
||
|
||
- 每个阶段都交付一个**可运行的“小产品”**(而不是一堆库)
|
||
- 优先把 **RK3588 的硬件链路打通**(解码 → 前处理 → NPU → 编码)
|
||
- 从 **最简单的“转码网关”** 一步步进化到 **“智能分析+报警+录像”**
|
||
|
||
建议整体 roadmap ≈ 7~8 个迭代:
|
||
|
||
1. Sprint 0:环境&硬件打底
|
||
2. Sprint 1:最小可用媒体服务器(纯转码网关)
|
||
3. Sprint 2:插件框架 + DAG 图 & 队列打通
|
||
4. Sprint 3:AI 推理链路(preprocess + ai_yolo + OSD)
|
||
5. Sprint 4:报警 + 录像插件,打通完整“安防通道”
|
||
6. Sprint 5:配置系统(templates/instances)+ 热更新 + Drain
|
||
7. Sprint 6:监控指标 + Web 控制台基础
|
||
8. Sprint 7:多路性能调优 + 长稳测试
|
||
|
||
下面每个 Sprint 我都给一个**目标 / 核心任务 / 关键里程碑**。
|
||
|
||
---
|
||
|
||
## Sprint 0:环境准备 & 板子打通(已完成)
|
||
|
||
**目标:** 所有开发人员都能在本地和 RK3588 板子上编译、部署、跑官方 demo。
|
||
|
||
**核心任务:**
|
||
|
||
- 建工程骨架:
|
||
- 建 Git 仓库 + 基础目录结构(`src/`, `include/`, `plugins/`, `cmake/` 等)
|
||
- CMake + Ninja 构建脚本
|
||
- 搭建交叉编译环境:
|
||
- 配置 `aarch64-linux-gnu-gcc`
|
||
- 能在 x86 上编译并在 RK3588 板子上运行“hello world”
|
||
- 验证 RK 官方 SDK:
|
||
- 跑 MPP 解码 demo(本地 H264 文件 → 屏幕或 /dev/null)
|
||
- 跑 RKNN demo(官方示例图片 → 得到推理结果)
|
||
- CI 基础:
|
||
- 至少有一个自动构建(例如 push 后自动编译)
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 在 RK3588 板子上成功运行:
|
||
- 一个 MPP 解码 demo
|
||
- 一个 RKNN 推理 demo
|
||
- ✅ `media-server` 空壳程序(打印版本号)能从 CI 输出产物并部署到板子上运行
|
||
|
||
---
|
||
|
||
## Sprint 1:最小可用媒体服务器(纯转码网关)
|
||
|
||
**目标:** 实现一个**最小可用产品**:
|
||
> 从 RTSP 拉流 → 解码 → 编码 → 再推 RTSP/HLS
|
||
> 还没有 AI、报警、录像,只是转码网关。
|
||
|
||
**核心任务:**
|
||
|
||
- 定义最初版 `Frame` 结构(至少包含:宽高、格式、 pts、dma_fd)
|
||
- 实现最简单的**线性 pipeline**(先不用 DAG):
|
||
- `input_rtsp` 插件:
|
||
- 拉 RTSP 流,断线重连
|
||
- 用 MPP 解码为 NV12/YUV
|
||
- `publish` 插件:
|
||
- 用 MPP VENC 编码 H264/H265
|
||
- 启动简单 RTSP server 或 HLS 输出
|
||
- 初版线程模型:
|
||
- 1 个拉流/解码线程
|
||
- 1 个编码/推流线程
|
||
- 中间通过 SPSC 队列传 `shared_ptr<Frame>`
|
||
- 配置文件:
|
||
- 简单 JSON,只支持一条通道,如:
|
||
```json
|
||
{
|
||
"graphs": [
|
||
{
|
||
"name": "cam1",
|
||
"nodes": [...],
|
||
"edges": [...]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 在 RK3588 板子上:
|
||
- 从外部摄像头 / NVR 拉一路 RTSP
|
||
- 在本机 8554 端口重新发布 RTSP(或 8080 HLS)
|
||
- VLC / ffplay 打开 `rtsp://board_ip:8554/live/cam1` 能稳定观看
|
||
- ✅ 初始版本 `Frame` + 队列已经固定下来,后面不会大改接口
|
||
|
||
---
|
||
|
||
## Sprint 2:插件框架 + DAG 图 & 队列体系
|
||
|
||
**目标:** 从「硬编码 pipeline」升级为「插件 + DAG 驱动」,让架构进入你 PRD 描述的形态。
|
||
|
||
**核心任务:**
|
||
|
||
- 定义 `INode` 接口(含 Init/Start/Process/UpdateConfig/Drain/Stop)
|
||
- 完成插件加载系统:
|
||
- `.so` 动态加载(dlopen)
|
||
- `REGISTER_NODE` 宏 + `GetNodeType`/`GetAbiVersion`
|
||
- 实现 GraphMgr:
|
||
- 支持从 JSON 读取 `graphs[nodes + edges]`
|
||
- 根据 type 创建节点、连接 edges
|
||
- 每条 edge 对应一个 SPSC 队列
|
||
- 多分支 DAG 支持:
|
||
- 允许一个节点连接多个下游,GraphMgr 为每条边创建独立队列
|
||
- 把 Sprint1 的 input/publish 改造成插件形式,跑在 DAG 上:
|
||
- Graph:`input_rtsp -> publish`
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 支持通过 JSON 配置任意组合节点(至少:`in→pub`、`in→pre→pub` 两种)
|
||
- ✅ 节点以 `.so` 的形式存在,可以单独编译、加载/卸载
|
||
- ✅ 框架已支持一对多分支(即使当前下游还只有一个,这点结构上要打好)
|
||
|
||
---
|
||
|
||
## Sprint 3:AI 链路打通(preprocess + ai_yolo + OSD)
|
||
|
||
**目标:** 打通一条完整的 **AI 视觉链路**:
|
||
> 拉流 → 前处理 → YOLO 推理(RKNN) → 叠框 OSD → 推流
|
||
|
||
**核心任务:**
|
||
|
||
- 完整定义 `Detection` / `DetectionResult` / 扩展版 `Frame` 结构(含 `det`)
|
||
- 实现 `preprocess` 插件:
|
||
- RGA 缩放(例如 1080p → 640×640)
|
||
- NV12 → RGB/BGR 格式转换
|
||
- 实现 `AiScheduler`:
|
||
- 提供提交/回调接口
|
||
- 内部管理 RKNN 上下文和 NPU 资源
|
||
- 暂时可只支持单路/单 batch,先打通功能
|
||
- 实现 `ai_yolo` 插件:
|
||
- 加载 `.rknn` 模型
|
||
- 调用 `AiScheduler` 做推理
|
||
- 将输出写入 `Frame::det`
|
||
- 实现 `osd` 插件:
|
||
- 根据 `Frame::det` 在原图上画框/标签
|
||
- 输出 NV12/YUV 帧(可以软绘+回写,也可以用 RGA)
|
||
- 在 JSON 中构建一条完整 AI pipeline:
|
||
- `input_rtsp -> preprocess -> ai_yolo -> osd -> publish`
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 在板子上:
|
||
- 拉一条实际摄像头画面
|
||
- web/RTSP 观看输出画面时,能看到实时的检测框
|
||
- ✅ 基础性能满足:
|
||
- 单路 1080p 流畅运行(不明显掉帧)
|
||
- CPU/NPU 占用在可接受范围内(不超出预期天花板)
|
||
|
||
---
|
||
|
||
## Sprint 4:报警 + 录像插件(完成一条“安防通道”)
|
||
|
||
**目标:** 在 AI 结果基础上,增加 **报警逻辑 + 录像**,形成完整“安防产品”的基本形态。
|
||
|
||
**核心任务:**
|
||
|
||
- 实现 `alarm` 插件:
|
||
- 支持按规则配置:object 类型、ROI、最小持续时间、时间段(类似你 PRD 里的 `rules`)
|
||
- 支持 HTTP/MQTT 等方式发报警(先做 HTTP)
|
||
- 实现 `storage` 插件:
|
||
- 支持 continuous 模式(持续录像,分段保存)
|
||
- 文件切片(`segment_sec`)
|
||
- 先不做 event 模式,后续可以扩展 pre/post-event 缓冲
|
||
- 在 DAG 上实现典型拓扑:
|
||
- `ai_yolo → alarm`(分支 1)
|
||
- `ai_yolo → osd → storage`(分支 2)
|
||
- `osd → publish`(分支 3)
|
||
- 简单报警可视化:
|
||
- HTTP 回调到一个简单日志服务,或直接打印到日志,先验证链路
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 配置一个规则:例如“白天有人进入 ROI 区域触发报警”
|
||
- ✅ 摄像头对准测试区域:
|
||
- 屏幕上能看到检测框
|
||
- 有人进 ROI 时,后台收到 HTTP 报警请求(或日志输出)
|
||
- 同时有录像文件被写入(在 `/rec/cam1` 下能找到)
|
||
|
||
---
|
||
|
||
## Sprint 5:配置系统完善 + 模板/实例 + 热更新 & Drain
|
||
|
||
**目标:** 从“能跑”升级到“**好配、好改**”:
|
||
实现 templates/instances、节点 enable/override、热更新、Drain/Stop 正常运转。
|
||
|
||
**核心任务:**
|
||
|
||
- (P0,热更新前置)Stop/Drain 语义对齐 & 线程/资源可回收:
|
||
- `Drain()`:不再接收新输入/不再产生新任务,仅负责把内部缓冲“处理完/落盘/发送完”
|
||
- `Stop()`:在 `Drain()` 之后调用,必须保证内部线程退出并 join、句柄/资源释放,可幂等
|
||
- 至少覆盖 alarm:HttpAction/ClipAction 等动作线程 Stop 后可回收,避免多次热更新/重建图堆线程
|
||
|
||
- 完善配置结构:
|
||
- `global`、`templates`、`instances`、`graphs`
|
||
- 节点通用字段(`enable`, `role`, `queue`)
|
||
- 模板 + 实例展开逻辑:
|
||
- 支持 `${placeholder}` 替换
|
||
- 支持实例级 `override`(开启/关闭某节点、覆盖某些参数)
|
||
- 实现 `UpdateConfig`:
|
||
- 对一部分节点(alarm/publish/storage)支持参数热更新
|
||
- 实现图的热更新流程:
|
||
- inotify 监控 config
|
||
- 构建新图 → 校验 → 若成功则原子切换
|
||
- 对旧节点按顺序调用 `Drain()` → `Stop()`
|
||
- 失败则回滚旧配置
|
||
- 对 "enable=false" 节点的处理:
|
||
- 构图时直接忽略该节点和相关 edges
|
||
- 或者实现旁路逻辑(看你最终选哪种,在代码里统一)
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 连续热更新/重建图 N 次后:
|
||
- action/node 线程数不随次数增长(至少 alarm 的 http/clip 线程不会残留)
|
||
- `Drain/Stop` 无死锁,Stop 后资源可回收
|
||
|
||
- ✅ 不重启进程的前提下:
|
||
- 通过修改 config,把某一路的 `ai_yolo.enable` 改为 false → 该通道立即变成纯转码网关
|
||
- 再改回 true,恢复智能分析
|
||
- ✅ 热更新时:
|
||
- 流“几乎不断”(允许 1 秒内小抖动)
|
||
- 没有崩溃 / 明显内存泄漏 / 死锁
|
||
|
||
---
|
||
|
||
## Sprint 6:监控指标 + Web 控制台基础
|
||
|
||
**目标:** 增加可观测性,让系统变成“可视化可管理”的产品,而不是黑盒程序。
|
||
|
||
**核心任务:**
|
||
|
||
- 指标采集模块:
|
||
- 节点级:input/output fps、queue 长度、drop 数、error 数、平均处理时长
|
||
- 图级:总 fps、报警数、当前推流客户端数
|
||
- HTTP API:
|
||
- `GET /api/graphs`
|
||
- `GET /api/graphs/{name}`
|
||
- `GET /api/nodes/{id}/metrics`
|
||
- (可选)`POST /api/nodes/{id}/config` 调用 `UpdateConfig`
|
||
- Web 控制台(Vue)第一版:
|
||
- 简单展示通道列表、节点状态、关键指标
|
||
- 不必太花哨,能看到“通道是否绿”、“fps 大概多少”即可
|
||
- 日志查看:
|
||
- 提供简单接口/页面看到最近日志(或直接解析 log 文件)
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 打开 Web 页面:
|
||
- 能看到所有通道,以及每个通道的状态(Running/Stopped/Error)
|
||
- 能看到某通道底下的节点链条(in→pre→ai→osd→alarm/storage/publish)
|
||
- 能看到每个节点的大致 fps & 队列深度
|
||
- ✅ 模拟异常(例如关闭摄像头):
|
||
- Web 页面能看到该通道出现错误/掉线状态,日志中有清晰错误信息
|
||
|
||
---
|
||
|
||
## Sprint 7:多路性能调优 + 长稳测试
|
||
|
||
**目标:** 把系统从 demo 提升到「能上生产」的水平:多路、低延迟、长时间稳定。
|
||
|
||
**核心任务:**
|
||
|
||
- 多路场景压测:
|
||
- 逐步从 2 路 → 4 路 → 8 路 → 16 路(根据目标)
|
||
- 观察 CPU、NPU、内存占用和延迟
|
||
- 性能调优点:
|
||
- 队列策略(`drop_oldest` vs `block`)
|
||
- OSD 放在 RGA/CPU 侧的性能权衡
|
||
- AiScheduler 模式(是否可以 batch、是否减少多线程抢 NPU)
|
||
- 绑核策略(input/ai/stroage/publish 分布在大/小核)
|
||
- 长稳测试:
|
||
- 7×24 小时连续跑,统计:
|
||
- 内存占用曲线
|
||
- CPU/NPU 曲线
|
||
- 崩溃/重启次数
|
||
- 回归测试:
|
||
- 关键功能场景(报警、录像、推流、热更新)跑完一遍
|
||
|
||
**关键里程碑:**
|
||
|
||
- ✅ 在目标多路场景(例如 8×1080p)下:
|
||
- 系统端到端延迟满足 PRD 要求
|
||
- CPU/NPU 占用在目标范围内(如 CPU <30%,NPU >90%)
|
||
- ✅ 连续运行 7×24 小时:
|
||
- 无崩溃 / 无明显内存泄漏(内存曲线稳定)
|
||
- 所有通道仍在正常推流/录制/报警
|
||
|
||
---
|
||
|