Files
cuframes-docs/site/i18n/ru/docusaurus-plugin-content-docs/current/integration/ffmpeg-demuxer.md
T
Claude Opus 7f45c36aa2 init
2026-05-26 23:23:25 +01:00

8.1 KiB
Raw Blame History

title, sidebar_position
title sidebar_position
FFmpeg cuframes:// demuxer 1

FFmpeg cuframes:// demuxer

cuframes поставляет два FFmpeg input demuxer'а, оба — патчем поверх upstream FFmpeg:

  • cuframes — подписывается на decoded NV12 frame ring и отдаёт его как rawvideo stream (один stream на URL).
  • cuframes_packets — подписывается на encoded packet ring и отдаёт его как h264 / hevc byte-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:

  1. Подключается к /run/cuframes/<key>.sock.
  2. Получает N POSIX file descriptors через SCM_RIGHTS (frame-слоты) плюс shm metadata header.
  3. Для cuframes: импортирует каждый FD как CUDA VMM allocation, объявляет один AV_PIX_FMT_NV12 stream на publisher'ском width / height / framerate.
  4. Для cuframes_packets: читает extradata (SPS/PPS для H264, VPS/SPS/PPS для HEVC) из handshake и объявляет один AV_CODEC_ID_H264 или AV_CODEC_ID_HEVC stream.

В 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».

См. также