693 lines
22 KiB
Markdown
693 lines
22 KiB
Markdown
|
||
|
||
## 回答
|
||
|
||
# RK3588 智能视频分析系统 PRD(完整方案 v1.1)
|
||
|
||
> 在你 v1.0 的基础上,已经补上你点名要的这几块:
|
||
> **DAG 多分支示例 + 节点通用字段 + 生命周期 Drain + Frame 结构 + 性能 / 线程 / 监控章节**,现在这份可以直接当总 PRD 用。
|
||
|
||
---
|
||
|
||
## 一、文档综述
|
||
|
||
### 1.1 项目背景
|
||
|
||
为了充分利用瑞芯微 RK3588 芯片提供的:
|
||
|
||
- **NPU:约 6 TOPS**(RKNN,INT8 推理能力)
|
||
- **VPU:多路 1080p 硬件解码/编码**
|
||
- **RGA:硬件图像处理(缩放/颜色转换/OSD 等)**
|
||
|
||
构建一套**纯 C++ 实现、高性能、低延迟、模块化**的边缘计算视频分析平台。平台主要部署在园区、仓库、工厂等场景,实现就地智能分析与处理,减少带宽与中心算力压力。
|
||
|
||
### 1.2 核心目标
|
||
|
||
- **高性能**
|
||
- 单 RK3588 支持 8–16 路 1080p 实时分析
|
||
- 单路端到端(拉流→推理→输出)延迟 < 500ms
|
||
- **低耦合**
|
||
- 基于插件 + DAG 流水线架构
|
||
- 算法、业务逻辑与底层框架完全解耦
|
||
- **易运维**
|
||
- JSON 配置驱动
|
||
- 支持热更新,无需重启进程即可变更流程/参数
|
||
- **多场景覆盖**
|
||
- 同一套框架通过不同配置图覆盖:
|
||
- 安防报警
|
||
- 录像存储
|
||
- 转码网关(RTSP/HLS)
|
||
- 混合场景(报警 + 存储 + 推流)
|
||
|
||
---
|
||
|
||
## 二、总体架构设计
|
||
|
||
### 2.1 系统逻辑架构
|
||
|
||
```text
|
||
[ media-server 主进程 ]
|
||
|
|
||
+-- Graph Manager (图管理:加载配置、构建DAG、热更新/回滚)
|
||
|
|
||
+-- Plugin Loader (插件加载:dlopen .so,检查 ABI 版本)
|
||
|
|
||
+-- AiScheduler (统一调度 NPU 推理请求)
|
||
|
|
||
+-- Metrics & Logger (监控指标、日志)
|
||
|
|
||
+-- HTTP Server (REST API + Web 控制台 + 状态查询)
|
||
```
|
||
|
||
- **media-server**:主入口,负责初始化配置、图构建、主事件循环。
|
||
- **Graph Manager(GraphMgr)**:
|
||
- 对应多个 graph(每个 graph 一路/一组业务流程)。
|
||
- 实例化节点(Node),建立边(Edge),负责热更新时的 diff & 切换。
|
||
- **Plugin Loader**:
|
||
- 动态加载插件 `.so`,校验节点类型和 ABI 版本。
|
||
- **AiScheduler**:
|
||
- 所有 AI 节点统一将推理任务提交到 AiScheduler,由其管理 NPU 资源。
|
||
- **HTTP Server**:
|
||
- 对外提供图状态查询、节点指标、配置热更新触发等。
|
||
|
||
### 2.2 数据流架构(DAG)
|
||
|
||
- **Graph(图)**:一个完整业务通道。例如:Camera 1 的“拉流→AI→报警+录像+推流”。
|
||
- **Node(节点)**:功能基本单元,由动态加载的 `.so` 实例实现。
|
||
- **Edge(边)**:节点间的数据通道,每条边对应一个 **SPSC(单生产者单消费者)环形队列**。
|
||
- **多分支支持**:一个节点可以有多个下游(多条边),实现一对多分支。
|
||
|
||
示意:
|
||
|
||
```text
|
||
[in] → [pre] → [ai] ─────→ [alarm]
|
||
└──→ [osd] → [storage]
|
||
└──→ [publish]
|
||
```
|
||
|
||
---
|
||
|
||
## 三、数据模型(Frame & 元数据)
|
||
|
||
### 3.1 检测结果结构(Detection & DetectionResult)
|
||
|
||
> 约定:**所有 AI 节点写入此结构**,OSD / 报警 等节点只读。
|
||
|
||
```cpp
|
||
struct Rect {
|
||
float x; // 左上角X,像素或归一化坐标
|
||
float y; // 左上角Y
|
||
float w; // 宽
|
||
float h; // 高
|
||
};
|
||
|
||
struct Detection {
|
||
int cls_id; // 类别ID
|
||
float score; // 置信度 [0,1]
|
||
Rect bbox; // 检测框
|
||
int track_id; // 跟踪ID,可选(无则 -1 或 0)
|
||
};
|
||
|
||
struct DetectionResult {
|
||
std::vector<Detection> items;
|
||
int img_w; // 推理输入宽
|
||
int img_h; // 推理输入高
|
||
std::string model_name; // 模型名/版本
|
||
};
|
||
```
|
||
|
||
**约定:**
|
||
|
||
- `bbox` 坐标标准在 PRD 中约定好(建议:像素坐标),避免后续插件互相搞混。
|
||
- class 映射表由 AI 节点自行维护,OSD/报警通过 `cls_id` 做规则匹配。
|
||
|
||
### 3.2 Frame 结构
|
||
|
||
```cpp
|
||
enum class PixelFormat {
|
||
NV12,
|
||
YUV420,
|
||
RGB,
|
||
BGR,
|
||
UNKNOWN
|
||
};
|
||
|
||
struct Frame {
|
||
int width;
|
||
int height;
|
||
PixelFormat format;
|
||
|
||
int dma_fd; // DMA-BUF FD;无则为 -1
|
||
uint8_t* data; // CPU 内存指针(可选,有则指向内存buffer)
|
||
|
||
uint64_t pts; // 时间戳(微秒)
|
||
uint64_t frame_id; // 单调递增ID
|
||
|
||
std::shared_ptr<DetectionResult> det; // AI 检测结果
|
||
std::shared_ptr<void> user_meta; // 扩展元数据(具体类型由插件间约定)
|
||
};
|
||
```
|
||
|
||
**约定:**
|
||
|
||
- **零拷贝优先**:解码/前处理尽量通过 `dma_fd` 传递,必要时才用 data。
|
||
- 节点默认只读图像数据,**可以更新 `det` / `user_meta`**。
|
||
- 所有队列中传递的都是 `std::shared_ptr<Frame>`,实现多下游共享。
|
||
|
||
---
|
||
|
||
## 四、插件模型与生命周期
|
||
|
||
### 4.1 节点通用字段(Node 通用配置)
|
||
|
||
每个节点配置中推荐包含以下通用字段:
|
||
|
||
```json
|
||
{
|
||
"id": "唯一节点ID",
|
||
"type": "节点类型,如 input_rtsp / ai_yolo",
|
||
"enable": true,
|
||
"role": "source|filter|sink", // 可选,但建议写上,便于校验
|
||
"queue": { // 可选:覆盖边上的默认队列策略
|
||
"size": 128,
|
||
"policy": "drop_oldest" // drop_oldest / drop_newest / block
|
||
},
|
||
"...特定节点参数..."
|
||
}
|
||
```
|
||
|
||
- `enable = false`:GraphMgr 构建图时可直接忽略该节点及相关 edges。
|
||
- `role` 主要用于配置校验(source 不应有上游,sink 不应有下游等)。
|
||
|
||
### 4.2 INode 接口(含 Drain)
|
||
|
||
```cpp
|
||
// NodeStatus 用于描述处理结果
|
||
enum class NodeStatus {
|
||
OK, // 正常处理
|
||
DROP, // 丢弃该帧(例如不满足条件)
|
||
ERROR // 发生错误,需要记录日志
|
||
};
|
||
|
||
class INode {
|
||
public:
|
||
virtual ~INode() = default;
|
||
|
||
// 1. 初始化:解析 JSON 配置,申请静态资源
|
||
virtual bool Init(const Json::Value& config) = 0;
|
||
|
||
// 2. 启动:启动内部线程(如有),准备接收/发送数据
|
||
virtual bool Start() = 0;
|
||
|
||
// 3. 处理数据:GraphMgr 从队列中取到 Frame 后调用
|
||
// - 对 filter/sink 节点:正常消费上游 Frame
|
||
// - 对 source 节点:通常可忽略(见下文约定)
|
||
virtual NodeStatus Process(std::shared_ptr<Frame> frame) = 0;
|
||
|
||
// 4. 动态配置更新:支持热更参数(如改变报警阈值、bitrate)
|
||
// 返回 true 表示就地更新成功,无需重建节点
|
||
virtual bool UpdateConfig(const Json::Value& new_config) { return false; }
|
||
|
||
// 5. Drain:停止前,**不再有新输入**,但需处理完内部队列/缓冲数据
|
||
// 如:storage 完成当前切片写入,publish 发送剩余帧等
|
||
virtual void Drain() {}
|
||
|
||
// 6. 停止:释放资源(线程、句柄、内存等),Drain 之后调用
|
||
virtual void Stop() = 0;
|
||
};
|
||
```
|
||
|
||
### 4.3 插件导出与 ABI 版本
|
||
|
||
```cpp
|
||
#define ABI_VERSION 20250101
|
||
|
||
#define REGISTER_NODE(TYPE_NAME, CLASS_NAME) \
|
||
extern "C" { \
|
||
INode* CreateNode() { \
|
||
return new CLASS_NAME(); \
|
||
} \
|
||
const char* GetNodeType() { \
|
||
return TYPE_NAME; \
|
||
} \
|
||
int GetAbiVersion() { \
|
||
return ABI_VERSION; \
|
||
} \
|
||
const char* GetNodeDescription() { \
|
||
return "node: " TYPE_NAME; \
|
||
} \
|
||
}
|
||
```
|
||
|
||
GraphMgr 在加载插件 `.so` 时:
|
||
|
||
- 调用 `GetAbiVersion()` 校验兼容性。
|
||
- 通过 `GetNodeType()` 找到对应 `type` 的构造函数。
|
||
|
||
### 4.4 Source 节点约定
|
||
|
||
为避免把所有采集逻辑都硬塞进 `Process()`,PRD 中统一约定:
|
||
|
||
- `role == "source"` 的节点(如 `input_rtsp` / `input_file`):
|
||
- 在 `Start()` 中启动**内部采集线程**。
|
||
- 线程中从外部拉流/读文件,构造 `Frame`,通过框架提供的 `FrameEmitter` 写入下游队列。
|
||
- `Process()` 可以是空实现或仅做心跳/监控。
|
||
- `role != "source"` 的节点:
|
||
- 由 GraphMgr 驱动 `Process(frame)`,消费上游队列元素。
|
||
|
||
GraphMgr 负责:
|
||
|
||
- 对非 source 节点:循环从对应队列取 `Frame` → 调用 `Process()`。
|
||
- 对所有节点:在热更新 / 停止时,控制 `Drain()` 和 `Stop()` 的调用顺序。
|
||
|
||
---
|
||
|
||
## 五、配置系统(Config System)
|
||
|
||
整个系统由 `config.json` 驱动,主要包含三层:
|
||
|
||
- `global`:全局配置
|
||
- `templates`:pipeline 模板(复用节点与拓扑)
|
||
- `instances` / `graphs`:具体业务实例(通道)
|
||
|
||
### 5.1 顶层结构示例
|
||
|
||
```json
|
||
{
|
||
"global": {
|
||
"plugin_path": "/usr/lib/rknodes",
|
||
"log_level": "info",
|
||
"metrics_port": 9000
|
||
},
|
||
"templates": { ... },
|
||
"instances": [ ... ],
|
||
"graphs": [ ... ] // 可选:直接手写graph
|
||
}
|
||
```
|
||
|
||
- **优先方式**:使用 `templates + instances` 快速生成多路通道。
|
||
- **高级用法**:直接使用 `graphs` 写复杂拓扑(例如:一条流拆成多个完全不同的分支)。
|
||
|
||
### 5.2 DAG 多分支完整示例(带报警 + 存储 + 推流)
|
||
|
||
```json
|
||
{
|
||
"graphs": [
|
||
{
|
||
"name": "cam1_pipeline",
|
||
"nodes": [
|
||
{
|
||
"id": "in_cam1",
|
||
"type": "input_rtsp",
|
||
"role": "source",
|
||
"enable": true,
|
||
"url": "rtsp://user:pwd@ip/stream1"
|
||
},
|
||
{
|
||
"id": "pre_cam1",
|
||
"type": "preprocess",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"dst_w": 640,
|
||
"dst_h": 640,
|
||
"keep_ratio": true
|
||
},
|
||
{
|
||
"id": "ai_cam1",
|
||
"type": "ai_yolo",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"model_path": "/models/yolov8n.rknn",
|
||
"conf": 0.5,
|
||
"nms": 0.5,
|
||
"class_filter": [0, 1]
|
||
},
|
||
{
|
||
"id": "osd_cam1",
|
||
"type": "osd",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"draw_bbox": true,
|
||
"draw_text": true
|
||
},
|
||
{
|
||
"id": "alarm_cam1",
|
||
"type": "alarm",
|
||
"role": "sink",
|
||
"enable": true,
|
||
"rules": [
|
||
{
|
||
"name": "intrusion_day",
|
||
"object": ["person"],
|
||
"roi": "roi1",
|
||
"min_duration_ms": 500,
|
||
"schedule": "08:00-20:00"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": "storage_cam1",
|
||
"type": "storage",
|
||
"role": "sink",
|
||
"enable": true,
|
||
"mode": "continuous",
|
||
"segment_sec": 300,
|
||
"path": "/rec/cam1"
|
||
},
|
||
{
|
||
"id": "publish_cam1",
|
||
"type": "publish",
|
||
"role": "sink",
|
||
"enable": true,
|
||
"codec": {
|
||
"video": "h264",
|
||
"bitrate_kbps": 2048,
|
||
"gop": 50
|
||
},
|
||
"outputs": [
|
||
{ "proto": "rtsp", "port": 8554, "path": "/live/cam1" },
|
||
{ "proto": "hls", "port": 8080, "path": "/hls/cam1", "segment_sec": 2 }
|
||
]
|
||
}
|
||
],
|
||
"edges": [
|
||
["in_cam1", "pre_cam1"],
|
||
["pre_cam1", "ai_cam1"],
|
||
["ai_cam1", "alarm_cam1"],
|
||
["ai_cam1", "osd_cam1"],
|
||
["osd_cam1", "storage_cam1"],
|
||
["osd_cam1", "publish_cam1"]
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
通过简单改动即可支持不同组合:
|
||
|
||
- 不报警:`alarm_cam1.enable = false` 或移除该节点/边。
|
||
- 只转码网关:直接连 `["in_cam1", "publish_cam1"]`,并禁用 AI/OSD/报警/storage。
|
||
- 只录像:`["in_cam1", "storage_cam1"]`。
|
||
|
||
### 5.3 模板(templates)与实例(instances)
|
||
|
||
#### 5.3.1 模板示例:标准安防流程
|
||
|
||
```json
|
||
"templates": {
|
||
"standard_security": {
|
||
"nodes": [
|
||
{ "id": "in", "type": "input_rtsp", "role": "source", "url": "${url}" },
|
||
{ "id": "pre", "type": "preprocess", "role": "filter", "dst_w": 640, "dst_h": 640 },
|
||
{ "id": "ai", "type": "ai_yolo", "role": "filter", "model_path": "/models/yolov8n.rknn", "conf": 0.5 },
|
||
{ "id": "osd", "type": "osd", "role": "filter" },
|
||
{ "id": "pub", "type": "publish", "role": "sink", "path": "/live/${name}" }
|
||
],
|
||
"edges": [
|
||
["in", "pre"],
|
||
["pre", "ai"],
|
||
["ai", "osd"],
|
||
["osd", "pub"]
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 5.3.2 实例与 override(禁用某些节点)
|
||
|
||
```json
|
||
"instances": [
|
||
{
|
||
"name": "cam_01",
|
||
"template": "standard_security",
|
||
"params": {
|
||
"url": "rtsp://192.168.1.101/stream1",
|
||
"name": "office"
|
||
},
|
||
"override": {
|
||
"nodes": {
|
||
"ai": { "enable": true },
|
||
"osd": { "enable": true }
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"name": "cam_02_only_transcode",
|
||
"template": "standard_security",
|
||
"params": {
|
||
"url": "rtsp://192.168.1.102/stream1",
|
||
"name": "transcoder"
|
||
},
|
||
"override": {
|
||
"nodes": {
|
||
"ai": { "enable": false },
|
||
"osd": { "enable": false }
|
||
}
|
||
}
|
||
}
|
||
]
|
||
```
|
||
|
||
> GraphMgr 在加载时将模板 + 实例 merge 成具体的 graph(节点 ID 会按实例名做前缀防冲突)。
|
||
|
||
### 5.4 热更新与回滚(简要)
|
||
|
||
- 通过 inotify 监控 `config.json`。
|
||
- 检测到变更时:
|
||
1. 读取新配置 → JSON Schema 校验;
|
||
2. 构建新的图对象(dry run),包括插件加载与节点实例化;
|
||
3. 与旧图做 diff:
|
||
- 能 `UpdateConfig` 的节点优先调用 `UpdateConfig`;
|
||
- 需要重建的节点:先创建新节点并预热(如加载模型),然后做 **边的原子切换**;
|
||
4. 对旧节点:依次调用 `Drain()` → `Stop()`;
|
||
5. 若任意步骤失败:回滚到旧配置,并输出错误日志。
|
||
- 保留上一份有效配置,支持手动回滚。
|
||
|
||
---
|
||
|
||
## 六、功能模块定义(插件类型)
|
||
|
||
> 下表是“典型插件类型”,后续可以扩展更多。
|
||
|
||
### 6.1 输入与前处理
|
||
|
||
| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |
|
||
| :------------- | :------------------------------- | :------------------------- | :--------------- |
|
||
| `input_rtsp` | 拉取 RTSP 流,断线重连 | `url`, `reconnect_sec` | MPP VDEC / 网络 |
|
||
| `input_file` | 读取本地 MP4/MKV 文件循环播放 | `path`, `loop` | MPP VDEC |
|
||
| `preprocess` | 缩放、裁剪、NV12→RGB/BGR 等 | `dst_w`, `dst_h`, `format` | RGA / GPU |
|
||
|
||
### 6.2 AI 分析
|
||
|
||
| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |
|
||
| :----------- | :------------------------- | :----------------------------------- | :------- |
|
||
| `ai_yolo` | 通用目标检测(YOLOv5/v8) | `model_path`, `model_version`, `num_classes`, `conf`, `nms`, `class_filter` | NPU(RKNN) |
|
||
| `ai_pose` | 关键点检测(可选扩展) | `model_path` | NPU |
|
||
| `ai_custom` | 自定义 AI 算法 | `model_path`, `custom_param` 等 | NPU |
|
||
|
||
**ai_yolo 参数说明:**
|
||
- `model_version`: `"v5"`, `"v8"` 或 `"auto"`(自动检测)
|
||
- `num_classes`: 模型类别数(默认 80)
|
||
- `class_filter`: 只检测指定类别 ID 的数组,如 `[0, 1, 2]`
|
||
|
||
> 所有 AI 节点通过 **AiScheduler** 使用 NPU,不直接持有 NPU 句柄。
|
||
|
||
### 6.3 业务逻辑与输出
|
||
|
||
| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |
|
||
| :---------- | :--------------------------------- | :----------------------------------- | :----------------- |
|
||
| `osd` | 绘制检测框、文字等 | `draw_bbox`, `draw_text`, `labels`, `line_width`, `font_scale` | RGA / CPU |
|
||
| `alarm` | 报警规则(区域、时间、目标类型) | `rules`(含 ROI、时间段、阈值等) | CPU |
|
||
| `storage` | 录像(MP4/TS 切片) | `mode`, `segment_sec`, `path` | CPU(IO) |
|
||
| `publish` | 编码并推流(RTSP/HLS) | `codec`, `bitrate`, `outputs` | MPP VENC + 网络 |
|
||
|
||
**osd 参数说明:**
|
||
- `labels`: 自定义类别标签数组,如 `["cat", "dog", "bird"]`,为空则使用 COCO 默认标签
|
||
|
||
---
|
||
|
||
## 七、线程模型(Thread Model)
|
||
|
||
### 7.1 线程类型与职责
|
||
|
||
| 线程类型 | 职责 | 绑核策略(可配置) |
|
||
| :-------------- | :-------------------------------------- | :-------------------------- |
|
||
| `main` | 程序入口、配置加载、GraphMgr 调度 | 默认 CPU0 |
|
||
| `input-io` | RTSP 网络 IO | 大核/通用 |
|
||
| `decode` | 调用 MPP 解码 | 大核 |
|
||
| `preprocess` | RGA/GPU 前处理 | 视情况绑核 |
|
||
| `ai-worker` | 调度 AiScheduler,异步调用 NPU | 大核 |
|
||
| `post/osd` | OSD 绘制、报警逻辑等 | 小核优先 |
|
||
| `storage` | 文件写入、切片管理 | 小核 |
|
||
| `publish` | RTSP/HLS 推流 | 小核 |
|
||
| `metrics/log` | 指标采集、日志异步写入 | 任意核 |
|
||
|
||
### 7.2 配置控制
|
||
|
||
- 节点配置中可增加字段:
|
||
|
||
```json
|
||
"cpu_affinity": [2, 3] // 可选
|
||
```
|
||
|
||
- 若未配置,由框架根据 `role` 和负载自动分配。
|
||
|
||
---
|
||
|
||
## 八、性能指标(Performance Targets)
|
||
|
||
| 场景 | 指标 | 目标值 |
|
||
| :------------- | :------------- | :--------------------- |
|
||
| 8×1080p@25fps | 整机总处理帧率 | ≥ 200 fps |
|
||
| 单路端到端 | 延迟 | ≤ 500 ms(典型 ≤ 380) |
|
||
| NPU | 利用率 | ≥ 90% |
|
||
| 8 路全开 | CPU 占用 | ≤ 30% |
|
||
| 7×24 小时运行 | 内存泄漏 | 无明显增长(< 10MB) |
|
||
|
||
测试要求(简要):
|
||
|
||
- 在实际 RK3588 设备上,使用典型 YOLO 模型和 8 路 RTSP 输入进行长稳测试。
|
||
- 统计长时间运行中的 CPU、内存、NPU 使用情况。
|
||
|
||
---
|
||
|
||
## 九、监控与运维(Metrics & Ops)
|
||
|
||
### 9.1 节点级指标
|
||
|
||
每个节点提供以下基本指标(通过 HTTP / Prometheus 等暴露):
|
||
|
||
- `node_input_fps` / `node_output_fps`
|
||
- `queue_length`(入队/出队队列长度)
|
||
- `frame_drop_count`
|
||
- `error_count`
|
||
- `avg_process_time_ms`(Process 平均处理耗时)
|
||
|
||
### 9.2 图 / 通道级指标
|
||
|
||
- 通道状态:running / error / disabled
|
||
- 报警次数 / 最近报警时间
|
||
- 当前推流客户端数(RTSP/HLS)
|
||
- 当前录像文件信息(路径 / 大小 / 最近片段时间)
|
||
|
||
### 9.3 管理接口(REST 简要)
|
||
|
||
- `GET /api/graphs`:列出所有 graph 状态
|
||
- `GET /api/graphs/{name}`:单通道详情
|
||
- `POST /api/config/reload`:触发配<EFBFBD><EFBFBD><EFBFBD>重加载
|
||
- `GET /api/nodes/{id}/metrics`:查询节点指标
|
||
- `POST /api/nodes/{id}/config`:动态更新节点配置(调用 `UpdateConfig`)
|
||
|
||
---
|
||
|
||
## 十、开发与部署环境
|
||
|
||
- **硬件平台**:RK3588(4×A76 + 4×A55 + NPU + VPU + RGA)
|
||
- **工具链**:
|
||
- 板上编译:GCC 10+(推荐)
|
||
- 交叉编译:`aarch64-linux-gnu-gcc 12+`(可选)
|
||
- 构建:CMake ≥ 3.20
|
||
- **调试**:
|
||
- `gdbserver` + VSCode 远程调试
|
||
- RKNN Profiler 分析 NPU 利用率
|
||
- **日志**:
|
||
- `spdlog` 异步日志
|
||
- 日志级别可运行时调整(通过配置或 REST 接口)
|
||
|
||
### 10.1 板上编译(推荐)
|
||
|
||
在 RK3588 板子上直接编译,无需配置交叉编译环境。
|
||
|
||
**依赖安装:**
|
||
|
||
```bash
|
||
# Ubuntu/Debian
|
||
sudo apt update
|
||
sudo apt install -y build-essential cmake git pkg-config \
|
||
libavformat-dev libavcodec-dev libavutil-dev libswscale-dev
|
||
```
|
||
|
||
**编译命令:**
|
||
|
||
```bash
|
||
cd Rk3588Sys
|
||
|
||
# 配置(启用所有硬件加速)
|
||
cmake -S . -B build \
|
||
-DCMAKE_BUILD_TYPE=Release \
|
||
-DRK3588_ENABLE_FFMPEG=ON \
|
||
-DRK3588_ENABLE_MPP=ON \
|
||
-DRK3588_ENABLE_RGA=ON \
|
||
-DRK3588_ENABLE_ZLMEDIAKIT=ON \
|
||
-DRK_ZLMK_API_LIB_PATH=$PWD/third_party/rknpu2/examples/3rdparty/zlmediakit/aarch64/libmk_api.so \
|
||
-DRK_ZLMEDIAKIT_INCLUDE_DIR=$PWD/third_party/rknpu2/examples/3rdparty/zlmediakit/include
|
||
|
||
# 编译
|
||
cmake --build build -j$(nproc)
|
||
```
|
||
|
||
**CMake 选项说明:**
|
||
|
||
| 选项 | 说明 | 默认值 |
|
||
|------|------|--------|
|
||
| `RK3588_ENABLE_FFMPEG` | 启用 FFmpeg 拉流/软解码 | OFF |
|
||
| `RK3588_ENABLE_MPP` | 启用 MPP 硬件编解码 | OFF |
|
||
| `RK3588_ENABLE_RGA` | 启用 RGA 硬件图像处理 | OFF |
|
||
| `RK3588_ENABLE_RKNN` | 启用 RKNN NPU 推理 | OFF |
|
||
| `RK3588_ENABLE_ZLMEDIAKIT` | 启用内置 RTSP Server | OFF |
|
||
|
||
**运行:**
|
||
|
||
```bash
|
||
# 纯转码网关
|
||
./build/media-server --config configs/sample_cam1.json
|
||
|
||
# 带预处理的 pipeline
|
||
./build/media-server --config configs/sample_preprocess.json
|
||
|
||
# 内置 RTSP Server 模式
|
||
./build/media-server --config configs/sample_cam1_rtsp_server.json
|
||
```
|
||
|
||
### 10.2 交叉编译(可选)
|
||
|
||
在 x86 主机上交叉编译,适合 CI/CD 或批量部署。
|
||
|
||
```bash
|
||
# 需要安装交叉编译工具链
|
||
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||
|
||
# 配置
|
||
cmake -S . -B build-cross \
|
||
-DCMAKE_TOOLCHAIN_FILE=cmake/aarch64-linux-gnu.cmake \
|
||
-DCMAKE_BUILD_TYPE=Release \
|
||
-DRK3588_ENABLE_FFMPEG=ON \
|
||
-DRK3588_ENABLE_MPP=ON \
|
||
-DRK3588_ENABLE_RGA=ON
|
||
|
||
# 编译
|
||
cmake --build build-cross -j$(nproc)
|
||
|
||
# 同步到板子
|
||
rsync -avz build-cross/ user@board_ip:/opt/media-server/
|
||
```
|
||
|
||
---
|
||
|
||
## 十一、扩展与演进
|
||
|
||
1. **新增算法插件**
|
||
- 实现新的 `INode` 子类(例如 `ai_cls`, `ai_reid`)。
|
||
- 使用 `REGISTER_NODE` 编译为 `.so` 放入 `plugin_path`。
|
||
- 在 `config.json` 中新增节点即可接入全流程。
|
||
|
||
2. **新增输出类型**
|
||
- 新增 `publish_webrtc`、`storage_s3` 等节点类型。
|
||
- 不改动上游图结构,只调整 sink 部分。
|
||
|
||
3. **分布式扩展(后续版本)**
|
||
- 引入 ZeroMQ / NanoMSG,将远端设备作为特殊节点处理(如 `remote_sink`)。
|
||
- 中控只维护抽象图,实际在多台 RK3588 设备上分布执行。
|
||
|
||
---
|