532 lines
25 KiB
Markdown
532 lines
25 KiB
Markdown
---
|
||
sidebar_position: 1
|
||
title: C API
|
||
---
|
||
|
||
# C API reference
|
||
|
||
Полный listing public C API из `<cuframes/cuframes.h>` (libcuframes 0.4.0). Source of truth — header в repo, эта страница его дублирует в Docusaurus формате с cross-links на концептуальные разделы.
|
||
|
||
## Headers & linkage
|
||
|
||
```c
|
||
#include <cuframes/cuframes.h>
|
||
```
|
||
|
||
```bash
|
||
# 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.
|
||
|
||
## Conventions
|
||
|
||
- Все функции возвращают `int` — `0` (CUFRAMES_OK) при успехе, отрицательный код из [`cuframes_error_t`](#error-codes) при ошибке. Расшифровка кода — [`cuframes_strerror`](#error-decoding).
|
||
- Все 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-платформами).
|
||
|
||
## Version & error codes
|
||
|
||
### Library version
|
||
|
||
```c
|
||
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 константы:
|
||
|
||
```c
|
||
#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 version не подключатся — publisher вернёт `HELLO_RESP(result=CUFRAMES_ERR_PROTOCOL)`. См. [Protocol reference](/docs/reference/protocol).
|
||
|
||
### Error codes
|
||
|
||
```c
|
||
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;
|
||
```
|
||
|
||
| Code | Name | Meaning |
|
||
|---|---|---|
|
||
| `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 died или сеть оборвалась |
|
||
| `-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 в библиотеке — repro и report'ить |
|
||
|
||
### Error decoding
|
||
|
||
```c
|
||
const char *cuframes_strerror(int err);
|
||
```
|
||
|
||
Возвращает human-readable строку для error code. Pointer указывает на static storage, дальше владеть им не надо. Никогда не возвращает NULL — для unknown code вернёт `"unknown error"`.
|
||
|
||
## Pixel formats
|
||
|
||
```c
|
||
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
|
||
|
||
```c
|
||
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
|
||
|
||
```c
|
||
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
|
||
|
||
```c
|
||
typedef enum cuframes_ownership_mode {
|
||
CUFRAMES_OWNERSHIP_LIBRARY = 0,
|
||
CUFRAMES_OWNERSHIP_EXTERNAL = 1,
|
||
} cuframes_ownership_mode_t;
|
||
```
|
||
|
||
- `LIBRARY` — library владеет VMM pool'ом (см. [Sync model](/docs/concepts/sync-vmm-stream)). 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`.
|
||
|
||
```c
|
||
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);
|
||
```
|
||
|
||
| Function | Returns |
|
||
|---|---|
|
||
| `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ить, например detect epoch change когда `pts_ns_curr < pts_ns_prev`.
|
||
|
||
## Publisher API
|
||
|
||
### Config struct
|
||
|
||
```c
|
||
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;
|
||
```
|
||
|
||
| Field | Constraints |
|
||
|---|---|
|
||
| `key` | ASCII `[a-zA-Z0-9_-]{1,63}`. Не NULL. |
|
||
| `width`, `height` | Pixels. Фиксированы после create. |
|
||
| `format` | См. [Pixel formats](#pixel-formats). Фиксирован. |
|
||
| `ownership` | В v0.4 — только `LIBRARY`. |
|
||
| `ring_size` | 2..16 для `LIBRARY`. Меньше — больше chance overrun, больше — больше VRAM. |
|
||
| `policy` | См. [Policy](#publisher-policy). |
|
||
| `consumer_ack_timeout_ms` | Только для `STRICT_WAIT`. `0` = ждать бесконечно. |
|
||
| `cuda_device` | Обычно `0`. Должен совпадать с consumer'ским. |
|
||
| `_reserved` | Reserved для ABI-stability, должно быть нулями. |
|
||
|
||
### Create / destroy
|
||
|
||
```c
|
||
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](/docs/concepts/sync-vmm-stream).
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `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)
|
||
|
||
```c
|
||
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`.
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `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](/docs/concepts/sync-vmm-stream) — почему именно stream sync, а не CUDA events.
|
||
|
||
| Param | Meaning |
|
||
|---|---|
|
||
| `stream` | CUDA stream на котором писались данные. `0` для default stream. |
|
||
| `pts_ns` | Timestamp, рекомендуется [`cuframes_now_ns()`](#utils). |
|
||
|
||
### Publish (EXTERNAL mode)
|
||
|
||
```c
|
||
int cuframes_publisher_publish_external(cuframes_publisher_t *pub,
|
||
void *cuda_ptr,
|
||
void *stream,
|
||
int64_t pts_ns);
|
||
```
|
||
|
||
**В v0.4 deprecated** — see note про `create_external` выше. Всегда возвращает `CUFRAMES_ERR_INVALID_ARG`.
|
||
|
||
## Subscriber API (sync)
|
||
|
||
### Config struct
|
||
|
||
```c
|
||
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;
|
||
```
|
||
|
||
| Field | Constraints |
|
||
|---|---|
|
||
| `key` | Должен совпадать с publisher'ским |
|
||
| `consumer_name` | Если NULL — library сгенерирует `subscriber-<pid>-<random>`. Unique в пределах publisher'а — иначе `ALREADY_EXISTS`. MAX 32 subscribers. |
|
||
| `mode` | См. [Subscriber mode](#subscriber-mode) |
|
||
| `cuda_device` | Должен совпадать с publisher'ским — VMM FD импортируется на тот же device |
|
||
| `connect_timeout_ms` | `0` = fail сразу с `NOT_FOUND`; `-1` = ждать вечно |
|
||
|
||
### Create / destroy
|
||
|
||
```c
|
||
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](/docs/reference/protocol).
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `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
|
||
|
||
```c
|
||
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` допустимо).
|
||
|
||
| Param | Meaning |
|
||
|---|---|
|
||
| `consumer_stream` | CUDA stream consumer'а. `0` допустимо. |
|
||
| `frame_out` | Output handle. Освободить через `release`. |
|
||
| `timeout_ms` | `<0` = блокироваться, `0` = non-blocking (вернёт `WOULD_BLOCK`), `>0` = с timeout'ом |
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `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)
|
||
|
||
```c
|
||
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, который sit'ит на `next`, вызывает `on_frame` / `on_error`, сам делает `release` после возврата из callback.
|
||
|
||
Constraints:
|
||
|
||
- Frame **валиден только в течение callback'а** — никаких saved pointer'ов;
|
||
- Library использует internal CUDA stream, pre-wait уже выполнен — для своего stream'а используйте sync API;
|
||
- `destroy` joins internal thread и гарантирует что callback больше не вызовется после возврата (может занять до длительности текущего callback'а).
|
||
|
||
## Packet ring API
|
||
|
||
См. [Frame vs Packet ring](/docs/concepts/frame-vs-packet-ring) — когда нужно использовать packet ring.
|
||
|
||
### Flags
|
||
|
||
```c
|
||
#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
|
||
|
||
```c
|
||
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'нуться.
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `NO_PACKET_RING` | Не вызвали `enable_packets` |
|
||
| `PACKET_OVERSIZED` | `size > max_packet_size` |
|
||
| `ALREADY_EXISTS` | (`enable_packets`) ring уже активирован |
|
||
|
||
### Subscriber-side
|
||
|
||
```c
|
||
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](/docs/reference/protocol).
|
||
|
||
Errors:
|
||
|
||
| Code | Когда |
|
||
|---|---|
|
||
| `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 past subscriber lifetime — копируйте сами. Возвращает `NO_CODEC_PARAMS` если publisher ещё не звал `set_codec_extradata`.
|
||
|
||
`release_packet` — NULL-safe. После release pointer'ы от `cuframes_packet_*` invalid.
|
||
|
||
## Utils
|
||
|
||
### Frame size calculation
|
||
|
||
```c
|
||
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
|
||
|
||
```c
|
||
int64_t cuframes_now_ns(void);
|
||
```
|
||
|
||
`CLOCK_MONOTONIC` в наносекундах. Используйте как `pts_ns` для real-time pipeline'ов:
|
||
|
||
```c
|
||
cuframes_publisher_publish(pub, stream, cuframes_now_ns());
|
||
```
|
||
|
||
## See also
|
||
|
||
- [C++ API](/docs/reference/api-cpp) — RAII wrapper.
|
||
- [Protocol reference](/docs/reference/protocol) — wire format, handshake, ABI layouts.
|
||
- [Frame vs Packet ring](/docs/concepts/frame-vs-packet-ring) — когда использовать какой.
|
||
- [Synchronization](/docs/concepts/sync-vmm-stream) — почему `cuStreamSynchronize`, а не CUDA events.
|
||
- [First publisher](/docs/getting-started/first-publisher) — работающий C-пример.
|