8c3c43709d
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>
111 lines
6.0 KiB
Markdown
111 lines
6.0 KiB
Markdown
---
|
||
title: Первый publisher
|
||
sidebar_position: 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`](https://git.goldix.org/gx/cuframes/src/branch/main/spike/smoke_v04/smoke_pub.c) из репозитория cuframes.
|
||
|
||
## Исходник
|
||
|
||
```c
|
||
/* 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](/docs/concepts/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](/docs/concepts/sync-vmm-stream).
|
||
|
||
**`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-пул.
|
||
|
||
## Компиляция
|
||
|
||
```bash
|
||
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`).
|
||
|
||
## Запуск
|
||
|
||
```bash
|
||
./first_publisher mykey
|
||
```
|
||
|
||
Пока процесс работает, ему принадлежат:
|
||
|
||
- `/run/cuframes/mykey.sock` — handshake / control socket
|
||
- `/dev/shm/cuframes-mykey` — shared metadata header (SHM)
|
||
|
||
Оба удаляются при чистом shutdown. Если publisher падает, stale-файлы могут остаться; следующий старт пересоздаёт их.
|
||
|
||
## Дальше
|
||
|
||
Открой второй терминал и подключи [Первый subscriber](./first-subscriber.md), который прочитает эти frame'ы и проверит pattern. Полное описание API — [Reference → C API](/docs/reference/api-c).
|