6.5 KiB
title, sidebar_position
| title | sidebar_position |
|---|---|
| Frame ring vs Packet ring | 1 |
Frame ring vs Packet ring
cuframes даёт publisher'у два независимых ring buffer'а с разной семантикой и разной стоимостью:
- Frame ring — decoded NV12 (или другой pixel format), shared zero-copy через CUDA VMM.
- Packet ring — encoded H.264 / H.265 NAL units, shared через POSIX shared memory.
Это два разных канала на одной паре publisher↔subscriber. Можно использовать один, оба, или иметь несколько consumer'ов где одни читают frame ring, другие — packet ring.
Зачем два канала
Один publisher обычно обслуживает разные классы потребителей. AI-детектор и GPU-композитор хотят уже decoded GPU pointer (frame ring); NVR-recorder и replay-сервис хотят compact encoded stream (packet ring). Заставлять recorder декодировать ради того чтобы потом снова encode'нуть для сохранения — пустая работа.
flowchart LR
RTSP[RTSP camera] --> Dec[NVDEC decode]
Dec --> Pub[Publisher]
Pub -- frame ring<br/>CUDA VMM --> AI[AI inference<br/>GPU consumer]
Pub -- frame ring<br/>CUDA VMM --> Comp[GPU compositor<br/>CUDA filter]
Pub -- packet ring<br/>POSIX shm --> NVR[NVR / recorder<br/>mp4 mux]
Pub -- packet ring<br/>POSIX shm --> Replay[Replay / seek service]
Frame ring
Что: ring из N CUDA-allocated slot'ов (cuMemCreate(POSIX_FILE_DESCRIPTOR)), экспортируются через SCM_RIGHTS consumer'у, импортируются через cuMemImportFromShareableHandle. Consumer получает CUDA device pointer на ту же физическую HBM-память что и publisher.
Когда использовать:
- consumer работает на GPU и хочет данные as-is (AI inference, CUDA filter, NVENC re-encode на другом codec);
- латентность критична — между publish и consume hardware coherence, без encode/decode roundtrip;
- consumer декодировать сам не хочет.
Стоимость: ring_size × frame_size GPU-памяти на publisher. Для NV12 1920×1080 ring=4 это ≈ 12 MiB на publisher (VMM granularity на RTX 5090 — 2 MiB, реально ≈ 16 MiB). Consumer'ы memory не платят — это та же физическая память.
API:
cuframes_publisher_create(&cfg, &pub); // ring аллоцируется
cuframes_publisher_acquire(pub, &cuda_ptr); // получаем slot
// ... NVDEC / kernel пишут в cuda_ptr ...
cuframes_publisher_publish(pub, stream, pts_ns);
Packet ring
Что: ring из slot'ов с metadata (pts, dts, size, flags) + отдельная data-секция (default 8 MiB) в POSIX shm /dev/shm/cuframes-<key>-packets. Publisher закидывает туда encoded NAL units (Annex B byte stream).
Когда использовать:
- consumer декодирует сам (FFmpeg demuxer, recorder, на остальном GPU нет места);
- нужен compact stream для записи на диск;
- late subscriber должен сам resync'нуться от ближайшего keyframe — это семантика ring'а (см. protocol reference).
Стоимость: POSIX shm на host — data_size + ring_slots × 64 байта. На GPU расход нулевой.
Packet ring опционален и отдельно активируется на уже созданном publisher'е:
cuframes_publisher_create(&cfg, &pub);
cuframes_packet_ring_options_t pkt_opts = {
.ring_slots = 64,
.data_size = 8 * 1024 * 1024,
.max_packet_size = 2 * 1024 * 1024,
.codec_id = 27, // AV_CODEC_ID_H264
};
cuframes_publisher_enable_packets(pub, &pkt_opts);
cuframes_publisher_set_codec_extradata(pub, sps_pps, sps_pps_size);
// в цикле:
cuframes_publisher_publish_packet(pub, nal_data, nal_size,
pts_ns, dts_ns,
CUFRAMES_PKT_FLAG_KEY);
Аналогично на subscriber'е:
cuframes_subscriber_create(&cfg, &sub);
cuframes_subscriber_enable_packets(sub); // открывает второй SHM
cuframes_packet_t *pkt;
cuframes_subscriber_next_packet(sub, &pkt, -1);
Subscriber может включить любую комбинацию: только frame ring, только packet ring, оба сразу. Это два независимых SHM segment'а с разными magic.
Сравнение
| Frame ring | Packet ring | |
|---|---|---|
| Содержимое | Decoded NV12 / RGB / etc | Encoded H.264 / H.265 NAL |
| Транспорт | CUDA VMM + POSIX FD | POSIX shm |
| Sync mechanism | atomic seq + cuStreamSynchronize |
atomic seq (нет CUDA) |
| Latency publish→consume | sub-frame, без encode roundtrip | sub-frame, но consumer декодирует |
| Memory cost (publisher) | ring_size × frame_size GPU |
data_size host shm |
| Memory cost (consumer) | 0 (shared physical pages) | 0 (mmap same shm) |
| Требует CUDA на consumer | да | нет |
| Late join semantics | newest frame документирован | resync на last keyframe |
| Типичный use case | AI inference, GPU compositor | NVR recording, replay |
Можно ли один без другого
Да. Frame ring аллоцируется в cuframes_publisher_create — без него publisher вообще не существует. Packet ring опционален: если cuframes_publisher_enable_packets не вызвать, publisher просто не примет publish_packet, а subscriber на enable_packets получит CUFRAMES_ERR_NOT_FOUND.
Обратное (packet ring без frame ring) в текущем API не поддерживается — для pure encoded-only сценариев это TODO будущей версии.
Следующее
- Ownership modes — как выбрать кто аллоцирует ring.
- Synchronization — почему frame ring sync через stream sync, а не через CUDA events.
- Первый publisher — рабочий C-пример без packet ring.