Тесты:
- libcuframes/tests/test_packet_ring.c — 2 scenarios:
1) normal flow: 1 pub × 1 sub × 2000 packets, varied sizes, GOP=30,
payload integrity check (seq в первых 8 байтах + pattern). PTS
monotonicity, first KEY seq, нет data errors.
2) slow consumer (10ms delay): publisher 200 fps, subscriber должен
detect OVERRUN, library resync на keyframe — verify received >10
даже на сильно медленном консьюмере.
- libcuframes/tests/CMakeLists.txt: add_test packet_ring_basic.
Docs:
- CHANGELOG.md: новая [Unreleased] секция с full v0.2 highlights и
явно declared limitations (sub-stream, audio, codec change → v0.3).
- docs/integrations/frigate.md: новая секция "v0.2: dual-input (detect +
record через один RTSP)" с config example, requirements, trade-offs.
Связано: #2, PR #4. Step 6 (final) перед снятием draft.
16 KiB
Frigate integration
Полный production-tested guide для интеграции cuframes с Frigate NVR. На основе реального deployment (Frigate 0.17.1-tensorrt + RTX 5090 + Dahua HEVC камеры).
Что вы получаете
- Один NVDEC decode на камеру вместо одного у Frigate + одного у каждого другого consumer'а (cctv-processor, AI-скрипт, mosaic-сервер).
- Frigate видит decoded frames через обычный FFmpeg URL — никакого fork'а Frigate-кода. Frigate сам не подозревает что под капотом cuframes.
Что вы НЕ получаете в v0.1
- Record path (
-c:v copyдля архива) — этот path в Frigate всё ещё через свой отдельный RTSP. v0.2 cuframes решит это через encoded packet sharing (см. issue #2). - Hwaccel CUDA filters для detect resize (
scale_cuda) — наш minimal FFmpeg собран без--enable-cuda-llvm(не работает на glibc < 2.38 что у Debian 12, на котором Frigate base). Workaround:hwaccel_args: []в config → CPU scale (cost ~5-10% CPU на FHD25).
Архитектура
Camera RTSP ──► cuframes-rtsp-source ──► [NVDEC ─► NV12 in CUDA IPC]
│
├──► Frigate (ffmpeg -f cuframes) → detect
├──► cctv-processor (CuframesSource) → motion+mosaic
└──► AI-script (Python ctypes) → inference
Требования
| Минимум | Note | |
|---|---|---|
| NVIDIA driver | 555+ | для CUDA 12 runtime |
| CUDA Toolkit (для build patched FFmpeg) | 12.4+ | host или builder container |
| GPU compute capability | ≥ 7.5 | требование CUDA IPC |
| OS на target (Frigate runtime) | Debian 12 bookworm | glibc 2.36 — это база Frigate stable-tensorrt |
| OS на builder | Ubuntu 22.04 (glibc 2.35) | forward-compat с Debian 12 |
| docker buildx | latest | для multi-stage build |
Шаг 1 — Build patched Frigate image
Cuframes integration требует patched FFmpeg внутри Frigate с cuframes://
demuxer. Самый простой путь — собрать overlay image поверх existing Frigate.
1.1. Минимальный Dockerfile (Debian 12 builder + custom FFmpeg)
# Build patched FFmpeg на Debian 12 (glibc-совместимо с Frigate runtime)
FROM debian:bookworm AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake git nasm pkg-config ca-certificates wget patch ninja-build \
libssl-dev libx264-dev libx265-dev libnuma-dev zlib1g-dev \
libfreetype-dev libfribidi-dev libharfbuzz-dev libfontconfig-dev \
libvpx-dev libopus-dev libmp3lame-dev libvorbis-dev libtheora-dev libwebp-dev \
libaom-dev libdav1d-dev libsvtav1enc-dev \
libssh-dev librist-dev libsrt-openssl-dev \
libdrm-dev libva-dev libxcb1-dev \
&& rm -rf /var/lib/apt/lists/*
# CUDA toolkit 12.x
RUN wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/cuda-keyring_1.1-1_all.deb \
&& dpkg -i cuda-keyring_1.1-1_all.deb && rm cuda-keyring_1.1-1_all.deb \
&& apt-get update && apt-get install -y --no-install-recommends cuda-toolkit-12-6 \
&& rm -rf /var/lib/apt/lists/*
ENV PATH=/usr/local/cuda/bin:$PATH
# nv-codec-headers (для FFmpeg ffnvcodec/nvenc/nvdec)
RUN git clone --depth 1 --branch n12.2.72.0 https://github.com/FFmpeg/nv-codec-headers.git /tmp/nvc \
&& make -C /tmp/nvc install && rm -rf /tmp/nvc
# Build libcuframes (static install в /opt/cuframes)
RUN git clone --depth 1 https://git.goldix.org/gx/cuframes.git /src/cuframes \
&& cmake -B /src/cuframes/build -S /src/cuframes -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF \
-DBUILD_EXAMPLES=OFF -DBUILD_TOOLS=OFF \
&& cmake --build /src/cuframes/build -j"$(nproc)" \
&& cmake --install /src/cuframes/build --prefix /opt/cuframes
# Clone patched FFmpeg fork (либо upstream + apply patch — см. filter/README.md)
RUN git clone --depth 1 --branch n7.1-cuframes \
https://git.goldix.org/gx/ffmpeg-patched.git /src/ffmpeg
# Configure (minimal-but-functional для Frigate)
RUN cd /src/ffmpeg && ./configure \
--prefix=/opt/ffmpeg \
--enable-gpl --enable-version3 --enable-nonfree \
--enable-libcuframes \
--enable-libx264 --enable-libx265 \
--enable-libvpx --enable-libopus --enable-libmp3lame \
--enable-libvorbis --enable-libtheora --enable-libwebp \
--enable-libaom --enable-libdav1d --enable-libsvtav1 \
--enable-libfreetype --enable-libfribidi --enable-libharfbuzz \
--enable-libssh --enable-librist --enable-libsrt \
--enable-openssl \
--enable-ffnvcodec --enable-cuvid --enable-nvenc --enable-nvdec \
--extra-cflags="-I/opt/cuframes/include -I/usr/local/cuda/include" \
--extra-ldflags="-L/opt/cuframes/lib -L/usr/local/cuda/lib64" \
--extra-libs="-lcudart -lpthread -lrt -lm" \
--disable-doc --disable-htmlpages --disable-manpages
RUN cd /src/ffmpeg && make -j"$(nproc)" && make install
# ─── Runtime: Frigate + наши binaries поверх ──────────────────────────
FROM ghcr.io/blakeblackshear/frigate:stable-tensorrt
# Missing dynamic .so которые требует наш patched ffmpeg (Frigate image их не имеет —
# bundled статически собран без них в DT_NEEDED)
RUN apt-get update && apt-get install -y --no-install-recommends \
libharfbuzz0b libfribidi0 librist4 libsrt1.5-openssl libssh-4 \
libvpx7 libwebpmux3 libwebp7 libdav1d6 libaom3 libmp3lame0 \
libsvtav1enc1 libtheora0 libvorbis0a libvorbisenc2 \
libx264-164 libx265-199 libopus0 \
&& rm -rf /var/lib/apt/lists/*
# Replace bundled ffmpeg (оригинал backup'нем под .orig)
RUN cp /usr/lib/ffmpeg/7.0/bin/ffmpeg /usr/lib/ffmpeg/7.0/bin/ffmpeg.orig \
&& cp /usr/lib/ffmpeg/7.0/bin/ffprobe /usr/lib/ffmpeg/7.0/bin/ffprobe.orig
COPY --from=builder /opt/ffmpeg/bin/ffmpeg /usr/lib/ffmpeg/7.0/bin/ffmpeg
COPY --from=builder /opt/ffmpeg/bin/ffprobe /usr/lib/ffmpeg/7.0/bin/ffprobe
COPY --from=builder /opt/cuframes/lib/libcuframes.so.0.1.0 /usr/local/lib/
RUN cd /usr/local/lib && ln -sf libcuframes.so.0.1.0 libcuframes.so.0 \
&& ln -sf libcuframes.so.0 libcuframes.so && ldconfig
# Build-time smoke: ldd resolved + cuframes demuxer registered
RUN ldd /usr/lib/ffmpeg/7.0/bin/ffmpeg | grep -q "not found" && exit 1 || true
RUN /usr/lib/ffmpeg/7.0/bin/ffmpeg -hide_banner -formats | grep -q cuframes \
&& echo "OK: cuframes demuxer registered in Frigate image"
Build:
docker build -t local/frigate-cuframes:latest -f Dockerfile.frigate .
Размер ~10 GB (наследует Frigate stable-tensorrt ~9 GB).
Шаг 2 — docker-compose: publisher + Frigate
services:
# Один publisher на камеру — единственный source RTSP, делает 1× NVDEC.
cuframes-pub-parking:
image: git.goldix.org/gx/cuframes:0.1 # либо local build из filter/Dockerfile.runtime
container_name: cuframes-pub-parking
restart: unless-stopped
runtime: nvidia
# CRITICAL: ipc=shareable — Frigate и другие consumers подсоединяются через
# ipc: container:cuframes-pub-parking
ipc: shareable
shm_size: 256m
environment:
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: compute,video,utility
volumes:
- cuframes_sock:/run/cuframes
command:
- /usr/local/bin/cuframes-rtsp-source
- --rtsp
- "rtsp://admin:${CAM_PASS}@cam-parking-ip:554/cam/realmonitor?channel=1&subtype=1"
- --key
- cam-parking
- --ring
- "6"
- --verbose
frigate:
image: local/frigate-cuframes:latest
container_name: frigate
restart: unless-stopped
depends_on:
cuframes-pub-parking:
condition: service_started
runtime: nvidia
privileged: true
shm_size: 512m
# CUDA IPC c publisher'ом: shared /dev/shm
# WARN: pid намерено НЕ share'ится — Frigate использует s6-overlay,
# которое требует PID 1 в своём namespace.
ipc: "container:cuframes-pub-parking"
environment:
FRIGATE_RTSP_PASSWORD: "${FRIGATE_RTSP_PASSWORD}"
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: compute,video,utility
ports:
- "5000:5000"
- "8971:8971"
volumes:
- cuframes_sock:/run/cuframes:ro
- ./config/config.yml:/config/config.yml:ro
- /home/user/frigate-media:/media/frigate
# ... остальные volumes как обычно
volumes:
cuframes_sock:
Шаг 3 — Frigate config.yml
Ключевые отличия от стандартного config:
ffmpeg:
# ВАЖНО: hwaccel cuda отключаем (наш ffmpeg без cuda-llvm → нет scale_cuda).
# Detect-path использует CPU scale, но decode уже done у publisher'а.
hwaccel_args: []
output_args:
record: preset-record-generic-audio-aac
cameras:
parking_overview:
enabled: true
ffmpeg:
inputs:
# main (full-res) — только запись в архив через прямой RTSP
# (decode у Frigate НЕ происходит — это `-c:v copy` мux)
- path: rtsp://admin:${FRIGATE_RTSP_PASSWORD}@cam-parking-ip:554/cam/realmonitor?channel=1&subtype=0
roles: [record]
# sub-stream → через cuframes (decoded у publisher'а, без второго NVDEC у Frigate)
- path: cuframes://cam-parking
input_args: -f cuframes
roles: [detect]
detect:
width: 640
height: 480
fps: 5
После v0.2 cuframes (encoded packet sharing) record-path тоже мoжет
переключиться на cuframes_packets://cam-parking — тогда никакого RTSP в
Frigate config'е вообще.
Шаг 4 — Run + verify
docker compose up -d
docker logs -f frigate
Что искать в logs:
[INFO] Camera processor started for parking_overview— normal startup- НЕТ
[ERROR] Ffmpeg process crashed— если есть, посмотри Troubleshooting - В
nvidia-smi dmon -s uколонка%decдолжна показывать ~1-2% на одну камеру (это publisher), Frigate сам не decode'ит cuframes input
# Проверить что Frigate реально читает cuframes:
docker exec frigate ps -ef | grep ffmpeg | grep cuframes
# Должна быть линия вида:
# ffmpeg ... -f cuframes -i cuframes://cam-parking -r 5 -vf fps=5,scale=640:480 ...
Troubleshooting
s6-overlay-suexec: fatal: can only run as pid 1
Появляется если попытались добавить pid: container:cuframes-pub-parking в
Frigate service. Frigate's s6-overlay strict требует PID 1.
Fix: убрать pid: из compose. Если только ipc: shared — большинство
случаев работают (Frigate подсоединяется первым и его CUDA context служит
для последующих).
Альтернатива: запустить Frigate с собственным namespace но дублировать publisher socket через bind-mount. Frigate сам управляется first CUDA context.
[AVFilterGraph] No such filter: 'scale_cuda'
Frigate config имеет hwaccel_args: preset-nvidia (default). Наш patched
ffmpeg собран без --enable-cuda-llvm (не работает на glibc < 2.38). Эта
опция компилирует CUDA filters, включая scale_cuda.
Fix: hwaccel_args: [] в config.yml. CPU scale (5-10% CPU per FHD25 камера).
Real fix (planned): cuframes v0.2 — publisher сам делает resize до detect-size и публикует pre-scaled frames. Тогда Frigate не нуждается в scale_cuda.
cudaIpcOpenEventHandle: invalid device context
Consumer container не имеет shared pid namespace с publisher'ом → CUDA driver не валидирует IPC peer.
Fix для cross-container CUDA IPC: pid: container:<publisher> + ipc: container:<publisher>. Для Frigate этот fix недоступен (см. предыдущий пункт).
Workaround — поднять Frigate первым после publisher (race window) или использовать
encoded packet path (v0.2).
Nonmatching transport in server reply от RTSP-output Frigate
Не относится к cuframes — это нормальное поведение Frigate's go2rtc для TCP transport. TV/VLC обычно использует UDP — оно работает.
v0.2: dual-input (detect + record через один RTSP)
После cuframes v0.2 publisher активирует encoded packet ring параллельно с decoded frames ring. Это даёт Frigate одновременно:
cuframes://<key>— decoded NV12 дляdetectrole (как в v0.1)cuframes_packets://<key>— encoded H.264/H.265 дляrecordrole (passthrough, без decode)
→ 1 RTSP connection к камере вместо 2-3 (Frigate сейчас открывает отдельный stream для record).
Setup
cuframes-rtsp-source \
--rtsp rtsp://admin:pw@192.168.88.98/cam/realmonitor?channel=1 \
--key cam-parking \
--enable-packet-ring
Publisher держит два SHM:
/dev/shm/cuframes-cam-parking(decoded NV12, v0.1)/dev/shm/cuframes-cam-parking-packets(encoded packets, v0.2)
Frigate config
cameras:
cam_parking:
ffmpeg:
inputs:
- path: cuframes://cam-parking
input_args: -f cuframes
roles: [detect]
- path: cuframes_packets://cam-parking
input_args: -f cuframes_packets
roles: [record]
Requirements
- Patched FFmpeg с обоими demuxer'ами: gx/ffmpeg-patched PR #1.
- Frigate Dockerfile перекомпилирован с этим ffmpeg (см. секцию выше про
cuframes-frigate:0.17build).
Trade-offs
| Метрика | v0.1 (frames only) | v0.2 (frames + packets) |
|---|---|---|
| RTSP к камере | 1 (publisher) | 1 (publisher) |
| Frigate-side RTSP | 1+ (record отдельно) | 0 — всё через cuframes |
| Camera RTSP streams | 2+ | 1 |
| Доп. VRAM | ring (~10 MB) | без изменений |
| Доп. host RAM | минимум | + 8 MB на packet ring |
| Доп. CPU | nominal | nominal (memcpy в shared ring) |
См. также
- filter/README.md — детали FFmpeg demuxer + patch
- docs/integration.md — общий integration guide
- docs/protocol.md §10 — wire-protocol spec для packet ring
- BENCHMARKS.md — production-measured результаты
- ROADMAP.md — v0.3+ planned features