--- ## 总体思路 - 每个阶段都交付一个**可运行的“小产品”**(而不是一堆库) - 优先把 **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` - 配置文件: - 简单 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 小时: - 无崩溃 / 无明显内存泄漏(内存曲线稳定) - 所有通道仍在正常推流/录制/报警 ---