v0.2: encoded packet ring #4

Merged
gx merged 6 commits from v0.2-encoded-packets into main 2026-05-19 17:47:10 +01:00
Owner

Draft PR — design + incremental implementation для issue #2 (encoded packet sharing).

Что в этом PR

Step 1: Protocol design (docs/protocol.md §10) — committed ad75aa9

Полная wire-protocol spec:

  • Отдельный SHM /dev/shm/cuframes-<key>-packets (variable-length byte buffer + slot index)
  • Backward-compat: proto_version=2 publishers принимают v1 subscribers (frames-only)
  • HELLO_REQ/HELLO_RESP extension через reserved bytes — не ломает v1 ABI
  • Codec extradata (SPS/PPS) в shared header — fixed 4 KB
  • Late subscriber → keyframe-aligned start через initial_packet_seq в SUBSCRIBE_RESP
  • Seqlock pattern для защиты subscriber от overrun mid-read
  • API extension: cuframes_publisher_publish_packet, cuframes_subscriber_next_packet, cuframes_subscriber_get_codec_params
  • 4 новых error codes

Steps 2-6: Implementation (TBD)

  • Step 2 (#90): libcuframes/src/packet_ring.c — bytes-backed ring impl (POSIX SHM, atomic head/tail, seqlock read)
  • Step 3 (#91): C API extension в cuframes.h + RAII wrappers cuframes.hpp
  • Step 4 (#92): cuframes-rtsp-source — дублирует AVPacket в packet ring до decoder
  • Step 5 (#93): libavformat/cuframes_packetsdec.c в ffmpeg-patched repo
  • Step 6 (#94): stress tests, integration docs update, BENCHMARKS

Что review сейчас (design)

Specifically прошу feedback на:

  1. Sizing defaults в §10.3:

    • packet_ring_slots = 64 (≈ 2 sec @ 30 fps)
    • packet_data_size = 8 MB
    • max_packet_size = 2 MB

    Для H.265 4K cameras с большими IDR (1-2 MB) этого может не хватать. Стоит ли поднять defaults?

  2. data_offset semantics в §10.5:

    • Сейчас "absolute byte cursor", subscriber делает % data_size
    • Альтернатива: pre-modulo, проще для subscriber но publisher теряет debug-friendly absolute counter
    • Какой предпочесть?
  3. codec_extradata_size = 4096 bytes max в §10.4:

    • Для H.264 SPS+PPS ≈ 50-100 bytes
    • Для H.265 VPS+SPS+PPS до ~200 bytes
    • 4 KB headroom — достаточно? Или избыточно?
  4. Sub-stream selection — отложено в v0.3 (§10.16). Сейчас один key = один stream. Но Dahua/Hikvision камеры дают main+sub stream через тот же RTSP. Стоит ли в v0.2 включать?

  5. Audio packets — тоже v0.3 (§10.16). Но если Frigate record хочет mux'ить audio (некоторые users) — нужно сразу заложить?

  6. Versioning при codec change mid-stream — текущий design требует publisher destroy+recreate. Возможна ли graceful invalidation (например, bump generation_id в SHM)?

Acceptance (для merge не-draft когда impl будет готова)

  • Stress test 1 publisher × 2 subscribers (frames + packets) × 2000 packets @ 30 fps — 0 lost (steady-state)
  • Slow subscriber detect overrun → resync на last keyframe
  • Frigate Dockerfile build с обновлённым patched FFmpeg — ffmpeg -formats | grep cuframes_packets шows demuxer
  • Backward-compat verified: v0.1 subscriber подключается к v0.2 publisher → получает frames только
  • Production deploy: Frigate cuframes://<key> для detect + cuframes_packets://<key> для record — 1 RTSP к камере вместо 2

Связано

  • #2 — feature request
  • docs/protocol.md §10 — full spec
**Draft PR** — design + incremental implementation для issue #2 (encoded packet sharing). ## Что в этом PR ### ✅ Step 1: Protocol design (`docs/protocol.md` §10) — committed `ad75aa9` Полная wire-protocol spec: - Отдельный SHM `/dev/shm/cuframes-<key>-packets` (variable-length byte buffer + slot index) - Backward-compat: `proto_version=2` publishers принимают v1 subscribers (frames-only) - HELLO_REQ/HELLO_RESP extension через reserved bytes — **не ломает v1 ABI** - Codec extradata (SPS/PPS) в shared header — fixed 4 KB - Late subscriber → keyframe-aligned start через `initial_packet_seq` в SUBSCRIBE_RESP - **Seqlock pattern** для защиты subscriber от overrun mid-read - API extension: `cuframes_publisher_publish_packet`, `cuframes_subscriber_next_packet`, `cuframes_subscriber_get_codec_params` - 4 новых error codes ### ⏳ Steps 2-6: Implementation (TBD) - [ ] **Step 2** (#90): `libcuframes/src/packet_ring.c` — bytes-backed ring impl (POSIX SHM, atomic head/tail, seqlock read) - [ ] **Step 3** (#91): C API extension в `cuframes.h` + RAII wrappers `cuframes.hpp` - [ ] **Step 4** (#92): `cuframes-rtsp-source` — дублирует AVPacket в packet ring до decoder - [ ] **Step 5** (#93): `libavformat/cuframes_packetsdec.c` в ffmpeg-patched repo - [ ] **Step 6** (#94): stress tests, integration docs update, BENCHMARKS ## Что **review** сейчас (design) Specifically прошу feedback на: 1. **Sizing defaults** в §10.3: - `packet_ring_slots = 64` (≈ 2 sec @ 30 fps) - `packet_data_size = 8 MB` - `max_packet_size = 2 MB` Для H.265 4K cameras с большими IDR (1-2 MB) этого может не хватать. Стоит ли поднять defaults? 2. **`data_offset` semantics** в §10.5: - Сейчас "absolute byte cursor", subscriber делает `% data_size` - Альтернатива: pre-modulo, проще для subscriber но publisher теряет debug-friendly absolute counter - Какой предпочесть? 3. **`codec_extradata_size` = 4096 bytes max** в §10.4: - Для H.264 SPS+PPS ≈ 50-100 bytes - Для H.265 VPS+SPS+PPS до ~200 bytes - 4 KB headroom — достаточно? Или избыточно? 4. **Sub-stream selection** — отложено в v0.3 (§10.16). Сейчас один `key` = один stream. Но Dahua/Hikvision камеры дают main+sub stream через тот же RTSP. Стоит ли в v0.2 включать? 5. **Audio packets** — тоже v0.3 (§10.16). Но если Frigate record хочет mux'ить audio (некоторые users) — нужно сразу заложить? 6. **Versioning при codec change mid-stream** — текущий design требует publisher destroy+recreate. Возможна ли graceful invalidation (например, bump `generation_id` в SHM)? ## Acceptance (для merge не-draft когда impl будет готова) - Stress test 1 publisher × 2 subscribers (frames + packets) × 2000 packets @ 30 fps — 0 lost (steady-state) - Slow subscriber detect overrun → resync на last keyframe - Frigate Dockerfile build с обновлённым patched FFmpeg — `ffmpeg -formats | grep cuframes_packets` шows demuxer - Backward-compat verified: v0.1 subscriber подключается к v0.2 publisher → получает frames только - Production deploy: Frigate `cuframes://<key>` для detect + `cuframes_packets://<key>` для record — 1 RTSP к камере вместо 2 ## Связано - #2 — feature request - `docs/protocol.md` §10 — full spec
gx added 1 commit 2026-05-19 16:05:19 +01:00
docs(protocol): v0.2 — encoded packet ring spec (§10)
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 1m35s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 1m39s
ad75aa9624
Полный wire-protocol spec для encoded packet ring:
- Отдельный SHM /dev/shm/cuframes-<key>-packets (variable-length)
- Backward-compat с v1: proto_version=2 publishers принимают v1 subscribers
- HELLO_REQ/HELLO_RESP extension через reserved bytes — без слома v1 layout
- Codec extradata (SPS/PPS) в shared header
- Late subscriber → keyframe-aligned start (initial_packet_seq)
- Seqlock pattern для защиты от overrun mid-read
- API extension: publish_packet, next_packet, get_codec_params
- 4 новых error codes (OVERSIZED, NO_PACKET_RING, NO_CODEC_PARAMS, PACKET_OVERRUN)

Связано: #2
gx added 1 commit 2026-05-19 16:11:53 +01:00
feat(libcuframes): packet ring buffer implementation (v0.2 Step 2)
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 1m37s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 1m21s
bd7fd95fef
Реализация encoded packet ring per docs/protocol.md §10.

Files:
- internal.h: cuframes_pkt_slot_t (64b packed), cuframes_pkt_header_t
  (0x1040 fixed header), cuframes_pkt_ring_t handle, constants for
  default sizing, packet flags, helper inline functions for slot/data
  pointer arithmetic.
- packet_ring.c (new, ~290 LOC): create/open/publish/read/destroy.
  Stale recovery симметрично frames SHM (pid liveness check). Seqlock
  pattern для subscriber защиты от overrun mid-read (post-check seq
  после copy). Wraparound memcpy helpers для variable-length data ring.
- utils.c: cuframes_internal_pkt_shm_name helper + strerror entries.
- cuframes.h: 4 новых error codes (PACKET_OVERSIZED, NO_PACKET_RING,
  NO_CODEC_PARAMS, PACKET_OVERRUN).
- CMakeLists.txt: src/packet_ring.c в sources.

API внутренний (cuframes_internal_pkt_ring_*) — publicly exposed
функции будут в Step 3 (cuframes.h API extension).

Связано: #2 (v0.2), PR #4 (draft).
gx added 1 commit 2026-05-19 16:27:06 +01:00
feat(api): public C API для packet ring (v0.2 Step 3)
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 1m36s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 1m24s
4cb0321a6f
Публичные функции в include/cuframes/cuframes.h:
- cuframes_publisher_enable_packets(opts)  — активирует ring на
  существующем publisher'е; default sizing (64 slots, 8MiB data, 2MiB max).
- cuframes_publisher_set_codec_extradata(data, size) — SPS/PPS bytes.
- cuframes_publisher_publish_packet(data, size, pts, dts, flags)
- cuframes_subscriber_enable_packets()  — открывает packet shm у subscriber'а.
- cuframes_subscriber_next_packet(pkt_out, timeout_ms) с поллингом 1ms.
- cuframes_packet_data/size/pts/dts/flags/seq accessors.
- cuframes_subscriber_release_packet()
- cuframes_subscriber_get_codec_params()

Internal:
- producer.c: расширена struct cuframes_publisher (has_pkt_ring,
  max_packet_size, pkt_ring); cleanup в destroy(); enable_packets()
  bump'ит proto_version=2 в frames header.
- consumer.c: расширена struct cuframes_subscriber (has_pkt_ring,
  pkt_ring, last_packet_seq, packet_obj); single-packet pattern (как
  frame_obj — busy flag, переиспользование buffer). enable_packets()
  стартует с last_keyframe_seq-1 для late subscriber resync. На
  PACKET_OVERRUN автоматически resync на last_keyframe и возвращает
  ERR наружу для signalling discontinuity.

Связано: #2, PR #4.
gx added 1 commit 2026-05-19 16:45:30 +01:00
feat(rtsp-source): packet ring publishing (v0.2 Step 4)
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 1m39s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 1m44s
8cd96721ff
- cuframes::Publisher (C++ wrapper): добавлены enable_packets(),
  set_codec_extradata(), publish_packet() методы.
- cuframes-rtsp-source: новый CLI flag --enable-packet-ring. При его
  установке после opening stream — pub.enable_packets(codec_id) +
  set_codec_extradata из vstream->codecpar->extradata.
- В main loop: после av_read_frame, до avcodec_send_packet, packet
  публикуется в packet ring с конверсией pts/dts из stream_tb в ns,
  AV_PKT_FLAG_KEY/CORRUPT/DISCONTINUITY → CUFRAMES_PKT_FLAG_*.

Тест:
  cuframes-rtsp-source --rtsp rtsp://... --key cam1 --enable-packet-ring
  # frames consumer'ы продолжают работать через cuframes:// (как v0.1)
  # record consumer'ы могут brать packets через cuframes_packets:// (Step 5)

Связано: #2, PR #4.
gx added 1 commit 2026-05-19 17:08:19 +01:00
test+docs: packet ring stress test + Frigate dual-input guide (v0.2 Step 6)
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Failing after 3m43s
build / ffmpeg filter patch (out-of-tree) (pull_request) Has been skipped
fca07bf669
Тесты:
- libcuframes/tests/test_packet_ring.c — 2 scenarios:
  1) normal flow: 1 pub × 1 sub × 2000 packets, varied sizes, GOP=30,
     payload integrity check (seq в первых 8 байтах + pattern). PTS
     monotonicity, first KEY seq, нет data errors.
  2) slow consumer (10ms delay): publisher 200 fps, subscriber должен
     detect OVERRUN, library resync на keyframe — verify received >10
     даже на сильно медленном консьюмере.
- libcuframes/tests/CMakeLists.txt: add_test packet_ring_basic.

Docs:
- CHANGELOG.md: новая [Unreleased] секция с full v0.2 highlights и
  явно declared limitations (sub-stream, audio, codec change → v0.3).
- docs/integrations/frigate.md: новая секция "v0.2: dual-input (detect +
  record через один RTSP)" с config example, requirements, trade-offs.

Связано: #2, PR #4. Step 6 (final) перед снятием draft.
gx added 1 commit 2026-05-19 17:31:35 +01:00
ci: retry + explicit Node 20 version check в bootstrap
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 6m24s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 6m21s
2b94742df4
Symptom (run #1826 fail на u4-runner):
  Bootstrap step молча установил Node 12 (Ubuntu default) вместо Node 20
  из NodeSource → actions/checkout@v4 не парсится (ES2022 static blocks).

Cause:
  curl ... setup_20.x на slow network (u4 через VPN) timeout/fail silently,
  apt install fallback на default ubuntu nodejs (Node 12). Без error.

Fix:
  - curl --retry 3 --retry-delay 5 --connect-timeout 30
  - retry-loop на NodeSource setup (3 попытки)
  - явная verification major version >= 18 после install, fail с exit 1
    если установился Node < 18

Применяется к обоим jobs (cmake-build и filter-build).

Связано: PR #4 (v0.2), run #1826 fail.
gx marked the pull request as ready for review 2026-05-19 17:46:59 +01:00
gx merged commit 5536d23992 into main 2026-05-19 17:47:10 +01:00
gx deleted branch v0.2-encoded-packets 2026-05-19 17:47:10 +01:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gx/cuframes#4