Skip to content

Commit 23bbc1f

Browse files
authored
sdp: Negotiate extmap-allow-mixed (#604)
This allows both one-byte and two-byte headers
1 parent 88dff7b commit 23bbc1f

File tree

5 files changed

+40
-0
lines changed

5 files changed

+40
-0
lines changed

sdp/src/description/media.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ pub struct MediaDescription {
6565
}
6666

6767
impl MediaDescription {
68+
/// Returns whether an attribute exists
69+
pub fn has_attribute(&self, key: &str) -> bool {
70+
self.attributes.iter().any(|a| a.key == key)
71+
}
72+
6873
/// attribute returns the value of an attribute and if it exists
6974
pub fn attribute(&self, key: &str) -> Option<Option<&str>> {
7075
for a in &self.attributes {

sdp/src/description/session.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub const ATTR_KEY_RECV_ONLY: &str = "recvonly";
3030
pub const ATTR_KEY_SEND_ONLY: &str = "sendonly";
3131
pub const ATTR_KEY_SEND_RECV: &str = "sendrecv";
3232
pub const ATTR_KEY_EXT_MAP: &str = "extmap";
33+
pub const ATTR_KEY_EXTMAP_ALLOW_MIXED: &str = "extmap-allow-mixed";
3334

3435
/// Constants for semantic tokens used in JSEP
3536
pub const SEMANTIC_TOKEN_LIP_SYNCHRONIZATION: &str = "LS";
@@ -409,6 +410,11 @@ impl SessionDescription {
409410
Err(Error::CodecNotFound)
410411
}
411412

413+
/// Returns whether an attribute exists
414+
pub fn has_attribute(&self, key: &str) -> bool {
415+
self.attributes.iter().any(|a| a.key == key)
416+
}
417+
412418
/// Attribute returns the value of an attribute and if it exists
413419
pub fn attribute(&self, key: &str) -> Option<&String> {
414420
for a in &self.attributes {

webrtc/src/peer_connection/peer_connection_internal.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ impl PeerConnectionInternal {
771771
let params = PopulateSdpParams {
772772
media_description_fingerprint: self.setting_engine.sdp_media_level_fingerprints,
773773
is_icelite: self.setting_engine.candidates.ice_lite,
774+
extmap_allow_mixed: true,
774775
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
775776
ice_gathering_state: self.ice_gathering_state(),
776777
match_bundle_group: None,
@@ -804,8 +805,12 @@ impl PeerConnectionInternal {
804805
let remote_description = self.remote_description().await;
805806
let mut media_sections = vec![];
806807
let mut already_have_application_media_section = false;
808+
let mut extmap_allow_mixed = false;
809+
807810
if let Some(remote_description) = remote_description.as_ref() {
808811
if let Some(parsed) = &remote_description.parsed {
812+
extmap_allow_mixed = parsed.has_attribute(ATTR_KEY_EXTMAP_ALLOW_MIXED);
813+
809814
for media in &parsed.media_descriptions {
810815
if let Some(mid_value) = get_mid_value(media) {
811816
if mid_value.is_empty() {
@@ -830,6 +835,8 @@ impl PeerConnectionInternal {
830835
continue;
831836
}
832837

838+
let extmap_allow_mixed = media.has_attribute(ATTR_KEY_EXTMAP_ALLOW_MIXED);
839+
833840
if let Some(t) = find_by_mid(mid_value, &mut local_transceivers).await {
834841
t.sender().await.set_negotiated();
835842
let media_transceivers = vec![t];
@@ -843,6 +850,7 @@ impl PeerConnectionInternal {
843850
transceivers: media_transceivers,
844851
rid_map: get_rids(media),
845852
offered_direction: (!include_unmatched).then(|| direction),
853+
extmap_allow_mixed,
846854
..Default::default()
847855
});
848856
} else {
@@ -896,6 +904,7 @@ impl PeerConnectionInternal {
896904
let params = PopulateSdpParams {
897905
media_description_fingerprint: self.setting_engine.sdp_media_level_fingerprints,
898906
is_icelite: self.setting_engine.candidates.ice_lite,
907+
extmap_allow_mixed,
899908
connection_role,
900909
ice_gathering_state: self.ice_gathering_state(),
901910
match_bundle_group,

webrtc/src/peer_connection/sdp/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ pub(crate) async fn add_transceiver_sdp(
462462
.with_property_attribute(ATTR_KEY_RTCPMUX.to_owned())
463463
.with_property_attribute(ATTR_KEY_RTCPRSIZE.to_owned());
464464

465+
if media_section.extmap_allow_mixed {
466+
media = media.with_property_attribute(ATTR_KEY_EXTMAP_ALLOW_MIXED.to_owned());
467+
}
468+
465469
let codecs = t.get_codecs().await;
466470
for codec in &codecs {
467471
let name = codec
@@ -772,11 +776,13 @@ pub(crate) struct MediaSection {
772776
pub(crate) data: bool,
773777
pub(crate) rid_map: Vec<SimulcastRid>,
774778
pub(crate) offered_direction: Option<RTCRtpTransceiverDirection>,
779+
pub(crate) extmap_allow_mixed: bool,
775780
}
776781

777782
pub(crate) struct PopulateSdpParams {
778783
pub(crate) media_description_fingerprint: bool,
779784
pub(crate) is_icelite: bool,
785+
pub(crate) extmap_allow_mixed: bool,
780786
pub(crate) connection_role: ConnectionRole,
781787
pub(crate) ice_gathering_state: RTCIceGatheringState,
782788
pub(crate) match_bundle_group: Option<String>,
@@ -876,6 +882,11 @@ pub(crate) async fn populate_sdp(
876882
d = d.with_value_attribute(ATTR_KEY_GROUP.to_owned(), bundle_value);
877883
}
878884

885+
if params.extmap_allow_mixed {
886+
// RFC 8285 6.
887+
d = d.with_property_attribute(ATTR_KEY_EXTMAP_ALLOW_MIXED.to_owned());
888+
}
889+
879890
Ok(d)
880891
}
881892

webrtc/src/peer_connection/sdp/sdp_test.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ async fn fingerprint_test(
581581
let params = PopulateSdpParams {
582582
media_description_fingerprint: sdpmedia_description_fingerprints,
583583
is_icelite: false,
584+
extmap_allow_mixed: false,
584585
connection_role: ConnectionRole::Active,
585586
ice_gathering_state: RTCIceGatheringState::New,
586587
match_bundle_group: None,
@@ -777,6 +778,7 @@ async fn test_populate_sdp() -> Result<()> {
777778
let params = PopulateSdpParams {
778779
media_description_fingerprint: se.sdp_media_level_fingerprints,
779780
is_icelite: se.candidates.ice_lite,
781+
extmap_allow_mixed: true,
780782
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
781783
ice_gathering_state: RTCIceGatheringState::Complete,
782784
match_bundle_group: None,
@@ -882,6 +884,7 @@ async fn test_populate_sdp() -> Result<()> {
882884
let params = PopulateSdpParams {
883885
media_description_fingerprint: se.sdp_media_level_fingerprints,
884886
is_icelite: se.candidates.ice_lite,
887+
extmap_allow_mixed: true,
885888
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
886889
ice_gathering_state: RTCIceGatheringState::Complete,
887890
match_bundle_group: None,
@@ -962,6 +965,7 @@ async fn test_populate_sdp() -> Result<()> {
962965
let params = PopulateSdpParams {
963966
media_description_fingerprint: se.sdp_media_level_fingerprints,
964967
is_icelite: se.candidates.ice_lite,
968+
extmap_allow_mixed: true,
965969
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
966970
ice_gathering_state: RTCIceGatheringState::Complete,
967971
match_bundle_group: None,
@@ -981,6 +985,8 @@ async fn test_populate_sdp() -> Result<()> {
981985
offer_sdp.attribute(ATTR_KEY_GROUP),
982986
Some(&"BUNDLE video".to_owned())
983987
);
988+
989+
assert!(offer_sdp.has_attribute(ATTR_KEY_EXTMAP_ALLOW_MIXED));
984990
}
985991

986992
//"Bundle matched"
@@ -1057,6 +1063,7 @@ async fn test_populate_sdp() -> Result<()> {
10571063
let params = PopulateSdpParams {
10581064
media_description_fingerprint: se.sdp_media_level_fingerprints,
10591065
is_icelite: se.candidates.ice_lite,
1066+
extmap_allow_mixed: true,
10601067
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
10611068
ice_gathering_state: RTCIceGatheringState::Complete,
10621069
match_bundle_group: Some("audio".to_owned()),
@@ -1122,6 +1129,7 @@ async fn test_populate_sdp() -> Result<()> {
11221129
let params = PopulateSdpParams {
11231130
media_description_fingerprint: se.sdp_media_level_fingerprints,
11241131
is_icelite: se.candidates.ice_lite,
1132+
extmap_allow_mixed: true,
11251133
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
11261134
ice_gathering_state: RTCIceGatheringState::Complete,
11271135
match_bundle_group: Some("".to_owned()),
@@ -1231,6 +1239,7 @@ async fn test_populate_sdp_reject() -> Result<()> {
12311239
let params = PopulateSdpParams {
12321240
media_description_fingerprint: se.sdp_media_level_fingerprints,
12331241
is_icelite: se.candidates.ice_lite,
1242+
extmap_allow_mixed: true,
12341243
connection_role: DEFAULT_DTLS_ROLE_OFFER.to_connection_role(),
12351244
ice_gathering_state: RTCIceGatheringState::Complete,
12361245
match_bundle_group: None,

0 commit comments

Comments
 (0)