Initial documentation site for cuframes:
- Landing page (src/pages/index.mdx) — hero, quick example (publisher +
subscriber), comparison table vs naive/DeepStream, honest "early but
production-tested" status
- /docs/intro — full overview
- /docs/getting-started/{install,first-publisher,first-subscriber}
- /docs/concepts/{frame-vs-packet-ring,ownership-modes,sync-vmm-stream}
with mermaid diagrams
- /docs/integration/{ffmpeg-demuxer,ffmpeg-filter,python}
- /docs/reference/{api-c,api-cpp,protocol} — full v4 wire protocol spec
incl. VMM_FDS message, magic 0xCC7C1DCE bump diff
- /docs/faq — comparison vs DeepStream/GStreamer, license, multi-host
limitations
- i18n/ru/ — parallel RU translation (tech terms latin, склонение апостроф)
Build:
- Docusaurus 3.10.1 + theme-mermaid + search-local
- Follows dagstack-* docs convention (canonical: dagstack-plugin-system-docs)
- Apache-2.0 license; cuframes lib itself remains LGPL-2.1+
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
25 KiB
sidebar_position, title
| sidebar_position | title |
|---|---|
| 1 | C API |
C API reference
Полный листинг public C API из <cuframes/cuframes.h> (libcuframes 0.4.0). Source of truth — header в repo, эта страница его дублирует в Docusaurus-формате с cross-links на концептуальные разделы.
Headers & linkage
#include <cuframes/cuframes.h>
# pkg-config (если установлено через .deb)
cc app.c $(pkg-config --cflags --libs cuframes)
# вручную
cc app.c -lcuframes
libcuframes.so.0 динамически линкуется к libcuda.so.1 (CUDA driver API, не runtime). Для большинства user-кода также нужен -lcudart, чтобы манипулировать своими CUDA streams.
Соглашения
- Все функции возвращают
int—0(CUFRAMES_OK) при успехе, отрицательный код изcuframes_error_tпри ошибке. Расшифровка кода —cuframes_strerror. - Все handle types (
cuframes_publisher_t,cuframes_subscriber_t,cuframes_frame_t,cuframes_packet_t) — opaque. Поля недоступны напрямую, только через accessor-функции. Это даёт ABI-stability в minor-релизах. - Каждый handle принадлежит одному потоку. Cross-thread access — undefined behavior. Несколько handle'ов в разных потоках — OK.
- Endianness — little-endian (это и так фиксируется CUDA-платформами).
Версии и error codes
Версия библиотеки
const char *cuframes_version_string(void);
uint32_t cuframes_protocol_version(void);
cuframes_version_string возвращает runtime-версию libcuframes в формате "MAJOR.MINOR.PATCH" (например "0.4.0"). Compile-time константы:
#define CUFRAMES_VERSION_MAJOR 0
#define CUFRAMES_VERSION_MINOR 4
#define CUFRAMES_VERSION_PATCH 0
cuframes_protocol_version возвращает версию wire-protocol (для v0.4 — 4). Subscribers с другой protocol-версией не подключатся — publisher вернёт HELLO_RESP(result=CUFRAMES_ERR_PROTOCOL). См. Protocol reference.
Error codes
typedef enum cuframes_error {
CUFRAMES_OK = 0,
CUFRAMES_ERR_INVALID_ARG = -1,
CUFRAMES_ERR_OUT_OF_MEMORY = -2,
CUFRAMES_ERR_CUDA = -3,
CUFRAMES_ERR_IO = -4,
CUFRAMES_ERR_NOT_FOUND = -5,
CUFRAMES_ERR_ALREADY_EXISTS = -6,
CUFRAMES_ERR_TIMEOUT = -7,
CUFRAMES_ERR_PROTOCOL = -8,
CUFRAMES_ERR_DISCONNECTED = -9,
CUFRAMES_ERR_FORMAT = -10,
CUFRAMES_ERR_WOULD_BLOCK = -11,
CUFRAMES_ERR_TOO_MANY = -12,
CUFRAMES_ERR_PACKET_OVERSIZED = -20,
CUFRAMES_ERR_NO_PACKET_RING = -21,
CUFRAMES_ERR_NO_CODEC_PARAMS = -22,
CUFRAMES_ERR_PACKET_OVERRUN = -23,
CUFRAMES_ERR_INTERNAL = -100,
} cuframes_error_t;
| Код | Имя | Значение |
|---|---|---|
0 |
CUFRAMES_OK |
Success |
-1 |
CUFRAMES_ERR_INVALID_ARG |
NULL pointer или невалидное значение в config |
-2 |
CUFRAMES_ERR_OUT_OF_MEMORY |
malloc / cudaMalloc fail |
-3 |
CUFRAMES_ERR_CUDA |
Ошибка CUDA runtime / driver |
-4 |
CUFRAMES_ERR_IO |
socket / mmap / eventfd |
-5 |
CUFRAMES_ERR_NOT_FOUND |
Publisher с таким key не найден |
-6 |
CUFRAMES_ERR_ALREADY_EXISTS |
Publisher с этим key уже есть, либо consumer_name занят |
-7 |
CUFRAMES_ERR_TIMEOUT |
Операция не завершилась за timeout |
-8 |
CUFRAMES_ERR_PROTOCOL |
Несовместимая версия wire protocol |
-9 |
CUFRAMES_ERR_DISCONNECTED |
Publisher умер или сеть оборвалась |
-10 |
CUFRAMES_ERR_FORMAT |
Неподдерживаемый pixel format или несовпадение размеров |
-11 |
CUFRAMES_ERR_WOULD_BLOCK |
Non-blocking call — данных пока нет |
-12 |
CUFRAMES_ERR_TOO_MANY |
Превышен MAX_SUBSCRIBERS (32) |
-20 |
CUFRAMES_ERR_PACKET_OVERSIZED |
publish_packet size > max_packet_size |
-21 |
CUFRAMES_ERR_NO_PACKET_RING |
Subscriber запросил packets, у publisher'а нет ring'а |
-22 |
CUFRAMES_ERR_NO_CODEC_PARAMS |
Extradata ещё не set publisher'ом |
-23 |
CUFRAMES_ERR_PACKET_OVERRUN |
Slow subscriber, packet seq уехал — resync на keyframe |
-100 |
CUFRAMES_ERR_INTERNAL |
Bug в библиотеке — воспроизводи и репорти |
Расшифровка ошибок
const char *cuframes_strerror(int err);
Возвращает human-readable строку для error code. Pointer указывает на static storage, владеть им дальше не надо. Никогда не возвращает NULL — для unknown code вернёт "unknown error".
Pixel formats
typedef enum cuframes_format {
CUFRAMES_FORMAT_NV12 = 0,
CUFRAMES_FORMAT_YUV420P = 1,
CUFRAMES_FORMAT_RGB = 2,
CUFRAMES_FORMAT_BGR = 3,
CUFRAMES_FORMAT_RGBA = 4,
CUFRAMES_FORMAT_GRAYSCALE = 5,
} cuframes_format_t;
| Format | Layout | Когда |
|---|---|---|
NV12 |
Y plane + interleaved UV plane | NVDEC native, default для video pipeline'ов |
YUV420P |
Y + U + V separate planes | FFmpeg yuv420p |
RGB |
24bpp packed RGB | ML inference, OpenGL |
BGR |
24bpp packed BGR | OpenCV native |
RGBA |
32bpp packed RGBA | overlays, compositing |
GRAYSCALE |
8bpp single plane | depth maps, masks |
Format фиксирован для publisher'а в момент create — поменять нельзя без destroy + recreate с новым key.
Policy & mode enums
Publisher policy
typedef enum cuframes_publisher_policy {
CUFRAMES_POLICY_DROP_OLDEST = 0,
CUFRAMES_POLICY_STRICT_WAIT = 1,
} cuframes_publisher_policy_t;
DROP_OLDEST— publisher не ждёт, перезаписывает next slot. Slow consumer пропускает кадры. Default для real-time.STRICT_WAIT— publisher блокируется, пока все подписанные consumers не ACK'нут. Не теряет кадры, но slowest consumer тормозит всех. Для recording или критичной аналитики.
Subscriber mode
typedef enum cuframes_subscriber_mode {
CUFRAMES_MODE_NEWEST_ONLY = 0,
CUFRAMES_MODE_STRICT_ORDER = 1,
} cuframes_subscriber_mode_t;
NEWEST_ONLY— брать самый свежий frame, пропускать промежуточные. Default.STRICT_ORDER— все frames по порядку. Если ring overflow — вернётсяCUFRAMES_ERR_DISCONNECTED, нужно reconnect.
Ownership mode
typedef enum cuframes_ownership_mode {
CUFRAMES_OWNERSHIP_LIBRARY = 0,
CUFRAMES_OWNERSHIP_EXTERNAL = 1,
} cuframes_ownership_mode_t;
LIBRARY— library владеет VMM-pool'ом (см. Sync model). Publisher делаетacquire()→ пишет →publish(). Единственный поддерживаемый mode в v0.4.EXTERNAL— в v0.4 deprecated.cuframes_publisher_create_externalвозвращаетCUFRAMES_ERR_INVALID_ARG. Для FFmpeg filter integration используйLIBRARY+ одна D2D-копия в acquire'нутый slot (cuframes-rtsp-source именно так и работает с v0.4).
Frame accessors
cuframes_frame_t — opaque handle на frame, полученный у subscriber'а. Валиден от cuframes_subscriber_next до cuframes_subscriber_release.
typedef struct cuframes_frame cuframes_frame_t;
void *cuframes_frame_cuda_ptr(const cuframes_frame_t *frame);
cuframes_format_t cuframes_frame_format(const cuframes_frame_t *frame);
void cuframes_frame_size(const cuframes_frame_t *frame,
int32_t *width_out, int32_t *height_out);
int32_t cuframes_frame_pitch_y(const cuframes_frame_t *frame);
int32_t cuframes_frame_pitch_uv(const cuframes_frame_t *frame);
uint64_t cuframes_frame_seq(const cuframes_frame_t *frame);
int64_t cuframes_frame_pts_ns(const cuframes_frame_t *frame);
| Функция | Возвращает |
|---|---|
cuda_ptr |
CUDA device pointer на frame data (read-only для consumer'а) |
format |
cuframes_format_t |
size |
Ширина и высота в пикселях через out-параметры |
pitch_y |
Pitch (байт на строку) для Y plane или единственного plane |
pitch_uv |
Pitch для UV plane (NV12 / YUV420P); 0 для форматов без UV |
seq |
Sequence number — монотонная нумерация у publisher'а |
pts_ns |
Timestamp publisher'а (наносекунды, CLOCK_MONOTONIC) |
PTS epoch caveat: publisher и consumer могут иметь разные эпохи CLOCK_MONOTONIC (после publisher restart counter сбрасывается). Consumer должен sanity-checkить — например, детектить epoch change, когда pts_ns_curr < pts_ns_prev.
Publisher API
Config struct
typedef struct cuframes_publisher_config {
const char *key; /* unique имя ("cam1"). Не NULL. */
int32_t width;
int32_t height;
cuframes_format_t format;
cuframes_ownership_mode_t ownership;
int32_t ring_size; /* 2..16, рекомендуется 4 */
cuframes_publisher_policy_t policy;
int32_t consumer_ack_timeout_ms; /* STRICT_WAIT; 0 = ждать вечно */
int32_t cuda_device;
uint64_t _reserved[4]; /* должно быть 0 */
} cuframes_publisher_config_t;
| Поле | Ограничения |
|---|---|
key |
ASCII [a-zA-Z0-9_-]{1,63}. Не NULL. |
width, height |
Pixels. Фиксированы после create. |
format |
См. Pixel formats. Фиксирован. |
ownership |
В v0.4 — только LIBRARY. |
ring_size |
2..16 для LIBRARY. Меньше — больше шанс overrun, больше — больше VRAM. |
policy |
См. Policy. |
consumer_ack_timeout_ms |
Только для STRICT_WAIT. 0 = ждать бесконечно. |
cuda_device |
Обычно 0. Должен совпадать с consumer'ским. |
_reserved |
Reserved для ABI-stability, должно быть нулями. |
Create / destroy
int cuframes_publisher_create(const cuframes_publisher_config_t *cfg,
cuframes_publisher_t **out);
int cuframes_publisher_create_external(const cuframes_publisher_config_t *cfg,
void *const *cuda_ptrs,
int32_t ptr_count,
size_t frame_size,
cuframes_publisher_t **out);
int cuframes_publisher_destroy(cuframes_publisher_t *pub);
cuframes_publisher_create аллоцирует ring_size × frame_size через cuMemCreate(POSIX_FILE_DESCRIPTOR), открывает Unix socket /run/cuframes/<key>.sock, mmap'ит /dev/shm/cuframes-<key>. См. Synchronization & VMM stream.
Ошибки:
| Код | Когда |
|---|---|
INVALID_ARG |
cfg NULL, ring_size out of range, key не проходит regex |
ALREADY_EXISTS |
Publisher с этим key уже есть и его процесс живой |
CUDA |
cuMemCreate fail (out of VRAM, unsupported driver) |
IO |
Не получилось bind() socket или shm_open() |
cuframes_publisher_create_external — в v0.4 возвращает CUFRAMES_ERR_INVALID_ARG. EXTERNAL ownership убран потому, что VMM требует cuMemCreate-allocated memory. Для упомянутого FFmpeg filter use case — переходи на LIBRARY + одна cudaMemcpyAsync(D2D) в acquire'нутый slot. Cuframes-rtsp-source работает именно так начиная с v0.4.
cuframes_publisher_destroy шлёт SHUTDOWN всем connected subscribers, unlink'ает socket и shm. NULL-safe.
Publish (LIBRARY mode)
int cuframes_publisher_acquire(cuframes_publisher_t *pub,
void **cuda_ptr_out);
int cuframes_publisher_publish(cuframes_publisher_t *pub,
void *stream, /* cudaStream_t */
int64_t pts_ns);
acquire возвращает CUDA device pointer на следующий slot в ring'е для записи. Pointer стабилен, пока ты держишь ring slot — обычно до следующего publish.
Ошибки:
| Код | Когда |
|---|---|
TIMEOUT |
Все slots заняты в STRICT_WAIT mode |
INVALID_ARG |
pub NULL, или publisher был создан в EXTERNAL mode |
publish финализирует acquire'нутый slot. Внутри: cuStreamSynchronize(stream) гарантирует, что producer's writes hardware-coherent, затем atomic update slot.seq + global_seq. См. Synchronization — почему именно stream sync, а не CUDA events.
| Параметр | Значение |
|---|---|
stream |
CUDA stream, на котором писались данные. 0 для default stream. |
pts_ns |
Timestamp, рекомендуется cuframes_now_ns(). |
Publish (EXTERNAL mode)
int cuframes_publisher_publish_external(cuframes_publisher_t *pub,
void *cuda_ptr,
void *stream,
int64_t pts_ns);
В v0.4 deprecated — см. note про create_external выше. Всегда возвращает CUFRAMES_ERR_INVALID_ARG.
Subscriber API (sync)
Config struct
typedef struct cuframes_subscriber_config {
const char *key;
const char *consumer_name; /* NULL = auto */
cuframes_subscriber_mode_t mode;
int32_t cuda_device;
int32_t connect_timeout_ms; /* 0=fail, -1=ждать вечно */
uint64_t _reserved[4];
} cuframes_subscriber_config_t;
| Поле | Ограничения |
|---|---|
key |
Должен совпадать с publisher'ским |
consumer_name |
Если NULL — library сгенерирует subscriber-<pid>-<random>. Unique в пределах publisher'а — иначе ALREADY_EXISTS. MAX 32 subscribers. |
mode |
См. Subscriber mode |
cuda_device |
Должен совпадать с publisher'ским — VMM FD импортируется на тот же device |
connect_timeout_ms |
0 = fail сразу с NOT_FOUND; -1 = ждать вечно |
Create / destroy
int cuframes_subscriber_create(const cuframes_subscriber_config_t *cfg,
cuframes_subscriber_t **out);
int cuframes_subscriber_destroy(cuframes_subscriber_t *sub);
create выполняет handshake (HELLO → SUBSCRIBE → VMM_FDS), импортирует N file descriptors через cuMemImportFromShareableHandle. См. Protocol reference §3.
Ошибки:
| Код | Когда |
|---|---|
NOT_FOUND |
Publisher с этим key не найден до connect_timeout_ms |
PROTOCOL |
Publisher имеет другую protocol version |
TOO_MANY |
Publisher уже имеет 32 subscriber'а |
ALREADY_EXISTS |
consumer_name занят |
CUDA |
cuMemImportFromShareableHandle fail |
destroy — graceful close: UNSUBSCRIBE msg → cleanup VMM mappings → close socket. NULL-safe.
Next frame
int cuframes_subscriber_next(cuframes_subscriber_t *sub,
void *consumer_stream,
cuframes_frame_t **frame_out,
int32_t timeout_ms);
int cuframes_subscriber_release(cuframes_subscriber_t *sub,
cuframes_frame_t *frame);
next блокируется до timeout_ms, ожидая новый frame. Семантика по mode:
NEWEST_ONLY— возвращает самый свежий frame, пропускает промежуточные;STRICT_ORDER— следующий по seq;DISCONNECTEDпри overflow.
consumer_stream — твой CUDA stream, на котором будешь читать frame. В v0.4 синхронизация делается на стороне publisher'а через cuStreamSynchronize, так что параметр зарезервирован для будущего event-based fast path и сейчас не обязателен (0 допустимо).
| Параметр | Значение |
|---|---|
consumer_stream |
CUDA stream consumer'а. 0 допустимо. |
frame_out |
Output handle. Освободить через release. |
timeout_ms |
<0 = блокироваться, 0 = non-blocking (вернёт WOULD_BLOCK), >0 = с timeout'ом |
Ошибки:
| Код | Когда |
|---|---|
WOULD_BLOCK |
timeout_ms=0 и нет данных |
TIMEOUT |
За timeout_ms ничего не пришло |
DISCONNECTED |
Publisher shutdown, либо ring overrun в STRICT_ORDER |
release ACK'ает frame publisher'у (важно для STRICT_WAIT policy). NULL-safe. После release frame handle invalid.
Subscriber API (async)
typedef void (*cuframes_frame_callback_t)(const cuframes_frame_t *frame,
void *user_data);
typedef void (*cuframes_error_callback_t)(int err, const char *msg,
void *user_data);
int cuframes_async_subscriber_create(const cuframes_subscriber_config_t *cfg,
cuframes_frame_callback_t on_frame,
cuframes_error_callback_t on_error,
void *user_data,
cuframes_async_subscriber_t **out);
int cuframes_async_subscriber_destroy(cuframes_async_subscriber_t *sub);
Callback-based wrapper над sync API. Library поднимает internal thread, который сидит на next, вызывает on_frame / on_error, сам делает release после возврата из callback.
Ограничения:
- Frame валиден только в течение callback'а — никаких saved pointer'ов;
- Library использует internal CUDA stream, pre-wait уже выполнен — для своего stream'а используй sync API;
destroyjoin'ит internal thread и гарантирует, что callback больше не вызовется после возврата (может занять до длительности текущего callback'а).
Packet ring API
См. Frame vs Packet ring — когда нужно использовать packet ring.
Flags
#define CUFRAMES_PKT_FLAG_KEY 0x01u
#define CUFRAMES_PKT_FLAG_CORRUPT 0x02u
#define CUFRAMES_PKT_FLAG_DISCONTINUITY 0x04u
#define CUFRAMES_PKT_FLAG_LAST_IN_AU 0x08u
Биты соответствуют AV_PKT_FLAG_* у FFmpeg.
Publisher-side
typedef struct cuframes_packet_ring_options {
uint32_t ring_slots; /* default 64 */
uint32_t data_size; /* default 8 MiB */
uint32_t max_packet_size; /* default 2 MiB */
uint32_t codec_id; /* AV_CODEC_ID_* */
uint64_t _reserved[4];
} cuframes_packet_ring_options_t;
int cuframes_publisher_enable_packets(cuframes_publisher_t *pub,
const cuframes_packet_ring_options_t *opts);
int cuframes_publisher_set_codec_extradata(cuframes_publisher_t *pub,
const void *extradata, size_t size);
int cuframes_publisher_publish_packet(cuframes_publisher_t *pub,
const void *data, size_t size,
int64_t pts_ns, int64_t dts_ns,
uint32_t flags);
enable_packets создаёт отдельный SHM /dev/shm/cuframes-<key>-packets. Должно быть вызвано до первого publish_packet и желательно до того, как subscribers начнут подключаться — иначе subscriber увидит publisher без ring'а и не получит packets. opts=NULL → default sizing.
set_codec_extradata пишет SPS/PPS/VPS bytes в shared header. Subscribers (FFmpeg demuxer) подставят это в AVCodecContext.extradata. Size ≤ 4096 байт.
publish_packet записывает один NAL unit (Annex B). На IDR обязательно ставить CUFRAMES_PKT_FLAG_KEY — иначе late subscriber не сможет resync'нуться.
Ошибки:
| Код | Когда |
|---|---|
NO_PACKET_RING |
Не вызвали enable_packets |
PACKET_OVERSIZED |
size > max_packet_size |
ALREADY_EXISTS |
(enable_packets) ring уже активирован |
Subscriber-side
typedef struct cuframes_packet cuframes_packet_t;
const void *cuframes_packet_data(const cuframes_packet_t *p);
size_t cuframes_packet_size(const cuframes_packet_t *p);
int64_t cuframes_packet_pts(const cuframes_packet_t *p);
int64_t cuframes_packet_dts(const cuframes_packet_t *p);
uint32_t cuframes_packet_flags(const cuframes_packet_t *p);
uint64_t cuframes_packet_seq(const cuframes_packet_t *p);
int cuframes_subscriber_enable_packets(cuframes_subscriber_t *sub);
int cuframes_subscriber_next_packet(cuframes_subscriber_t *sub,
cuframes_packet_t **pkt_out,
int32_t timeout_ms);
int cuframes_subscriber_release_packet(cuframes_subscriber_t *sub,
cuframes_packet_t *pkt);
int cuframes_subscriber_get_codec_params(cuframes_subscriber_t *sub,
uint32_t *codec_id_out,
const void **extradata_out,
size_t *extradata_size_out);
enable_packets открывает второй SHM (если publisher его создал). Subscriber может одновременно иметь frames ring и packets ring, или только один из них.
next_packet — late subscriber на первом вызове начнёт с last_keyframe_seq publisher'а (decoder получит valid stream без glitches). См. Protocol §10.14.
Ошибки:
| Код | Когда |
|---|---|
WOULD_BLOCK |
timeout_ms=0, нет данных |
TIMEOUT |
За timeout_ms ничего не пришло |
PACKET_OVERRUN |
Subscriber отстал; library автоматически resync'нется на keyframe на next call |
DISCONNECTED |
Publisher shutdown |
NOT_FOUND |
(enable_packets) publisher не имеет packet ring |
get_codec_params возвращает pointer в library-local buffer, валидный пока subscriber жив. Если хочешь hold data past subscriber lifetime — копируй сам. Возвращает NO_CODEC_PARAMS, если publisher ещё не звал set_codec_extradata.
release_packet — NULL-safe. После release pointer'ы от cuframes_packet_* invalid.
Utils
Расчёт frame size
int cuframes_calc_frame_size(cuframes_format_t format,
int32_t width, int32_t height,
size_t *size_out,
int32_t *pitch_y_out,
int32_t *pitch_uv_out);
Учитывает pitch alignment 256 байт (CUDA recommendation). pitch_y_out / pitch_uv_out опциональны (можно NULL). Возвращает INVALID_ARG для unknown format.
Monotonic time
int64_t cuframes_now_ns(void);
CLOCK_MONOTONIC в наносекундах. Используй как pts_ns для real-time pipeline'ов:
cuframes_publisher_publish(pub, stream, cuframes_now_ns());
См. также
- C++ API — RAII wrapper.
- Protocol reference — wire format, handshake, ABI layouts.
- Frame vs Packet ring — когда какой использовать.
- Synchronization — почему
cuStreamSynchronize, а не CUDA events. - First publisher — рабочий C-пример.