新增支持人脸识别的压测脚本;优化部署脚本和文档

This commit is contained in:
haotian 2026-02-26 21:51:35 +08:00
parent aea8ec0622
commit b5b36457e4
8 changed files with 1367 additions and 105 deletions

View File

@ -0,0 +1,340 @@
{
"global": {
"metrics_port": 9000,
"web_root": "web"
},
"queue": {
"size": 4,
"strategy": "drop_oldest"
},
"templates": {
"face_recog_pipeline": {
"nodes": [
{
"id": "in",
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "${url}",
"fps": "${fps}",
"width": "${src_w}",
"height": "${src_h}",
"use_mpp": true,
"use_ffmpeg": false,
"force_tcp": true,
"reconnect_sec": 5,
"reconnect_backoff_max_sec": 30
},
{
"id": "pre",
"type": "preprocess",
"role": "filter",
"enable": true,
"dst_w": 640,
"dst_h": 640,
"dst_format": "rgb",
"dst_packed": true,
"keep_ratio": false,
"rga_gate": "${name}",
"use_rga": true
},
{
"id": "ai",
"type": "ai_yolo",
"role": "filter",
"enable": true,
"infer_fps": 10,
"model_path": "./models/best-640.rknn",
"model_version": "v8",
"num_classes": 3,
"conf": 0.35,
"nms": 0.45,
"class_filter": []
},
{
"id": "face_det",
"type": "ai_face_det",
"role": "filter",
"enable": true,
"model_path": "./models/RetinaFace_mobile320.rknn",
"conf": 0.7,
"nms": 0.4,
"max_faces": 10,
"output_landmarks": true,
"input_format": "rgb"
},
{
"id": "face_recog",
"type": "ai_face_recog",
"role": "filter",
"enable": true,
"model_path": "./models/mobilefacenet_arcface.rknn",
"align": true,
"emit_embedding": false,
"max_faces": 10,
"input_format": "rgb",
"input_dtype": "uint8",
"threshold": {
"accept": 0.45,
"margin": 0.05
},
"gallery": {
"backend": "sqlite",
"path": "${face_gallery_path}",
"load_on_start": true,
"expected_dim": 512,
"dtype": "auto"
}
},
{
"id": "trk",
"type": "tracker",
"role": "filter",
"enable": true,
"mode": "bytetrack_lite",
"per_class": true,
"state_key": "${name}",
"track_classes": [0, 1, 2],
"ignore_classes": [],
"allowed_models": ["yolov5", "yolov8"],
"high_th": 0.5,
"low_th": 0.1,
"iou_th": 0.3,
"max_age_ms": 1500,
"min_hits": 2,
"max_tracks": 128,
"debug": {
"stats": false,
"stats_interval": 200
}
},
{
"id": "osd",
"type": "osd",
"role": "filter",
"enable": true,
"draw_bbox": true,
"draw_text": true,
"line_width": 2,
"font_scale": 1,
"labels": [],
"use_rga_bbox": false,
"draw_face_det": true,
"draw_face_bbox": true
},
{
"id": "post",
"type": "preprocess",
"role": "filter",
"enable": true,
"queue": {
"size": 2,
"policy": "drop_oldest"
},
"dst_w": "${src_w}",
"dst_h": "${src_h}",
"dst_format": "nv12",
"keep_ratio": false,
"rga_gate": "${name}",
"use_rga": true
},
{
"id": "alarm",
"type": "alarm",
"role": "sink",
"enable": true,
"eval_fps": 10,
"labels": [],
"rules": [
{
"name": "object_in_view",
"class_ids": [0, 1, 2],
"roi": {"x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0},
"min_score": 0.4,
"min_box_area_ratio": 0.02,
"require_track_id": true,
"min_duration_ms": 1500,
"min_hits": 3,
"hit_window_ms": 1500,
"cooldown_ms": 5000,
"per_track_cooldown_ms": 5000
}
],
"actions": {
"log": {"enable": true, "level": "info"},
"snapshot": {
"enable": true,
"min_interval_ms": 15000,
"format": "jpg",
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "${minio_endpoint}",
"bucket": "${minio_bucket}",
"region": "us-east-1",
"access_key": "${minio_ak}",
"secret_key": "${minio_sk}"
}
},
"clip": {
"enable": true,
"min_interval_ms": 15000,
"pre_sec": 5,
"post_sec": 10,
"format": "mp4",
"fps": "${fps}",
"upload": {
"type": "minio",
"endpoint": "${minio_endpoint}",
"bucket": "${minio_bucket}",
"region": "us-east-1",
"access_key": "${minio_ak}",
"secret_key": "${minio_sk}"
}
},
"http": {
"enable": false,
"url": "http://127.0.0.1:8080/api/alarm",
"timeout_ms": 3000,
"include_media_url": true,
"method": "POST"
},
"external_api": {
"enable": true,
"getTokenUrl": "http://10.0.0.49:8080/api/getToken",
"putMessageUrl": "http://10.0.0.49:8080/api/putMessage",
"tenantCode": "32",
"channelNo": "${name}",
"timeout_ms": 3000,
"include_media_url": true,
"token_header": "X-Access-Token",
"token_json_path": "responseBody.token",
"token_cache_sec": 1200
}
}
},
{
"id": "pub",
"type": "publish",
"role": "filter",
"enable": true,
"queue": {"size": 2, "policy": "drop_oldest"},
"codec": "h264",
"fps": "${fps}",
"gop": "${gop}",
"bitrate_kbps": "${bitrate_kbps}",
"use_mpp": true,
"use_ffmpeg_mux": true,
"outputs": [
{"proto": "rtsp_server", "port": 8555, "path": "/live/${name}"},
{"proto": "hls", "path": "./web/hls/${name}/index.m3u8", "segment_sec": 2}
],
"debug": {"stats": true, "stats_interval": 100}
}
],
"edges": [
["in", "pre"],
["pre", "ai"],
["ai", "trk"],
["trk", "face_det"],
["face_det", "face_recog"],
["face_recog", "osd"],
["osd", "post"],
["post", "pub"],
["pub", "alarm"]
]
}
},
"instances": [
{
"name": "cam1",
"template": "face_recog_pipeline",
"params": {
"name": "cam1",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"face_gallery_path": "./models/face_gallery_cam1.db",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "myminio",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"
}
},
{
"name": "cam2",
"template": "face_recog_pipeline",
"params": {
"name": "cam2",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"face_gallery_path": "./models/face_gallery_cam2.db",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "myminio",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"
}
},
{
"name": "cam3",
"template": "face_recog_pipeline",
"params": {
"name": "cam3",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"face_gallery_path": "./models/face_gallery_cam3.db",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "myminio",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"
}
},
{
"name": "cam4",
"template": "face_recog_pipeline",
"params": {
"name": "cam4",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"face_gallery_path": "./models/face_gallery_cam4.db",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "myminio",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"
}
},
{
"name": "cam5",
"template": "face_recog_pipeline",
"params": {
"name": "cam5",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"face_gallery_path": "./models/face_gallery_cam5.db",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "myminio",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"
}
}
]
}

View File

@ -3,20 +3,40 @@
## 架构说明
```
┌─────────────────┐ ┌─────────────────┐
│ Media Server │ │ RK3588 Agent │
│ (systemd) │◄────│ (systemd) │
│ 高可用独立服务 │ │ 监控/控制 │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
RTSP 输出流 Web 管理界面
HLS 输出流 http://IP:9100
┌─────────────────────────────────────────────────────────────┐
│ RK3588 Device │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Media Server │ │ RK3588 Agent │ │
│ │ (systemd) │◄────│ (systemd) │ │
│ │ │ │ │ │
│ │ - RTSP输出 │ │ - Web管理界面 │ │
│ │ - HLS输出 │ │ - 服务监控 │ │
│ │ - NPU推理 │ │ - 远程配置 │ │
│ └─────────┬───────────┘ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 运行时数据目录 │ │
│ │ /var/lib/rk3588-media-server/ │ │
│ │ ├── hls/ - HLS分片输出 │ │
│ │ ├── logs/ - 应用日志 │ │
│ │ ├── alarms/ - 告警截图 │ │
│ │ └── clips/ - 告警视频片段 │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ /opt/rk3588-media-server/ # 程序文件 │
│ /opt/rk3588-agent/ # Agent程序 │
└─────────────────────────────────────────────────────────────┘
```
### 设计特点
- **Media Server**: 独立的 systemd 服务,高可用(崩溃自动重启)
- **Agent**: 独立的 systemd 服务,通过 `systemctl` 监控和控制 Media Server
- **两者可独立重启**: 重启 Agent 不会影响 Media Server
- **运行时数据分离**: 程序文件在 `/opt/`,数据在 `/var/lib/`
- **自动清理**: HLS 分片、日志、告警数据自动轮转清理
---
@ -34,47 +54,188 @@ mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
# 编译 Agent
# 编译 Agent(可选)
cd ../agent
go build -o rk3588-agent_linux_arm64 ./cmd/rk3588-agent
```
### 2. 安装
### 2. 部署
```bash
cd ~/apps/OrangePi3588Media
sudo ./scripts/manage.sh install
sudo ./scripts/deploy.sh install
```
安装过程会提示选择**配置文件**
- `test_cam1_face_det_rtsp_server.json` - 人脸检测(推荐)
- `sample_cam1_native_rtsp_face.json` - 完整功能
- `test_cam1_face_det_recog_rtsp_server.json` - 人脸识别
- `sample_cam1.json` - 完整功能(需手动修复)
部署过程会
1. 安装 Media Server 和 Agent 到 `/opt/`
2. 创建运行时数据目录 `/var/lib/rk3588-media-server/`
3. 配置日志轮转和自动清理
4. 启动 systemd 服务
### 3. 查看状态
```bash
sudo ./scripts/manage.sh status
sudo ./scripts/deploy.sh status
```
显示内容:
- 系统运行时间
- NPU 负载
- Media Server 状态PID、CPU/内存、端口)
- Agent 状态
- 最近日志
### 4. 卸载
```bash
sudo ./scripts/manage.sh uninstall
sudo ./scripts/deploy.sh uninstall
```
---
## 完整命令参考
### 部署脚本命令
| 命令 | 说明 |
|------|------|
| `sudo ./deploy.sh install` | 安装/部署服务 |
| `sudo ./deploy.sh upgrade` | 升级服务(保留配置和数据)|
| `sudo ./deploy.sh status` | 查看详细运行状态 |
| `sudo ./deploy.sh logs` | 查看实时日志 |
| `sudo ./deploy.sh clean-hls [天数]` | 清理 HLS 旧分片 |
| `sudo ./deploy.sh uninstall` | 卸载服务 |
### Media Server 服务管理
```bash
# 启动/停止/重启
sudo systemctl start media-server
sudo systemctl stop media-server
sudo systemctl restart media-server
# 查看状态
sudo systemctl status media-server
# 查看日志
sudo journalctl -u media-server -f
sudo journalctl -u media-server --since "1 hour ago"
```
### Agent 服务管理
```bash
# 启动/停止/重启(不影响 Media Server
sudo systemctl start rk3588-agent
sudo systemctl stop rk3588-agent
sudo systemctl restart rk3588-agent
# 查看状态
sudo systemctl status rk3588-agent
# 查看日志
sudo journalctl -u rk3588-agent -f
```
---
## 目录结构
### 程序目录 (/opt/)
```
/opt/rk3588-media-server/
├── bin/
│ ├── media-server # 主程序
│ ├── cleanup-hls.sh # HLS清理脚本
│ └── plugins/ # 插件目录
│ ├── libinput_rtsp.so
│ ├── libai_yolo.so
│ ├── libosd.so
│ ├── libtracker.so
│ └── ...
├── lib/ # 依赖库
├── etc/
│ └── media-server.json # 配置文件
├── models/ # AI模型文件
│ ├── best-640.rknn
│ ├── yolov5s-640-640.rknn
│ └── ...
└── web/ # Web静态文件
├── index.html
├── graph.html
├── hls_player.html # HLS播放器
└── hls/ # HLS输出(符号链接)
/opt/rk3588-agent/
├── rk3588-agent # Agent程序
└── agent.config.json # Agent配置
```
### 运行时数据目录 (/var/lib/)
```
/var/lib/rk3588-media-server/
├── hls/ # HLS直播分片
│ ├── cam1/
│ │ ├── index.m3u8
│ │ ├── index0.ts
│ │ └── ...
│ ├── cam2/
│ └── ...
├── logs/ # 应用日志
├── alarms/ # 告警截图
└── clips/ # 告警视频片段
/var/lib/rk3588-agent/
└── device_id # 设备唯一标识
```
---
## 配置修改
编辑配置文件:
```bash
sudo nano /opt/rk3588-media-server/etc/media-server.json
```
修改后重启服务:
```bash
sudo systemctl restart media-server
```
---
## 数据管理
### HLS 分片清理
HLS 分片自动清理策略:
- **默认保留**: 3天
- **自动执行**: 每天凌晨3点
- **手动执行**: `sudo /opt/rk3588-media-server/bin/cleanup-hls.sh [天数]`
### 日志轮转
日志自动轮转策略:
- **保留周期**: 7天
- **压缩**: 启用
- **位置**: `/var/lib/rk3588-media-server/logs/`
### 告警数据清理
自动清理策略(通过 cron
- **告警视频** (clips): 保留30天
- **告警图片** (alarms): 保留90天
查看当前存储使用:
```bash
sudo ./scripts/deploy.sh status
```
---
## 升级
升级步骤(保留配置和数据):
```bash
cd ~/apps/OrangePi3588Media
@ -89,46 +250,16 @@ make -j$(nproc)
cd ../agent
go build -o rk3588-agent_linux_arm64 ./cmd/rk3588-agent
# 3. 重新安装
# 3. 执行升级
cd ..
sudo ./scripts/manage.sh install
sudo ./scripts/deploy.sh upgrade
```
---
## 管理命令
## Web 管理界面
### Media Server独立服务
```bash
# 启动/停止/重启
sudo systemctl start media-server
sudo systemctl stop media-server
sudo systemctl restart media-server
# 查看状态
sudo systemctl status media-server
# 查看日志
sudo journalctl -u media-server -f
```
### Agent独立服务
```bash
# 启动/停止/重启(不影响 Media Server
sudo systemctl start rk3588-agent
sudo systemctl stop rk3588-agent
sudo systemctl restart rk3588-agent
# 查看状态
sudo systemctl status rk3588-agent
# 查看日志
sudo journalctl -u rk3588-agent -f
```
### Web 管理界面
### Agent 管理界面
```
http://<设备IP>:9100
@ -136,45 +267,20 @@ http://<设备IP>:9100
功能:
- 查看 Media Server 运行状态
- 启动/停止/重启 Media Server(通过 systemctl
- 启动/停止/重启 Media Server
- 查看配置信息
---
## 配置修改
编辑配置文件:
```bash
sudo nano /opt/rk3588-media-server/etc/media-server.json
```
修改后重启 Media Server
```bash
sudo systemctl restart media-server
```
---
## 目录结构
### HLS 播放器
```
/opt/rk3588-media-server/
├── bin/
│ ├── media-server # 主程序
│ └── plugins/ # 插件
├── etc/
│ └── media-server.json # 配置文件
├── models/ # AI 模型
├── logs/ # 日志
└── web/hls/ # HLS 输出
/opt/rk3588-agent/
├── rk3588-agent # Agent 程序
└── agent.config.json # Agent 配置
http://<设备IP>:9000/hls_player.html
```
功能:
- 5 路视频同时播放
- 自适应布局
- 实时状态显示
---
## 故障排查
@ -182,34 +288,113 @@ sudo systemctl restart media-server
### 服务无法启动
```bash
# Media Server 错误
# 查看详细错误
sudo journalctl -u media-server -n 50
# Agent 错误
sudo journalctl -u rk3588-agent -n 50
# 检查配置文件语法
sudo cat /opt/rk3588-media-server/etc/media-server.json | python3 -m json.tool
# 检查目录权限
ls -la /var/lib/rk3588-media-server/
ls -la /opt/rk3588-media-server/
```
### 检查 NPU
### NPU 问题
```bash
cat /sys/kernel/debug/rknpu/load
ls /dev/rknpu
# 查看 NPU 状态
cat /proc/rknpu/load
cat /proc/rknpu/version
# 查看设备节点
ls -la /dev/rknpu*
# 查看温度
cat /sys/class/thermal/thermal_zone6/temp
```
### RTSP 测试
### HLS 无法播放
```bash
# 检查 HLS 输出目录
ls -la /var/lib/rk3588-media-server/hls/
# 检查分片生成
ls -la /var/lib/rk3588-media-server/hls/cam1/
# 检查服务端口
ss -tlnp | grep 9000
```
### RTSP 无法连接
```bash
# 本地测试
ffplay rtsp://localhost:8555/live/cam1_face_det
ffplay rtsp://localhost:8555/live/cam1
# 远程测试
ffplay rtsp://<设备IP>:8555/live/cam1_face_det
# 检查端口
ss -tlnp | grep 8555
# 查看编码器状态
grep "RKVENC" /proc/mpp_service/sessions-summary
```
---
## 系统要求
- **硬件**: Orange Pi 5 Plus (RK3588)
- **系统**: Ubuntu 22.04 LTS (ARM64)
- **硬件**: Orange Pi 5 Plus (RK3588) 或其他 RK3588 设备
- **系统**: Ubuntu 22.04 LTS (ARM64) 或 Debian 11
- **内存**: 建议 4GB 以上
- **存储**: 建议 32GB 以上HLS 分片占用空间)
- **依赖**: RKNN Runtime, MPP, FFmpeg
---
## 性能参考
5 路 720p@30fps 并发处理:
| 资源 | 使用率 | 备注 |
|------|--------|------|
| NPU Core0 | 70-80% | YOLO 推理 |
| CPU | 20-30% | 预处理、编码 |
| 编码器 | 5 路 720p | H264 CBR 2Mbps |
| 温度 | 50-60°C | 正常范围 |
| 内存 | 3-4GB | 包含系统 |
---
## 备份与恢复
### 备份配置和数据
```bash
# 创建备份
BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
# 备份配置
cp /opt/rk3588-media-server/etc/media-server.json "$BACKUP_DIR/"
# 备份模型
cp -r /opt/rk3588-media-server/models "$BACKUP_DIR/"
# 备份 HLS可选占用空间大
cp -r /var/lib/rk3588-media-server/hls "$BACKUP_DIR/"
echo "备份完成: $BACKUP_DIR"
```
### 恢复
```bash
# 恢复配置
cp "$BACKUP_DIR/media-server.json" /opt/rk3588-media-server/etc/
# 恢复模型
cp -r "$BACKUP_DIR/models"/* /opt/rk3588-media-server/models/
# 重启服务
sudo systemctl restart media-server
```

BIN
models/face_gallery_cam1.db Normal file

Binary file not shown.

BIN
models/face_gallery_cam2.db Normal file

Binary file not shown.

BIN
models/face_gallery_cam3.db Normal file

Binary file not shown.

BIN
models/face_gallery_cam4.db Normal file

Binary file not shown.

BIN
models/face_gallery_cam5.db Normal file

Binary file not shown.

737
scripts/deploy.sh Executable file
View File

@ -0,0 +1,737 @@
#!/bin/bash
# RK3588 Media Server + Agent 完善部署脚本
# 部署到 /opt包含完整的运行时目录、权限、日志轮转等
#
# 用法: sudo ./deploy.sh [install|upgrade|status|logs|clean-hls|uninstall]
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 目录定义
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$PROJECT_DIR/build"
# 安装目录
INSTALL_DIR="/opt/rk3588-media-server"
AGENT_INSTALL_DIR="/opt/rk3588-agent"
SERVICE_DIR="/etc/systemd/system"
LOGROTATE_DIR="/etc/logrotate.d"
# 运行时数据目录(持久化数据)
RUNTIME_DIR="/var/lib/rk3588-media-server"
HLS_DIR="$RUNTIME_DIR/hls"
LOGS_DIR="$RUNTIME_DIR/logs"
ALARMS_DIR="$RUNTIME_DIR/alarms"
CLIPS_DIR="$RUNTIME_DIR/clips"
# 创建必要的运行时目录
create_runtime_dirs() {
echo -e "${CYAN}[创建运行时目录]${NC}"
# 主运行时目录
mkdir -p "$RUNTIME_DIR"
mkdir -p "$HLS_DIR"
mkdir -p "$LOGS_DIR"
mkdir -p "$ALARMS_DIR"
mkdir -p "$CLIPS_DIR"
# 设置权限root用户运行但允许orangepi组读取
chown -R root:root "$RUNTIME_DIR"
chmod 755 "$RUNTIME_DIR"
chmod 755 "$HLS_DIR"
chmod 755 "$LOGS_DIR"
chmod 755 "$ALARMS_DIR"
chmod 755 "$CLIPS_DIR"
echo -e "${GREEN}${NC} 运行时目录创建完成:"
echo " HLS输出: $HLS_DIR"
echo " 日志: $LOGS_DIR"
echo " 告警图片: $ALARMS_DIR"
echo " 告警视频: $CLIPS_DIR"
}
# 安装日志轮转配置
install_logrotate() {
echo -e "${CYAN}[配置日志轮转]${NC}"
# Media Server 日志轮转
cat > "$LOGROTATE_DIR/rk3588-media-server" << 'EOF'
/var/lib/rk3588-media-server/logs/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 644 root root
sharedscripts
postrotate
/bin/kill -HUP $(cat /var/run/syslogd.pid 2>/dev/null) 2>/dev/null || true
endscript
}
EOF
# HLS 分片清理保留最近7天
cat > "$LOGROTATE_DIR/rk3588-hls" << 'EOF'
/var/lib/rk3588-media-server/hls/*/index*.ts {
daily
rotate 1
maxage 7
missingok
notifempty
nocompress
}
/var/lib/rk3588-media-server/hls/*/index*.m3u8 {
daily
rotate 1
maxage 7
missingok
notifempty
nocompress
}
EOF
chmod 644 "$LOGROTATE_DIR/rk3588-media-server"
chmod 644 "$LOGROTATE_DIR/rk3588-hls"
echo -e "${GREEN}${NC} 日志轮转配置完成"
}
# 安装 HLS 清理脚本
install_hls_cleanup() {
echo -e "${CYAN}[安装 HLS 清理脚本]${NC}"
cat > "$INSTALL_DIR/bin/cleanup-hls.sh" << 'EOF'
#!/bin/bash
# HLS 分片清理脚本 - 保留最近 N 天的分片
KEEP_DAYS=${1:-3}
HLS_DIR="/var/lib/rk3588-media-server/hls"
if [ ! -d "$HLS_DIR" ]; then
echo "HLS 目录不存在: $HLS_DIR"
exit 1
fi
# 清理旧的分片文件
find "$HLS_DIR" -name "*.ts" -type f -mtime +$KEEP_DAYS -delete 2>/dev/null
# 清理空的摄像头目录
find "$HLS_DIR" -type d -empty -delete 2>/dev/null
echo "$(date): HLS 清理完成 (保留最近 $KEEP_DAYS 天)"
EOF
chmod +x "$INSTALL_DIR/bin/cleanup-hls.sh"
# 添加到 crontab每天凌晨3点执行
CRON_FILE="/etc/cron.d/rk3588-media-server"
cat > "$CRON_FILE" << 'EOF'
# RK3588 Media Server 定时任务
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# 每天凌晨3点清理HLS旧分片保留3天
0 3 * * * root /opt/rk3588-media-server/bin/cleanup-hls.sh 3 >> /var/lib/rk3588-media-server/logs/cleanup.log 2>&1
# 每周日凌晨4点清理告警视频保留30天
0 4 * * 0 root find /var/lib/rk3588-media-server/clips -name "*.mp4" -type f -mtime +30 -delete 2>/dev/null
# 每周日凌晨4点清理告警图片保留90天
30 4 * * 0 root find /var/lib/rk3588-media-server/alarms -name "*.jpg" -type f -mtime +90 -delete 2>/dev/null
EOF
chmod 644 "$CRON_FILE"
echo -e "${GREEN}${NC} HLS 清理脚本安装完成"
echo " 清理策略:"
echo " - HLS分片: 保留3天"
echo " - 告警视频: 保留30天"
echo " - 告警图片: 保留90天"
}
# 安装 Media Server
cmd_install_media_server() {
echo -e "${BLUE}[1/5] 安装 Media Server...${NC}"
# 检查编译产物
if [ ! -f "$BUILD_DIR/media-server" ]; then
echo -e "${RED}错误: 未找到编译好的 media-server${NC}"
echo "请先编译: cd $PROJECT_DIR && cmake --build build"
exit 1
fi
# 创建目录结构
mkdir -p "$INSTALL_DIR/bin/plugins"
mkdir -p "$INSTALL_DIR/lib"
mkdir -p "$INSTALL_DIR/etc"
mkdir -p "$INSTALL_DIR/web"
mkdir -p "$INSTALL_DIR/models"
# 复制二进制文件
cp "$BUILD_DIR/media-server" "$INSTALL_DIR/bin/"
chmod +x "$INSTALL_DIR/bin/media-server"
# 复制插件
if [ -d "$BUILD_DIR/plugins" ]; then
cp "$BUILD_DIR"/plugins/*.so "$INSTALL_DIR/bin/plugins/" 2>/dev/null || true
fi
# 复制依赖库
if ls "$BUILD_DIR"/*.so 1> /dev/null 2>&1; then
cp "$BUILD_DIR"/*.so "$INSTALL_DIR/lib/" 2>/dev/null || true
fi
# 复制 web 静态文件
if [ -d "$PROJECT_DIR/web" ]; then
cp -r "$PROJECT_DIR/web"/* "$INSTALL_DIR/web/" 2>/dev/null || true
fi
# 复制模型文件
if [ -d "$PROJECT_DIR/models" ]; then
cp -r "$PROJECT_DIR/models"/* "$INSTALL_DIR/models/" 2>/dev/null || true
fi
if [ -d "$PROJECT_DIR/third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588" ]; then
cp "$PROJECT_DIR/third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588"/*.rknn "$INSTALL_DIR/models/" 2>/dev/null || true
fi
# 选择并复制配置文件
select_config
# 修复配置文件路径
fix_config_paths
echo -e "${GREEN}${NC} Media Server 安装完成"
}
# 选择配置文件
select_config() {
echo -e "${CYAN}[选择配置文件]${NC}"
# 查找所有可用的配置文件
CONFIGS=()
for f in "$PROJECT_DIR/configs"/*.json; do
if [ -f "$f" ]; then
CONFIGS+=("$(basename "$f")")
fi
done
echo "可用的配置文件:"
for i in "${!CONFIGS[@]}"; do
printf " %2d) %s\n" $((i+1)) "${CONFIGS[$i]}"
done
echo ""
printf "请选择 [1-${#CONFIGS[@]}默认1]: "
read choice
choice=${choice:-1}
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#CONFIGS[@]}" ]; then
choice=1
fi
SELECTED_CONFIG="$PROJECT_DIR/configs/${CONFIGS[$((choice-1))]}"
cp "$SELECTED_CONFIG" "$INSTALL_DIR/etc/media-server.json"
echo -e "${GREEN}${NC} 使用配置: ${CONFIGS[$((choice-1))]}"
}
# 修复配置文件中的路径
fix_config_paths() {
echo -e "${CYAN}[修复配置路径]${NC}"
python3 << 'EOF'
import json
import os
import re
config_file = "/opt/rk3588-media-server/etc/media-server.json"
install_dir = "/opt/rk3588-media-server"
runtime_dir = "/var/lib/rk3588-media-server"
project_dir = os.path.expanduser("~/apps/OrangePi3588Media")
def fix_path(path):
"""修复配置文件中的路径"""
if not path or not isinstance(path, str):
return path
# 模型路径 -> 安装目录的 models
if 'third_party/rknpu2' in path or '.rknn' in path:
return os.path.join(install_dir, 'models', os.path.basename(path))
# HLS 输出路径 -> 运行时目录的 hls
if 'web/hls' in path or path.startswith('./web/hls'):
return os.path.join(runtime_dir, 'hls', os.path.basename(os.path.dirname(path)), 'index.m3u8')
# web 根目录 -> 安装目录的 web
if path.startswith('./web/'):
return os.path.join(install_dir, path[2:])
# models 目录 -> 安装目录的 models
if path.startswith('./models/'):
return os.path.join(install_dir, path[2:])
# 其他相对路径 -> 尝试解析
if path.startswith('./'):
abs_path = os.path.join(project_dir, path)
if os.path.exists(abs_path):
return abs_path
return os.path.join(install_dir, path[2:])
return path
def process_node(node):
"""处理节点配置"""
# 修复模型路径
if 'model_path' in node:
node['model_path'] = fix_path(node['model_path'])
# 修复图库路径
if 'gallery' in node and isinstance(node['gallery'], dict) and 'path' in node['gallery']:
node['gallery']['path'] = fix_path(node['gallery']['path'])
# 修复输出路径HLS、报警图片等
if 'outputs' in node and isinstance(node['outputs'], list):
for output in node['outputs']:
if 'path' in output:
output['path'] = fix_path(output['path'])
# 修复 MinIO 端点(如果是本地)
if 'minio_endpoint' in node:
endpoint = node['minio_endpoint']
if '127.0.0.1' in endpoint or 'localhost' in endpoint:
# 保持本地端点不变
pass
return node
try:
with open(config_file, 'r') as f:
config = json.load(f)
# 处理 graphs 格式
if 'graphs' in config:
for graph in config['graphs']:
if 'nodes' in graph:
for node in graph['nodes']:
process_node(node)
# 处理 templates + instances 格式
if 'templates' in config and 'instances' in config:
for template_name, template in config['templates'].items():
if 'nodes' in template:
for node in template['nodes']:
process_node(node)
# 添加全局配置(如果不存在)
if 'global' not in config:
config['global'] = {}
# 确保 web_root 指向正确位置
config['global']['web_root'] = os.path.join(install_dir, 'web')
config['global']['metrics_port'] = config['global'].get('metrics_port', 9000)
with open(config_file, 'w') as f:
json.dump(config, f, indent=2)
print(f"配置路径修复完成")
except Exception as e:
print(f"配置处理出错: {e}")
import traceback
traceback.print_exc()
EOF
}
# 安装 Agent
cmd_install_agent() {
echo -e "${BLUE}[2/5] 安装 Agent...${NC}"
# 查找 Agent 编译产物
AGENT_SOURCE=""
if [ -f "$PROJECT_DIR/agent/rk3588-agent_linux_arm64" ]; then
AGENT_SOURCE="$PROJECT_DIR/agent/rk3588-agent_linux_arm64"
elif [ -f "$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent" ]; then
AGENT_SOURCE="$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent"
fi
if [ -z "$AGENT_SOURCE" ]; then
echo -e "${YELLOW}警告: 未找到编译好的 Agent跳过安装${NC}"
return
fi
mkdir -p "$AGENT_INSTALL_DIR"
mkdir -p "/var/lib/rk3588-agent"
cp "$AGENT_SOURCE" "$AGENT_INSTALL_DIR/rk3588-agent"
chmod +x "$AGENT_INSTALL_DIR/rk3588-agent"
# Agent 配置
cat > "$AGENT_INSTALL_DIR/agent.config.json" << EOF
{
"agent": {
"listen": "0.0.0.0:9100",
"token": "$(openssl rand -hex 16)",
"require_token_for_read": false,
"discovery_enable": true,
"discovery_port": 35688,
"device_name": "rk3588_$(hostname)",
"device_id_path": "/var/lib/rk3588-agent/device_id",
"models_dir": "$INSTALL_DIR/models",
"max_upload_mb": 200,
"config_path": "$INSTALL_DIR/etc/media-server.json",
"media_server_process": {
"enable": false
},
"media_server_base_url": "http://127.0.0.1:9000",
"media_server_timeout_ms": 3000,
"media_server_retry": { "max_attempts": 3, "backoff_ms": [200, 500] }
}
}
EOF
chown -R orangepi:orangepi "/var/lib/rk3588-agent"
echo -e "${GREEN}${NC} Agent 安装完成"
}
# 安装 systemd 服务
cmd_install_services() {
echo -e "${BLUE}[3/5] 安装 Systemd 服务...${NC}"
# Media Server 服务
cat > "$SERVICE_DIR/media-server.service" << EOF
[Unit]
Description=RK3588 Media Server
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=$INSTALL_DIR
Environment="LD_LIBRARY_PATH=$INSTALL_DIR/lib:/usr/local/lib"
Environment="RKNPU_LOG_LEVEL=2"
ExecStartPre=/bin/mkdir -p $HLS_DIR $LOGS_DIR $ALARMS_DIR $CLIPS_DIR
ExecStart=$INSTALL_DIR/bin/media-server --config $INSTALL_DIR/etc/media-server.json
Restart=always
RestartSec=5
StartLimitInterval=60s
StartLimitBurst=3
StandardOutput=journal
StandardError=journal
SyslogIdentifier=media-server
# 资源限制
LimitNOFILE=65536
LimitNPROC=4096
# 优雅关闭
TimeoutStopSec=30
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target
EOF
# Agent 服务
cat > "$SERVICE_DIR/rk3588-agent.service" << EOF
[Unit]
Description=RK3588 Agent Service
After=network.target media-server.service
Wants=media-server.service
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=$AGENT_INSTALL_DIR
ExecStart=$AGENT_INSTALL_DIR/rk3588-agent --config $AGENT_INSTALL_DIR/agent.config.json
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=rk3588-agent
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
echo -e "${GREEN}${NC} Systemd 服务安装完成"
}
# 主安装流程
cmd_install() {
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ RK3588 Media Server 完善部署脚本 ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
# 检查 root 权限
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
exit 1
fi
echo "项目目录: $PROJECT_DIR"
echo "安装目录: $INSTALL_DIR"
echo "运行时目录: $RUNTIME_DIR"
echo ""
# 执行安装步骤
cmd_install_media_server
cmd_install_agent
create_runtime_dirs
cmd_install_services
install_logrotate
install_hls_cleanup
# 启动服务
echo -e "${BLUE}[启动服务]${NC}"
systemctl enable media-server
systemctl start media-server
systemctl enable rk3588-agent
systemctl start rk3588-agent
echo ""
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ 部署完成! ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "目录结构:"
echo " 程序: $INSTALL_DIR/"
echo " 数据: $RUNTIME_DIR/"
echo " 配置: $INSTALL_DIR/etc/media-server.json"
echo ""
echo "服务管理:"
echo " 状态: sudo systemctl status media-server"
echo " 日志: sudo journalctl -u media-server -f"
echo " 界面: http://$(hostname -I | awk '{print $1}'):9100"
echo ""
echo "HLS播放:"
echo " http://$(hostname -I | awk '{print $1}'):9000/hls_player.html"
}
# 升级
cmd_upgrade() {
echo -e "${YELLOW}========== 升级 Media Server ==========${NC}"
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误: 请使用 sudo 运行${NC}"
exit 1
fi
# 备份配置
BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)"
mkdir -p "$BACKUP_DIR"
[ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/"
echo "配置已备份到: $BACKUP_DIR"
# 停止服务
echo "停止服务..."
systemctl stop media-server rk3588-agent
# 重新安装
cmd_install
echo -e "${GREEN}升级完成${NC}"
}
# 查看状态
cmd_status() {
echo -e "${BLUE}========== RK3588 Media Server 状态 ==========${NC}"
echo ""
# 系统信息
echo -e "${CYAN}[系统信息]${NC}"
echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo " 运行时间: $(uptime -p 2>/dev/null || uptime | awk -F',' '{print $1}')"
echo ""
# NPU
echo -e "${CYAN}[NPU 负载]${NC}"
if [ -f /proc/rknpu/load ]; then
cat /proc/rknpu/load | sed 's/^/ /'
else
echo " NPU 信息不可用"
fi
echo ""
# Media Server
echo -e "${CYAN}[Media Server]${NC}"
if systemctl is-active --quiet media-server 2>/dev/null; then
echo -e " 状态: ${GREEN}● 运行中${NC}"
PID=$(systemctl show --property=MainPID --value media-server 2>/dev/null)
echo " PID: $PID"
if [ -n "$PID" ] && [ "$PID" != "0" ]; then
CPU_MEM=$(ps -p $PID -o %cpu,%mem --no-headers 2>/dev/null || echo "N/A")
echo " CPU/MEM: $CPU_MEM"
fi
echo " 端口:"
ss -tlnp 2>/dev/null | grep -E "(9000|8555)" | head -3 | sed 's/^/ /'
else
echo -e " 状态: ${RED}○ 停止${NC}"
fi
echo ""
# HLS 输出
echo -e "${CYAN}[HLS 输出]${NC}"
if [ -d "$HLS_DIR" ]; then
CHANNELS=$(find "$HLS_DIR" -maxdepth 1 -type d | wc -l)
CHANNELS=$((CHANNELS - 1))
echo " 通道数: $CHANNELS"
if [ $CHANNELS -gt 0 ]; then
echo " 分片统计:"
for d in "$HLS_DIR"/*/; do
[ -d "$d" ] || continue
name=$(basename "$d")
count=$(find "$d" -name "*.ts" | wc -l)
size=$(du -sh "$d" 2>/dev/null | cut -f1)
echo " $name: ${count}个分片, ${size}"
done
fi
else
echo " HLS 目录不存在"
fi
echo ""
# Agent
echo -e "${CYAN}[RK3588 Agent]${NC}"
if systemctl is-active --quiet rk3588-agent 2>/dev/null; then
echo -e " 状态: ${GREEN}● 运行中${NC}"
echo " 管理界面: http://$(hostname -I | awk '{print $1}'):9100"
else
echo -e " 状态: ${RED}○ 停止${NC}"
fi
echo ""
# 存储使用
echo -e "${CYAN}[存储使用]${NC}"
echo " HLS 输出: $(du -sh "$HLS_DIR" 2>/dev/null | cut -f1)"
echo " 日志: $(du -sh "$LOGS_DIR" 2>/dev/null | cut -f1)"
echo " 告警图片: $(du -sh "$ALARMS_DIR" 2>/dev/null | cut -f1)"
echo " 告警视频: $(du -sh "$CLIPS_DIR" 2>/dev/null | cut -f1)"
echo ""
echo "常用命令:"
echo " 查看日志: sudo journalctl -u media-server -f"
echo " HLS清理: sudo $INSTALL_DIR/bin/cleanup-hls.sh"
echo " 实时监控: watch -n 1 cat /proc/rknpu/load"
}
# 查看日志
cmd_logs() {
SERVICE="${2:-media-server}"
echo -e "${BLUE}查看 $SERVICE 日志 (按 Ctrl+C 退出)...${NC}"
journalctl -u "$SERVICE" -f
}
# 清理 HLS
cmd_clean_hls() {
echo -e "${YELLOW}清理 HLS 旧分片...${NC}"
KEEP_DAYS="${2:-3}"
if [ -f "$INSTALL_DIR/bin/cleanup-hls.sh" ]; then
"$INSTALL_DIR/bin/cleanup-hls.sh" "$KEEP_DAYS"
else
echo "清理脚本不存在"
exit 1
fi
}
# 卸载
cmd_uninstall() {
echo -e "${YELLOW}========== 卸载 RK3588 Media Server ==========${NC}"
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误: 请使用 sudo 运行${NC}"
exit 1
fi
read -p "确定要卸载? (y/N): " -r
[ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ] && echo "已取消" && exit 0
echo "[1/4] 停止服务..."
systemctl stop media-server rk3588-agent 2>/dev/null || true
systemctl disable media-server rk3588-agent 2>/dev/null || true
echo "[2/4] 删除服务文件..."
rm -f "$SERVICE_DIR/media-server.service"
rm -f "$SERVICE_DIR/rk3588-agent.service"
rm -f "$LOGROTATE_DIR/rk3588-media-server"
rm -f "$LOGROTATE_DIR/rk3588-hls"
rm -f "/etc/cron.d/rk3588-media-server"
systemctl daemon-reload
echo "[3/4] 备份数据..."
BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)"
mkdir -p "$BACKUP_DIR"
[ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/"
[ -d "$HLS_DIR" ] && cp -r "$HLS_DIR" "$BACKUP_DIR/" 2>/dev/null || true
echo "数据已备份到: $BACKUP_DIR"
echo "[4/4] 删除文件..."
read -p "是否删除所有数据 (包括 HLS/日志/告警)? (y/N): " -r
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR" "$RUNTIME_DIR"
echo "所有数据已删除"
else
rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR"
echo "程序已删除,数据保留在: $RUNTIME_DIR"
fi
echo -e "${GREEN}卸载完成${NC}"
}
# 主入口
case "${1:-install}" in
install)
cmd_install
;;
upgrade)
cmd_upgrade
;;
status)
cmd_status
;;
logs)
cmd_logs "$@"
;;
clean-hls)
cmd_clean_hls "$@"
;;
uninstall)
cmd_uninstall
;;
*)
echo "RK3588 Media Server 部署脚本"
echo ""
echo "用法: sudo ./deploy.sh [命令]"
echo ""
echo "命令:"
echo " install 安装/部署服务 (默认)"
echo " upgrade 升级服务 (保留配置和数据)"
echo " status 查看运行状态"
echo " logs 查看日志"
echo " clean-hls 清理 HLS 旧分片"
echo " uninstall 卸载服务"
echo ""
echo "示例:"
echo " sudo ./deploy.sh install"
echo " sudo ./deploy.sh status"
echo " sudo ./deploy.sh clean-hls 7 # 保留最近7天"
exit 1
;;
esac