Skip to content

fix(whep_output): strip the offered level from every viewer's video chain - #899

Merged
srperens merged 2 commits into
Eyevinn:mainfrom
wagenet:wagenet/whep-level-above-3-1
Oct 19, 2026
Merged

srperens merged 2 commits into
Eyevinn:mainfrom
wagenet:wagenet/whep-level-above-3-1

Conversation

@wagenet

@wagenet wagenet commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #896. Its commit is the first one here; review the commits after it. This PR waits for #896 to merge.

A WHEP viewer in Chrome gets no video when the block is fed pre-encoded H.264 above level 3.1. That covers 1080p (level 4) and 720p at 50/60 fps (level 3.2), on every build and in every stream order. Chrome offers level 3.1 on every H.264 payload type. webrtcsink puts that level and all levels below it on the capsfilter in front of each viewer's payloader, so the session fails at the first keyframe with not-negotiated.

Raw input the block encodes itself fails the same way, without an error: the sink's encoder reads the same level list, and x264enc refuses 1080p (Frame size larger than level 31 allows), so the viewer gets nothing.

The fix adds level to the fields whep/profile_filter.rs strips, next to the profile fields that #896 already removes. A pre-encoded source whose level rises mid-stream still stops the flow, with or without this PR; that is #900.

The decision in this PR

This sends above the level the viewer declared. RFC 6184 does not allow that, even when both sides set level-asymmetry-allowed=1: the stream to the offerer must use the highest level the offerer can receive, and Chrome's offer has no max-recv-level, so that is 3.1. The alternative is that no Chrome viewer ever gets pre-encoded H.264 above 720p30. Refusing with an operator error would make that permanent, so this PR strips the level instead.

Evidence

Real Chrome 153 on macOS, headless, driven by Playwright. One Strom binary, where the only difference between arms was an environment switch that kept the level field. The flow was videotestsrc → x264enc (High) → h264parse → WHEP Output with one viewer:

Stream level kept level stripped
1920x1080@30, High, level 4 0/3 got video (not-negotiated) 3/3 decoded 1920x1080 at 30 fps
1280x720@60, High, level 3.2 0/3 3/3 decoded 1280x720 at 59-61 fps
  • The answer still carried profile-level-id=42001f.
  • In one extra run with capture permission granted, Chrome reported decoderImplementation: ExternalDecoder (VideoToolboxVideoDecoder), so the macOS hardware decoder took the level 4 stream.
  • Firefox and Safari were not tested (neither could be automated on this machine).

Tests

New tests in whep_output_profile_filter_test.rs. Each posts a Chrome-shaped offer with level 3.1, and asserts that the viewer's payloader pushed buffers and was given the expected level:

  • level_3_1_offer_gets_720p60_video: pre-encoded 720p60 High from x264enc (level 3.2).
  • level_3_1_offer_gets_1080p_video_encoded_by_the_block: raw 1080p30, encoded inside the block (level 4).

Revert check: with only the "level" entry removed, both new tests failed 3/3 (0 buffers payloaded), and passed 3/3 with the fix. That held with the block's encoder left at the macOS default (vtenc) and with vtenc ranked out so x264enc encodes, as on Linux CI.

  • Where it ran: macOS with GStreamer 1.28.6, and the Check (Linux) CI job; both with STROM_REQUIRE_GST_PLUGINS=1. All three tests in the file ran and passed in CI.

Opened by Claude (Claude Code) on behalf of @wagenet.

🤖 Generated with Claude Code

@wagenet
wagenet marked this pull request as ready for review September 29, 2026 03:38
wagenet added a commit to wagenet/strom that referenced this pull request Sep 29, 2026
Integration fix-up: Eyevinn#799 makes the WHEP Output block read
video_convert_mode() at build time, which panics until detection has
run. The block tests from Eyevinn#896 and Eyevinn#899 build it without that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wagenet added a commit to wagenet/strom that referenced this pull request Sep 29, 2026
Integration fix-up: Eyevinn#799 makes the WHEP Output block read
video_convert_mode() at build time, which panics until detection has
run. The block tests from Eyevinn#896 and Eyevinn#899 build it without that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wagenet added a commit to wagenet/strom that referenced this pull request Sep 29, 2026
Integration fix-up: Eyevinn#799 makes the WHEP Output block read
video_convert_mode() at build time, which panics until detection has
run. The block tests from Eyevinn#896 and Eyevinn#899 build it without that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wagenet added a commit to wagenet/strom that referenced this pull request Oct 1, 2026
Integration fix-up: Eyevinn#799 makes the WHEP Output block read
video_convert_mode() at build time, which panics until detection has
run. The block tests from Eyevinn#896 and Eyevinn#899 build it without that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wagenet
wagenet marked this pull request as draft October 2, 2026 15:28
@srperens

srperens commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Noted as a draft, so I am holding off on a full review until it is marked ready for review. If you want one before then, say so here or request me as a reviewer.

wagenet and others added 2 commits October 19, 2026 17:20
…hain

Chrome offers H.264 level 3.1 on every payload type, and webrtcsink puts
that level and the ones below it on the capsfilter in front of each
viewer's payloader. Pre-encoded H.264 above 3.1 (1080p is level 4,
720p at 50/60 fps is 3.2) then fails with not-negotiated at the first
keyframe and the viewer gets no video. Chrome decodes those streams on
the 3.1 payload type, so remove `level` along with the profile fields.

This sends above the level the viewer declared, which RFC 6184 does not
allow even with level-asymmetry-allowed=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The block's own encoder reads the same level list as pre-encoded input,
so a raw 1080p source gave a Chrome viewer nothing: x264enc refuses the
frame size for level 3.1. The new test feeds raw 1080p30 and checks the
viewer's payloader gets level 4. The level oracle now reads the
payloader's input caps, so it works for both input kinds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@srperens
srperens force-pushed the wagenet/whep-level-above-3-1 branch from 14e84bf to 6664e5d Compare October 19, 2026 15:25
@srperens
srperens marked this pull request as ready for review October 19, 2026 15:26
@srperens
srperens merged commit 578022f into Eyevinn:main Oct 19, 2026
5 checks passed
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