From cbc6a05465f9c2cd93f331405eb9fd86b2bf272c Mon Sep 17 00:00:00 2001 From: foxxcn Date: Sun, 11 Oct 2026 23:43:02 +0800 Subject: [PATCH] webrtc: negotiate H.264 High profile when the browser offers it mpph264enc emits High profile (level 5.2, 640034), but only a Constrained Baseline codec was registered, so the answer always labelled the stream 42e01f. Chrome tolerates the mismatch; stricter decoders may not. Register a High profile H.264 codec alongside the existing Constrained Baseline one and give the video track the High fmtp, so binding picks the negotiated High payload when the offer has one (Chrome, Safari, Edge) and otherwise falls back to the first negotiated H.264 payload as before. This assumes the default encoder profile; a --video-profile override is not reflected in the SDP. Co-Authored-By: Claude Opus 5.5 --- app/src/webrtc.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app/src/webrtc.rs b/app/src/webrtc.rs index 93a3662..e34fac3 100644 --- a/app/src/webrtc.rs +++ b/app/src/webrtc.rs @@ -47,6 +47,10 @@ impl Default for SessionConfig { } } +/// High profile, level 5.2: what mpph264enc produces by default. +const H264_HIGH_FMTP: &str = + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640034"; + pub struct WebRTCApi; impl WebRTCApi { @@ -107,7 +111,7 @@ impl WebRTCApi { sdp_fmtp_line: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e034" .to_owned(), - rtcp_feedback: video_rtcp_feedback, + rtcp_feedback: video_rtcp_feedback.clone(), }, payload_type: 102, ..Default::default() @@ -115,6 +119,26 @@ impl WebRTCApi { RTPCodecType::Video, ) .context("register H264 codec failed")?; + // The encoder emits High profile; registering it lets a browser that + // offers High (Chrome, Safari, Edge) negotiate the profile actually + // sent. The Constrained Baseline entry above stays as the fallback for + // offers without High, which keeps today's behaviour for them. + media_engine + .register_codec( + RTCRtpCodecParameters { + capability: RTCRtpCodecCapability { + mime_type: MIME_TYPE_H264.to_owned(), + clock_rate: 90_000, + channels: 0, + sdp_fmtp_line: H264_HIGH_FMTP.to_owned(), + rtcp_feedback: video_rtcp_feedback, + }, + payload_type: 106, + ..Default::default() + }, + RTPCodecType::Video, + ) + .context("register H264 High codec failed")?; media_engine .register_codec( RTCRtpCodecParameters { @@ -146,8 +170,15 @@ impl WebRTCApi { let peer_connection = Arc::new(api.new_peer_connection(configuration).await?); + // The fmtp makes binding prefer a negotiated High profile payload; with + // none it falls back to the first negotiated H264 payload, as before. let video_track = Arc::new(TrackLocalStaticSample::new( - RTCRtpCodecCapability { mime_type: MIME_TYPE_H264.to_owned(), ..Default::default() }, + RTCRtpCodecCapability { + mime_type: MIME_TYPE_H264.to_owned(), + clock_rate: 90_000, + sdp_fmtp_line: H264_HIGH_FMTP.to_owned(), + ..Default::default() + }, "video".to_owned(), "rustkvm".to_owned(), ));