Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions webrtc/src/api/media_engine/media_engine_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SessionDescription> {
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(())
}
18 changes: 16 additions & 2 deletions webrtc/src/api/media_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link

Copilot AI Sep 27, 2025

Choose a reason for hiding this comment

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

The new field negotiate_multi_codecs is not initialized in the default constructor. This could lead to undefined behavior. Consider adding initialization in the Default implementation or constructor.

Copilot uses AI. Check for mistakes.

pub(crate) video_codecs: Vec<RTCRtpCodecParameters>,
pub(crate) audio_codecs: Vec<RTCRtpCodecParameters>,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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))
Comment on lines +668 to +675
Copy link

Copilot AI Sep 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The parentheses around the first condition are unnecessary and reduce readability. The logical precedence of ! and || operators makes these parentheses redundant.

Copilot uses AI. Check for mistakes.
Comment on lines +674 to +675
Copy link

Copilot AI Sep 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The parentheses around the first condition are unnecessary and reduce readability. The logical precedence of ! and || operators makes these parentheses redundant.

Copilot uses AI. Check for mistakes.
&& media.media_name.media.to_lowercase() == "video"
{
self.negotiated_video.store(true, Ordering::SeqCst);
Expand Down
Loading