From 3479ccf8c7211057659c1a64891b290911fffb36 Mon Sep 17 00:00:00 2001 From: yngrtc Date: Sat, 27 Sep 2025 10:09:54 -0700 Subject: [PATCH 1/2] Add support for multi codec negotiation --- .../src/api/media_engine/media_engine_test.rs | 74 +++++++++++++++++++ webrtc/src/api/media_engine/mod.rs | 18 ++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/webrtc/src/api/media_engine/media_engine_test.rs b/webrtc/src/api/media_engine/media_engine_test.rs index 22b3fc9ef..2ed2e6108 100644 --- a/webrtc/src/api/media_engine/media_engine_test.rs +++ b/webrtc/src/api/media_engine/media_engine_test.rs @@ -801,3 +801,77 @@ a=rtpmap:111 opus/48000/2 Ok(()) } + +#[tokio::test] +async fn test_multi_codec_negotiation() -> Result<()> { + const OFFER_SDP: &str = "v=0 +o=- 781500112831855234 6 IN IP4 127.0.0.1 +s=- +t=0 0 +a=group:BUNDLE 0 1 2 3 +a=extmap-allow-mixed +a=msid-semantic: WMS be0216be-f3d8-40ca-a624-379edf70f1c9 +m=application 53555 UDP/DTLS/SCTP webrtc-datachannel +a=mid:0 +a=sctp-port:5000 +a=max-message-size:262144 +m=video 9 UDP/TLS/RTP/SAVPF 98 +a=mid:1 +a=sendonly +a=msid:be0216be-f3d8-40ca-a624-379edf70f1c9 3d032b3b-ffe5-48ec-b783-21375668d1c3 +a=rtcp-mux +a=rtcp-rsize +a=rtpmap:98 VP9/90000 +a=rtcp-fb:98 goog-remb +a=rtcp-fb:98 transport-cc +a=rtcp-fb:98 ccm fir +a=rtcp-fb:98 nack +a=rtcp-fb:98 nack pli +a=fmtp:98 profile-id=0 +a=rid:q send +a=rid:h send +a=simulcast:send q;h +m=video 9 UDP/TLS/RTP/SAVPF 96 +a=mid:2 +a=sendonly +a=msid:6ff05509-be96-4ef1-a74f-425e14720983 16d5d7fe-d076-4718-9ca9-ec62b4543727 +a=rtcp-mux +a=rtcp-rsize +a=rtpmap:96 VP8/90000 +a=rtcp-fb:96 goog-remb +a=rtcp-fb:96 transport-cc +a=rtcp-fb:96 ccm fir +a=rtcp-fb:96 nack +a=rtcp-fb:96 nack pli +a=ssrc:4281768245 cname:JDM9GNMEg+9To6K7 +a=ssrc:4281768245 msid:6ff05509-be96-4ef1-a74f-425e14720983 16d5d7fe-d076-4718-9ca9-ec62b4543727 +"; + let must_parse = |raw: &str| -> Result { + let mut reader = Cursor::new(raw.as_bytes()); + Ok(SessionDescription::unmarshal(&mut reader)?) + }; + + // "Multi codec negotiation disabled" + { + let mut m = MediaEngine::default(); + m.register_default_codecs()?; + m.update_from_remote_description(&must_parse(OFFER_SDP)?) + .await?; + let negotiated_video_codecs = m.negotiated_video_codecs.lock(); + assert_eq!(negotiated_video_codecs.len(), 1) + } + + // "Multi codec negotiation enabled" + { + let mut m = MediaEngine::default(); + m.set_multi_codec_negotiation(true); + assert!(m.multi_codec_negotiation()); + m.register_default_codecs()?; + m.update_from_remote_description(&must_parse(OFFER_SDP)?) + .await?; + let negotiated_video_codecs = m.negotiated_video_codecs.lock(); + assert_eq!(negotiated_video_codecs.len(), 2) + } + + Ok(()) +} diff --git a/webrtc/src/api/media_engine/mod.rs b/webrtc/src/api/media_engine/mod.rs index 0114ad26c..7c678f7dd 100644 --- a/webrtc/src/api/media_engine/mod.rs +++ b/webrtc/src/api/media_engine/mod.rs @@ -88,6 +88,7 @@ pub struct MediaEngine { // If we have attempted to negotiate a codec type yet. pub(crate) negotiated_video: AtomicBool, pub(crate) negotiated_audio: AtomicBool, + pub(crate) negotiate_multi_codecs: AtomicBool, pub(crate) video_codecs: Vec, pub(crate) audio_codecs: Vec, @@ -335,6 +336,17 @@ impl MediaEngine { codecs.push(codec); } + /// set_multi_codec_negotiation enables or disables the negotiation of multiple codecs. + pub fn set_multi_codec_negotiation(&mut self, negotiate_multi_codecs: bool) { + self.negotiate_multi_codecs + .store(negotiate_multi_codecs, Ordering::SeqCst); + } + + /// multi_codec_negotiation returns the current state of the negotiation of multiple codecs. + pub fn multi_codec_negotiation(&self) -> bool { + self.negotiate_multi_codecs.load(Ordering::SeqCst) + } + /// register_codec adds codec to the MediaEngine /// These are the list of codecs supported by this PeerConnection. /// register_codec is not safe for concurrent use. @@ -653,12 +665,14 @@ impl MediaEngine { desc: &SessionDescription, ) -> Result<()> { for media in &desc.media_descriptions { - let typ = if !self.negotiated_audio.load(Ordering::SeqCst) + let typ = if (!self.negotiated_audio.load(Ordering::SeqCst) + || self.negotiate_multi_codecs.load(Ordering::SeqCst)) && media.media_name.media.to_lowercase() == "audio" { self.negotiated_audio.store(true, Ordering::SeqCst); RTPCodecType::Audio - } else if !self.negotiated_video.load(Ordering::SeqCst) + } else if (!self.negotiated_video.load(Ordering::SeqCst) + || self.negotiate_multi_codecs.load(Ordering::SeqCst)) && media.media_name.media.to_lowercase() == "video" { self.negotiated_video.store(true, Ordering::SeqCst); From 111ed072608f902edbffb387277d18f0de7b2d61 Mon Sep 17 00:00:00 2001 From: yngrtc Date: Sat, 27 Sep 2025 11:03:19 -0700 Subject: [PATCH 2/2] Implemented alternative proposal via SettingEngine --- webrtc/src/api/media_engine/mod.rs | 22 +++++++++---------- webrtc/src/api/setting_engine/mod.rs | 11 ++++++++++ .../api/setting_engine/setting_engine_test.rs | 12 ++++++++++ .../peer_connection_internal.rs | 10 ++++++--- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/webrtc/src/api/media_engine/mod.rs b/webrtc/src/api/media_engine/mod.rs index 7c678f7dd..19f787e59 100644 --- a/webrtc/src/api/media_engine/mod.rs +++ b/webrtc/src/api/media_engine/mod.rs @@ -336,17 +336,6 @@ impl MediaEngine { codecs.push(codec); } - /// set_multi_codec_negotiation enables or disables the negotiation of multiple codecs. - pub fn set_multi_codec_negotiation(&mut self, negotiate_multi_codecs: bool) { - self.negotiate_multi_codecs - .store(negotiate_multi_codecs, Ordering::SeqCst); - } - - /// multi_codec_negotiation returns the current state of the negotiation of multiple codecs. - pub fn multi_codec_negotiation(&self) -> bool { - self.negotiate_multi_codecs.load(Ordering::SeqCst) - } - /// register_codec adds codec to the MediaEngine /// These are the list of codecs supported by this PeerConnection. /// register_codec is not safe for concurrent use. @@ -473,6 +462,17 @@ impl MediaEngine { } } + /// set_multi_codec_negotiation enables or disables the negotiation of multiple codecs. + pub(crate) fn set_multi_codec_negotiation(&self, negotiate_multi_codecs: bool) { + self.negotiate_multi_codecs + .store(negotiate_multi_codecs, Ordering::SeqCst); + } + + /// multi_codec_negotiation returns the current state of the negotiation of multiple codecs. + pub(crate) fn multi_codec_negotiation(&self) -> bool { + self.negotiate_multi_codecs.load(Ordering::SeqCst) + } + pub(crate) async fn get_codec_by_payload( &self, payload_type: PayloadType, diff --git a/webrtc/src/api/setting_engine/mod.rs b/webrtc/src/api/setting_engine/mod.rs index 0c0394a8b..fb4781202 100644 --- a/webrtc/src/api/setting_engine/mod.rs +++ b/webrtc/src/api/setting_engine/mod.rs @@ -99,6 +99,7 @@ pub struct SettingEngine { //iceProxyDialer :proxy.Dialer,? pub(crate) udp_network: UDPNetwork, pub(crate) disable_media_engine_copy: bool, + pub(crate) disable_media_engine_multiple_codecs: bool, pub(crate) srtp_protection_profiles: Vec, pub(crate) receive_mtu: usize, pub(crate) mid_generator: Option String + Send + Sync>>, @@ -342,6 +343,16 @@ impl SettingEngine { self.disable_media_engine_copy = is_disabled; } + /// disable_media_engine_multiple_codecs disables the MediaEngine negotiating different codecs. + /// With the default value multiple media sections in the SDP can each negotiate different + /// codecs. This is the new default behvior, because it makes Pion more spec compliant. + /// The value of this setting will get copied to every copy of the MediaEngine generated + /// for new PeerConnections (assuming DisableMediaEngineCopy is set to false). + /// Note: this setting is targeted to be removed in release 4.2.0 (or later). + pub fn disable_media_engine_multiple_codecs(&mut self, is_disabled: bool) { + self.disable_media_engine_multiple_codecs = is_disabled; + } + /// set_receive_mtu sets the size of read buffer that copies incoming packets. This is optional. /// Leave this 0 for the default receive_mtu pub fn set_receive_mtu(&mut self, receive_mtu: usize) { diff --git a/webrtc/src/api/setting_engine/setting_engine_test.rs b/webrtc/src/api/setting_engine/setting_engine_test.rs index cb5433f58..5343d6120 100644 --- a/webrtc/src/api/setting_engine/setting_engine_test.rs +++ b/webrtc/src/api/setting_engine/setting_engine_test.rs @@ -269,3 +269,15 @@ async fn test_setting_engine_set_disable_media_engine_copy() -> Result<()> { Ok(()) } + +#[test] +fn test_setting_engine_media_engine_and_mtu_flags() -> Result<()> { + let mut s = SettingEngine::default(); + + s.disable_media_engine_multiple_codecs(true); + assert!(s.disable_media_engine_multiple_codecs); + + s.set_receive_mtu(1337); + assert_eq!(1337, s.receive_mtu); + Ok(()) +} diff --git a/webrtc/src/peer_connection/peer_connection_internal.rs b/webrtc/src/peer_connection/peer_connection_internal.rs index 722a39e4b..0b5ceaec0 100644 --- a/webrtc/src/peer_connection/peer_connection_internal.rs +++ b/webrtc/src/peer_connection/peer_connection_internal.rs @@ -121,10 +121,14 @@ impl PeerConnectionInternal { peer_connection_state: Arc::new(AtomicU8::new(RTCPeerConnectionState::New as u8)), setting_engine: Arc::clone(&api.setting_engine), - media_engine: if !api.setting_engine.disable_media_engine_copy { - Arc::new(api.media_engine.clone_to()) - } else { + media_engine: if api.setting_engine.disable_media_engine_copy { Arc::clone(&api.media_engine) + } else { + let cloned_media_engine = Arc::new(api.media_engine.clone_to()); + cloned_media_engine.set_multi_codec_negotiation( + !api.setting_engine.disable_media_engine_multiple_codecs, + ); + cloned_media_engine }, interceptor, stats_interceptor,