См. spike-v2 (commit ad54305) + arch review 2026-05-15.
cudaStreamSynchronize-only фактически работает на single-host single-GPU
(0 torn в 4 scenarios PoC), но NVIDIA Programming Guide §3.2.8 не даёт
contractual гарантии. Переключаемся на cudaIpcEventHandle_t как default,
stream-sync остаётся опциональным fallback.
Net: +20µs mean latency, -3× max latency (predictable tail), future-proof
для multi-GPU.
20 KiB
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) | 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) | 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++)
Пример:
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
#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)
#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)
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: добавить filterallfilters.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 → consumerend-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.Xghcr.io/<org>/cuframes-frigate:0.17(Frigate с patched ffmpeg).debpackages для Ubuntu 22.04/24.04- CI matrix: build всех вариантов на каждый push
- Deliverable:
docker pullready-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 cuframesworks на Python 3.10/3.11/3.12- README с benchmark vs naive double-decode
6. Open questions для discussion
-
Name.
cuframesрабочий, варианты:vramshare,cudapipe,nvframe-bus,cuda-frame-bridge,framesink. Финализируем перед public. -
License. LGPL-2.1+ для совместимости с FFmpeg LGPL и spirit (linkable с MIT/Apache). Sourcing — на LGPL.
-
Upstream FFmpeg PR vs custom build. Best-effort вклад в FFmpeg, но не блокер — distrubution через own Docker images работает независимо.
-
AMD/ROCm support. Out of scope для v1. После v1 — если будет community-interest, добавить через HIP IPC.
-
Multi-GPU. Producer и consumer на разных CUDA devices — CUDA IPC ограничивает same device. В v1 — ограничение, документировать.
-
Sync semantics. ✅ РЕШЕНО (architectural review 2026-05-15 + spike-v2): Default — CUDA IPC events (
cudaIpcEventHandle_t). Rationale:- NVIDIA Programming Guide §3.2.8 явно требует cross-process sync на стороне
application;
cudaStreamSynchronizeproducer'а гарантий не даёт contractually. - Spike-v2 measurements (
docs/measurements/spike-v2/) показали: на нашем single-host single-GPU setup stream-only sync работает (0 torn frames на 1500 frames × 4 scenarios), но это happens-to-work, не contract. - Events добавляют mean +20µs overhead но дают predictable tail latency (max 5.2ms vs 14.7ms для FHD@60fps) и future-proof для multi-GPU.
- API option
--sync=streamоставлен как fallback для legacy / debugging.
- NVIDIA Programming Guide §3.2.8 явно требует cross-process sync на стороне
application;
-
Где хостить. 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 спеки:
git init /home/claude/projects/cuframes/— пустой OSS-репо- Phase 0 spike — 3 дня PoC
- После Phase 0 — review результатов, корректировка дизайна (если CUDA IPC повёл себя не как ожидали)
- Phase 1+ по плану