Skip to content

Commit 7f93d0c

Browse files
authored
sdp: Match group bundle from offer (#568)
1 parent 7b3740f commit 7f93d0c

File tree

3 files changed

+258
-4
lines changed

3 files changed

+258
-4
lines changed

webrtc/src/peer_connection/peer_connection_internal.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ impl PeerConnectionInternal {
700700
is_icelite: self.setting_engine.candidates.ice_lite,
701701
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
702702
ice_gathering_state: self.ice_gathering_state(),
703+
match_bundle_group: None,
703704
};
704705
populate_sdp(
705706
d,
@@ -780,7 +781,7 @@ impl PeerConnectionInternal {
780781
}
781782

782783
// If we are offering also include unmatched local transceivers
783-
if include_unmatched {
784+
let match_bundle_group = if include_unmatched {
784785
for t in &local_transceivers {
785786
t.sender().await.set_negotiated();
786787
media_sections.push(MediaSection {
@@ -803,7 +804,14 @@ impl PeerConnectionInternal {
803804
..Default::default()
804805
});
805806
}
806-
}
807+
None
808+
} else {
809+
remote_description
810+
.as_ref()
811+
.and_then(|d| d.parsed.as_ref())
812+
.and_then(|d| d.attribute(ATTR_KEY_GROUP))
813+
.map(ToOwned::to_owned)
814+
};
807815

808816
let dtls_fingerprints = if let Some(cert) = self.dtls_transport.certificates.first() {
809817
cert.get_fingerprints()
@@ -816,6 +824,7 @@ impl PeerConnectionInternal {
816824
is_icelite: self.setting_engine.candidates.ice_lite,
817825
connection_role,
818826
ice_gathering_state: self.ice_gathering_state(),
827+
match_bundle_group,
819828
};
820829
populate_sdp(
821830
d,

webrtc/src/peer_connection/sdp/mod.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,13 @@ impl TryFrom<&String> for SimulcastRid {
738738
}
739739
}
740740

741+
fn bundle_match(bundle: Option<&String>, id: &str) -> bool {
742+
match bundle {
743+
None => true,
744+
Some(b) => b.split_whitespace().any(|s| s == id),
745+
}
746+
}
747+
741748
#[derive(Default)]
742749
pub(crate) struct MediaSection {
743750
pub(crate) id: String,
@@ -752,6 +759,7 @@ pub(crate) struct PopulateSdpParams {
752759
pub(crate) is_icelite: bool,
753760
pub(crate) connection_role: ConnectionRole,
754761
pub(crate) ice_gathering_state: RTCIceGatheringState,
762+
pub(crate) match_bundle_group: Option<String>,
755763
}
756764

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

821829
if should_add_id {
822-
append_bundle(&m.id, &mut bundle_value, &mut bundle_count);
830+
if bundle_match(params.match_bundle_group.as_ref(), &m.id) {
831+
append_bundle(&m.id, &mut bundle_value, &mut bundle_count);
832+
} else if let Some(desc) = d.media_descriptions.last_mut() {
833+
desc.media_name.port = RangedPort {
834+
value: 0,
835+
range: None,
836+
}
837+
}
823838
}
824839
}
825840

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

840-
Ok(d.with_value_attribute(ATTR_KEY_GROUP.to_owned(), bundle_value))
855+
if bundle_count > 0 {
856+
d = d.with_value_attribute(ATTR_KEY_GROUP.to_owned(), bundle_value);
857+
}
858+
859+
Ok(d)
841860
}
842861

843862
pub(crate) fn get_mid_value(media: &MediaDescription) -> Option<&String> {

webrtc/src/peer_connection/sdp/sdp_test.rs

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ async fn fingerprint_test(
538538
is_icelite: false,
539539
connection_role: ConnectionRole::Active,
540540
ice_gathering_state: RTCIceGatheringState::New,
541+
match_bundle_group: None,
541542
};
542543

543544
let s = populate_sdp(
@@ -732,6 +733,7 @@ async fn test_populate_sdp() -> Result<()> {
732733
is_icelite: se.candidates.ice_lite,
733734
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
734735
ice_gathering_state: RTCIceGatheringState::Complete,
736+
match_bundle_group: None,
735737
};
736738
let offer_sdp = populate_sdp(
737739
d,
@@ -836,6 +838,7 @@ async fn test_populate_sdp() -> Result<()> {
836838
is_icelite: se.candidates.ice_lite,
837839
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
838840
ice_gathering_state: RTCIceGatheringState::Complete,
841+
match_bundle_group: None,
839842
};
840843
let offer_sdp = populate_sdp(
841844
d,
@@ -869,6 +872,228 @@ async fn test_populate_sdp() -> Result<()> {
869872
assert!(found_vp8, "vp8 should be present in sdp");
870873
}
871874

875+
//"Bundle all"
876+
{
877+
let se = SettingEngine::default();
878+
let mut me = MediaEngine::default();
879+
me.register_default_codecs()?;
880+
881+
let api = APIBuilder::new().with_media_engine(me).build();
882+
let interceptor = api.interceptor_registry.build("")?;
883+
let transport = Arc::new(RTCDtlsTransport::default());
884+
let receiver = Arc::new(api.new_rtp_receiver(
885+
RTPCodecType::Video,
886+
Arc::clone(&transport),
887+
Arc::clone(&interceptor),
888+
));
889+
890+
let sender = Arc::new(
891+
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
892+
.await,
893+
);
894+
895+
let tr = RTCRtpTransceiver::new(
896+
receiver,
897+
sender,
898+
RTCRtpTransceiverDirection::Recvonly,
899+
RTPCodecType::Video,
900+
api.media_engine.video_codecs.clone(),
901+
Arc::clone(&api.media_engine),
902+
None,
903+
)
904+
.await;
905+
906+
let media_sections = vec![MediaSection {
907+
id: "video".to_owned(),
908+
transceivers: vec![tr],
909+
data: false,
910+
rid_map: vec![],
911+
..Default::default()
912+
}];
913+
914+
let d = SessionDescription::default();
915+
916+
let params = PopulateSdpParams {
917+
media_description_fingerprint: se.sdp_media_level_fingerprints,
918+
is_icelite: se.candidates.ice_lite,
919+
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
920+
ice_gathering_state: RTCIceGatheringState::Complete,
921+
match_bundle_group: None,
922+
};
923+
let offer_sdp = populate_sdp(
924+
d,
925+
&[],
926+
&api.media_engine,
927+
&[],
928+
&RTCIceParameters::default(),
929+
&media_sections,
930+
params,
931+
)
932+
.await?;
933+
934+
assert_eq!(
935+
offer_sdp.attribute(ATTR_KEY_GROUP),
936+
Some(&"BUNDLE video".to_owned())
937+
);
938+
}
939+
940+
//"Bundle matched"
941+
{
942+
let se = SettingEngine::default();
943+
let mut me = MediaEngine::default();
944+
me.register_default_codecs()?;
945+
946+
let api = APIBuilder::new().with_media_engine(me).build();
947+
let interceptor = api.interceptor_registry.build("")?;
948+
let transport = Arc::new(RTCDtlsTransport::default());
949+
950+
let video_receiver = Arc::new(api.new_rtp_receiver(
951+
RTPCodecType::Video,
952+
Arc::clone(&transport),
953+
Arc::clone(&interceptor),
954+
));
955+
let audio_receiver = Arc::new(api.new_rtp_receiver(
956+
RTPCodecType::Audio,
957+
Arc::clone(&transport),
958+
Arc::clone(&interceptor),
959+
));
960+
961+
let video_sender = Arc::new(
962+
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
963+
.await,
964+
);
965+
let audio_sender = Arc::new(
966+
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
967+
.await,
968+
);
969+
970+
let trv = RTCRtpTransceiver::new(
971+
video_receiver,
972+
video_sender,
973+
RTCRtpTransceiverDirection::Recvonly,
974+
RTPCodecType::Video,
975+
api.media_engine.video_codecs.clone(),
976+
Arc::clone(&api.media_engine),
977+
None,
978+
)
979+
.await;
980+
981+
let tra = RTCRtpTransceiver::new(
982+
audio_receiver,
983+
audio_sender,
984+
RTCRtpTransceiverDirection::Recvonly,
985+
RTPCodecType::Audio,
986+
api.media_engine.audio_codecs.clone(),
987+
Arc::clone(&api.media_engine),
988+
None,
989+
)
990+
.await;
991+
992+
let media_sections = vec![
993+
MediaSection {
994+
id: "video".to_owned(),
995+
transceivers: vec![trv],
996+
data: false,
997+
rid_map: vec![],
998+
..Default::default()
999+
},
1000+
MediaSection {
1001+
id: "audio".to_owned(),
1002+
transceivers: vec![tra],
1003+
data: false,
1004+
rid_map: vec![],
1005+
..Default::default()
1006+
},
1007+
];
1008+
1009+
let d = SessionDescription::default();
1010+
1011+
let params = PopulateSdpParams {
1012+
media_description_fingerprint: se.sdp_media_level_fingerprints,
1013+
is_icelite: se.candidates.ice_lite,
1014+
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
1015+
ice_gathering_state: RTCIceGatheringState::Complete,
1016+
match_bundle_group: Some("audio".to_owned()),
1017+
};
1018+
let offer_sdp = populate_sdp(
1019+
d,
1020+
&[],
1021+
&api.media_engine,
1022+
&[],
1023+
&RTCIceParameters::default(),
1024+
&media_sections,
1025+
params,
1026+
)
1027+
.await?;
1028+
1029+
assert_eq!(
1030+
offer_sdp.attribute(ATTR_KEY_GROUP),
1031+
Some(&"BUNDLE audio".to_owned())
1032+
);
1033+
}
1034+
1035+
//"empty bundle group"
1036+
{
1037+
let se = SettingEngine::default();
1038+
let mut me = MediaEngine::default();
1039+
me.register_default_codecs()?;
1040+
1041+
let api = APIBuilder::new().with_media_engine(me).build();
1042+
let interceptor = api.interceptor_registry.build("")?;
1043+
let transport = Arc::new(RTCDtlsTransport::default());
1044+
let receiver = Arc::new(api.new_rtp_receiver(
1045+
RTPCodecType::Video,
1046+
Arc::clone(&transport),
1047+
Arc::clone(&interceptor),
1048+
));
1049+
1050+
let sender = Arc::new(
1051+
api.new_rtp_sender(None, Arc::clone(&transport), Arc::clone(&interceptor))
1052+
.await,
1053+
);
1054+
1055+
let tr = RTCRtpTransceiver::new(
1056+
receiver,
1057+
sender,
1058+
RTCRtpTransceiverDirection::Recvonly,
1059+
RTPCodecType::Video,
1060+
api.media_engine.video_codecs.clone(),
1061+
Arc::clone(&api.media_engine),
1062+
None,
1063+
)
1064+
.await;
1065+
1066+
let media_sections = vec![MediaSection {
1067+
id: "video".to_owned(),
1068+
transceivers: vec![tr],
1069+
data: false,
1070+
rid_map: vec![],
1071+
..Default::default()
1072+
}];
1073+
1074+
let d = SessionDescription::default();
1075+
1076+
let params = PopulateSdpParams {
1077+
media_description_fingerprint: se.sdp_media_level_fingerprints,
1078+
is_icelite: se.candidates.ice_lite,
1079+
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
1080+
ice_gathering_state: RTCIceGatheringState::Complete,
1081+
match_bundle_group: Some("".to_owned()),
1082+
};
1083+
let offer_sdp = populate_sdp(
1084+
d,
1085+
&[],
1086+
&api.media_engine,
1087+
&[],
1088+
&RTCIceParameters::default(),
1089+
&media_sections,
1090+
params,
1091+
)
1092+
.await?;
1093+
1094+
assert_eq!(offer_sdp.attribute(ATTR_KEY_GROUP), None);
1095+
}
1096+
8721097
Ok(())
8731098
}
8741099

@@ -962,6 +1187,7 @@ async fn test_populate_sdp_reject() -> Result<()> {
9621187
is_icelite: se.candidates.ice_lite,
9631188
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
9641189
ice_gathering_state: RTCIceGatheringState::Complete,
1190+
match_bundle_group: None,
9651191
};
9661192
let offer_sdp = populate_sdp(
9671193
d,

0 commit comments

Comments
 (0)