Files
cuframes-docs/site/i18n/ru/docusaurus-plugin-content-docs/current/concepts/frame-vs-packet-ring.md
T
Claude Opus 7f45c36aa2 init
2026-05-26 23:23:25 +01:00

119 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Frame ring vs Packet ring
sidebar_position: 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'нуть для сохранения — пустая работа.
```mermaid
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:
```c
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](/docs/reference/protocol)).
**Стоимость:** POSIX shm на host — `data_size + ring_slots × 64 байта`. На GPU расход нулевой.
Packet ring **опционален и отдельно активируется** на уже созданном publisher'е:
```c
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'е:
```c
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](/docs/concepts/ownership-modes) — как выбрать кто аллоцирует ring.
- [Synchronization](/docs/concepts/sync-vmm-stream) — почему frame ring sync через stream sync, а не через CUDA events.
- [Первый publisher](/docs/getting-started/first-publisher) — рабочий C-пример без packet ring.