# 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/.sock`, handshake (negotiate format, ring size) - Mmap shared memory header `/dev/shm/cuframes.` - 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_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::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//cuframes-ffmpeg:N` — drop-in замена `/usr/lib/ffmpeg/N/bin/ffmpeg` в Frigate image - **Frigate-specific image**: `ghcr.io//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//cuframes-ffmpeg:N.x-cuda12.X` - [ ] `ghcr.io//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 `/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+ по плану