32 KiB
回答
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 系统逻辑架构
[ 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(单生产者单消费者)环形队列。
- 多分支支持:一个节点可以有多个下游(多条边),实现一对多分支。
示意:
[in] → [pre] → [ai_yolo] → [det_post] ───→ [alarm]
└──────→ [osd] → [storage]
└──→ [publish]
说明:
det_post是检测结果后处理节点(可选)。它会对frame->det做过滤/修正/二次验证,使 OSD 与告警读取到一致的“后处理结果”。
三、数据模型(Frame & 元数据)
3.1 检测结果结构(Detection & DetectionResult)
约定:所有 AI 节点写入此结构,OSD / 报警 等节点只读。
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 结构
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 通用配置)
每个节点配置中推荐包含以下通用字段:
{
"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)
// 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 版本
#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 驱动
GraphMgr 负责:
- 对非 source 节点:循环从对应队列取
Frame→ 调用Process()。 - 对所有节点:在热更新 / 停止时,控制
Drain()和Stop()的调用顺序。
五、配置系统(Config System)
整个系统由 config.json 驱动,主要包含三层:
global:全局配置templates:pipeline 模板(复用节点与拓扑)instances/graphs:具体业务实例(通道)
5.1 顶层结构示例
{
"global": {
"plugin_path": "/usr/lib/rknodes",
"log_level": "info",
"metrics_port": 9000
},
"templates": { ... },
"instances": [ ... ],
"graphs": [ ... ] // 可选:直接手写graph
}
- 优先方式:使用
templates + instances快速生成多路通道。 - 高级用法:直接使用
graphs写复杂拓扑(例如:一条流拆成多个完全不同的分支)。
5.2 DAG 多分支完整示例(带报警 + 存储 + 推流)
{
"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": "pp_cam1",
"type": "det_post",
"role": "filter",
"enable": true,
"default": { "enable": false },
"per_class": [
{
"class_id": 0,
"enable": true,
"processors": [
{ "type": "conf_gate", "conf_min": 0.65 },
{ "type": "bbox_gate", "min_w": 10, "min_h": 10 }
]
}
]
},
{
"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", "path": "/data/hls/cam1/index.m3u8", "segment_sec": 2 }
]
}
],
"edges": [
["in_cam1", "pre_cam1"],
["pre_cam1", "ai_cam1"],
["ai_cam1", "pp_cam1"],
["pp_cam1", "alarm_cam1"],
["pp_cam1", "osd_cam1"],
["osd_cam1", "storage_cam1"],
["osd_cam1", "publish_cam1"]
]
}
]
}
说明:如果不需要后处理:
- 推荐:保留
pp_cam1节点(enable:true),并确保其不启用任何处理链(例如:default.enable=false且per_class为空),此时det_post会作为轻量透传。- 若确实要移除该节点:需要同时删除并重连拓扑(例如改回
ai_cam1 -> osd_cam1与ai_cam1 -> alarm_cam1),仅设置enable=false会导致下游节点无输入而构图失败。
通过简单改动即可支持不同组合:
- 不报警:
alarm_cam1.enable = false或移除该节点/边。 - 只转码网关:直接连
["in_cam1", "publish_cam1"],并禁用 AI/OSD/报警/storage。 - 只录像:
["in_cam1", "storage_cam1"]。
5.3 模板(templates)与实例(instances)
5.3.1 模板示例:标准安防流程
"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": "pp", "type": "det_post", "role": "filter", "enable": true, "default": { "enable": false } },
{ "id": "osd", "type": "osd", "role": "filter" },
{ "id": "pub", "type": "publish", "role": "sink", "path": "/live/${name}" }
],
"edges": [
["in", "pre"],
["pre", "ai"],
["ai", "pp"],
["pp", "osd"],
["osd", "pub"]
]
}
}
5.3.2 实例与 override(禁用某些节点)
"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。 - 检测到变更时:
- 读取新配置 → JSON Schema 校验;
- 构建新的图对象(dry run),包括插件加载与节点实例化;
- 与旧图做 diff:
- 能
UpdateConfig的节点优先调用UpdateConfig; - 需要重建的节点:先创建新节点并预热(如加载模型),然后做 边的原子切换;
- 能
- 对旧节点:依次调用
Drain()→Stop(); - 若任意步骤失败:回滚到旧配置,并输出错误日志。
- 保留上一份有效配置,支持手动回滚。
六、功能模块定义(插件类型)
下表是“典型插件类型”,后续可以扩展更多。
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 |
6.2.1 det_post(检测结果后处理,按类别策略链)
det_post 是一个 Filter 节点,位于 ai_yolo 之后,负责对 frame->det->items 做按类别可配置的后处理。
设计目标:
- 不同类别可以挂不同的处理器链(Processor/Strategy Registry),避免“一个类别一个插件”。
- 默认改写
frame->det,让osd/alarm读到一致结果。
配置示例:
{
"id": "pp_cam1",
"type": "det_post",
"role": "filter",
"enable": true,
"default": { "enable": false },
"per_class": [
{
"class_id": 3,
"enable": true,
"processors": [
{ "type": "conf_gate", "conf_min": 0.65 },
{ "type": "bbox_gate", "min_w": 10, "min_h": 10 },
{ "type": "roi_gate", "roi": { "x": 0.1, "y": 0.1, "w": 0.8, "h": 0.8 }, "mode": "center" }
]
},
{
"class_id": 5,
"enable": true,
"processors": [
{ "type": "hsv_ratio", "h_min": 0, "h_max": 20, "s_min": 20, "v_min": 70, "ratio_min": 0.7,
"max_samples": 2048, "step": 0, "min_samples": 32,
"crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } }
]
},
{
"class_id": 7,
"enable": true,
"processors": [
{ "type": "lab_kmeans", "k": 3, "iters": 8, "l_max": 50, "ratio_min": 0.4,
"max_samples": 2048, "step": 0, "min_samples": 64,
"crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } }
]
}
]
}
内置 processors(当前实现):
conf_gate:按置信度过滤(conf_min)bbox_gate:按框尺寸过滤(min_w,min_h)roi_gate:按 ROI 过滤(roi:{x,y,w,h}归一化;mode:center使用 bbox 中心点判断)hsv_ratio:框内采样像素转 HSV,统计阈值命中占比(h_min/h_max/s_min/s_max/v_min/v_max/ratio_min,支持crop/max_samples/step)lab_kmeans:框内采样像素转 Lab,KMeans 聚类后取最暗簇做判定(k/iters/l_max/ratio_min,支持crop/max_samples/step)
注意:
hsv_ratio/lab_kmeans需要Frame提供可读的像素数据(frame->data可访问)。若上游是纯 DMA-BUF 且未映射到 CPU,这两类处理器会自然退化为“不过滤”。
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, actions(见下文详细说明) |
CPU |
storage |
持续录像(MP4/TS 切片) | mode, segment_sec, path |
MPP VENC + IO |
publish |
编码并推流(RTSP/HLS) | codec, bitrate, outputs |
MPP VENC + 网络 |
zlm_http |
内嵌 HTTP 文件服务(为 HLS 提供访问) | root, prefix, port, ssl |
ZLMediaKit(mk_api) |
说明:当前实现中
publish的proto:"hls"为落盘生成 m3u8/ts 文件,path应指向index.m3u8文件路径;若需要通过网络播放,建议新增zlm_http节点启动内嵌 HTTP,并将root指向 HLS 父目录(如/data/hls)。
osd 参数说明:
labels: 自定义类别标签数组,如["cat", "dog", "bird"],为空则使用 COCO 默认标签
6.4 alarm 插件详细说明
alarm 插件采用模块化 Actions 架构,支持多种报警动作的灵活组合:
架构设计:
┌─────────────────────────────────────────────────────────┐
│ AlarmNode │
│ ┌──────────────┐ ┌──────────────────────────────────┐ │
│ │ RuleEngine │ │ FrameRingBuffer (pre-event) │ │
│ │ - 类别过滤 │ │ - 保存最近 N 秒的帧 │ │
│ │ - ROI 检测 │ └──────────────────────────────────┘ │
│ │ - 持续时间 │ │
│ │ - 冷却控制 │ ┌──────────────────────────────────┐ │
│ └──────────────┘ │ AlarmActions (可配置) │ │
│ │ - LogAction → 日志输出 │ │
│ │ - HttpAction → HTTP 回调 │ │
│ │ - ExternalApiAction → 外部API报警 │ │
│ │ - SnapshotAction → 截图+上传 │ │
│ │ - ClipAction → 视频片段+上传 │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
完整配置示例:
{
"id": "alarm_cam1",
"type": "alarm",
"role": "sink",
"labels": ["矿体A", "矿体B", "异物"],
"rules": [
{
"name": "ore_detection",
"class_ids": [0, 1],
"objects": ["矿体A"],
"roi": { "x": 0.1, "y": 0.1, "w": 0.8, "h": 0.8 },
"min_duration_ms": 500,
"cooldown_ms": 5000,
"schedule": "08:00-20:00"
}
],
"actions": {
"log": { "enable": true, "level": "info" },
"http": {
"enable": true,
"url": "http://业务系统/api/alarm",
"timeout_ms": 3000,
"include_media_url": true
},
"external_api": {
"enable": false,
"getTokenUrl": "http://业务系统/api/getToken",
"putMessageUrl": "http://业务系统/api/putMessage",
"tenantCode": "32",
"channelNo": "${vod_channelNo}",
"timeout_ms": 3000,
"include_media_url": true,
"token_header": "X-Access-Token",
"token_json_path": "responseBody.token",
"token_cache_sec": 600
},
"snapshot": {
"enable": true,
"format": "jpg",
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://minio:9000",
"bucket": "alarms",
"access_key": "${MINIO_ACCESS_KEY}",
"secret_key": "${MINIO_SECRET_KEY}"
}
},
"clip": {
"enable": true,
"pre_sec": 5,
"post_sec": 10,
"format": "mp4",
"upload": { "type": "local", "path": "/tmp/alarms" }
}
}
}
external_api 动作说明(对接外部系统报警:先取 token,再上报告警):
- 取 token:对
getTokenUrl发起 HTTP POST,默认从响应 JSON 的responseBody.token读取(可用token_json_path修改点号路径)。 - 上报告警:对
putMessageUrl发起 HTTP POST,并携带 Header:Content-Type: application/json、X-Access-Token: <token>(可用token_header修改)。 - 上报请求体(固定结构):
{
"tenantCode": "32",
"channelNo": "<vod_channelNo>",
"alarmContent": "<msg>",
"alarmTime": "YYYY-MM-DD HH:MM:SS",
"picInfo": [{"url": "<picUrl>"}],
"videoInfo": [{"url": "<videoUrl>"}]
}
alarmTime使用本地时间格式化。picUrl/videoUrl来自snapshot/clip动作上传后的 URL(通常为 MinIO 返回链接);若为空则对应数组为空。alarmContent:若配置中未提供alarmContent,默认使用rule_name。
external_api 参数说明:
- 必填:
getTokenUrl,putMessageUrl,tenantCode,channelNo - 常用可选:
timeout_ms(默认 3000)、include_media_url(默认 true)、token_cache_sec(默认 600;为 0 时仅在 401/403 时刷新 token) - 其它可选:
token_header、token_json_path、alarmContent、max_queue_size/queue_policy、max_retries/retry_backoff_ms
规则参数说明:
class_ids: 要监控的类别 ID 数组(与模型输出对应)objects: 要监控的类别名称数组(需配合labels使用)roi: 感兴趣区域,归一化坐标 (0-1)min_duration_ms: 目标在 ROI 内持续多久才触发报警cooldown_ms: 同一规则连续触发的冷却时间schedule: 有效时间段,格式 "HH:MM-HH:MM"
上传器类型:
local: 保存到本地文件系统minio: 上传到 MinIO 对象存储s3: 上传到 AWS S3(兼容 MinIO)
6.5 storage 插件详细说明
storage 插件用于持续录像(7x24 小时),与 alarm 的事件录像功能互补。
配置示例:
{
"id": "storage_cam1",
"type": "storage",
"role": "sink",
"mode": "continuous",
"format": "mp4",
"codec": "h264",
"segment_sec": 300,
"path": "/rec/cam1",
"filename_pattern": "%Y%m%d/%H%M%S",
"fps": 25,
"bitrate_kbps": 2000
}
参数说明:
mode: 录像模式,当前支持continuous(持续录像)format: 输出格式,mp4或tssegment_sec: 单个文件时长(秒)filename_pattern: 文件名模式,支持 strftime 格式
七、线程模型(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 配置控制
- 节点配置中可增加字段:
"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_fpsqueue_length(入队/出队队列长度)frame_drop_counterror_countavg_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 板子上直接编译,无需配置交叉编译环境。
依赖安装:
# Ubuntu/Debian
sudo apt update
sudo apt install -y build-essential cmake git pkg-config \
libavformat-dev libavcodec-dev libavutil-dev libswscale-dev
编译命令:
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 |
运行:
# 纯转码网关
./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 或批量部署。
# 需要安装交叉编译工具链
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/
十一、扩展与演进
-
新增算法插件
- 实现新的
INode子类(例如ai_cls,ai_reid)。 - 使用
REGISTER_NODE编译为.so放入plugin_path。 - 在
config.json中新增节点即可接入全流程。
- 实现新的
-
新增输出类型
- 新增
publish_webrtc、storage_s3等节点类型。 - 不改动上游图结构,只调整 sink 部分。
- 新增
-
分布式扩展(后续版本)
- 引入 ZeroMQ / NanoMSG,将远端设备作为特殊节点处理(如
remote_sink)。 - 中控只维护抽象图,实际在多台 RK3588 设备上分布执行。
- 引入 ZeroMQ / NanoMSG,将远端设备作为特殊节点处理(如