Skip to content

fix(whip,whep): make do-retransmission configurable to work around GStreamer RTX+hdrext crash - #663

Draft
wagenet wants to merge 3 commits into
Eyevinn:mainfrom
wagenet:wagenet/pr-a-do-retransmission
Draft

fix(whip,whep): make do-retransmission configurable to work around GStreamer RTX+hdrext crash#663
wagenet wants to merge 3 commits into
Eyevinn:mainfrom
wagenet:wagenet/pr-a-do-retransmission

Conversation

@wagenet

@wagenet wagenet commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Description

While diagnosing a WHIP Input -> WHEP Output flow (browser publishes via WHIP, another browser views via WHEP), we hit a hard process crash caused by an upstream gst-plugins-base bug: RTX (retransmission) combined with RTP header-extension aggregation trips an assertion in gst_rtp_base_depayload_handle_buffer (priv->hdrext_buffers not empty when hdrext_delayed is set). This was reproduced twice independently on two different code paths hitting the identical assertion, indicating a general invariant bug in the base class rather than something element-specific. A GStreamer upstream bug report is being filed separately.

RTX was previously hardcoded on for both whipserversrc (WHIP Input) and whepserversink (WHEP Output). This PR exposes do_retransmission as a block property (default true, matching upstream) on both, so it can be disabled as a workaround when this crash is hit, without losing the normal benefit of RTX (cheap resend of lost packets instead of a full PLI/keyframe cycle) for sessions that don't trigger it.

Related Issue

N/A — no existing issue; found and fixed live while testing the WHIP/WHEP flow. A GStreamer upstream bug report will be filed and linked here once available.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Checklist

  • I have run cargo fmt --all
  • I have run cargo clippy --workspace --all-targets -- -D warnings (backend crate is warning-free; the workspace-wide run is currently blocked by a pre-existing, unrelated frontend/src/themes.rs lint failure — confirmed via cargo clippy -p strom --all-targets that no new warnings were introduced)
  • I have run cargo test --workspace (all tests pass, including 6 new unit tests for the do_retransmission property parsing)
  • I have updated documentation as needed (not applicable — internal implementation detail, no navigational docs affected)
  • I have added tests for new functionality

🤖 Generated with Claude Code

…treamer RTX+hdrext crash

Diagnosed a hard process crash while testing a WHIP Input -> WHEP Output
flow: an upstream gst-plugins-base bug where RTX (retransmission) combined
with RTP header-extension aggregation trips an assertion in
gst_rtp_base_depayload_handle_buffer (priv->hdrext_buffers not empty when
hdrext_delayed is set). Reproduced twice independently on two different
code paths hitting the identical assertion, indicating a general invariant
bug in the base class rather than something element-specific. A GStreamer
upstream bug report is being filed separately.

RTX was previously hardcoded on for both whipserversrc (WHIP Input) and
whepserversink (WHEP Output). Expose `do_retransmission` as a block
property (default true, matching upstream) on both, so it can be disabled
as a workaround when this crash is hit, without losing the normal benefit
of RTX (cheap resend of lost packets instead of a full PLI/keyframe cycle)
for sessions that don't trigger it.

Extracted `parse_do_retransmission` as a pure, independently testable
function on both blocks, with unit tests for the default and both explicit
values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wagenet added a commit to wagenet/open-live that referenced this pull request Aug 20, 2026
RTX combined with RTP header-extension aggregation trips an assertion in
gst_rtp_base_depayload_handle_buffer ('priv->hdrext_delayed' should be
NULL, gstrtpbasedepayload.c:897), taking the whole process down. It has
been hit on two independent code paths, so it looks like an invariant bug
in the base class rather than something element-specific.

Set do_retransmission=false on both the WHIP input and the WHEP output
until the upstream bug is fixed.

Requires Eyevinn/strom#663, which exposes do_retransmission as a block
property on builtin.whip_input and builtin.whep_output. Without it the
property is not read and this change has no effect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
srperens
srperens previously approved these changes Aug 21, 2026

@srperens srperens left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retracted — this review was misattributed.

The original text of this review described the changes in #665 (jitterbuffer_latency_ms on WHIP Input), not this PR. It was posted here by an automated reviewer that crossed two PRs in the same run, so it never verified this PR's diff at all. The text has been replaced to stop it being read as a review of do-retransmission.

This PR has not yet had a valid review. It will be re-reviewed under a stricter protocol that requires an inline citation — code path, commit, doc section or test — for every claim, and that verifies the PR's own description claim by claim. Nothing here should be taken as a signal about this change's correctness either way.

The original text is retained outside GitHub for the record.

srperens and others added 2 commits August 21, 2026 13:50
Picks up the rust-toolchain.toml pin (1.97.1) and CI package list from Eyevinn#678, which
clear the float_literal_f32_fallback and result_large_err failures on this branch.
Resolves the conflict with Eyevinn#665 (jitterbuffer_latency_ms). Both changes add a
new property to the WHIP Input block at the same five insertion points, so git
read the additions as competing. Every conflict was resolved by keeping both:

- `parse_do_retransmission` / `parse_jitterbuffer_latency_ms` helpers
- both `let` bindings in build_whipserversrc_config
- both fields in the WhipEndpointConfig literal and in the struct definition
- both ExposedProperty entries in the block metadata
- both sets of unit tests

No behaviour was changed on either side. Verified in a scratch worktree:
`cargo check` clean, `cargo test --workspace` all green (512 lib tests, which
includes both features' unit tests), `cargo clippy --workspace --all-targets
-- -D warnings` exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@srperens

Copy link
Copy Markdown
Collaborator

Review: the WHEP half of this change cannot work around the crash it cites

Verified against the PR diff (commit range of wagenet/pr-a-do-retransmission), origin/main at 4a36815, and gst-inspect-1.0 on GStreamer 1.28.6. The WHIP half looks correct; the WHEP half does not.

1. whepserversink never depayloads, so do-retransmission there cannot prevent a depayloader assertion

The stated motivation is an assertion in gst_rtp_base_depayload_handle_buffer (priv->hdrext_buffers non-empty while hdrext_delayed is set) — i.e. inside a depayloader. But whepserversink has no RTP input and contains no depayloader:

$ gst-inspect-1.0 whepserversink
SINK template: 'video_%u'
  Capabilities: video/x-raw, video/x-raw(memory:CUDAMemory|GLMemory|NVMM|D3D11Memory|VAMemory),
                video/x-vp8, video/x-h264, video/x-vp9, video/x-h265, video/x-av1
SINK template: 'audio_%u'
  Capabilities: audio/x-raw, audio/x-opus

It takes raw or encoded media and payloads it. The property semantics confirm the direction:

$ gst-inspect-1.0 whipserversrc
  do-retransmission : Send retransmission events upstream when a packet is late

$ gst-inspect-1.0 whepserversink
  do-retransmission : Whether the element should offer to honor retransmission requests
  • On whipserversrc it is the receive side: the jitterbuffer requests RTX, and the recovered packets pass through the depayloader that webrtcsrc instantiates internally. That is where the cited assertion can bite, so the WHIP property is a plausible workaround.
  • On whepserversink it is the send side: honor NACKs from the viewer's browser. No depayloader exists on that path inside the Strom process. Neither whep.rs nor whip.rs adds one (grep -n depay backend/src/blocks/builtin/whep.rs → no matches).

So do_retransmission=false on WHEP Output cannot stop that crash.

2. The incorrect causal claim ships in operator-facing text

whep.rs, the new ExposedProperty:

"Disable only for diagnostics — a known GStreamer bug can crash the process when RTX is combined with RTP header-extension aggregation; without it, packet loss forces a full keyframe request instead of a cheap resend."

The same claim is added to the code comment replacing whep.rs:1033 (whepserversink.set_property("do-retransmission", true); on main). This tells an operator to disable RTX for every viewer — making each packet loss escalate to PLI plus a full keyframe — in pursuit of a crash the switch does not affect.

3. The new tests do not guard the change

All six tests exercise only parse_do_retransmission against itself:

assert!(parse_do_retransmission(&raw_props(&[])));

None asserts that the parsed value reaches set_property("do-retransmission", ...) on the element, or that it survives the WhipEndpointConfig -> create_whipserversrc_for_session hop (the only non-trivial plumbing in the diff). Revert the fix and the helper and its tests disappear together, so nothing fails. Per the repo's test rules that is a demonstration, not a regression guard.

4. The workaround itself is not stated as verified

The description says the crash "was reproduced twice independently on two different code paths hitting the identical assertion", but does not state that setting do-retransmission=false was observed to stop it. Worth confirming before the property is justified as a workaround at all.

Suggested resolution

  • Keep do_retransmission on WHIP Input: right side of the crash, correct semantics, default true matches the upstream default.
  • Drop it from WHEP Output, or keep it purely as a tuning knob with all crash-workaround language removed from both the property description and the code comment.
  • Add one test that shows the value actually propagates to the element, so the change is guarded.

For the record, the duplicated parse_do_retransmission helper in both files is not an objection — the inline PropertyValue::Bool(b) => Some(*b) pattern is the established idiom in this codebase (aes67.rs, mpegtssrt_input.rs, compositor.rs, efpsrt_input.rs, and others).

@srperens

Copy link
Copy Markdown
Collaborator

Tested this live on macOS today. The crash is now tracked in #685 with the log that locates it: the asserting depayloader is rtph264depay0 in the flow's main pipeline, autoplugged by Strom's own decodebin_video_0. whipserversrc hands out application/x-rtp and Strom does the depayloading.

That confirms the review above from measurement rather than reasoning: the WHIP half of this PR is on the right side of the bug, the WHEP half cannot affect it.

One thing worth trying before trading RTX away: the header extension in play is TWCC, and Strom turns it up itself (Set twcc-feedback-interval=200ms on rtpsession1, whip.rs:626-635). Dropping that is a cheaper mitigation if it is sufficient. Both that and do-retransmission=false were scaffolded today but never exercised — the assertion is intermittent and a clean baseline run stopped reproducing it. See #685 for the reproduction status.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants