fca07bf669
Тесты:
- 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.
365 lines
16 KiB
Markdown
365 lines
16 KiB
Markdown
# 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:<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** для `detect` role (как в v0.1)
|
||
- `cuframes_packets://<key>` — **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
|