Managed/mount push_data parity + JVM panic-safety fold-ins (chunk #2)#40
Conversation
…rip test The data round-trip test referenced a non-existent DemuxEvent.Unknown variant (the real one is DemuxEvent.UnknownSample), so the consumer thread died on AttributeError after one event and the assertion only checked that *some* event flowed — a regression dropping every data record would still pass. Rework the test to collect the UnknownSample events on the data PID and assert byte-faithful payload round-trip for both push_data and push_data_to, plus PTS fidelity (carries_pts=True ⇒ non-zero pushed PTS preserved) and PID/stream_type. Also fix the close ordering: close the receiver before the sender so the consumer unblocks via the cancel handle while parked in recv_event, instead of letting a sender-close break the link and wedge the managed receiver in a non-cancellable re-accept (which would HANG on a dropped record rather than fail loud).
There was a problem hiding this comment.
Pull request overview
This PR closes the remaining “private-data stream push” parity gap by adding push_data / push_data_to (and corresponding handle accessors) to the live-shell APIs that previously only accepted data-stream configs: the SRT ManagedMuxSender and RTSP MountHandle. It also folds in several JVM JNI hardening changes (panic/exception behavior, strict handle decoding, and array length validation), and updates ABI/docs/ratchets accordingly.
Changes:
- Add Rust
tst-rtpMountHandle::{push_data,push_data_to,data_handles}plus unit tests and public-api baseline update. - Extend C/Python/JVM bindings to expose the same data-push family for managed SRT senders and RTSP mounts; add live-loopback and handle-forgery rejection tests.
- JVM fold-ins: strict
jlong→u32handle decoding for older*Tonatives,Muxer.nPullJNI-failure paths now throwRuntimeException, and sibling-array length checks inbuild_muxer_config_from_arrays.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/coverage/surface-manifest.toml | Adds exemptions for new MountHandle data APIs to keep surface coverage ratchets in sync. |
| scripts/check/c/abi-rustdoc-coverage.sh | Allowlists new C ABI symbols for coverage checks. |
| crates/tst-rtp/src/rtsp/server/mount.rs | Implements push_data / push_data_to / data_handles on MountHandle and adds Rust tests. |
| crates/tst-rtp/public-api.txt | Updates public API baseline for the new MountHandle methods. |
| CHANGELOG.md | Updates unreleased notes: removes “not yet exposed” caveats and documents JVM hardening + ABI bump. |
| bindings/python/tests/test_srt_managed_convenience.py | Adds managed SRT loopback fidelity tests for private-data push + handle behavior. |
| bindings/python/tests/test_rtsp_server.py | Adds mount-side data push/handles tests and stats assertions. |
| bindings/python/src/srt/managed_convenience.rs | Exposes ManagedMuxSender.push_data/push_data_to/data_handle in Python. |
| bindings/python/src/rtp/server.rs | Exposes MountHandle.push_data/push_data_to/data_handle/data_handles in Python. |
| bindings/python/python/tstrans/srt.pyi | Updates type stubs for new managed SRT data APIs. |
| bindings/python/python/tstrans/rtp.pyi | Updates type stubs for new mount data APIs. |
| bindings/jvm/src/test/java/org/tstrans/srt/SrtManagedLiveTest.java | Extends live SRT managed test to include private-data round-trip + forged-handle rejection. |
| bindings/jvm/src/test/java/org/tstrans/rtp/RtspServerTest.java | Adds mount data push/stats test and strict-handle-decode regression coverage. |
| bindings/jvm/src/srt/mux_sender.rs | Switches older *To natives to strict handle decode (no truncating as u32). |
| bindings/jvm/src/srt/managed_convenience.rs | Adds managed sender pushData/pushDataTo/dataHandle natives + strict decode. |
| bindings/jvm/src/rtp/server.rs | Adds mount pushData/pushDataTo/dataHandle/dataHandles natives + strict decode. |
| bindings/jvm/src/rtp/mux_sender.rs | Switches older *To natives to strict handle decode. |
| bindings/jvm/src/mpegts/muxer.rs | Adds sibling-array length checks; changes nPull JNI failure paths to RuntimeException. |
| bindings/jvm/src/main/java/org/tstrans/srt/ManagedMuxSender.java | Adds Java surface for pushData/pushDataTo/dataHandle. |
| bindings/jvm/src/main/java/org/tstrans/rtp/MountHandle.java | Adds Java surface for pushData/pushDataTo/dataHandle/dataHandles. |
| bindings/c/tests/receiving/live_pair.rs | Adds managed-sender SRT data-stream live loopback test in C. |
| bindings/c/include/tstrans.h | Bumps ABI minor to 13; adds new managed-sender + mount data push declarations and type placement. |
| bindings/c/core/src/sender/mux_sender.rs | Implements new managed-sender C ABI *_send_data{,_to} entrypoints. |
| bindings/c/core/src/rtsp/server/mount.rs | Implements new mount C ABI *_push_data{,_to} entrypoints + null-handle tests. |
| bindings/c/core/src/lib.rs | Updates ABI minor constant/docs to 13. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| int64_t pts_90khz); | ||
| #endif | ||
|
|
||
| #if (defined(TST_HAS_RTP) && defined(TST_HAS_RTP)) |
There was a problem hiding this comment.
Thanks — correct that the guard is redundant, but this is a pre-existing cbindgen generation artifact, not something this PR introduced. cbindgen ANDs the module-level #[cfg(feature = "rtp")] (the rtsp/rtp modules are gated in lib.rs) with the per-function #[cfg(feature = "rtp")], yielding TST_HAS_RTP && TST_HAS_RTP. It's present on all 83 RTP prototypes (and equivalently on TCP/UDP/RIST — 190 sites total); SRT is the only single-guarded surface because its fns rely solely on the module gate. Since tstrans.h is generated and the header_drift ratchet requires it to match a fresh cbindgen regen exactly, it can't be hand-edited here, and single-guarding only these 2 new mount fns would make the header less uniform. Tracked as a dedicated follow-up: strip the redundant per-function cfgs across all four transport surfaces (matching SRT) + regenerate, with per-feature-mode gating verification, as its own PR. Declining for this PR.
| int64_t pts_90khz); | ||
| #endif | ||
|
|
||
| #if (defined(TST_HAS_RTP) && defined(TST_HAS_RTP)) |
There was a problem hiding this comment.
Thanks — correct that the guard is redundant, but this is a pre-existing cbindgen generation artifact, not something this PR introduced. cbindgen ANDs the module-level #[cfg(feature = "rtp")] (the rtsp/rtp modules are gated in lib.rs) with the per-function #[cfg(feature = "rtp")], yielding TST_HAS_RTP && TST_HAS_RTP. It's present on all 83 RTP prototypes (and equivalently on TCP/UDP/RIST — 190 sites total); SRT is the only single-guarded surface because its fns rely solely on the module gate. Since tstrans.h is generated and the header_drift ratchet requires it to match a fresh cbindgen regen exactly, it can't be hand-edited here, and single-guarding only these 2 new mount fns would make the header less uniform. Tracked as a dedicated follow-up: strip the redundant per-function cfgs across all four transport surfaces (matching SRT) + regenerate, with per-feature-mode gating verification, as its own PR. Declining for this PR.
Every extern-C fn in the rtp/rtsp/udp/tcp/rist C surfaces carried a per-function #[cfg(feature = "X")] on top of its already-#[cfg]-gated module. cbindgen ANDs the module-path cfg with the item cfg, emitting a redundant `#if (defined(TST_HAS_X) && defined(TST_HAS_X))` double-guard on 190 prototypes in tstrans.h (RTP 83, TCP 39, UDP 34, RIST 34). The SRT and HLS surfaces never had per-fn cfgs and already produced single guards. Remove the 190 redundant per-function cfgs (pure deletions; every removed line exactly duplicated its module's feature — no compound cfg was touched) and regenerate tstrans.h, collapsing all 190 to `#if defined(TST_HAS_X)`. The module-level gate in lib.rs remains the sole guard, so symbol gating is unchanged: verified by nm (--no-default-features exports zero transport symbols; --features udp exports the 34 udp symbols and no rtp/tcp/rist) and by compiling all eight feature modes. The committed header's function set is byte-for-byte identical apart from the guard collapse. Also refresh the now-stale explanatory comments in header-conditional-sections.sh, which described the per-fn cfg as the gating mechanism; the module cfg is. Resolves the cbindgen double-guard comments raised on PR #40.
Chunk #2 of the 4-chunk polish/parity pass (chunk #1 was the JNI panic-safety sweep, #39). Closes the last functional gap from the private-data arc: the
ManagedMuxSender(srt auto-reconnect) andMountHandle(rtp RTSP server) shells accepted data-stream configs (W5) but had nopush_data/push_data_to, while every other payload kind did. Now closed across C / Python / JVM, mirroring thepush_klvfamily exactly, plus three JVM hardening fold-ins and a CHANGELOG entry for chunk #1.Waves
MountHandle::push_data/push_data_to/data_handles(mirrorpush_klv/klv_handles). Updates thetst-rtpcargo public-apibaseline (the one non-binding change) +[[exempt]]surface-manifest rows.tst_managed_mux_sender_send_data/_to(viawith_inner_ref) andtst_rtsp_mount_push_data/_to(ffi_catch+mount_error_to_code); regeneratedtstrans.h; 4abi-rustdoc-coverageallowlist entries. Additive — no symbol removed, no signature/layout change.ManagedMuxSender(srt) +MountHandle(rtp) gainpush_data/push_data_to/data_handle()(+MountHandle.data_handles()list accessor for parity);.pyistubs + tests (byte-faithful round-trip incl. PTS fidelity over a live SRT loopback).ManagedMuxSender+MountHandlegainpushData/pushDataTo/dataHandle()(mount alsodataHandles()) natives (each injni_catch) + Java; new*Tonatives use the strict handle decode. Live-loopback byte-faithful test + forged/negative-handle rejection tests.JVM fold-ins (W5 follow-ups)
Muxer.nPullJNI-bridge failure paths now throw an uncheckedRuntimeException(was a checkedMuxException), keeping the hotpull()drain method free of athrowsclause — matches the W5nDataHandlesprecedent.*Tonatives switch the truncatingas u32decode to the strictu32::try_from(...).and_then(try_from_raw)chain (reject out-of-range / negative handles instead of silently truncating).build_muxer_config_from_arrayslength-checks the four stream sibling arrays (prevents a panic-across-FFI on a mismatched-array marshalling bug).CHANGELOG
Drops the "not yet exposed: MountHandle / ManagedMuxSender data push families" caveats in the v0.2.0
[Unreleased]data-stream entries; adds aChangedentry covering chunk #1 panic-safety (JVM panics now surface asRuntimeExceptioninstead of aborting the JVM) + these fold-ins.Verification (all green locally)
fmt; clippy
--workspace --all-targets -D warnings;cargo test --workspaceboth--all-featuresand--no-default-features(0 failures);cargo public-apiclean across all 8 ratcheted crates (tst-rtp baseline updated);#[non_exhaustive]count 265 == BASELINE; all ~44 bash ratchets (incl. jni-catch-coverage, abi-rustdoc-coverage, surface-manifest, header_drift); fuzz check; platform-capabilities. JVM gradle suite + Python pytest green; managed/mount data round-trips byte-faithful.