5.9 KiB
title, sidebar_position
| title | sidebar_position |
|---|---|
| Первый publisher | 2 |
Первый publisher
Минимальный publisher, который экспонирует CUDA-resident ring из 4 NV12-кадров 1920×1080 и пишет в него 10 frame'ов. Каждый frame заполняется однобайтовым pattern'ом через cudaMemsetAsync, чтобы subscriber потом мог end-to-end проверить содержимое.
Это упрощённая версия spike/smoke_v04/smoke_pub.c из репозитория cuframes.
Исходник
/* first_publisher.c — publish 10 NV12 1920x1080 frames, then exit. */
#include <cuframes/cuframes.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv) {
const char *key = argc > 1 ? argv[1] : "mykey";
cuframes_publisher_config_t cfg = {0};
cfg.key = key;
cfg.width = 1920;
cfg.height = 1080;
cfg.format = CUFRAMES_FORMAT_NV12;
cfg.ownership = CUFRAMES_OWNERSHIP_LIBRARY;
cfg.ring_size = 4;
cfg.policy = CUFRAMES_POLICY_DROP_OLDEST;
cfg.cuda_device = 0;
cuframes_publisher_t *pub = NULL;
int r = cuframes_publisher_create(&cfg, &pub);
if (r != CUFRAMES_OK) {
fprintf(stderr, "create: %s\n", cuframes_strerror(r));
return 1;
}
cudaStream_t stream;
cudaStreamCreate(&stream);
for (int i = 0; i < 10; i++) {
void *ptr = NULL;
if ((r = cuframes_publisher_acquire(pub, &ptr)) != CUFRAMES_OK) break;
/* NV12 = Y plane + interleaved UV plane = width*height*3/2 bytes */
cudaMemsetAsync(ptr, (uint8_t)i, 1920 * 1080 * 3 / 2, stream);
r = cuframes_publisher_publish(pub, stream, cuframes_now_ns());
if (r != CUFRAMES_OK) break;
struct timespec ts = {.tv_nsec = 40000000}; /* 25 fps */
nanosleep(&ts, NULL);
}
cudaStreamDestroy(stream);
cuframes_publisher_destroy(pub);
return r == CUFRAMES_OK ? 0 : 1;
}
Разбор
cuframes_publisher_config_t cfg = {0}; — всегда zero-initialise. В структуре есть поле _reserved[4], которое должно оставаться нулевым ради forward ABI compatibility.
cfg.key = "mykey" — уникально именует publisher в пределах хоста. Это становится path-компонентом Unix socket'а (/run/cuframes/mykey.sock) и POSIX SHM-сегмента (/dev/shm/cuframes-mykey). Два publisher'а не могут шарить один key — второй получит CUFRAMES_ERR_ALREADY_EXISTS.
cfg.format = CUFRAMES_FORMAT_NV12 плюс width/height — геометрия frame'а фиксирована на всю жизнь publisher'а. Subscriber'ы увидят ровно эти размеры.
cfg.ownership = CUFRAMES_OWNERSHIP_LIBRARY — library сама аллоцирует CUDA ring buffer. Альтернатива, CUFRAMES_OWNERSHIP_EXTERNAL, позволяет передать уже аллоцированные device pointer'ы (обычно из FFmpeg-пула AVHWFramesContext). Подробнее — Концепции → Ownership modes.
cfg.ring_size = 4 — количество frame-слотов. 2 — минимум, 4 — разумный default, 16 — потолок. С policy DROP_OLDEST медленный consumer просто пропускает frame'ы; publisher никогда не блокируется.
cuframes_publisher_acquire(pub, &ptr) — возвращает CUDA device pointer на следующий writable slot. Действителен только до соответствующего вызова publish().
cudaMemsetAsync(ptr, ..., stream) — заполняем frame на CUDA stream'е по вашему выбору. Не нужно синхронизировать этот stream перед publish. Library внутри publish() сделает cudaEventRecord на тот же stream, а каждый subscriber вызовет cudaStreamWaitEvent на своём stream'е перед чтением. Это и есть cross-process контракт синхронизации — см. Концепции → Cross-process sync.
cuframes_publisher_publish(pub, stream, pts_ns) — делает slot видимым subscriber'ам. pts_ns непрозрачен для library; рекомендуемый источник — cuframes_now_ns() (CLOCK_MONOTONIC в наносекундах).
Cleanup — cuframes_publisher_destroy() закрывает socket, unlink'ает SHM-сегмент и освобождает CUDA-пул.
Компиляция
gcc -O2 -I/usr/local/include -I/usr/local/cuda/include \
-o first_publisher first_publisher.c \
-L/usr/local/lib -lcuframes \
-L/usr/local/cuda/lib64 -lcudart -lcuda
Если cuframes собран без cmake --install, направь -I и -L на своё build/-дерево (-I./include -L./build/libcuframes).
Запуск
./first_publisher mykey
Пока процесс работает, ему принадлежат:
/run/cuframes/mykey.sock— handshake / control socket/dev/shm/cuframes-mykey— shared metadata header (SHM)
Оба удаляются при чистом shutdown. Если publisher падает, stale-файлы могут остаться; следующий старт пересоздаёт их.
Дальше
Открой второй терминал и подключи Первый subscriber, который прочитает эти frame'ы и проверит pattern. Полное описание API — Reference → C API.