# Frigate integration Полный production-tested guide для интеграции cuframes с [Frigate NVR](https://github.com/blakeblackshear/frigate). На основе реального 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](https://git.goldix.org/gx/cuframes/issues/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) ```dockerfile # 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: ```bash docker build -t local/frigate-cuframes:latest -f Dockerfile.frigate . ``` Размер ~10 GB (наследует Frigate `stable-tensorrt` ~9 GB). ## Шаг 2 — docker-compose: publisher + Frigate ```yaml 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: ```yaml 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 ```bash docker compose up -d docker logs -f frigate ``` Что искать в logs: - `[INFO] Camera processor started for parking_overview` — normal startup - НЕТ `[ERROR] Ffmpeg process crashed` — если есть, посмотри [Troubleshooting](#troubleshooting) - В `nvidia-smi dmon -s u` колонка `%dec` должна показывать ~1-2% на одну камеру (это publisher), Frigate сам не decode'ит cuframes input ```bash # Проверить что 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:` + `ipc: container:`. Для 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://` — **decoded NV12** для `detect` role (как в v0.1) - `cuframes_packets://` — **encoded H.264/H.265** для `record` role (passthrough, без decode) → **1 RTSP connection** к камере вместо 2-3 (Frigate сейчас открывает отдельный stream для record). ### Setup ```bash 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 ```yaml 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](https://git.goldix.org/gx/ffmpeg-patched/pulls/1). - Frigate Dockerfile перекомпилирован с этим ffmpeg (см. секцию выше про `cuframes-frigate:0.17` build). ### 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](../../filter/README.md) — детали FFmpeg demuxer + patch - [docs/integration.md](../integration.md) — общий integration guide - [docs/protocol.md §10](../protocol.md#10-v02-extension-encoded-packet-ring-proto_version2) — wire-protocol spec для packet ring - [BENCHMARKS.md](../../BENCHMARKS.md) — production-measured результаты - [ROADMAP.md](../../ROADMAP.md) — v0.3+ planned features