Files
cuframes-docs/site/i18n/ru/docusaurus-plugin-content-docs/current/getting-started/first-publisher.md
T
Claude Opus 8c3c43709d docs: full content + landing + RU translations
Initial documentation site for cuframes:

- Landing page (src/pages/index.mdx) — hero, quick example (publisher +
  subscriber), comparison table vs naive/DeepStream, honest "early but
  production-tested" status
- /docs/intro — full overview
- /docs/getting-started/{install,first-publisher,first-subscriber}
- /docs/concepts/{frame-vs-packet-ring,ownership-modes,sync-vmm-stream}
  with mermaid diagrams
- /docs/integration/{ffmpeg-demuxer,ffmpeg-filter,python}
- /docs/reference/{api-c,api-cpp,protocol} — full v4 wire protocol spec
  incl. VMM_FDS message, magic 0xCC7C1DCE bump diff
- /docs/faq — comparison vs DeepStream/GStreamer, license, multi-host
  limitations
- i18n/ru/ — parallel RU translation (tech terms latin, склонение апостроф)

Build:
- Docusaurus 3.10.1 + theme-mermaid + search-local
- Follows dagstack-* docs convention (canonical: dagstack-plugin-system-docs)
- Apache-2.0 license; cuframes lib itself remains LGPL-2.1+

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 23:31:03 +01:00

6.0 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() делает cuStreamSynchronize(stream) чтобы дождаться flush pending GPU writes, потом атомарно публикует sequence number. Subscriber видит данные через hardware coherence при DtoD memcpy на том же GPU — никакие CUDA events не нужны. Полное обоснование: Концепции → Sync: stream sync, не CUDA events.

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.