Skip to content

Commit

Permalink
sdp: Match group bundle from offer
Browse files Browse the repository at this point in the history
  • Loading branch information
haaspors committed May 14, 2024
1 parent dcfefd7 commit 6bf9c11
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 4 deletions.
13 changes: 11 additions & 2 deletions webrtc/src/peer_connection/peer_connection_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ impl PeerConnectionInternal {
is_icelite: self.setting_engine.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: self.ice_gathering_state(),
match_bundle_group: None,
};
populate_sdp(
d,
Expand Down Expand Up @@ -780,7 +781,7 @@ impl PeerConnectionInternal {
}

// If we are offering also include unmatched local transceivers
if include_unmatched {
let match_bundle_group = if include_unmatched {
for t in &local_transceivers {
t.sender().await.set_negotiated();
media_sections.push(MediaSection {
Expand All @@ -803,7 +804,14 @@ impl PeerConnectionInternal {
..Default::default()
});
}
}
None
} else {
remote_description
.as_ref()
.and_then(|d| d.parsed.as_ref())
.and_then(|d| d.attribute(ATTR_KEY_GROUP))
.map(ToOwned::to_owned)
};

let dtls_fingerprints = if let Some(cert) = self.dtls_transport.certificates.first() {
cert.get_fingerprints()
Expand All @@ -816,6 +824,7 @@ impl PeerConnectionInternal {
is_icelite: self.setting_engine.candidates.ice_lite,
connection_role,
ice_gathering_state: self.ice_gathering_state(),
match_bundle_group,
};
populate_sdp(
d,
Expand Down
23 changes: 21 additions & 2 deletions webrtc/src/peer_connection/sdp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,13 @@ impl TryFrom<&String> for SimulcastRid {
}
}

fn bundle_match(bundle: Option<&String>, id: &str) -> bool {
match bundle {
None => true,
Some(b) => b.split_whitespace().any(|s| s == id),
}
}

#[derive(Default)]
pub(crate) struct MediaSection {
pub(crate) id: String,
Expand All @@ -752,6 +759,7 @@ pub(crate) struct PopulateSdpParams {
pub(crate) is_icelite: bool,
pub(crate) connection_role: ConnectionRole,
pub(crate) ice_gathering_state: RTCIceGatheringState,
pub(crate) match_bundle_group: Option<String>,
}

/// populate_sdp serializes a PeerConnections state into an SDP
Expand Down Expand Up @@ -819,7 +827,14 @@ pub(crate) async fn populate_sdp(
};

if should_add_id {
append_bundle(&m.id, &mut bundle_value, &mut bundle_count);
if bundle_match(params.match_bundle_group.as_ref(), &m.id) {
append_bundle(&m.id, &mut bundle_value, &mut bundle_count);
} else if let Some(desc) = d.media_descriptions.last_mut() {
desc.media_name.port = RangedPort {
value: 0,
range: None,
}
}
}
}

Expand All @@ -837,7 +852,11 @@ pub(crate) async fn populate_sdp(
d = d.with_value_attribute(ATTR_KEY_ICELITE.to_owned(), ATTR_KEY_ICELITE.to_owned());
}

Ok(d.with_value_attribute(ATTR_KEY_GROUP.to_owned(), bundle_value))
if bundle_count > 0 {
d = d.with_value_attribute(ATTR_KEY_GROUP.to_owned(), bundle_value);
}

Ok(d)
}

pub(crate) fn get_mid_value(media: &MediaDescription) -> Option<&String> {
Expand Down
226 changes: 226 additions & 0 deletions webrtc/src/peer_connection/sdp/sdp_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ async fn fingerprint_test(
is_icelite: false,
connection_role: ConnectionRole::Active,
ice_gathering_state: RTCIceGatheringState::New,
match_bundle_group: None,
};

let s = populate_sdp(
Expand Down Expand Up @@ -732,6 +733,7 @@ async fn test_populate_sdp() -> Result<()> {
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: None,
};
let offer_sdp = populate_sdp(
d,
Expand Down Expand Up @@ -836,6 +838,7 @@ async fn test_populate_sdp() -> Result<()> {
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: None,
};
let offer_sdp = populate_sdp(
d,
Expand Down Expand Up @@ -869,6 +872,228 @@ async fn test_populate_sdp() -> Result<()> {
assert!(found_vp8, "vp8 should be present in sdp");
}

//"Bundle all"
{
let se = SettingEngine::default();
let mut me = MediaEngine::default();
me.register_default_codecs()?;

let api = APIBuilder::new().with_media_engine(me).build();
let interceptor = api.interceptor_registry.build("")?;
let transport = Arc::new(RTCDtlsTransport::default());
let receiver = Arc::new(api.new_rtp_receiver(
RTPCodecType::Video,
Arc::clone(&transport),
Arc::clone(&interceptor),
));

let sender = Arc::new(
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
.await,
);

let tr = RTCRtpTransceiver::new(
receiver,
sender,
RTCRtpTransceiverDirection::Recvonly,
RTPCodecType::Video,
api.media_engine.video_codecs.clone(),
Arc::clone(&api.media_engine),
None,
)
.await;

let media_sections = vec![MediaSection {
id: "video".to_owned(),
transceivers: vec![tr],
data: false,
rid_map: vec![],
..Default::default()
}];

let d = SessionDescription::default();

let params = PopulateSdpParams {
media_description_fingerprint: se.sdp_media_level_fingerprints,
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: None,
};
let offer_sdp = populate_sdp(
d,
&[],
&api.media_engine,
&[],
&RTCIceParameters::default(),
&media_sections,
params,
)
.await?;

assert_eq!(
offer_sdp.attribute(ATTR_KEY_GROUP),
Some(&"BUNDLE video".to_owned())
);
}

//"Bundle matched"
{
let se = SettingEngine::default();
let mut me = MediaEngine::default();
me.register_default_codecs()?;

let api = APIBuilder::new().with_media_engine(me).build();
let interceptor = api.interceptor_registry.build("")?;
let transport = Arc::new(RTCDtlsTransport::default());

let video_receiver = Arc::new(api.new_rtp_receiver(
RTPCodecType::Video,
Arc::clone(&transport),
Arc::clone(&interceptor),
));
let audio_receiver = Arc::new(api.new_rtp_receiver(
RTPCodecType::Audio,
Arc::clone(&transport),
Arc::clone(&interceptor),
));

let video_sender = Arc::new(
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
.await,
);
let audio_sender = Arc::new(
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
.await,
);

let trv = RTCRtpTransceiver::new(
video_receiver,
video_sender,
RTCRtpTransceiverDirection::Recvonly,
RTPCodecType::Video,
api.media_engine.video_codecs.clone(),
Arc::clone(&api.media_engine),
None,
)
.await;

let tra = RTCRtpTransceiver::new(
audio_receiver,
audio_sender,
RTCRtpTransceiverDirection::Recvonly,
RTPCodecType::Audio,
api.media_engine.audio_codecs.clone(),
Arc::clone(&api.media_engine),
None,
)
.await;

let media_sections = vec![
MediaSection {
id: "video".to_owned(),
transceivers: vec![trv],
data: false,
rid_map: vec![],
..Default::default()
},
MediaSection {
id: "audio".to_owned(),
transceivers: vec![tra],
data: false,
rid_map: vec![],
..Default::default()
},
];

let d = SessionDescription::default();

let params = PopulateSdpParams {
media_description_fingerprint: se.sdp_media_level_fingerprints,
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: Some("audio".to_owned()),
};
let offer_sdp = populate_sdp(
d,
&[],
&api.media_engine,
&[],
&RTCIceParameters::default(),
&media_sections,
params,
)
.await?;

assert_eq!(
offer_sdp.attribute(ATTR_KEY_GROUP),
Some(&"BUNDLE audio".to_owned())
);
}

//"empty bundle group"
{
let se = SettingEngine::default();
let mut me = MediaEngine::default();
me.register_default_codecs()?;

let api = APIBuilder::new().with_media_engine(me).build();
let interceptor = api.interceptor_registry.build("")?;
let transport = Arc::new(RTCDtlsTransport::default());
let receiver = Arc::new(api.new_rtp_receiver(
RTPCodecType::Video,
Arc::clone(&transport),
Arc::clone(&interceptor),
));

let sender = Arc::new(
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
.await,
);

let tr = RTCRtpTransceiver::new(
receiver,
sender,
RTCRtpTransceiverDirection::Recvonly,
RTPCodecType::Video,
api.media_engine.video_codecs.clone(),
Arc::clone(&api.media_engine),
None,
)
.await;

let media_sections = vec![MediaSection {
id: "video".to_owned(),
transceivers: vec![tr],
data: false,
rid_map: vec![],
..Default::default()
}];

let d = SessionDescription::default();

let params = PopulateSdpParams {
media_description_fingerprint: se.sdp_media_level_fingerprints,
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: Some("".to_owned()),
};
let offer_sdp = populate_sdp(
d,
&[],
&api.media_engine,
&[],
&RTCIceParameters::default(),
&media_sections,
params,
)
.await?;

assert_eq!(offer_sdp.attribute(ATTR_KEY_GROUP), None);
}

Ok(())
}

Expand Down Expand Up @@ -962,6 +1187,7 @@ async fn test_populate_sdp_reject() -> Result<()> {
is_icelite: se.candidates.ice_lite,
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
ice_gathering_state: RTCIceGatheringState::Complete,
match_bundle_group: None,
};
let offer_sdp = populate_sdp(
d,
Expand Down

0 comments on commit 6bf9c11

Please sign in to comment.