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

19 KiB
Raw Blame History

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: добавить 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+ по плану