Files
cuframes/docs/architecture.md
T
gx c8ab4522f2 initial commit: design specification + repo scaffolding
cuframes — open-source FFmpeg-плагин и runtime library для zero-copy
sharing декодированных видеокадров между процессами через CUDA IPC.

Содержимое initial commit:
- docs/architecture.md — полная design-spec (418 строк) с prior art,
  protocol design, API draft, phase plan, acceptance criteria
- README.md — landing с описанием идеи, состава, quickstart-tease,
  roadmap, ссылки на community-discussions подтверждающие спрос
- CONTRIBUTING.md — guidelines, code style, commit message convention
- CHANGELOG.md — Keep a Changelog format, Unreleased / 0.0.1
- LICENSE — LGPL-2.1+ (compatibility с FFmpeg)
- .gitignore — build/CMake/Docker/Python/CUDA-specific

Следующие шаги (отдельные коммиты):
- docker/Dockerfile.dev (CUDA 12.x dev environment)
- tools/spike/ (Phase 0 PoC код для measurement CUDA IPC latency)
2026-05-14 21:17:34 +01:00

419 lines
19 KiB
Markdown
Raw 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.
# CUDA IPC Frame Bus — open-source FFmpeg-плагин
**Working name:** `cuframes` (рабочий, finalize при release)
**License:** LGPL-2.1+ (compatibility с FFmpeg)
**Статус:** дизайн-спецификация v0.1
**Дата:** 2026-05-14
## TL;DR
FFmpeg-плагин и C/C++ библиотека для zero-copy sharing декодированных
видеокадров между процессами через CUDA IPC. Один процесс делает decode на
GPU, кадр остаётся в VRAM, любое количество других процессов читает его без
копирования. Distributed как open-source.
Use case 0 (наш): Frigate decode'ит видео, `cctv-companion` consume'ит те же
кадры для mosaic-композиции на TV. Без двойного decode.
Use case N (общий): любая комбинация AI-pipeline + NVR + аналитика на одной
машине с NVIDIA GPU.
## 1. Контекст и мотивация
Сейчас типичный setup:
```
Camera → ffmpeg #1 (NVR, decode + record)
→ ffmpeg #2 (AI-detector, decode + inference)
→ ffmpeg #3 (custom analytics, decode + ...)
Каждый decode = NVDEC operation + VRAM. Inter-process передача
требует cudaMemcpy в host RAM и обратно. На 16 cameras × 25 fps ×
3 consumers = 75% VRAM-bandwidth впустую.
```
Существующие решения:
| | Что | Почему не подходит |
|---|---|---|
| NVIDIA DeepStream | Production-grade SDK, в т.ч. shared GPU buffers | Закрытое, vendor lock-in, требует переписи pipeline |
| GStreamer NVMM | NVIDIA Multimedia memory plugin | Inter-process только через DMA-buf, сложная интеграция |
| Custom CUDA IPC | Прямой `cudaIpcGetMemHandle` | Требует написать с нуля у каждого; нет интеграции с FFmpeg |
| Frigate +1 ffmpeg | Каждый сервис свой decode | Дублирование, что и побудило проект |
Пустая ниша: **простой FFmpeg-filter** который добавляется в любой
ffmpeg-pipeline и публикует декодированные кадры через CUDA IPC. Без
рерайта пайплайна, без vendor SDK, без специальной библиотеки на стороне
producer'а — просто extra-flag к ffmpeg.
## 1.bis Prior art / competitive analysis
Research проведён 2026-05-14, документирует что **ниша свободна**.
### Существующие технологии (building blocks, не plug-and-play)
| Проект | License | Что | Почему не аналог |
|---|---|---|---|
| **NVIDIA CUDA IPC API** | Proprietary headers, free use | Core `cudaIpcGetMemHandle`/`cudaIpcOpenMemHandle` | Сырой API, не плагин для FFmpeg — наш фундамент |
| **NVIDIA DeepStream** | Closed-source SDK | `NvDsBufSurface` shared-buffers + ML pipeline | Vendor lock-in, требует переписать pipeline в DeepStream API. Не FFmpeg-filter. |
| **NVIDIA VPF** ([github](https://github.com/NVIDIA/VideoProcessingFramework)) | Apache 2.0 | Python bindings вокруг NvCodec | **Single-process только**, inter-process не делает |
| **PyNvVideoCodec** | NVIDIA-proprietary, free | Python decoder с PyTorch zero-copy interop | Single-process |
| **dora-rs CUDA IPC** ([dora-rs.ai](https://dora-rs.ai/docs/guides/Development/Cuda/)) | Apache 2.0 | `torch_to_ipc_buffer()` / `ipc_buffer_to_ipc_handle()` для dataflow framework | Robotics-focused, работает на уровне torch tensors. Не FFmpeg-плагин. **Можем переиспользовать low-level подходы**. |
| **GStreamer NVMM** | LGPL | NVIDIA Multimedia Memory plugins | Inter-thread (intra-process); inter-process через DMA-buf только на Jetson |
| **BabitMF** (Bytedance) | Apache 2.0 | GPU filtering framework | Single-process intra-pipeline, не FFmpeg-filter |
| **Frigate go2rtc relay** | MIT | Re-stream compressed bytes между consumers | Не zero-copy — каждый consumer декодит снова |
### Подтверждение спроса в community
Threads в `blakeblackshear/frigate`, ровно описывающие нашу проблему:
- **#17033** — «Recommended GPU for Video Decoding with ~80 Cameras» — обсуждение saturation NVDEC engines при наивном double-decode
- **#20191** — «As soon as I add a second camera, nothing works» — пользователи натыкаются на decode-related issues
- **#21559** — «System with 25 cameras — which GPU» — все ответы про "купите больше железа", решения через shared decode нет
- **#21865** — «Combining Nvidia GPU and integrated GPU» — workaround через split decoders, что лишний раз доказывает что shared decode был бы полезен
### Проверка FFmpeg upstream
- FFmpeg-devel mailing list 2024-2026: **ноль** patches содержащих CUDA IPC inter-process filter
- FFmpeg master tree (8.0.1): CUDA-фильтры (`scale_cuda`, `bilateral_cuda`, `bwdif_cuda`, `libvmaf_cuda`, `overlay_cuda`) — все intra-process
- Search GitHub `vf_cuda_ipc | cudaipc | cuda_ipc_export`: zero forks/patches с этой функциональностью
### Вывод
**Ниша свободна.** Подтверждённый спрос (Frigate community), стабильные building-blocks (NVIDIA CUDA IPC API + dora-rs reference). Нет risk'а дублирования усилий. Sources проверены 2026-05-14.
## 2. Архитектура
### 2.1 Компоненты репозитория
```
cuframes/ ← OSS repo
├── filter/
│ └── vf_cuda_ipc_export.c FFmpeg filter (C, ~300 строк)
├── libcuframes/ producer/consumer ядро
│ ├── include/cuframes.h публичный C API
│ ├── src/
│ │ ├── producer.c ring buffer + Unix socket server
│ │ ├── consumer.c subscribe + cudaIpcOpenMemHandle
│ │ └── protocol.c wire format, версионирование
│ └── tests/
│ ├── pingpong.c smoke test: 1 producer ↔ 1 consumer
│ ├── multi_consumer.c 1 → N
│ ├── stress.c 24h, leak detection
│ └── bench.c latency, throughput
├── bindings/
│ ├── cpp/ C++ RAII wrapper
│ └── python/ pybind11 binding (numpy/torch interop)
├── examples/
│ ├── ffmpeg-basic/ минимальный FFmpeg → consumer
│ ├── opencv-consumer/ cv::cuda::GpuMat из shared frame
│ ├── pytorch-consumer/ torch.Tensor zero-copy
│ ├── frigate-integration/ Frigate config + Docker compose
│ └── mosaic-compositor/ наш cctv-companion (как demo)
├── docker/
│ ├── ffmpeg-cuframes/ FFmpeg с встроенным filter
│ └── ffmpeg-cuframes-frigate/ Drop-in replacement для Frigate's ffmpeg
├── docs/
│ ├── architecture.md эта спека после редактирования для OSS
│ ├── protocol.md wire format
│ ├── quickstart.md 5-минутный пример
│ ├── building-ffmpeg.md как пересобрать FFmpeg с filter
│ ├── frigate-howto.md конкретно под Frigate
│ ├── benchmarks.md numbers
│ └── faq.md
├── scripts/
│ ├── build-ffmpeg-patched.sh автоматизация upstream build
│ ├── package-deb.sh .deb для Ubuntu
│ └── ci/
└── .github/
└── workflows/ CI: build matrix Ubuntu 22.04/24.04, CUDA 12.x
```
### 2.2 Wire protocol (краткое)
Producer:
- Аллоцирует ring buffer (default 4 slots) в VRAM через `cudaMalloc`
- На каждый decoded frame — пишет в next slot, делает `cudaStreamSynchronize`
- Публикует `seq` атомарно
- Сигналит consumers через eventfd
Consumer:
- Connect к `/run/cuframes/<key>.sock`, handshake (negotiate format, ring size)
- Mmap shared memory header `/dev/shm/cuframes.<key>`
- Wait eventfd → read latest slot → `cudaIpcOpenMemHandle` → CUDA pointer
- Использует frame zero-copy → ACK через atomic bit в bitmap
Backpressure: drop oldest (newest-wins). Multi-consumer независимы — медленный
не блокирует быстрых.
Полный формат — в `docs/protocol.md`. Здесь — концепт.
### 2.3 FFmpeg filter API
```
-vf "[any pipeline before],cuda_ipc_export=key=NAME[:ring=N][:sync_event=1],[pipeline after]"
```
Опции:
- `key` *(required)* — имя shared resource. Должно совпадать у producer и consumer.
- `ring` — глубина ring buffer (default 4)
- `sync_event` — использовать `cudaIpcEventHandle_t` вместо `cudaStreamSynchronize`
(zero-copy sync, но experimental)
Filter:
- Принимает frame в CUDA hwframe-context
- Side-effect: публикует через `libcuframes`
- Passthrough — frame идёт дальше в pipeline без модификации (refcount++)
Пример:
```bash
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
-i rtsp://camera/stream \
-vf "scale_cuda=1920:1080,cuda_ipc_export=key=cam1" \
-c:v copy -f segment recording.mp4
```
В этой команде frame и публикуется через CUDA IPC, и пишется в архив. Один
decode для всего.
### 2.4 Consumer API
#### C
```c
#include <cuframes.h>
cuframes_subscriber_t* sub = cuframes_subscribe("cam1", "my-app");
cuframes_frame_t frame;
while (cuframes_next(sub, &frame, /*timeout_ms=*/100) == 0) {
// frame.cuda_ptr — device pointer, zero-copy
// frame.width, height, pitch_y, pitch_uv, format
// frame.pts_ns
do_inference_on_cuda(frame.cuda_ptr, frame.width, frame.height);
cuframes_release(&frame); // ACK обратно producer'у
}
cuframes_unsubscribe(sub);
```
#### C++ (RAII)
```cpp
#include <cuframes.hpp>
cuframes::Subscriber sub("cam1", "my-app");
while (auto frame = sub.next(std::chrono::milliseconds(100))) {
// frame->cuda_ptr — zero-copy
cv::cuda::GpuMat mat(frame->height, frame->width, CV_8UC2, // NV12
frame->cuda_ptr, frame->pitch_y);
// ... use mat
// frame destructor → ACK
}
```
#### Python (numpy/torch interop)
```python
import cuframes
with cuframes.Subscriber("cam1", "my-app") as sub:
for frame in sub:
# frame.tensor: torch.Tensor on CUDA, zero-copy
# frame.dlpack: __dlpack__ для numpy/torch/jax interop
output = model(frame.tensor)
```
### 2.5 Distribution и установка
#### Уровень 1 — FFmpeg patch
Минимальный патч к FFmpeg `libavfilter/`:
- `vf_cuda_ipc_export.c` (новый файл)
- `Makefile`: добавить filter
- `allfilters.c`: регистрация
Это **один commit** к FFmpeg-репо. Возможен upstream PR (надеемся примут;
прецедент есть — Nvidia donated `scale_cuda` и др.).
#### Уровень 2 — `libcuframes` runtime
Stand-alone shared library. Зависимости:
- `libcuda.so.1` (NVIDIA driver, и так у всех)
- `cudart` (CUDA runtime)
- Linux только (POSIX SHM, eventfd, AF_UNIX)
#### Уровень 3 — упаковка
- **Debian/Ubuntu**: `.deb` через `dpkg-buildpackage`
- **Arch**: AUR package
- **Docker**: pre-built image `ghcr.io/<org>/cuframes-ffmpeg:N` — drop-in
замена `/usr/lib/ffmpeg/N/bin/ffmpeg` в Frigate image
- **Frigate-specific image**: `ghcr.io/<org>/cuframes-frigate:0.17`
Frigate stable-tensorrt с pre-installed cuframes ffmpeg
## 3. Use cases для документации
### 3.1 Наш кейс — `cctv-companion` (mosaic)
```
Frigate (ffmpeg с cuda_ipc_export filter):
Camera 1..16 → decode CUDA → publish key=cam_N
↘ свой Frigate pipeline (detect, record)
cctv-companion:
- подписан на cam_1..cam_16 через cuframes
- подписан на frigate/events через MQTT
- decision logic: какую сетку показать (full/2x2/4x4)
- composite через CUDA-ядра (CUB / OpenCV CUDA / custom kernels)
- encode → RTSP → TV
```
### 3.2 Multi-AI на одном decode
```
ffmpeg decode → publish key=cam_main
Subscriber A: YOLOv8 object detection
Subscriber B: face recognition (ArcFace)
Subscriber C: license plate OCR
Subscriber D: motion-triggered cloud upload
```
Все читают **тот же** decoded frame. Один NVDEC operation вместо четырёх.
### 3.3 Замена RAM-based mmstreams
Существующие custom-решения часто строят бypass через FIFO/pipe в RAM с
encoding между процессами. cuframes делает это zero-copy на CUDA.
## 4. План работ
### Phase 0 — Spike (3 дня)
- [ ] Прототип producer + consumer без FFmpeg: голый CUDA IPC + Unix socket
- [ ] Замерить bandwidth, latency на RTX 5090
- [ ] Verify cross-container (docker → docker) и host → docker
- [ ] Решить: sync через `cudaStreamSynchronize` или CUDA IPC events
- **Deliverable:** `tools/spike/pingpong.cpp` + измерения
### Phase 1 — libcuframes (1 неделя)
- [ ] Producer C API
- [ ] Consumer C API
- [ ] Wire-protocol implementation
- [ ] Unit tests (pingpong, multi-consumer, crash-recovery)
- [ ] Stress test rig (24h-runner)
- **Deliverable:** Self-contained library + tests passing
### Phase 2 — FFmpeg filter (1-2 недели)
- [ ] `vf_cuda_ipc_export.c` в стиле FFmpeg
- [ ] Patch script для применения к FFmpeg N.x (4.4, 5.x, 6.x, 7.x)
- [ ] Build на CUDA 12.0+ matrix
- [ ] Integration test: `ffmpeg → cuframes filter → consumer` end-to-end
- **Deliverable:** Patched FFmpeg builds (Ubuntu 22.04, 24.04, in Docker)
### Phase 3 — Bindings (1 неделя)
- [ ] C++ RAII wrapper
- [ ] Python binding через pybind11
- [ ] PyTorch tensor interop (DLPack)
- **Deliverable:** `pip install cuframes` (через `pyproject.toml`)
### Phase 4 — Docker / packaging (3-5 дней)
- [ ] `ghcr.io/<org>/cuframes-ffmpeg:N.x-cuda12.X`
- [ ] `ghcr.io/<org>/cuframes-frigate:0.17` (Frigate с patched ffmpeg)
- [ ] `.deb` packages для Ubuntu 22.04/24.04
- [ ] CI matrix: build всех вариантов на каждый push
- **Deliverable:** `docker pull` ready-to-use
### Phase 5 — Naш use case: cctv-companion (1 неделя)
- [ ] Минимальный C++ daemon
- [ ] Subscribe на N камер
- [ ] MQTT subscriber на Frigate events
- [ ] Smart-grid logic (existing в проекте, переиспользовать)
- [ ] Mosaic compositor на CUDA (cv::cuda::warpAffine для placement)
- [ ] Output через `nvenc` → RTSP сервер для TV
- **Deliverable:** Working setup на R9 со всеми 16 камерами
### Phase 6 — OSS launch (3-5 дней)
- [ ] README с GIF (visual что это даёт)
- [ ] Quickstart (5 минут от `docker pull` до working demo)
- [ ] Benchmarks page с numbers
- [ ] HackerNews / r/selfhosted / r/homeassistant анонс
- [ ] Submit upstream PR в FFmpeg
**Итого: ~6-8 недель** для одного разработчика, **3-4 недели** для двоих.
## 5. Acceptance criteria для v1.0
- [ ] FFmpeg-filter работает на 4.4, 5.x, 6.x, 7.x (Frigate diversity)
- [ ] Latency producer→consumer ≤ 1 ms (p99)
- [ ] 16 cameras × 4 consumers × 25 fps без drops 24h
- [ ] Frigate (stable-tensorrt) drop-in replacement работает
- [ ] PyTorch consumer: `frame.tensor` без копий на CUDA
- [ ] Docker images < 4 GB сжатые
- [ ] CI green на Ubuntu 22.04, 24.04
- [ ] `pip install cuframes` works на Python 3.10/3.11/3.12
- [ ] README с benchmark vs naive double-decode
## 6. Open questions для discussion
1. **Name.** `cuframes` рабочий, варианты: `vramshare`, `cudapipe`,
`nvframe-bus`, `cuda-frame-bridge`, `framesink`. Финализируем перед public.
2. **License.** LGPL-2.1+ для совместимости с FFmpeg LGPL и spirit (linkable
с MIT/Apache). Sourcing — на LGPL.
3. **Upstream FFmpeg PR vs custom build.** Best-effort вклад в FFmpeg, но не
блокер — distrubution через own Docker images работает независимо.
4. **AMD/ROCm support.** Out of scope для v1. После v1 — если будет
community-interest, добавить через HIP IPC.
5. **Multi-GPU.** Producer и consumer на разных CUDA devices — CUDA IPC
ограничивает same device. В v1 — ограничение, документировать.
6. **Sync semantics.** `cudaStreamSynchronize` гарантирует correctness но
serialize'ит producer. CUDA IPC events дают overlap, но complicate API.
Default: stream sync. Advanced flag: events.
7. **Где хостить.** GitHub `<personal-org>/cuframes`. Возможно перенос в
`frigate-community/cuframes` после adoption.
## 7. Риски
| Риск | Митигация |
|---|---|
| Upstream FFmpeg PR не примут | Distributable через custom builds, не блокирует release |
| CUDA IPC subtle bugs | Phase 0 spike до commit'а; Stress test 24h |
| Frigate не пропустит custom filter | `frigate-cuframes` docker image — pre-patched, готов к использованию |
| Conflict с NVIDIA DeepStream APIs | None expected (разные scope) |
| Community не подхватит | Это secondary — primary value для нас (cctv-companion работает) |
## 8. Зависимости (build-time)
- CUDA Toolkit ≥ 12.0
- FFmpeg sources (target versions 4.4, 5.x, 6.x, 7.x)
- pkg-config, autoconf, make
- Optional: OpenCV CUDA (для C++ examples)
- Optional: pybind11 (для Python binding)
## 9. Связь с этим репозиторием (cctv)
Текущий `cpp/apps/cctv-processor` — full-featured C++ NVR с своим decode +
mosaic + RTSP-server. После v1 cuframes:
- `cuframes` выпускается **как отдельный** opensource-репо
(`/home/claude/projects/cuframes/`)
- В `cctv` создаётся новый `cpp/apps/cctv-companion/` — тонкий клиент к
Frigate + cuframes (не Replaces existing processor, а соседствует)
- Существующий cctv-processor — оставить как baseline / fallback для
setups без Frigate
## 10. Готовность к next step
После approval спеки:
1. `git init /home/claude/projects/cuframes/` — пустой OSS-репо
2. Phase 0 spike — 3 дня PoC
3. После Phase 0 — review результатов, корректировка дизайна (если CUDA IPC
повёл себя не как ожидали)
4. Phase 1+ по плану