XCAICX docs Product Contact

C ABI reference

core/include/xcaicx/xcaicx.h is the entire supported integration surface. Anything not in that header is internal and will move.

Current version: XCAICX_VERSION_MAJOR 1, MINOR 0, PATCH 0.

ABI contract#

  • Structs are versioned by a leading size_t struct_size. Callers set it to sizeof the struct they compiled against; the library uses it to detect which fields are present. New fields are only ever appended.
  • Enum values are never renumbered. New values are appended.
  • Functions are never removed within a major version.

Always call the matching *_init function before setting fields on a config struct — that is what stamps struct_size.

Threading#

  • xcaicx_engine is thread-safe for submit/poll from different threads.
  • A single xcaicx_stream must be driven from one thread at a time.

Memory#

Any xcaicx_* pointer returned by the library is owned by the library and valid until the next call on the same handle, unless documented otherwise. Copy anything you need to retain.


Status codes#

c
typedef enum xcaicx_status {
    XCAICX_OK                         = 0,
    XCAICX_ERR_INVALID_ARG            = -1,
    XCAICX_ERR_OOM                    = -2,
    XCAICX_ERR_NOT_INITIALIZED        = -3,
    XCAICX_ERR_INTERNAL               = -4,
    XCAICX_ERR_UNSUPPORTED            = -5,
    XCAICX_ERR_ABI_MISMATCH           = -6,

    /* Licensing — handle these explicitly in firmware. */
    XCAICX_ERR_LICENSE_MISSING        = -100,
    XCAICX_ERR_LICENSE_MALFORMED      = -101,
    XCAICX_ERR_LICENSE_BAD_SIGNATURE  = -102,
    XCAICX_ERR_LICENSE_EXPIRED        = -103,
    XCAICX_ERR_LICENSE_NOT_YET_VALID  = -104,
    XCAICX_ERR_LICENSE_WRONG_DEVICE   = -105,
    XCAICX_ERR_LICENSE_REVOKED        = -106,
    XCAICX_ERR_LICENSE_CLOCK_ROLLBACK = -107,
    XCAICX_ERR_ENTITLEMENT_DENIED     = -108,
    XCAICX_ERR_LIMIT_EXCEEDED         = -109,
    XCAICX_ERR_ACTIVATION_REQUIRED    = -110,

    /* Runtime */
    XCAICX_ERR_MODEL_LOAD             = -200,
    XCAICX_ERR_BACKEND                = -201,
    XCAICX_ERR_FRAME_FORMAT           = -202,
    XCAICX_ERR_QUEUE_FULL             = -203,
    XCAICX_ERR_NO_RESULT              = -204
} xcaicx_status;

Causes and fixes for each: Status codes.

c
const char *xcaicx_status_str(xcaicx_status s);

Human-readable name. Never NULL, statically allocated.

c
const char *xcaicx_last_error(void);

Thread-local detail string for the last failing call on this thread. Empty string if there is no detail. Valid until the next XCAICX call on this thread. Written to be read by a field technician — surface it verbatim in diagnostics.


Modules#

c
typedef enum xcaicx_module {
    XCAICX_MODULE_DETECT = 1 << 0,   /* object + person detection, tracking */
    XCAICX_MODULE_DEFECT = 1 << 1,   /* anomaly / defect inspection         */
    XCAICX_MODULE_PPE    = 1 << 2,   /* helmet / vest / glove compliance    */
    XCAICX_MODULE_ANPR   = 1 << 3    /* plate detect + OCR                  */
} xcaicx_module;

typedef uint32_t xcaicx_module_mask;

const char *xcaicx_module_name(xcaicx_module m);

These map one-to-one onto licence entitlements. A module whose entitlement bit is false cannot be enabled: xcaicx_engine_create fails with XCAICX_ERR_ENTITLEMENT_DENIED.


Frames#

c
typedef enum xcaicx_pixel_format {
    XCAICX_PIX_UNKNOWN = 0,
    XCAICX_PIX_RGB8    = 1,   /* 3 bytes/px, packed                   */
    XCAICX_PIX_BGR8    = 2,   /* 3 bytes/px, packed (OpenCV native)   */
    XCAICX_PIX_GRAY8   = 3,   /* 1 byte/px                            */
    XCAICX_PIX_NV12    = 4,   /* Y plane + interleaved UV, ISP native */
    XCAICX_PIX_I420    = 5    /* Y + U + V planes                     */
} xcaicx_pixel_format;

typedef struct xcaicx_frame {
    size_t              struct_size;
    const uint8_t      *data;
    size_t              data_len;
    int32_t             width;
    int32_t             height;
    int32_t             stride;    /* bytes per row of plane 0; 0 = packed */
    xcaicx_pixel_format format;
    int64_t             pts_us;    /* presentation timestamp, microseconds */
    uint64_t            frame_id;  /* caller-assigned, echoed in results   */
} xcaicx_frame;

data is a borrowed view. XCAICX never takes ownership and never frees it; it must stay valid for the duration of the submit call only.

Dimensions above 16384 are rejected. data_len is validated against the declared geometry, and a mismatch returns XCAICX_ERR_FRAME_FORMAT with both numbers in xcaicx_last_error().

See Feeding frames for strides and zero-copy patterns.


Results#

c
typedef struct xcaicx_box {
    float x, y, w, h;    /* normalized [0,1] relative to frame */
} xcaicx_box;

typedef struct xcaicx_detection {
    size_t         struct_size;
    xcaicx_box     box;
    float          score;        /* [0,1] */
    int32_t        class_id;
    const char    *class_name;   /* library-owned, stable for engine lifetime */
    int64_t        track_id;     /* -1 if untracked */
    xcaicx_module  source;       /* which module produced this */
    const char    *text;         /* ANPR only: UTF-8 plate text, else NULL */
    float          text_score;
} xcaicx_detection;

typedef enum xcaicx_event_kind {
    XCAICX_EVENT_NONE           = 0,
    XCAICX_EVENT_ZONE_ENTER     = 1,
    XCAICX_EVENT_ZONE_EXIT      = 2,
    XCAICX_EVENT_LINE_CROSS     = 3,
    XCAICX_EVENT_DWELL_EXCEEDED = 4,
    XCAICX_EVENT_PPE_VIOLATION  = 5,
    XCAICX_EVENT_DEFECT_FOUND   = 6,
    XCAICX_EVENT_PLATE_READ     = 7
} xcaicx_event_kind;

typedef struct xcaicx_event {
    size_t            struct_size;
    xcaicx_event_kind kind;
    int64_t           track_id;
    int32_t           zone_id;     /* -1 if not zone-scoped */
    const char       *zone_name;   /* NULL if not zone-scoped */
    const char       *detail;      /* "no_helmet", "scratch", "a_to_b", plate text */
    float             score;
    int64_t           pts_us;
} xcaicx_event;

typedef struct xcaicx_result {
    size_t                  struct_size;
    uint64_t                frame_id;
    int64_t                 pts_us;
    const xcaicx_detection *detections;
    size_t                  detection_count;
    const xcaicx_event     *events;
    size_t                  event_count;
    float                   inference_ms;
    float                   total_ms;
} xcaicx_result;

Careful

xcaicx_result and every pointer inside it belong to the stream and are valid only until the next call on that stream. That includes class_name, zone_name and detail. Copy before queueing.

Class names from the reference backend: person, vehicle, object, defect, ppe_violation, plate.


Licence state#

c
typedef enum xcaicx_license_state {
    XCAICX_LIC_INVALID = 0,
    XCAICX_LIC_VALID   = 1,
    XCAICX_LIC_GRACE   = 2,   /* expired but inside grace window */
    XCAICX_LIC_EXPIRED = 3,
    XCAICX_LIC_REVOKED = 4
} xcaicx_license_state;

typedef struct xcaicx_license_info {
    size_t               struct_size;
    xcaicx_license_state state;
    const char          *license_id;
    const char          *sku;
    const char          *customer_id;
    const char          *device_id;   /* bound unit, or "*" for batch */
    const char          *batch_id;    /* NULL if not an OEM batch key */
    xcaicx_module_mask   entitlements;
    int32_t              max_streams;
    int32_t              max_fps_per_stream;
    int64_t              issued_at;         /* unix seconds */
    int64_t              expires_at;        /* unix seconds; 0 = perpetual */
    int64_t              seconds_remaining;
    int32_t              grace_days;
    uint64_t             metered_inferences;
    uint64_t             metered_stream_seconds;
} xcaicx_license_info;

XCAICX_LIC_GRACE means expired but everything still runs. Surface it as a warning, not an error.


Configuration#

c
typedef enum xcaicx_log_level {
    XCAICX_LOG_OFF = 0, XCAICX_LOG_ERROR = 1, XCAICX_LOG_WARN = 2,
    XCAICX_LOG_INFO = 3, XCAICX_LOG_DEBUG = 4
} xcaicx_log_level;

typedef void (*xcaicx_log_fn)(xcaicx_log_level level, const char *msg, void *user);

typedef enum xcaicx_backend {
    XCAICX_BACKEND_AUTO      = 0,
    XCAICX_BACKEND_REFERENCE = 1,
    XCAICX_BACKEND_ORT_CPU   = 2,
    XCAICX_BACKEND_ORT_CUDA  = 3,
    XCAICX_BACKEND_TENSORRT  = 4,
    XCAICX_BACKEND_OPENVINO  = 5,
    XCAICX_BACKEND_RKNN      = 6,
    XCAICX_BACKEND_HAILO     = 7
} xcaicx_backend;

typedef struct xcaicx_config {
    size_t             struct_size;

    const char        *license_token;   /* "XCAICX1.<payload>.<sig>" */
    const char        *license_path;    /* file containing the token */
    const char        *license_pubkey;  /* base64 Ed25519; NULL = built-in */

    const char        *state_dir;
    const char        *model_dir;

    xcaicx_module_mask modules;

    xcaicx_backend     backend;
    int32_t            num_threads;      /* 0 = auto */
    int32_t            max_queue_depth;  /* per stream; 0 = default 4 */

    xcaicx_log_level   log_level;
    xcaicx_log_fn      log_fn;           /* NULL = stderr */
    void              *log_user;

    const char        *cloud_endpoint;   /* NULL = offline only */
    int32_t            heartbeat_seconds;/* 0 = default 3600 */
    int32_t            offline;          /* nonzero = never touch network */
} xcaicx_config;

xcaicx_status xcaicx_config_init(xcaicx_config *cfg);

Provide exactly one of license_token / license_path.

state_dir must be writable and survive reboot — see state_dir requirements.

license_pubkey overrides the compiled-in verification key, which is how a key rotation is staged through configuration before a firmware release makes it permanent.

Custom logging#

c
static void my_log(xcaicx_log_level lvl, const char *msg, void *user) {
    (void)user;
    syslog(lvl == XCAICX_LOG_ERROR ? LOG_ERR : LOG_INFO, "xcaicx: %s", msg);
}

cfg.log_fn    = my_log;
cfg.log_user  = NULL;
cfg.log_level = XCAICX_LOG_INFO;

Keep it at INFO in the field. The engine logs which backend it selected at INFO on every start, and that line is how you discover a camera silently running the reference backend because its model bundle is missing.


Engine lifecycle#

c
typedef struct xcaicx_engine xcaicx_engine;
typedef struct xcaicx_stream xcaicx_stream;

xcaicx_status xcaicx_engine_create(const xcaicx_config *cfg, xcaicx_engine **out);
void          xcaicx_engine_destroy(xcaicx_engine *e);

xcaicx_status xcaicx_engine_license_info(xcaicx_engine *e, xcaicx_license_info *out);
xcaicx_status xcaicx_engine_heartbeat(xcaicx_engine *e);

const char        *xcaicx_version_string(void);
xcaicx_module_mask xcaicx_engine_active_modules(xcaicx_engine *e);

xcaicx_engine_destroy flushes metering to disk. Skipping it loses the session's usage.

xcaicx_engine_heartbeat forces a licensing/metering round-trip now rather than at the next scheduled one. Returns XCAICX_OK and does nothing when offline.

Set out->struct_size = sizeof *out before calling xcaicx_engine_license_info.


Zones and rules#

c
typedef struct xcaicx_point { float x, y; } xcaicx_point;   /* normalized */

xcaicx_status xcaicx_stream_add_zone(xcaicx_stream *s,
                                     const char *name,
                                     const xcaicx_point *pts,
                                     size_t pt_count,
                                     int32_t dwell_seconds,
                                     int32_t *out_zone_id);

xcaicx_status xcaicx_stream_add_line(xcaicx_stream *s,
                                     const char *name,
                                     xcaicx_point a, xcaicx_point b,
                                     int32_t *out_line_id);

Polygons need three or more points and may be concave. Tripwires are directed: ab crossings report detail = "a_to_b".

Tracks are tested at the midpoint of their bottom edge. See Zones, tripwires & events.


Streams#

c
typedef struct xcaicx_stream_config {
    size_t             struct_size;
    const char        *name;            /* logs + metering attribution */
    xcaicx_module_mask modules;         /* subset of engine modules; 0 = all */
    float              detect_threshold;
    float              nms_threshold;
    int32_t            detect_every_n;  /* 1 = every frame */
} xcaicx_stream_config;

xcaicx_status xcaicx_stream_config_init(xcaicx_stream_config *cfg);

xcaicx_status xcaicx_stream_open(xcaicx_engine *e,
                                 const xcaicx_stream_config *cfg,
                                 xcaicx_stream **out);
void          xcaicx_stream_close(xcaicx_stream *s);

xcaicx_status xcaicx_stream_process(xcaicx_stream *s,
                                    const xcaicx_frame *frame,
                                    const xcaicx_result **out);

xcaicx_status xcaicx_stream_submit(xcaicx_stream *s, const xcaicx_frame *frame);
xcaicx_status xcaicx_stream_poll(xcaicx_stream *s,
                                 int32_t timeout_ms,
                                 const xcaicx_result **out);

Defaults from xcaicx_stream_config_init: detect_threshold = 0.35, nms_threshold = 0.45, detect_every_n = 1.

Opening more streams than max_streams allows fails with XCAICX_ERR_LIMIT_EXCEEDED.

submit() returns XCAICX_ERR_QUEUE_FULL under backpressure — flow control, not an error. poll() returns XCAICX_ERR_NO_RESULT when nothing is ready.


Windows export macros#

c
#define XCAICX_USING_SHARED      /* before including, when linking the DLL */
#include "xcaicx/xcaicx.h"

On non-Windows platforms XCAICX_API is __attribute__((visibility("default"))) and internal symbols are hidden.

Next#

Commercial software. Use requires a valid XCAICX licence token. Questions an integrator cannot answer from this page belong in an email to [email protected] — and, usually, in a fix to this page.

© 2026 AZMX AI · xcaicx.com