Files
cuframes-docs/site/i18n/ru/docusaurus-plugin-content-docs/current/getting-started/first-publisher.md
T
Claude Opus 7f45c36aa2 init
2026-05-26 23:23:25 +01:00

5.9 KiB
Raw Blame History

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 в наносекундах).

Cleanupcuframes_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.