@@ -8,7 +8,7 @@ use crate::crypto::Fingerprint;
8
8
use crate :: format:: CodecConfig ;
9
9
use crate :: format:: PayloadParams ;
10
10
use crate :: io:: Id ;
11
- use crate :: media:: Media ;
11
+ use crate :: media:: { Media , Simulcast } ;
12
12
use crate :: packet:: MediaKind ;
13
13
use crate :: rtp_:: MidRid ;
14
14
use crate :: rtp_:: Rid ;
@@ -128,7 +128,7 @@ impl<'a> SdpApi<'a> {
128
128
/// let mut rtc = Rtc::new();
129
129
///
130
130
/// let mut changes = rtc.sdp_api();
131
- /// let mid = changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None);
131
+ /// let mid = changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None );
132
132
/// let (offer, pending) = changes.apply().unwrap();
133
133
///
134
134
/// // send offer to remote peer, receive answer back
@@ -201,7 +201,7 @@ impl<'a> SdpApi<'a> {
201
201
/// let mut changes = rtc.sdp_api();
202
202
/// assert!(!changes.has_changes());
203
203
///
204
- /// let mid = changes.add_media(MediaKind::Audio, Direction::SendRecv, None, None);
204
+ /// let mid = changes.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None );
205
205
/// assert!(changes.has_changes());
206
206
/// # }
207
207
/// ```
@@ -227,7 +227,7 @@ impl<'a> SdpApi<'a> {
227
227
///
228
228
/// let mut changes = rtc.sdp_api();
229
229
///
230
- /// let mid = changes.add_media(MediaKind::Audio, Direction::SendRecv, None, None);
230
+ /// let mid = changes.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None );
231
231
/// # }
232
232
/// ```
233
233
pub fn add_media (
@@ -236,6 +236,7 @@ impl<'a> SdpApi<'a> {
236
236
dir : Direction ,
237
237
stream_id : Option < String > ,
238
238
track_id : Option < String > ,
239
+ simulcast : Option < crate :: media:: Simulcast > ,
239
240
) -> Mid {
240
241
let mid = self . rtc . new_mid ( ) ;
241
242
@@ -266,8 +267,15 @@ impl<'a> SdpApi<'a> {
266
267
Id :: < 20 > :: random ( ) . to_string ( )
267
268
} ;
268
269
269
- let rtx = kind. is_video ( ) . then ( || self . rtc . session . streams . new_ssrc ( ) ) ;
270
- let ssrcs = vec ! [ ( self . rtc. session. streams. new_ssrc( ) , rtx) ] ;
270
+ let mut ssrcs = Vec :: new ( ) ;
271
+
272
+ // Main SSRC, not counting RTX.
273
+ let main_ssrc_count = simulcast. as_ref ( ) . map ( |s| s. send . len ( ) ) . unwrap_or ( 1 ) ;
274
+
275
+ for _ in 0 ..main_ssrc_count {
276
+ let rtx = kind. is_video ( ) . then ( || self . rtc . session . streams . new_ssrc ( ) ) ;
277
+ ssrcs. push ( ( self . rtc . session . streams . new_ssrc ( ) , rtx) ) ;
278
+ }
271
279
272
280
// TODO: let user configure stream/track name.
273
281
let msid = Msid {
@@ -282,6 +290,7 @@ impl<'a> SdpApi<'a> {
282
290
kind,
283
291
dir,
284
292
ssrcs,
293
+ simulcast,
285
294
286
295
// Added later
287
296
pts : vec ! [ ] ,
@@ -459,11 +468,11 @@ impl<'a> SdpApi<'a> {
459
468
/// # use str0m::Rtc;
460
469
/// let mut rtc = Rtc::new();
461
470
/// let mut changes = rtc.sdp_api();
462
- /// changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None);
471
+ /// changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None );
463
472
/// let (_offer, pending) = changes.apply().unwrap();
464
473
///
465
474
/// let mut changes = rtc.sdp_api();
466
- /// changes.add_media(MediaKind::Video, Direction::SendOnly, None, None);
475
+ /// changes.add_media(MediaKind::Video, Direction::SendOnly, None, None, None );
467
476
/// changes.merge(pending);
468
477
///
469
478
/// // This `SdpOffer` will have changes from the first `SdpPendingChanges`
@@ -489,7 +498,7 @@ impl<'a> SdpApi<'a> {
489
498
/// let mut rtc = Rtc::new();
490
499
///
491
500
/// let mut changes = rtc.sdp_api();
492
- /// let mid = changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None);
501
+ /// let mid = changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None );
493
502
/// let (offer, pending) = changes.apply().unwrap();
494
503
///
495
504
/// // send offer to remote peer, receive answer back
@@ -561,6 +570,7 @@ pub(crate) struct AddMedia {
561
570
pub kind : MediaKind ,
562
571
pub dir : Direction ,
563
572
pub ssrcs : Vec < ( Ssrc , Option < Ssrc > ) > ,
573
+ pub simulcast : Option < Simulcast > ,
564
574
565
575
// pts and index are filled in when creating the SDP OFFER.
566
576
// The default PT order is set by the Session (BUNDLE).
@@ -894,9 +904,12 @@ fn add_pending_changes(session: &mut Session, pending: Changes) {
894
904
media. set_cname ( add_media. cname ) ;
895
905
media. set_msid ( add_media. msid ) ;
896
906
897
- for ( ssrc, rtx) in add_media. ssrcs {
898
- // TODO: When we allow sending RID, we need to add that here.
899
- let midrid = MidRid ( add_media. mid , None ) ;
907
+ // If there are RIDs, the SSRC order matches that of the rid order.
908
+ let rids = add_media. simulcast . map ( |x| x. send ) . unwrap_or ( vec ! [ ] ) ;
909
+
910
+ for ( i, ( ssrc, rtx) ) in add_media. ssrcs . into_iter ( ) . enumerate ( ) {
911
+ let maybe_rid = rids. get ( i) . cloned ( ) ;
912
+ let midrid = MidRid ( add_media. mid , maybe_rid) ;
900
913
901
914
let stream = session. streams . declare_stream_tx ( ssrc, rtx, midrid) ;
902
915
@@ -1068,8 +1081,14 @@ fn update_media(
1068
1081
media. set_direction ( new_dir) ;
1069
1082
}
1070
1083
1071
- for rid in m. rids ( ) . iter ( ) {
1072
- media. expect_rid ( * rid) ;
1084
+ if new_dir. is_sending ( ) {
1085
+ // The other side has declared how it EXPECTING to receive. We must only send
1086
+ // the RIDs declared in the answer.
1087
+ media. set_rid_tx ( m. rids ( ) . into ( ) ) ;
1088
+ }
1089
+ if new_dir. is_receiving ( ) {
1090
+ // The other side has declared what it proposes to send. We are accepting it.
1091
+ media. set_rid_rx ( m. rids ( ) . into ( ) ) ;
1073
1092
}
1074
1093
1075
1094
// Narrowing/ordering of of PT
@@ -1102,44 +1121,47 @@ fn update_media(
1102
1121
}
1103
1122
media. set_remote_extmap ( remote_extmap) ;
1104
1123
1105
- if new_dir. is_receiving ( ) {
1106
- // SSRC changes
1107
- // This will always be for ReceiverSource since any incoming a=ssrc line will be
1108
- // about the remote side's SSRC.
1109
- let infos = m. ssrc_info ( ) ;
1110
- let main = infos. iter ( ) . filter ( |i| i. repairs . is_none ( ) ) ;
1111
-
1112
- if m. simulcast ( ) . is_none ( ) {
1113
- // Only use pre-communicated SSRC if we are running without simulcast.
1114
- // We found a bug in FF where the order of the simulcast lines does not
1115
- // correspond to the order of the simulcast declarations. In this case
1116
- // it's better to fall back on mid/rid dynamic mapping.
1117
-
1118
- for i in main {
1119
- // TODO: If the remote is communicating _BOTH_ rid and a=ssrc this will fail.
1120
- info ! ( "Adding pre-communicated SSRC: {:?}" , i) ;
1121
- let repair_ssrc = infos
1122
- . iter ( )
1123
- . find ( |r| r. repairs == Some ( i. ssrc ) )
1124
- . map ( |r| r. ssrc ) ;
1125
-
1126
- // If remote communicated a main a=ssrc, but no RTX, we will not send nacks.
1127
- let midrid = MidRid ( media. mid ( ) , None ) ;
1128
- let suppress_nack = repair_ssrc. is_none ( ) ;
1129
- streams. expect_stream_rx ( i. ssrc , repair_ssrc, midrid, suppress_nack) ;
1130
- }
1131
- }
1124
+ // SSRC changes
1125
+ // This will always be for ReceiverSource since any incoming a=ssrc line will be
1126
+ // about the remote side's SSRC.
1127
+ if !new_dir. is_receiving ( ) {
1128
+ return ;
1129
+ }
1132
1130
1133
- // Simulcast configuration
1134
- if let Some ( s) = m. simulcast ( ) {
1135
- if s. is_munged {
1136
- warn ! ( "Not supporting simulcast via munging SDP" ) ;
1137
- } else if media. simulcast ( ) . is_none ( ) {
1138
- // Invert before setting, since it has a recv and send config.
1139
- media. set_simulcast ( s. invert ( ) ) ;
1140
- }
1131
+ // Simulcast configuration
1132
+ if let Some ( s) = m. simulcast ( ) {
1133
+ if s. is_munged {
1134
+ warn ! ( "Not supporting simulcast via munging SDP" ) ;
1135
+ } else if media. simulcast ( ) . is_none ( ) {
1136
+ // Invert before setting, since it has a recv and send config.
1137
+ media. set_simulcast ( s. invert ( ) ) ;
1141
1138
}
1142
1139
}
1140
+
1141
+ // Only use pre-communicated SSRC if we are running without simulcast.
1142
+ // We found a bug in FF where the order of the simulcast lines does not
1143
+ // correspond to the order of the simulcast declarations. In this case
1144
+ // it's better to fall back on mid/rid dynamic mapping.
1145
+ if m. simulcast ( ) . is_some ( ) {
1146
+ return ;
1147
+ }
1148
+
1149
+ let infos = m. ssrc_info ( ) ;
1150
+ let main = infos. iter ( ) . filter ( |i| i. repairs . is_none ( ) ) ;
1151
+
1152
+ for i in main {
1153
+ // TODO: If the remote is communicating _BOTH_ rid and a=ssrc this will fail.
1154
+ info ! ( "Adding pre-communicated SSRC: {:?}" , i) ;
1155
+ let repair_ssrc = infos
1156
+ . iter ( )
1157
+ . find ( |r| r. repairs == Some ( i. ssrc ) )
1158
+ . map ( |r| r. ssrc ) ;
1159
+
1160
+ // If remote communicated a main a=ssrc, but no RTX, we will not send nacks.
1161
+ let midrid = MidRid ( media. mid ( ) , None ) ;
1162
+ let suppress_nack = repair_ssrc. is_none ( ) ;
1163
+ streams. expect_stream_rx ( i. ssrc , repair_ssrc, midrid, suppress_nack) ;
1164
+ }
1143
1165
}
1144
1166
1145
1167
trait AsSdpMediaLine {
@@ -1291,18 +1313,13 @@ impl AsSdpMediaLine for Media {
1291
1313
}
1292
1314
}
1293
1315
1294
- let count = ssrcs_tx. len ( ) ;
1295
- #[ allow( clippy:: comparison_chain) ]
1296
- if count == 1 {
1297
- let ( ssrc, ssrc_rtx) = & ssrcs_tx[ 0 ] ;
1316
+ for ( ssrc, ssrc_rtx) in ssrcs_tx {
1298
1317
if let Some ( ssrc_rtx) = ssrc_rtx {
1299
1318
attrs. push ( MediaAttribute :: SsrcGroup {
1300
1319
semantics : "FID" . to_string ( ) ,
1301
1320
ssrcs : vec ! [ * ssrc, * ssrc_rtx] ,
1302
1321
} ) ;
1303
1322
}
1304
- } else {
1305
- // TODO: handle simulcast
1306
1323
}
1307
1324
1308
1325
MediaLine {
@@ -1549,7 +1566,10 @@ impl Change {
1549
1566
1550
1567
#[ cfg( test) ]
1551
1568
mod test {
1569
+ use sdp:: RestrictionId ;
1570
+
1552
1571
use crate :: format:: Codec ;
1572
+ use crate :: media:: Simulcast ;
1553
1573
use crate :: sdp:: RtpMap ;
1554
1574
1555
1575
use super :: * ;
@@ -1595,11 +1615,11 @@ mod test {
1595
1615
1596
1616
let mut rtc = Rtc :: new ( ) ;
1597
1617
let mut changes = rtc. sdp_api ( ) ;
1598
- changes. add_media ( MediaKind :: Audio , Direction :: SendOnly , None , None ) ;
1618
+ changes. add_media ( MediaKind :: Audio , Direction :: SendOnly , None , None , None ) ;
1599
1619
let ( offer, pending) = changes. apply ( ) . unwrap ( ) ;
1600
1620
1601
1621
let mut changes = rtc. sdp_api ( ) ;
1602
- changes. add_media ( MediaKind :: Video , Direction :: SendOnly , None , None ) ;
1622
+ changes. add_media ( MediaKind :: Video , Direction :: SendOnly , None , None , None ) ;
1603
1623
changes. merge ( pending) ;
1604
1624
let ( new_offer, _) = changes. apply ( ) . unwrap ( ) ;
1605
1625
@@ -1624,7 +1644,7 @@ mod test {
1624
1644
. build ( ) ;
1625
1645
1626
1646
let mut change1 = rtc1. sdp_api ( ) ;
1627
- change1. add_media ( MediaKind :: Video , Direction :: SendOnly , None , None ) ;
1647
+ change1. add_media ( MediaKind :: Video , Direction :: SendOnly , None , None , None ) ;
1628
1648
let ( offer1, _) = change1. apply ( ) . unwrap ( ) ;
1629
1649
1630
1650
let answer = rtc2. sdp_api ( ) . accept_offer ( offer1) . unwrap ( ) ;
@@ -1652,4 +1672,73 @@ mod test {
1652
1672
"VP9 was not offered, so it should not be present in the answer"
1653
1673
) ;
1654
1674
}
1675
+
1676
+ #[ test]
1677
+ fn simulcast_ssrc_allocation ( ) {
1678
+ crate :: init_crypto_default ( ) ;
1679
+
1680
+ let mut rtc1 = Rtc :: new ( ) ;
1681
+
1682
+ let mut change = rtc1. sdp_api ( ) ;
1683
+ change. add_media (
1684
+ MediaKind :: Video ,
1685
+ Direction :: SendOnly ,
1686
+ None ,
1687
+ None ,
1688
+ Some ( Simulcast {
1689
+ send : vec ! [ "m" . into( ) , "h" . into( ) , "l" . into( ) ] ,
1690
+ recv : vec ! [ ] ,
1691
+ } ) ,
1692
+ ) ;
1693
+
1694
+ let Change :: AddMedia ( am) = & change. changes [ 0 ] else {
1695
+ panic ! ( "Not AddMedia?!" ) ;
1696
+ } ;
1697
+
1698
+ // these should be organized in order: m, h, l
1699
+ let pending_ssrcs = am. ssrcs . clone ( ) ;
1700
+ assert_eq ! ( pending_ssrcs. len( ) , 3 ) ;
1701
+
1702
+ for p in & pending_ssrcs {
1703
+ assert ! ( p. 1 . is_some( ) ) ; // all should have rtx
1704
+ }
1705
+
1706
+ let ( offer, _) = change. apply ( ) . unwrap ( ) ;
1707
+ let sdp = offer. into_inner ( ) ;
1708
+ let line = & sdp. media_lines [ 0 ] ;
1709
+
1710
+ assert_eq ! (
1711
+ line. simulcast( ) . unwrap( ) . send,
1712
+ SimulcastGroups ( vec![
1713
+ RestrictionId ( "m" . into( ) , true ) ,
1714
+ RestrictionId ( "h" . into( ) , true ) ,
1715
+ RestrictionId ( "l" . into( ) , true ) ,
1716
+ ] )
1717
+ ) ;
1718
+
1719
+ // Each SSRC, both regular and RTX get their own a=ssrc line.
1720
+ assert_eq ! ( line. ssrc_info( ) . len( ) , pending_ssrcs. len( ) * 2 ) ;
1721
+
1722
+ let fids: Vec < _ > = line
1723
+ . attrs
1724
+ . iter ( )
1725
+ . filter_map ( |a| {
1726
+ if let MediaAttribute :: SsrcGroup { semantics, ssrcs } = a {
1727
+ // We don't have any other semantics right now.
1728
+ assert_eq ! ( semantics, "FID" ) ;
1729
+ assert_eq ! ( ssrcs. len( ) , 2 ) ;
1730
+ Some ( ( ssrcs[ 0 ] , ssrcs[ 1 ] ) )
1731
+ } else {
1732
+ None
1733
+ }
1734
+ } )
1735
+ . collect ( ) ;
1736
+
1737
+ assert_eq ! ( fids. len( ) , pending_ssrcs. len( ) ) ;
1738
+
1739
+ for ( a, b) in fids. iter ( ) . zip ( pending_ssrcs. iter ( ) ) {
1740
+ assert_eq ! ( a. 0 , b. 0 ) ;
1741
+ assert_eq ! ( Some ( a. 1 ) , b. 1 ) ;
1742
+ }
1743
+ }
1655
1744
}
0 commit comments