346 lines
12 KiB
Plaintext
346 lines
12 KiB
Plaintext
感谢您提供 `Dockerfile` 和 `docker-compose.yml` 文件。这是极其宝贵的信息,它让我能将之前的 C++ 架构蓝图,完美适配到您**已经高度定制化**的 RK3588 开发环境中。
|
||
|
||
[cite\_start]您提供的 `Dockerfile` 非常出色。它已经正确处理了最复杂的部分:通过 `PPA` [cite: 1, 2] [cite\_start]安装了特定于 Rockchip 的 GStreamer 插件 (`gstreamer1.0-rockchip` [cite: 2][cite\_start]) 以及 MPP/RGA 的**C++ 开发库** (`librockchip-mpp-dev`, `librga-dev` [cite: 2])。
|
||
|
||
您的 `docker-compose.yml` 也配置正确,通过 `devices` 和 `group_add` 将 NPU/VPU/RGA 硬件能力 成功映射到了容器内。
|
||
|
||
我们的架构决策保持不变:**将流媒体服务作为独立的 C++ 进程**。这可以确保它在崩溃时(例如 C++ 的段错误)**绝对不会**影响您现有的 Modbus/MQTT 核心服务。
|
||
|
||
基于您的环境,我为您制定了以下**高度定制**的开发计划。
|
||
|
||
-----
|
||
|
||
### A. 核心架构规划 (基于您的 Docker 环境)
|
||
|
||
1. **独立进程:** 我们将开发一个全新的 C++ 可执行文件(例如 `edge-streamer-cpp`)。
|
||
2. [cite\_start]**共享容器:** 这个新进程将与您现有的(Modbus/MQTT)C++ 服务**运行在同一个 `edge-proxy-dev` 容器中**。这允许它们共享所有硬件资源 和已安装的库 [cite: 2],同时保持进程级的故障隔离。
|
||
3. **开发工作流:** 您将在主机上编写代码(位于 `docker-compose.yml` 同级的目录中),代码会通过 `volumes` 自动同步到容器的 `/app` 目录。您将在容器内执行所有编译和运行操作。
|
||
4. **服务间通信 (IPC) 优化:**
|
||
* **控制(C++ -\> C++):** 您现有的服务(Modbus)将通过 **HTTP**(例如 `http://localhost:8001/api/start`)来控制新的流媒体服务。
|
||
* [cite\_start]**AI 结果(C++ -\> MQTT):** 新的流媒体服务在获得 AI 结果后,将使用 `paho.mqtt.cpp` 库(您的 `Dockerfile` 已经编译了它 [cite: 8])将 JSON 结果直接发布到 `docker-compose.yml` 中定义的 `mqtt-broker` 服务。这是最高效、最解耦的方案。
|
||
|
||
-----
|
||
|
||
### B. 关键发现:环境依赖检查 (RKNN)
|
||
|
||
[cite\_start]我发现了一个关键点:您的 `Dockerfile` 安装了 GStreamer 和 VPU/MPP 的开发库 (`librockchip-mpp-dev` [cite: 2]),这对于**视频编解码**是完美的。
|
||
|
||
但是,它**缺失了 AI 检测所需的 NPU (RKNN) C-API 开发库**(即 `rknn_api.h` 和 `librknnrt.so`)。
|
||
|
||
我的计划将基于您需要 NPU 加速 AI 检测的前提。因此,**我们的第一步必须是**将这些缺失的库添加到您的 Docker 镜像中。
|
||
|
||
-----
|
||
|
||
### C. 阶段一:完善您的 Docker 环境 (添加 RKNN, HTTP, JSON)
|
||
|
||
**目标:** 将缺失的 NPU C-API、C++ HTTP 库和 C++ JSON 库添加到您的 `edge-proxy-dev` 镜像中。
|
||
|
||
**步骤:**
|
||
|
||
1. **准备 RKNN SDK (在主机上):**
|
||
|
||
* 在您的主机项目目录(`docker-compose.yml` 所在的目录)下,创建一个新目录,例如 `docker/rknn_sdk/`。
|
||
* 从您的 RK3588 SDK 中,复制 `include/rknn_api.h` 到 `docker/rknn_sdk/include/rknn_api.h`。
|
||
* 复制 `lib/librknnrt.so` 到 `docker/rknn_sdk/lib/librknnrt.so`。
|
||
|
||
2. **修改您的 `docker/Dockerfile`:**
|
||
|
||
* [cite\_start]找到构建 `paho.mqtt.cpp` [cite: 8] 的 `RUN` 指令块。
|
||
* [cite\_start]在 `cmake --build build --target install && \` [cite: 9] [cite\_start]之后,`rm -rf /tmp/build-context` [cite: 9] 之前,插入以下代码:
|
||
|
||
<!-- end list -->
|
||
|
||
```dockerfile
|
||
# ... (paho.mqtt.cpp 的 cmake install)
|
||
cmake --build build --target install && \
|
||
|
||
# --- 规划师建议:添加 RKNN, HTTP, JSON 库 ---
|
||
|
||
# 1. 复制 RKNN C-API (NPU 库) (假设已按步骤1放置)
|
||
# 注意:COPY 指令的源路径是相对于 docker-compose.yml 的 context
|
||
COPY docker/rknn_sdk/include/rknn_api.h /usr/local/include/
|
||
COPY docker/rknn_sdk/lib/librknnrt.so /usr/local/lib/
|
||
|
||
# 2. 安装 C++ HTTP 和 JSON 的 header-only 库
|
||
# (需要先安装 curl)
|
||
apt-get update && apt-get install -y --no-install-recommends curl && \
|
||
# C++ HTTP Lib
|
||
curl -L https://raw.githubusercontent.com/yhirose/cpp-httplib/master/httplib.h -o /usr/local/include/httplib.h && \
|
||
# C++ JSON Lib
|
||
curl -L https://github.com/nlohmann/json/releases/latest/download/json.hpp -o /usr/local/include/json.hpp && \
|
||
|
||
# 3. 更新动态链接库缓存 (使系统找到 librknnrt.so)
|
||
ldconfig && \
|
||
|
||
# 4. 清理 apt 缓存
|
||
apt-get remove -y curl && \
|
||
apt-get autoremove -y && \
|
||
|
||
# (Dockerfile 原有的清理命令)
|
||
rm -rf /tmp/build-context
|
||
|
||
# (Dockerfile 的剩余部分)
|
||
RUN rm -rf /var/lib/apt/lists/*
|
||
# ...
|
||
```
|
||
|
||
3. **重建 Docker 镜像 (关键步骤):**
|
||
|
||
* 在主机的终端中执行:
|
||
|
||
<!-- end list -->
|
||
|
||
```bash
|
||
docker-compose build edge-proxy-dev
|
||
```
|
||
|
||
-----
|
||
|
||
### D. 阶段二:开发工作流程与项目设置
|
||
|
||
1. **启动开发环境:**
|
||
|
||
```bash
|
||
docker-compose up -d
|
||
```
|
||
|
||
* 这将启动 `edge-proxy-dev` 和 `mqtt-broker` 两个服务。
|
||
|
||
2. **创建新服务目录 (在主机上):**
|
||
|
||
* 在您的项目根目录(`docker-compose.yml` 所在位置)创建一个新目录:
|
||
|
||
<!-- end list -->
|
||
|
||
```bash
|
||
mkdir edge-streamer-cpp
|
||
```
|
||
|
||
3. **进入容器的开发 Shell:**
|
||
|
||
```bash
|
||
docker-compose exec edge-proxy-dev /bin/bash
|
||
```
|
||
|
||
4. **在容器内初始化项目骨架:**
|
||
|
||
* **注意:** 以下所有命令均在 **容器内的 shell** (`/app` 目录) 中执行。
|
||
|
||
<!-- end list -->
|
||
|
||
```bash
|
||
# (容器内)
|
||
# /app 目录是您主机项目的挂载点
|
||
cd /app/edge-streamer-cpp
|
||
|
||
# 创建源码和构建目录
|
||
mkdir src include build
|
||
|
||
# 创建初始文件 (您将在主机上编辑它们)
|
||
touch src/main.cpp
|
||
touch src/StreamManager.cpp
|
||
touch include/StreamManager.h
|
||
touch CMakeLists.txt
|
||
```
|
||
|
||
5. **开始编码:**
|
||
|
||
* 现在,在您的**主机**上,使用您喜欢的 IDE (如 VS Code) 打开 `edge-streamer-cpp` 目录,开始编辑 `CMakeLists.txt` 和 `.cpp` / `.h` 文件。
|
||
|
||
-----
|
||
|
||
### E. 阶段三:配置 CMakeLists.txt
|
||
|
||
[cite\_start]**目标:** 配置 CMake,使其能正确链接 GStreamer [cite: 2][cite\_start]、RKNN (阶段 C 添加的)、Paho MQTT [cite: 7, 8] 和 HTTP/JSON 库。
|
||
|
||
* 将以下内容粘贴到您在**主机**上打开的 `edge-streamer-cpp/CMakeLists.txt` 文件中:
|
||
|
||
```cmake
|
||
cmake_minimum_required(VERSION 3.10)
|
||
project(EdgeStreamer CXX)
|
||
|
||
set(CMAKE_CXX_STANDARD 17)
|
||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||
|
||
# [cite_start]--- 1. 查找 GStreamer (来自 Dockerfile) [cite: 2] ---
|
||
find_package(PkgConfig REQUIRED)
|
||
pkg_check_modules(GST REQUIRED gstreamer-1.0 gstreamer-app-1.0)
|
||
|
||
# [cite_start]--- 2. 查找 Paho MQTT (来自 Dockerfile) [cite: 7, 8] ---
|
||
# 您的 Dockerfile 编译并安装了它,我们可以直接 find_package
|
||
find_package(paho-mqttpp3 REQUIRED) # C++ 库
|
||
find_package(paho-mqtt-c REQUIRED) # C 库 (依赖)
|
||
|
||
# --- 3. 包含 RKNN, HTTP, JSON (来自 阶段C) ---
|
||
# 这些头文件已在 /usr/local/include,会自动被 C++ 编译器找到
|
||
include_directories(
|
||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||
${GST_INCLUDE_DIRS}
|
||
/usr/local/include
|
||
)
|
||
|
||
# --- 4. 定义可执行文件 ---
|
||
add_executable(edge-streamer
|
||
src/main.cpp
|
||
src/StreamManager.cpp
|
||
)
|
||
|
||
# --- 5. 链接所有库 ---
|
||
target_link_libraries(edge-streamer
|
||
PRIVATE
|
||
# GStreamer
|
||
${GST_LIBRARIES}
|
||
|
||
# Paho MQTT (C++ 和 C)
|
||
paho-mqttpp3
|
||
paho-mqtt-c
|
||
|
||
# RKNN (来自 librknnrt.so)
|
||
rknnrt
|
||
|
||
# 线程 (GStreamer, httplib, Paho 都需要)
|
||
pthread
|
||
|
||
# [cite_start]Paho-MQTT SSL 依赖 [cite: 9] [cite_start](Dockerfile 有 libssl-dev [cite: 2])
|
||
ssl
|
||
crypto
|
||
)
|
||
```
|
||
|
||
-----
|
||
|
||
### F. 阶段四:C++ 核心代码实现 (IPC 调整)
|
||
|
||
您在 `src/` 和 `include/` 中的代码逻辑与我上一个计划(C++ 核心版)非常相似,但**IPC 部分将进行关键优化**:
|
||
|
||
* **`main.cpp`**:
|
||
|
||
* 完全不变。使用 `httplib.h` 在 `8001` 端口(或您选择的端口)启动一个 HTTP 服务,用于接收您现有 Modbus 服务的 "Start/Stop Stream" 命令。
|
||
|
||
* **`include/StreamManager.h`**:
|
||
|
||
* 需要添加 `paho.mqtt.cpp` 的头文件和成员变量:
|
||
|
||
<!-- end list -->
|
||
|
||
```cpp
|
||
#include <mqtt/async_client.h> // Paho MQTT C++
|
||
// ...
|
||
class StreamManager {
|
||
// ...
|
||
private:
|
||
// ... (GstElement* 等)
|
||
|
||
// --- 新增 MQTT 客户端 ---
|
||
const std::string m_mqtt_server_address = "tcp://mqtt-broker:1883"; //
|
||
const std::string m_mqtt_client_id = "edge-streamer-ai";
|
||
mqtt::async_client m_mqtt_client;
|
||
};
|
||
```
|
||
|
||
* **`src/StreamManager.cpp` (关键的 AI 回调调整):**
|
||
|
||
* 在 `StreamManager` 的构造函数或初始化方法中,连接到 MQTT Broker。
|
||
* 在 GStreamer 的 `on_new_sample_from_sink` 回调函数中,当 RKNN 推理完成后:
|
||
|
||
<!-- end list -->
|
||
|
||
```cpp
|
||
// (伪代码)
|
||
// 静态 GStreamer 回调函数
|
||
static GstFlowReturn on_new_sample_from_sink(GstAppSink *sink, gpointer user_data) {
|
||
StreamManager *manager = static_cast<StreamManager*>(user_data);
|
||
|
||
// ... (1. 拉取 GstBuffer) ...
|
||
|
||
// ... (2. 调用 RKNN C-API 进行推理) ...
|
||
// rknn_outputs_get(ctx, 1, outputs, NULL);
|
||
|
||
// ... (3. (TODO) 将 'outputs' 格式化为 JSON 字符串) ...
|
||
// nlohmann::json ai_result;
|
||
// ai_result["stream_id"] = manager->get_stream_id();
|
||
// ai_result["object_count"] = ...;
|
||
// std::string payload = ai_result.dump();
|
||
|
||
// 4. (优化) 将 JSON 结果发布到内部 MQTT Broker
|
||
try {
|
||
std::string topic = "ai/results/" + manager->get_stream_id();
|
||
manager->get_mqtt_client().publish(topic, payload);
|
||
} catch (const mqtt::exception& exc) {
|
||
std::cerr << "Error publishing to MQTT: " << exc.what() << std::endl;
|
||
}
|
||
|
||
// ... (5. 释放 GstBuffer 和 RKNN outputs) ...
|
||
|
||
return GST_FLOW_OK;
|
||
}
|
||
```
|
||
|
||
-----
|
||
|
||
### G. 阶段五:编译与运行 (容器内)
|
||
|
||
在您**主机**上编写完代码后,回到您**容器内的 shell** (`docker-compose exec ...` 的那个)。
|
||
|
||
1. **编译项目 (在容器内):**
|
||
|
||
```bash
|
||
# (容器内)
|
||
cd /app/edge-streamer-cpp/build
|
||
|
||
# 运行 CMake (仅需一次)
|
||
cmake ..
|
||
|
||
# 编译 (每次代码变更后执行)
|
||
make -j$(nproc)
|
||
```
|
||
|
||
2. **运行新服务 (在容器内):**
|
||
|
||
```bash
|
||
# (容器内)
|
||
# 编译好的可执行文件位于 build/ 目录
|
||
./edge-streamer
|
||
# 您应该会看到 HTTP API 和 GStreamer 启动的日志
|
||
```
|
||
|
||
3. **测试 (在 *第二个* 容器 Shell 中):**
|
||
|
||
* 在**主机**上打开一个**新的**终端,再次 `exec` 进同一个容器:
|
||
|
||
<!-- end list -->
|
||
|
||
```bash
|
||
docker-compose exec edge-proxy-dev /bin/bash
|
||
```
|
||
|
||
* **A. 测试 API (控制):**
|
||
```bash
|
||
# (第二个容器内)
|
||
# (需要先 apt-get install curl)
|
||
curl -X POST http://localhost:8001/api/v1/stream/start \
|
||
-d '{"stream_id":"cam1", "rtsp_url":"rtsp://..."}'
|
||
```
|
||
* **B. 监听 AI 结果 (MQTT):**
|
||
```bash
|
||
# (第二个容器内)
|
||
# (需要先 apt-get install mosquitto-clients)
|
||
mosquitto_sub -h mqtt-broker -t "ai/results/#" -v
|
||
```
|
||
* 您现在应该能在一个终端看到服务日志,在另一个终端看到 AI 推理结果。
|
||
|
||
-----
|
||
|
||
### H. 阶段六:生产部署 (可选)
|
||
|
||
您当前的 `docker-compose.yml` 使用 `command: sleep infinity`,这非常适合开发。
|
||
|
||
当您准备部署时,您需要一个进程管理器来同时启动和监控您的**两个 C++ 服务**(Modbus服务 和 `edge-streamer`服务)。
|
||
|
||
**推荐方案:**
|
||
|
||
1. 修改 `Dockerfile` 以安装 `supervisor` (`apt-get install -y supervisor`)。
|
||
2. 创建一个 `supervisord.conf` 文件,配置 `[program:modbus_service]` 和 `[program:streamer_service]`。
|
||
3. 修改 `docker-compose.yml`,将 `command:` 更改为 `"/usr/bin/supervisord -c /etc/supervisor/supervisord.conf"`。
|
||
|
||
这将确保两个独立的服务都在容器启动时自动运行,并能在崩溃时自动重启。 |