119 lines
6.5 KiB
Markdown
119 lines
6.5 KiB
Markdown
---
|
||
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.
|