From 0be617cd52a539c98f5183bda419e7309567b1ea Mon Sep 17 00:00:00 2001 From: Martin Algesten Date: Fri, 10 Jan 2025 11:27:44 +0100 Subject: [PATCH] Set up simulcast send SSRC --- src/change/sdp.rs | 90 ++++++++++++++++++++++++++++++++++++++++------- src/sdp/mod.rs | 5 +++ 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/change/sdp.rs b/src/change/sdp.rs index a3124485..ea8e83c2 100644 --- a/src/change/sdp.rs +++ b/src/change/sdp.rs @@ -269,17 +269,10 @@ impl<'a> SdpApi<'a> { let mut ssrcs = Vec::new(); - let ssrc_group_count = if let Some(simulcast) = &simulcast { - if simulcast.force_ssrc_groups && dir.is_sending() { - simulcast.send.len() - } else { - 0usize - } - } else { - 1usize - }; + // Main SSRC, not counting RTX. + let main_ssrc_count = simulcast.as_ref().map(|s| s.send.len()).unwrap_or(1); - for _ in 0..ssrc_group_count { + for _ in 0..main_ssrc_count { let rtx = kind.is_video().then(|| self.rtc.session.streams.new_ssrc()); ssrcs.push((self.rtc.session.streams.new_ssrc(), rtx)); } @@ -1309,9 +1302,8 @@ impl AsSdpMediaLine for Media { } let count = ssrcs_tx.len(); - #[allow(clippy::comparison_chain)] - for _ in 0..count { - let (ssrc, ssrc_rtx) = &ssrcs_tx[0]; + for i in 0..count { + let (ssrc, ssrc_rtx) = &ssrcs_tx[i]; if let Some(ssrc_rtx) = ssrc_rtx { attrs.push(MediaAttribute::SsrcGroup { semantics: "FID".to_string(), @@ -1564,7 +1556,10 @@ impl Change { #[cfg(test)] mod test { + use sdp::RestrictionId; + use crate::format::Codec; + use crate::media::Simulcast; use crate::sdp::RtpMap; use super::*; @@ -1667,4 +1662,73 @@ mod test { "VP9 was not offered, so it should not be present in the answer" ); } + + #[test] + fn simulcast_ssrc_allocation() { + crate::init_crypto_default(); + + let mut rtc1 = Rtc::new(); + + let mut change = rtc1.sdp_api(); + change.add_media( + MediaKind::Video, + Direction::SendOnly, + None, + None, + Some(Simulcast { + send: vec!["m".into(), "h".into(), "l".into()], + recv: vec![], + }), + ); + + let Change::AddMedia(am) = &change.changes[0] else { + panic!("Not AddMedia?!"); + }; + + // these should be organized in order: m, h, l + let pending_ssrcs = am.ssrcs.clone(); + assert_eq!(pending_ssrcs.len(), 3); + + for p in &pending_ssrcs { + assert!(p.1.is_some()); // all should have rtx + } + + let (offer, _) = change.apply().unwrap(); + let sdp = offer.into_inner(); + let line = &sdp.media_lines[0]; + + assert_eq!( + line.simulcast().unwrap().send, + SimulcastGroups(vec![ + RestrictionId("m".into(), true), + RestrictionId("h".into(), true), + RestrictionId("l".into(), true), + ]) + ); + + // Each SSRC, both regular and RTX get their own a=ssrc line. + assert_eq!(line.ssrc_info().len(), pending_ssrcs.len() * 2); + + let fids: Vec<_> = line + .attrs + .iter() + .filter_map(|a| { + if let MediaAttribute::SsrcGroup { semantics, ssrcs } = a { + // We don't have any other semantics right now. + assert_eq!(semantics, "FID"); + assert_eq!(ssrcs.len(), 2); + Some((ssrcs[0], ssrcs[1])) + } else { + None + } + }) + .collect(); + + assert_eq!(fids.len(), pending_ssrcs.len()); + + for (a, b) in fids.iter().zip(pending_ssrcs.iter()) { + assert_eq!(a.0, b.0); + assert_eq!(Some(a.1), b.1); + } + } } diff --git a/src/sdp/mod.rs b/src/sdp/mod.rs index 3c4d5a14..918c3655 100644 --- a/src/sdp/mod.rs +++ b/src/sdp/mod.rs @@ -42,6 +42,11 @@ impl SdpOffer { pub fn to_sdp_string(&self) -> String { self.0.to_string() } + + #[cfg(test)] + pub(crate) fn into_inner(self) -> Sdp { + self.0 + } } #[derive(Debug, PartialEq, Eq)]