8.1 KiB
title, sidebar_position
| title | sidebar_position |
|---|---|
| FFmpeg cuframes:// demuxer | 1 |
FFmpeg cuframes:// demuxer
cuframes поставляет два FFmpeg input demuxer'а, оба — патчем поверх upstream FFmpeg:
cuframes— подписывается на decoded NV12 frame ring и отдаёт его какrawvideostream (один stream на URL).cuframes_packets— подписывается на encoded packet ring и отдаёт его какh264/hevcbyte-stream, сextradataиз publisher'ского handshake.
Оба demuxer'а — чистые consumer'ы. Они никогда не декодируют, никогда не re-encode'ят, и никогда не лезут в сеть — реальный pull RTSP происходит один раз в publisher'е (обычно cuframes-rtsp-source). FFmpeg просто цепляется к уже существующему ring'у через Unix socket.
URL-схема
cuframes://<key> # decoded NV12 frames (raw GPU surfaces)
cuframes_packets://<key> # encoded H264/HEVC packets (Annex-B)
<key> — это key publisher'а, та же строка что передаётся в cuframes_publisher_create() или в --key для cuframes-rtsp-source. Legacy-форма cuframes:<key> (без //) тоже принимается.
Два типа ring'ов независимы. Один publisher может экспонировать оба: decoded frame'ы для composer'ов / AI, encoded packets для recorder'ов которые хотят пропустить re-encode.
Что делает demuxer
При open demuxer:
- Подключается к
/run/cuframes/<key>.sock. - Получает N POSIX file descriptors через
SCM_RIGHTS(frame-слоты) плюс shm metadata header. - Для
cuframes: импортирует каждый FD как CUDA VMM allocation, объявляет одинAV_PIX_FMT_NV12stream на publisher'ском width / height / framerate. - Для
cuframes_packets: читаетextradata(SPS/PPS для H264, VPS/SPS/PPS для HEVC) из handshake и объявляет одинAV_CODEC_ID_H264илиAV_CODEC_ID_HEVCstream.
В read-loop demuxer poll'ит publisher'ский global_seq, копирует frame / packet в pipeline и проставляет pts по publisher'ским часам. Wire format — Protocol reference.
Примеры pipeline'ов
Один источник — decoded ring → NVENC → MPEG-TS
Re-encode опубликованной камеры в H.264 MPEG-TS UDP stream. NVDEC в этом ffmpeg не запускается — publisher уже один раз декодировал, этот процесс просто NVENC'ит shared NV12 surface.
ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
-f cuframes -i cuframes://cam-parking \
-c:v h264_nvenc -preset p4 -b:v 4M \
-f mpegts udp://192.168.88.50:5000
Packet ring — true copy, без decode и без encode
Когда нужно только записать или restream'ить существующую камеру и decoded пиксели не интересны — подписывайся на packet ring через -c:v copy. NVDEC и NVENC оба простаивают.
ffmpeg -f cuframes_packets -i cuframes_packets://cam-parking \
-c:v copy -f mp4 /var/recordings/cam-parking.mp4
Это самый дешёвый способ размножить один decode на N recorder'ов.
Композиция — 4 входа в CUDA-grid
Короткий пример multi-input wiring. Полный справочник по фильтру — FFmpeg vf_cuda_grid filter.
ffmpeg \
-hwaccel cuda -hwaccel_output_format cuda \
-f cuframes -i cuframes://cam1 \
-f cuframes -i cuframes://cam2 \
-f cuframes -i cuframes://cam3 \
-f cuframes -i cuframes://cam4 \
-filter_complex "[0:v][1:v][2:v][3:v]cuda_grid=layout=quad[out]" \
-map "[out]" -c:v h264_nvenc -preset p4 \
-f rtsp rtsp://127.0.0.1:8554/grid
Сборка / запуск с пропатченным FFmpeg
Demuxer'ы живут в libavformat/cuframesdec.c и libavformat/cuframes_packetsdec.c и в upstream FFmpeg их нет. Есть два варианта.
Вариант A — готовый Docker-образ
Production-tested образ опубликован как ffmpeg-vf-cuda-grid:phase8. Внутри — пропатченный ffmpeg binary, libcuframes.so и фильтр vf_cuda_grid. Референсный docker compose-setup, который связывает его с publisher-контейнером, лежит в репо localhost-infra — копируй и адаптируй, не пинни как публичную зависимость.
docker run --rm --runtime=nvidia \
--ipc=container:cuframes-publisher \
ffmpeg-vf-cuda-grid:phase8 \
ffmpeg -f cuframes -i cuframes://cam1 -c:v copy -f null -
Флаг --ipc=container:... подключает IPC namespace publisher'а, чтобы POSIX shm header был виден. Шарить PID namespace не требуется начиная с cuframes v0.4.
Вариант B — собрать самому
Используй toolchain ffmpeg-builds (fork BtbN/FFmpeg-Builds). Скрипт scripts.d/50-libcuframes.sh клонирует cuframes, собирает его статически, и --enable-libcuframes автоматически добавляется когда активен addin cuframes.
git clone <ffmpeg-builds repo> ffmpeg-builds
cd ffmpeg-builds
ADDITIONAL_SCRIPTS=50-libcuframes.sh ./build.sh <target> <variant>
Пропатченное дерево исходников ffmpeg (с обоими demuxer'ами и фильтром) лежит в ffmpeg-fresh/. Если хочешь вендорить патч в свой fork FFmpeg, скопируй три файла (libavformat/cuframesdec.c, libavformat/cuframes_packetsdec.c, libavfilter/vf_cuda_grid.c) плюс соответствующие Makefile и регистрации в allformats.c / allfilters.c.
Поведение при reconnect
Publisher'ы приходят и уходят — рестарты контейнеров, reboot RTSP-камер, обновление cuframes-rtsp-source на host'е. Demuxer спроектирован переживать рестарт publisher'а без обрушивания FFmpeg pipeline'а.
Когда subscriber видит CUFRAMES_ERR_DISCONNECTED:
- Demuxer не возвращает
AVERROR_EOF. - Освобождает мёртвый subscriber и пробует
cuframes_subscriber_create()снова, с rate-limit'ом одна попытка в 2 секунды. - Пока идёт reconnect,
av_read_frame()возвращаетAVERROR(EAGAIN). Pipeline блокируется, но остаётся живым. - При успехе demuxer пишет в лог
cuframes: reconnected to '<key>'на уровнеINFOи возобновляет доставку frame'ов.
Это важно для long-running consumer'ов (NVR recorder'ов, RTSP-restreamer'ов, NVENC-composer'ов), которым иначе нужен был бы внешний supervisor чтобы рестартовать ffmpeg на каждый publisher-икни.
Если на disconnect реально нужен EOF — например, one-shot transcode который должен остановиться когда источник умер — обёртывай demuxer через -timeout или собственный watchdog. Built-in поведение — «ждать вечно», не «fail fast».
См. также
- Первый publisher — минимальный C-producer.
- Protocol reference — wire format и handshake.
- Фильтр
vf_cuda_grid— multi-camera композиция.