Files
gx ba68550f4c Phase 1: NVENC через dlopen + источник через cuframes_subscriber
Скелет проекта 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
2026-06-03 04:28:33 +01:00

85 lines
5.0 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 */