ba68550f4c
Скелет проекта cuframes-composer (LGPL-2.1+) и MVP кодирования
одного источника в файл H.264.
Что включает Phase 1:
- LICENSE (LGPL-2.1+), README с поэтапным планом, корневой CMake
- Подмодули: cuframes v0.4 (pinned), nv-codec-headers (n12.2.72.0)
- include/cuframes_composer/source.h — публичный API источника
с явной машиной состояний (DISCONNECTED → CONNECTING → ACTIVE →
STALE → DEAD) и snapshot-паттерном для чтения без блокировки
- include/cuframes_composer/nvenc.h — публичный API кодировщика
на CUdeviceptr-вход (zero-copy через VMM-mapped указатели)
- src/nvenc_loader.{h,c} — dlopen libnvidia-encode.so.1 и инициализация
таблицы функций NVENC через NvEncodeAPICreateInstance. Идёт через
pthread_once. Сделано отдельно чтобы держать LGPL-совместимость:
проприетарный SDK не статически линкуется
- src/nvenc.c — обвязка над NVENC: open session, init encoder, кеш
registered resources, encode/lock/unlock, flush с EOS, поддержка
H.264 CBR low-latency, preset GUID p1/p4/p7
- src/source.c — обвязка над cuframes_subscriber c фоновым потоком,
exponential backoff reconnect (1с → 30с), и переходами по таймаутам
для stale/dead-детекта
- examples/simple_record — smoke-test программа: подписка на cuframes,
кодирование, запись в .h264 файл, корректное завершение по SIGINT
85 lines
5.0 KiB
C
85 lines
5.0 KiB
C
/* cuframes-composer — обвязка над cuframes_subscriber с явной машиной
|
||
* состояний и автоматическим переподключением. Создаёт собственный поток
|
||
* для блокирующего cuframes_subscriber_next и держит снимок последнего
|
||
* успешного кадра, который читатели берут не блокируясь.
|
||
*
|
||
* Используется в композиторе для каждого входного источника (одна камера).
|
||
*
|
||
* Lifecycle:
|
||
* create() → запускается поток, который начинает с DISCONNECTED, идёт
|
||
* в CONNECTING (попытка подписаться), затем ACTIVE при успехе
|
||
* get_latest() → не блокируется, возвращает снимок последнего кадра
|
||
* + текущее состояние
|
||
* destroy() → останавливает поток, освобождает ресурсы cuframes
|
||
*
|
||
* Thread safety: create/destroy — main thread. get_latest — любой поток.
|
||
*
|
||
* Лицензия: LGPL-2.1+
|
||
*/
|
||
|
||
#ifndef CUFRAMES_COMPOSER_SOURCE_H
|
||
#define CUFRAMES_COMPOSER_SOURCE_H
|
||
|
||
#include <cuda.h>
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* Состояние источника. ACTIVE — единственное «здоровое» состояние,
|
||
* остальные означают какую-то форму потери связи или ожидания. */
|
||
typedef enum cfc_source_state {
|
||
CFC_SOURCE_DISCONNECTED = 0, /* стартовое или после destroy подписки */
|
||
CFC_SOURCE_CONNECTING, /* идёт subscribe handshake */
|
||
CFC_SOURCE_ACTIVE, /* получаем кадры, последний < N мс назад */
|
||
CFC_SOURCE_STALE, /* подписка жива, но кадры не приходят */
|
||
CFC_SOURCE_DEAD, /* подписка отвалилась, ждём backoff до retry */
|
||
} cfc_source_state_t;
|
||
|
||
/* Конфигурация одного источника. */
|
||
typedef struct cfc_source_config {
|
||
const char *key; /* cuframes key (например "cam-parking") */
|
||
const char *consumer_name; /* имя для cuframes, должно быть уникально на publisher */
|
||
int32_t cuda_device; /* индекс CUDA-устройства, обычно 0 */
|
||
int32_t reconnect_min_ms; /* минимальный backoff при DEAD → CONNECTING (по умолчанию 1000) */
|
||
int32_t reconnect_max_ms; /* максимальный backoff (по умолчанию 30000) */
|
||
int32_t stale_threshold_ms; /* без кадра > N → ACTIVE → STALE (по умолчанию 500) */
|
||
int32_t dead_threshold_ms; /* без кадра > N → STALE → DEAD (по умолчанию 5000) */
|
||
} cfc_source_config_t;
|
||
|
||
typedef struct cfc_source cfc_source_t;
|
||
|
||
/* Снимок последнего успешного кадра. ptr указывает на VMM-mapped CUDA-память
|
||
* cuframes publisher'а. Он действителен до следующего вызова get_latest:
|
||
* после этого источник может перейти на следующий слот ring buffer'а. */
|
||
typedef struct cfc_source_snapshot {
|
||
CUdeviceptr ptr; /* указатель на NV12 frame (Y plane) */
|
||
int32_t width;
|
||
int32_t height;
|
||
int32_t pitch_y;
|
||
int32_t pitch_uv;
|
||
int64_t pts_ns; /* timestamp от publisher'а (CLOCK_MONOTONIC ns) */
|
||
uint64_t seq; /* sequence number */
|
||
cfc_source_state_t state; /* текущее состояние источника */
|
||
int64_t last_frame_age_us; /* сколько микросекунд назад последний успешный кадр; -1 если никогда */
|
||
} cfc_source_snapshot_t;
|
||
|
||
/* Создать источник. Возвращает 0 при успехе. Старт асинхронный:
|
||
* cfc_source_create возвращает сразу, фоновый поток выполняет subscribe.
|
||
* До первого успешного кадра состояние будет CONNECTING либо DEAD. */
|
||
int cfc_source_create(const cfc_source_config_t *cfg, cfc_source_t **out);
|
||
|
||
/* Получить снимок последнего кадра. Не блокируется. Возвращает 0 при успехе.
|
||
* Поле state в out выставляется всегда (даже если кадров ещё не было). */
|
||
int cfc_source_get_latest(cfc_source_t *src, cfc_source_snapshot_t *out);
|
||
|
||
/* Освободить источник. Останавливает поток (joins), отключает подписку. */
|
||
int cfc_source_destroy(cfc_source_t *src);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* CUFRAMES_COMPOSER_SOURCE_H */
|