C SDK 概述
C ABI 内核(darra_ai.h + DarraAI.Core)。全部推理唯一入口:darra_session_open / darra_session_open_plain → darra_session_infer_image → darra_session_close。GUI、PLC、客户应用同一路径,无第二套。
本文档对应 C SDK v1.0.0(与 darra_ai.h 的 DARRA_VERSION_MAJOR/MINOR/PATCH 同一组数字)。加载后第一件事核对 ABI:DARRA_VERSION_DECODE_MAJOR(darra_version()) == DARRA_VERSION_MAJOR,不等 = 装错了库,禁止继续。运行时字符串用 darra_version_string(),客户机必须看到 1.0.0+runtime。
公开 runtime 构建只含解密、验票、推理分发、诊断、运行时包管理;物理上没有加密 / 签发符号。权威契约 = darra_ai.h,语义冲突以头文件为准。
安装
从 下载页面 获取内核包或 darra-ai-c-sdk.zip(头文件 + import lib + 示例)。
- Windows x64
- Linux x64
- Linux ARM64
- CMake
包:darra-ai-core-windows-x64.zip(DarraAI.Core.dll、darra_ai.h、import lib、darra-selftest)。
cl /utf-8 /Fe:my_app.exe main.c DarraAI.Core.lib /I include
运行目录必须带 DarraAI.Core.dll。MSVC 消费方加 /utf-8(头文件 UTF-8 无 BOM,注释全中文)。
包:darra-ai-core-linux-x64.tar.gz(libdarraai_core.so + 头文件)。
gcc -o my_app main.c -I include -L. -ldarraai_core -Wl,-rpath,'$ORIGIN'
包:darra-ai-core-linux-arm64.tar.gz(RK3588 板端与客户板共用)。edge-rknn 只在此平台运行。
gcc -o my_app main.c -I include -L. -ldarraai_core -Wl,-rpath,'$ORIGIN'
add_executable(my_app main.c)
target_include_directories(my_app PRIVATE ${SDK_PATH}/include)
# Windows 链接 DarraAI.Core;Linux 链接 darraai_core
target_link_libraries(my_app PRIVATE DarraAI.Core)
#include "darra_ai.h"
只交付 x64 / ARM64,两平台均单调用约定,无 __stdcall 变体。
环境要求
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows x64 / Linux x86_64 / Linux ARM64(RK3588)。macOS 不在 v1 |
| 编译器 | MSVC(/utf-8)/ GCC / Clang,C11 或更高 |
| 头文件 | darra_ai.h |
| 运行库 | Windows:DarraAI.Core.dll;Linux:libdarraai_core.so |
| 权限 | 装运行时包到系统目录时可能需要管理员;SDK 不代装 NVIDIA / RK3588 NPU 等系统驱动 |
| 重依赖 | ORT / CUDA / TensorRT / OpenVINO / RKNN 走 Provider;首次开会话时 darra_runtime_ensure 按硬件探测下载校验 |
- 不承诺 dump 免疫。 授权机 + 管理员级调试器在会话存活期暂停 dump,挡不住。本 SDK 防的是静态拷走、转发、无授权运行、常规 dump 工具、逆向授权算法。
- 加密单向。 官方不提供解回明文的通道。永久密码只用于无限次推理。
- 驱动不代装。 进程内能解决的全部自动;要动系统的只诊断不代劳(
darra_diag_collect给出原因)。 - macOS 不在 v1。 不是「即将支持」。
- 不承诺硬实时。 推理是阻塞调用。PLC 侧必须在扫描周期外进程 / 线程调用。
- 诊断 JSON 不含密钥 / 密码 / token / 机器指纹字节。
快速开始
客户现场加载 .darmodel(DARM1)+ .darmkey(或永久密码)。导出格式即加载格式,用户零配置。
#include <stdio.h>
#include "darra_ai.h"
int main(void) {
if (DARRA_VERSION_DECODE_MAJOR(darra_version()) != DARRA_VERSION_MAJOR) {
fprintf(stderr, "ABI 不匹配: %s\n", darra_version_string());
return 1;
}
darra_session* s = NULL;
int32_t rc = darra_session_open("model.darmodel", "model.darmkey", NULL, NULL, &s);
if (rc != DARRA_OK) {
darra_error_t err; err.size = sizeof(err);
darra_last_error(&err);
fprintf(stderr, "[%d] %s\n建议: %s\n", err.code, err.message, err.hint);
return (int)rc;
}
/* 读图、填 darra_image_desc、darra_session_infer_image … */
darra_session_close(s);
return 0;
}
对应包未装时,open 内部会调一次 darra_runtime_ensure(无进度回调,可能阻塞下载)。想看进度请先自己调 ensure(幂等)。
备选方式:明文模型(试跑 / 自有无需加密模型)
与加密入口同一推理路径,只跳过容器解码与验票。
darra_session* s = NULL;
int32_t rc = darra_session_open_plain("model.onnx", NULL, &s);
open_plain 的路径可以是 .onnx、.pdmodel(同目录须有 .pdiparams)、或含二者的目录。.pt / .pth 仍拒,返回 DARRA_UNSUPPORTED_PAYLOAD。
.darmodel+ 授权 — 生产环境,推荐。open_plain— Studio 试跑 / 客户自有明文模型。
约定
返回值
除注明 void 的函数外,一律返回 int32_t 错误码:0(DARRA_OK)= 成功;非 0 = darra_error_code 之一,返回值 == 本线程 darra_last_error 的 code。细节用 darra_last_error 取。
int32_t rc = darra_xxx(...);
if (rc != DARRA_OK) {
darra_error_t err; err.size = sizeof(err);
darra_last_error(&err); /* err.message 是什么错,err.hint 怎么修 */
}
错误槽是线程局部的:每个 API 进入时清空本线程槽,失败时填满。失败后立刻在同一线程取,不要被同线程下一次 API 冲掉。逐码文案见 错误码。
参数契约违反(NULL 句柄 / NULL 出参 / size 未填 / 裸缓冲缺宽高)属调用方 bug:返回 DARRA_INTERNAL,message 指明哪个参数。SDK 在任何非法入参下都不崩溃。
字符串所有权
| 来源 | 释放 |
|---|---|
char** 出参(diag / meta / infer 结果) | 必须 darra_string_free,禁止 free() / 跨 CRT 堆释放 |
darra_version_string() | 静态字符串,禁止释放 |
| 编码 | UTF-8,SDK 保证 NUL 结尾 |
结构体 size
所有结构体首字段 uint32_t size,调用方填 sizeof(结构体)。SDK 只读写它认识的字段;旧头配新 dll/so 照常工作。
darra_image_desc desc;
memset(&desc, 0, sizeof(desc));
desc.size = sizeof(darra_image_desc);
线程安全
| 函数 | 线程安全 | 备注 |
|---|---|---|
darra_version / darra_version_string | 是 | 纯查询,不改错误槽 |
darra_last_error / darra_clear_error | 是 | 只碰本线程槽 |
darra_string_free | 是 | NULL 安全;重复释放 = 未定义行为 |
darra_diag_collect | 是 | 耗时可到数百毫秒,禁止在 PLC 扫描周期调 |
darra_runtime_ensure | 是 | 进程内互斥串行,重复 / 并发安全 |
darra_runtime_ensure_cancel | 是 | 任意线程可调;进度回调内禁止 |
darra_session_open / open_plain / open_ex / open_plain_ex | 是 | 各开各的会话可并发;缺包时内部 ensure 走同一把进程锁 |
darra_session_meta_json | 是 | 只读会话内不可变数据 |
darra_session_infer_image | 同会话可并发 | 吞吐扩展推荐每线程一个会话;v1 无视频流 |
darra_session_close | 仅在不与其它调用并发时 | close 后句柄失效,再用 = 未定义行为 |
API 总览
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 版本 | darra_version | uint32_t | 查询 | 打包版本,用 DARRA_VERSION_DECODE_* 宏拆 major/minor/patch |
darra_version_string | const char* | 查询 | 静态字符串,形如 1.0.0+runtime;禁止释放 | |
| 错误 | darra_last_error | int32_t | 查询 | 取本线程最近一次失败细节;out 可为 NULL |
darra_clear_error | void | 过程 | 显式清空本线程错误槽 | |
| 字符串 | darra_string_free | void | 释放 | 释放 SDK 堆字符串;NULL 安全 |
| 诊断 | darra_diag_collect | int32_t | 查询 | 本机环境诊断 JSON;用完 darra_string_free |
| 运行时包 | darra_runtime_ensure | int32_t | 过程 | 确保 Provider / profile 已装;幂等、事务性 |
darra_runtime_ensure_cancel | void | 过程 | 取消在飞的 ensure;无在飞则为 no-op | |
| 会话 | darra_session_open | int32_t | 过程 | 打开 .darmodel 建会话(options = NULL) |
darra_session_open_plain | int32_t | 过程 | 打开明文模型建会话(options = NULL) | |
darra_session_open_ex | int32_t | 过程 | 打开 .darmodel,带 darra_session_options | |
darra_session_open_plain_ex | int32_t | 过程 | 打开明文模型,带 darra_session_options | |
darra_session_meta_json | int32_t | 查询 | 会话元信息 JSON;用完 darra_string_free | |
darra_session_infer_image | int32_t | 过程 | 单张图像推理,结果 JSON;v1 无视频流 | |
darra_session_close | void | 释放 | 关闭会话;NULL 安全;禁止与在飞调用并发 |
一模型一实例:一个 darra_session* = 一份已加载模型 + 一个 Provider。多模型或多份同模型 = 多会话,API 完全相同。指名 Provider 不可用 = fail-closed,绝不静默降级。
版本
darra_version()
uint32_t darra_version(void);
返回打包版本 (major << 16) | (minor << 8) | patch,用 DARRA_VERSION_DECODE_MAJOR / MINOR / PATCH 宏拆。不会失败,不改错误槽。
头文件常量:DARRA_VERSION_MAJOR 1、DARRA_VERSION_MINOR 0、DARRA_VERSION_PATCH 0;DARRA_HEADER_VERSION 为本头打包值。绑定侧核对:运行时 major 必须等于 DARRA_VERSION_MAJOR,否则 ABI 已破坏。
darra_version_string()
const char* darra_version_string(void);
返回形如 "1.0.0+runtime" / "1.0.0+authoring" 的静态字符串。生命周期 = 进程,禁止释放。客户机上必须看到 +runtime。
if (DARRA_VERSION_DECODE_MAJOR(darra_version()) != DARRA_VERSION_MAJOR) {
fprintf(stderr, "ABI 不匹配: 头=%d 库=%s\n", DARRA_VERSION_MAJOR, darra_version_string());
return 1;
}
错误
darra_error_code 与 darra_error_t:
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 错误码 | DARRA_OK | 0 | 只读 | 成功 |
| 错误码 | DARRA_IO_NOT_FOUND | 1 | 只读 | 文件不存在(容器 / 授权 / 图片 / 离线包路径错) |
| 错误码 | DARRA_IO_DENIED | 2 | 只读 | 文件存在但读 / 写被拒 |
| 错误码 | DARRA_BAD_CONTAINER | 3 | 只读 | .darmodel 损坏 / 非 DARM1 / 被截断篡改 |
| 错误码 | DARRA_BAD_LICENSE | 4 | 只读 | .darmkey 损坏或与容器不配对 |
| 错误码 | DARRA_PASSWORD_REQUIRED | 5 | 只读 | 该容器需要密码但调用方没给 |
| 错误码 | DARRA_PASSWORD_WRONG | 6 | 只读 | 密码错误 |
| 错误码 | DARRA_MACHINE_MISMATCH | 7 | 只读 | 绑机授权在非目标机器上使用 |
| 错误码 | DARRA_TRIAL_EXHAUSTED | 8 | 只读 | 试用计次已用完 |
| 错误码 | DARRA_UNSUPPORTED_PAYLOAD | 9 | 只读 | 载荷类型在当前平台 / Provider 下不支持 |
| 错误码 | DARRA_UNSUPPORTED_PLATFORM | 10 | 只读 | 整个 OS / 架构不在支持矩阵(如 macOS、x86) |
| 错误码 | DARRA_PROVIDER_MISSING | 11 | 只读 | 需要的运行时 Provider 包未安装 |
| 错误码 | DARRA_PROVIDER_ABI_MISMATCH | 12 | 只读 | Provider 包与 Core 的 ABI 版本不匹配 |
| 错误码 | DARRA_HARDWARE_MISSING | 13 | 只读 | 未探测到所需硬件(N 卡 / Intel / RK3588 NPU) |
| 错误码 | DARRA_DRIVER_TOO_OLD | 14 | 只读 | 驱动版本低于 Provider 要求(SDK 不代装) |
| 错误码 | DARRA_NETWORK_FAILED | 15 | 只读 | 清单 / 运行时包下载失败 |
| 错误码 | DARRA_CHECKSUM_MISMATCH | 16 | 只读 | 下载包或离线包 sha256 校验不过 |
| 错误码 | DARRA_CANCELLED | 17 | 只读 | 操作被取消(见 darra_runtime_ensure_cancel) |
| 错误码 | DARRA_INTERNAL | 18 | 只读 | SDK 内部错误;也用于调用方参数契约违反 |
| 结构 | darra_error_t.size | uint32_t | 调用方填 | sizeof(darra_error_t) |
| 结构 | darra_error_t.code | int32_t | 只读 | darra_error_code |
| 结构 | darra_error_t.message | char[256] | 只读 | UTF-8,保证 NUL 结尾,无需释放 |
| 结构 | darra_error_t.hint | char[512] | 只读 | 修复建议,可为空串;文案见 错误码 |
darra_last_error()
int32_t darra_last_error(darra_error_t* out);
取本线程最近一次失败的细节。out 可为 NULL(只查码);非 NULL 时调用方先填 out->size = sizeof(*out)。无错误(或上次调用成功)返回 DARRA_OK。
darra_clear_error()
void darra_clear_error(void);
显式清空本线程错误槽。一般不需要手动调(每次 API 进入自动清),供绑定层在特殊时序下使用。
字符串
darra_string_free()
void darra_string_free(char* s);
释放 SDK 返回的堆字符串。NULL 安全;重复释放 = 未定义行为。Windows 上 SDK 与调用方可能不是同一 CRT 堆,禁止 free() / delete[]。
环境自检
darra_diag_collect()
int32_t darra_diag_collect(char** out_json);
采集本机环境诊断,输出统一 JSON(schema 见 附录 A)。覆盖:os/arch、CPU、GPU 型号 + 驱动版本 + CUDA 上限、已装 Provider 及版本、授权状态(去敏:绝不含密钥 / 密码 / token / 机器指纹字节)、最近错误环形缓冲(去敏)。
- 尽力而为:单个字段采不到时 JSON 中该字段为
null或缺席,整体不失败;只有彻底无法采集才返回DARRA_INTERNAL。 - 注意:可能触发 WMI / NVML / sysfs 查询,耗时可到数百毫秒——禁止在 PLC 扫描周期等实时路径调用。
out_json:SDK 分配的 UTF-8 JSON,用完darra_string_free。
char* diag = NULL;
if (darra_diag_collect(&diag) == DARRA_OK) {
puts(diag);
darra_string_free(diag);
}
运行时包
合法 ID(头文件第 5 节,不得另造):
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 细包 | onnx-cpu | Provider | 入参 | ORT CPU |
| 细包 | onnx-cuda | Provider | 入参 | ORT CUDA EP |
| 细包 | onnx-openvino | Provider | 入参 | ORT OpenVINO EP |
| 细包 | onnx-trt-ep | Provider | 入参 | ORT TensorRT EP |
| 细包 | trt-native | Provider | 入参 | 裸 TensorRT 引擎 |
| 细包 | edge-rknn | Provider | 入参 | 仅 linux-arm64;Windows 上指名返回 DARRA_UNSUPPORTED_PLATFORM |
| 场景包 | cpu-only | profile | 入参 | 细包清单,不产生新二进制 |
| 场景包 | nvidia-gpu | profile | 入参 | 同上 |
| 场景包 | intel-iap | profile | 入参 | 同上 |
| 场景包 | amd-dml | profile | 入参 | 同上 |
| 场景包 | board-rk3588 | profile | 入参 | 同上 |
| 自动 | NULL | — | 入参 | 按硬件探测自动选细包(默认推荐) |
进度回调:
typedef void (*darra_progress_cb)(float percent, const char* stage, void* user);
percent 0..100,每个 stage 内单调不降。stage:probe / manifest / download / verify / install / done。回调在 SDK 内部工作线程触发:回调里禁止调用任何 darra_* 函数,只准记录或转发到调用方自己的 UI 队列。
darra_runtime_ensure()
int32_t darra_runtime_ensure(const char* profile_or_provider_id,
const char* offline_zip_or_null,
darra_progress_cb cb,
void* user);
确保指定 Provider / profile 已装好;没装就探测 → 选包 → 下载 → sha256 校验 → 解压登记。
profile_or_provider_id:上表细包 / 场景包;NULL = 按硬件探测自动选。offline_zip_or_null:离线整合包 zip 路径;给定时跳过 manifest/download,仍过内嵌 sha256 清单校验。NULL = 在线下载。cb/user:进度回调与透传指针;cb 可为 NULL。- 幂等:已装且清单匹配 → 直接成功(回调收到一次
"done", 100)。 - 进程内串行:并发调用被内部互斥串行化。
- 事务性:全部步骤成功才登记;取消或任何失败不留半成品。
- SDK 不自动安装 / 升级系统驱动。
典型错误:DARRA_IO_NOT_FOUND(离线包路径错)/ DARRA_NETWORK_FAILED / DARRA_CHECKSUM_MISMATCH / DARRA_IO_DENIED(目标目录需管理员)/ DARRA_UNSUPPORTED_PLATFORM / DARRA_PROVIDER_ABI_MISMATCH / DARRA_CANCELLED。
static void on_progress(float percent, const char* stage, void* user) {
(void)user;
printf("\r[%-8s] %5.1f%%", stage, percent); fflush(stdout);
}
int32_t rc = darra_runtime_ensure("cpu-only", NULL, on_progress, NULL);
darra_runtime_ensure_cancel()
void darra_runtime_ensure_cancel(void);
请求取消当前在飞的 darra_runtime_ensure(可从任意线程调,含进度回调外的 UI 线程)。没有在飞的 ensure 时为 no-op。被取消的 ensure 返回 DARRA_CANCELLED。进度回调内部禁止调用。
推理会话
打开时按 PayloadKind × 硬件 × prefer_provider 选 Provider;对应包未装则内部调一次 darra_runtime_ensure(cb=NULL,可能阻塞下载)。指名但不可用(无硬件 / 驱动过旧 / 平台不支持)= fail-closed。
Windows 收到 rknn 载荷返回 DARRA_UNSUPPORTED_PAYLOAD。禁止改 Windows ReservedCpuSets,禁止动 PLC 隔离核——darra_session_options 只约束本会话推理线程池。
Paddle 是运行时载荷(PayloadKind=paddle):明文 open_plain / open_plain_ex 可接 .pdmodel(同目录须有 .pdiparams)或含二者的目录。.pt / .pth 仍拒。
不透明句柄 darra_session:绑定侧永远只持有指针。
darra_session_open()
int32_t darra_session_open(const char* container_path,
const char* key_path_or_null,
const char* password_or_null,
const char* prefer_provider_or_null,
darra_session** out);
打开加密容器(.darmodel,DARM1)建会话。本函数 ≡ darra_session_open_ex(..., options=NULL)。
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 入参 | container_path | const char* | 必填 | .darmodel 路径 |
| 入参 | key_path_or_null | const char* | 可选 | .darmkey;NULL = 走密码 / 试用通道 |
| 入参 | password_or_null | const char* | 可选 | 永久密码;NULL = 走授权文件 / 试用通道。两个通道至少给一个;都给则 key 文件优先验票 |
| 入参 | prefer_provider_or_null | const char* | 可选 | 指名 Provider;NULL = 自动选。指名但不可用 = fail-closed,绝不静默降级 |
| 出参 | out | darra_session** | 写出 | 成功时 *out 拿到会话;失败时 *out == NULL |
典型错误:DARRA_IO_NOT_FOUND / DARRA_IO_DENIED / DARRA_BAD_CONTAINER / DARRA_BAD_LICENSE / DARRA_PASSWORD_REQUIRED / DARRA_PASSWORD_WRONG / DARRA_MACHINE_MISMATCH / DARRA_TRIAL_EXHAUSTED / DARRA_UNSUPPORTED_PAYLOAD / DARRA_PROVIDER_MISSING / DARRA_PROVIDER_ABI_MISMATCH / DARRA_HARDWARE_MISSING / DARRA_DRIVER_TOO_OLD / DARRA_NETWORK_FAILED / DARRA_CHECKSUM_MISMATCH / DARRA_CANCELLED(后三码来自内部 ensure)。
darra_session* s = NULL;
int32_t rc = darra_session_open("model.darmodel", "model.darmkey", NULL, NULL, &s);
if (rc != DARRA_OK) { /* darra_last_error 取细节 */ return rc; }
darra_session_open_plain()
int32_t darra_session_open_plain(const char* onnx_path,
const char* prefer_provider_or_null,
darra_session** out);
打开明文模型建会话。本函数 ≡ darra_session_open_plain_ex(..., options=NULL)。与 darra_session_open 同一推理路径,只跳过容器解码与验票。
onnx_path(历史参数名,语义为明文模型路径):.onnx 文件;.pdmodel 文件(同目录须有对应 .pdiparams);含 *.pdmodel + *.pdiparams 的目录。.pt / .pth 仍拒 → DARRA_UNSUPPORTED_PAYLOAD。
其余(含内部 ensure、fail-closed)同 darra_session_open。
darra_session_options
typedef struct darra_session_options {
uint32_t size;
int32_t intra_op_threads;
int32_t inter_op_threads;
uint64_t cpu_affinity_mask;
} darra_session_options;
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 选项 | size | uint32_t | 调用方填 | sizeof(darra_session_options) |
| 选项 | intra_op_threads | int32_t | 读写 | 0 = 自动,自动上限 min(硬件逻辑核, 4);>0 照填给本会话线程池;<0 = DARRA_INTERNAL |
| 选项 | inter_op_threads | int32_t | 读写 | 同上 |
| 选项 | cpu_affinity_mask | uint64_t | 读写 | 0 = 不设;非 0 = 本会话线程池 CPU 位图(bit0 = 逻辑核 0)。禁止改 ReservedCpuSets / PLC 隔离核 |
传 NULL 给 *_ex = 与旧 open / open_plain 完全相同(自动线程、不设亲和)。多会话各用各的线程池,互不抢同一把全局锁(darra_runtime_ensure 的进程内互斥除外)。
darra_session_open_ex()
int32_t darra_session_open_ex(const char* container_path,
const char* key_path_or_null,
const char* password_or_null,
const char* prefer_provider_or_null,
const darra_session_options* options_or_null,
darra_session** out);
打开加密容器建会话(带选项)。options_or_null == NULL 时与 darra_session_open 逐字相同。非 NULL 时须先填 options->size;size 未填 / 线程数为负 = DARRA_INTERNAL。其余同 darra_session_open。
darra_session_options opt;
memset(&opt, 0, sizeof(opt));
opt.size = sizeof(opt);
opt.intra_op_threads = 2;
opt.inter_op_threads = 1;
darra_session* s = NULL;
int32_t rc = darra_session_open_ex("model.darmodel", "model.darmkey", NULL, NULL, &opt, &s);
darra_session_open_plain_ex()
int32_t darra_session_open_plain_ex(const char* model_path,
const char* prefer_provider_or_null,
const darra_session_options* options_or_null,
darra_session** out);
打开明文模型建会话(带选项)。model_path:.onnx / .pdmodel(伴生 .pdiparams)/ 含二者的目录。.pt / .pth 仍拒。options_or_null == NULL 时与 darra_session_open_plain 逐字相同。
darra_session_meta_json()
int32_t darra_session_meta_json(darra_session* session, char** out_json);
读会话元信息(schema 见 附录 B):task / labels / 输入宽高与通道 / Layout / Mean / Std / PayloadKind / 实际选中的 Provider / 是否加密 / 授权摘要(去敏,含试用剩余次数)。全部来自容器头或明文探测,用户零配置。 头里缺的字段派生 JSON 里为 null / 缺席,不编默认 640/80/17。
调用代价:读会话缓存的头信息,微秒级。out_json 用完 darra_string_free。
darra_session_infer_image()
int32_t darra_session_infer_image(darra_session* session,
const uint8_t* image_bytes,
size_t len,
const darra_image_desc* desc,
char** out_results_json);
对一张图片跑推理,结果输出 JSON(envelope 见 附录 C)。v1 只接受单张图像,不做视频流(连续帧请循环调用本函数)。阻塞调用,耗时随 Provider/硬件而定。不承诺硬实时。
图像格式与描述:
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 格式 | DARRA_IMAGE_AUTO | 0 | 入参 | 按字节魔数自嗅探 JPG/PNG/BMP。裸缓冲禁止用 AUTO |
| 格式 | DARRA_IMAGE_ENCODED | 1 | 入参 | 显式已编码字节流;v1 行为同 AUTO |
| 格式 | DARRA_IMAGE_RGB888 | 2 | 入参 | 裸缓冲 RGB 8bit,须给宽高 |
| 格式 | DARRA_IMAGE_BGR888 | 3 | 入参 | 裸缓冲 BGR 8bit,须给宽高 |
| 格式 | DARRA_IMAGE_GRAY8 | 4 | 入参 | 裸缓冲灰度 8bit,须给宽高 |
| 格式 | DARRA_IMAGE_RGBA8888 | 5 | 入参 | 裸缓冲 RGBA 8bit,须给宽高 |
| 描述 | darra_image_desc.size | uint32_t | 调用方填 | sizeof(darra_image_desc) |
| 描述 | darra_image_desc.format | darra_image_format | 读写 | 见上 |
| 描述 | darra_image_desc.width | uint32_t | 读写 | AUTO/ENCODED 忽略填 0;裸缓冲必填 >0 |
| 描述 | darra_image_desc.height | uint32_t | 读写 | 同上 |
| 描述 | darra_image_desc.stride | uint32_t | 读写 | 每行字节数;0 = 紧凑:RGB/BGR=width*3,GRAY8=width,RGBA=width*4 |
image_bytes / len:AUTO/ENCODED = 编码文件字节流;裸缓冲 = 像素内存,len 必须 ≥ stride*height(stride 为 0 时按 format 紧凑宽度计)。裸缓冲缺宽高 / len 不足 / desc->size 未填 = DARRA_INTERNAL。DARRA_IO_DENIED = 裸缓冲指针不可读。
darra_image_desc desc;
memset(&desc, 0, sizeof(desc));
desc.size = sizeof(desc);
desc.format = DARRA_IMAGE_BGR888; /* 相机原生 BGR */
desc.width = 1920;
desc.height = 1080;
desc.stride = 0;
char* results = NULL;
int32_t rc = darra_session_infer_image(s, frame, (size_t)1920 * 1080 * 3, &desc, &results);
if (rc == DARRA_OK) { puts(results); darra_string_free(results); }
同一会话可并发调用(底层运行时会话线程安全;解密明文窗口由 SDK 内部串行管理);吞吐扩展推荐每线程一个会话。
darra_session_close()
void darra_session_close(darra_session* session);
关闭会话并释放全部资源(含解密明文窗口清零)。NULL 安全。close 后句柄失效,再用 = 未定义行为;禁止与任何在飞调用并发。
完整例子
场景:一台刚装完系统的客户机,没有任何 AI 运行时。目标:装齐环境 → 打开加密模型 → 读元信息 → 推理一张图片 → 干净退出。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "darra_ai.h"
static int fail(const char* where, int32_t rc) {
darra_error_t err; err.size = sizeof(err);
if (darra_last_error(&err) == DARRA_OK) {
fprintf(stderr, "%s 失败 rc=%d(无错误细节)\n", where, rc);
} else {
fprintf(stderr, "%s 失败 [%d] %s\n建议: %s\n", where, err.code, err.message, err.hint);
}
return (int)rc;
}
static void on_progress(float percent, const char* stage, void* user) {
(void)user;
printf("\r环境准备 [%-8s] %5.1f%% ", stage, percent);
fflush(stdout);
}
static uint8_t* read_file(const char* path, size_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
if (n <= 0) { fclose(f); return NULL; }
uint8_t* buf = (uint8_t*)malloc((size_t)n);
if (buf && fread(buf, 1, (size_t)n, f) != (size_t)n) { free(buf); buf = NULL; }
fclose(f);
if (buf) *out_len = (size_t)n;
return buf;
}
int main(void) {
int32_t rc;
darra_session* session = NULL;
char* meta = NULL;
char* results = NULL;
uint8_t* image = NULL;
int exit_code = 1;
if (DARRA_VERSION_DECODE_MAJOR(darra_version()) != DARRA_VERSION_MAJOR) {
fprintf(stderr, "ABI 不匹配,库版本 = %s\n", darra_version_string());
return 1;
}
{
char* diag = NULL;
if (darra_diag_collect(&diag) == DARRA_OK) {
fprintf(stderr, "[diag] %s\n", diag);
darra_string_free(diag);
}
}
/* NULL = 按硬件自动选细包;断网现场换成
* darra_runtime_ensure("cpu-only", "darra-ai-runtime-cpu-only-offline.zip", ...) */
rc = darra_runtime_ensure(NULL, NULL, on_progress, NULL);
printf("\n");
if (rc != DARRA_OK) return fail("darra_runtime_ensure", rc);
rc = darra_session_open("model.darmodel", "model.darmkey", NULL, NULL, &session);
if (rc != DARRA_OK) return fail("darra_session_open", rc);
rc = darra_session_meta_json(session, &meta);
if (rc != DARRA_OK) { fail("darra_session_meta_json", rc); goto cleanup; }
printf("[meta] %s\n", meta);
{
size_t image_len = 0;
image = read_file("photo.jpg", &image_len);
if (!image) { fprintf(stderr, "读图片失败\n"); goto cleanup; }
darra_image_desc desc;
memset(&desc, 0, sizeof(desc));
desc.size = sizeof(desc);
desc.format = DARRA_IMAGE_AUTO;
rc = darra_session_infer_image(session, image, image_len, &desc, &results);
if (rc != DARRA_OK) { fail("darra_session_infer_image", rc); goto cleanup; }
printf("[results] %s\n", results);
}
exit_code = 0;
cleanup:
free(image);
if (results) darra_string_free(results);
if (meta) darra_string_free(meta);
darra_session_close(session);
return exit_code;
}
要点:
- ABI 核对先行,装错库不往下走;
runtime_ensure幂等——已装好就是秒过,可每次启动都调;- 所有
char*出参一律darra_string_free,调用方自己malloc的自己free; - 任何一步失败,
darra_last_error的hint就是给最终用户的修复建议(见 错误码)。
附录 A:诊断 JSON
darra_diag_collect 输出:
{
"schema": 1,
"sdk": { "version": "1.0.0+runtime", "abi": 65536 },
"os": { "name": "windows", "version": "10.0.26100", "arch": "x64" },
"cpu": { "model": "AMD Ryzen 7 9800X3D", "cores": 16 },
"gpus": [ { "model": "NVIDIA RTX 4070", "driver": "560.94", "cudaMax": "12.6" } ],
"providers": [ { "id": "onnx-cpu", "version": "1.0.0", "path": "C:/.../Env/onnx-cpu/1.0.0" } ],
"license": { "present": true, "mode": "trial", "trialRemaining": 12, "machineBound": false },
"recentErrors": [ { "code": 11, "message": "...", "when": "2026-09-01T10:00:00+08:00" } ]
}
schema恒存在;其余字段采不到时为null或缺席,消费方必须容忍。- 去敏:永不出现密钥 / 密码 / token / 机器指纹原始字节;
license只有状态摘要。 gpus无独显时为[];providers未装任何包时为[]。
附录 B:会话元信息 JSON
darra_session_meta_json 输出:
{
"schema": 1,
"task": "detect",
"labels": ["scratch", "dent"],
"input": { "width": 640, "height": 640, "channels": 3 },
"payloadKind": "onnx",
"provider": "onnx-cuda",
"encrypted": true,
"containerFormat": "DARM1",
"license": { "mode": "trial", "trialRemaining": 12, "machineBound": true }
}
task派生短名随容器头;推理结果results元素结构以此为准。缺席不编detect。头文件对结果形态的承诺:detect→box,classify→top-k,segment→掩码。payloadKind:onnx/trt-engine/openvino/rknn/paddle。input.width/height/channels来自容器头。缺则null,不编 640/80/17。provider是实际选中的 Provider(不是 prefer 入参)。encrypted=false时无containerFormat/license(明文会话)。- 标准字段表见 元数据。
附录 C:推理结果 JSON
darra_session_infer_image 输出:
{
"schema": 1,
"task": "detect",
"provider": "onnx-cuda",
"elapsedMs": 3.2,
"image": { "width": 1920, "height": 1080 },
"results": [ { "label": "scratch", "score": 0.93, "box": [120, 40, 36, 18] } ]
}
- envelope 字段(schema / task / provider / elapsedMs / image / results)恒定;
results元素随task而变:detect:{label, score, box:[x,y,w,h]};classify:{label, score}top-k 按分降序;segment:{label, score, maskRle};- 其余 task 的逐元素 schema 随对应 Provider 落地,本契约只承诺 envelope。
- 结果坐标基于
image字段(解码后、送入模型前的尺寸);裸缓冲输入时等于 desc 的宽高。 - 永不包含模型明文、密钥等任何敏感字节。