@desklink/host 0.5.14 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1190,12 +1190,6 @@ impl VideoPeer {
1190
1190
  lock(&self.path).take()
1191
1191
  }
1192
1192
 
1193
- /// Stand in for transport-wide feedback that reports a path, for a pipeline under test.
1194
- #[cfg(test)]
1195
- pub fn report_path(&self, report: PathReport) {
1196
- *lock(&self.path) = Some(report);
1197
- }
1198
-
1199
1193
  /// Take the pending "the far end needs a reference frame" request, if any.
1200
1194
  pub fn take_keyframe_request(&self) -> bool {
1201
1195
  self.wants_keyframe.swap(false, Ordering::SeqCst)
@@ -1239,118 +1233,6 @@ pub(crate) mod tests {
1239
1233
  use super::*;
1240
1234
  use webrtc::media_stream::track_remote::{TrackRemote, TrackRemoteEvent};
1241
1235
 
1242
- #[test]
1243
- fn a_standing_queue_is_measured_and_one_frames_burst_is_not() {
1244
- let epoch = Instant::now();
1245
- let report = Arc::new(Mutex::new(None));
1246
- let mut estimator = QueueDelay::new(report.clone(), 20_000_000.0);
1247
- let mut id = 0u64;
1248
- // One 1200-byte packet every 10 ms, sent at `ms` and `extra` ms late
1249
- // over a path with 20 ms of base delay.
1250
- let mut feed = |from: u64, to: u64, extra: &dyn Fn(u64) -> u64| {
1251
- let reports: Vec<PacketReport> = (from..to)
1252
- .step_by(10)
1253
- .map(|ms| {
1254
- id += 1;
1255
- PacketReport {
1256
- ssrc: 1,
1257
- id,
1258
- rtp_sequence_number: id as u16,
1259
- is_twcc: true,
1260
- twcc_sequence_number: id as u16,
1261
- size: 1200,
1262
- arrived: true,
1263
- departure: epoch + Duration::from_millis(ms),
1264
- arrival: Some(Duration::from_millis(1_000 + ms + 20 + extra(ms))),
1265
- ecn: Default::default(),
1266
- }
1267
- })
1268
- .collect();
1269
- estimator.on_reports(epoch + Duration::from_millis(to + 20), &reports);
1270
- lock(&report).take().expect("a report")
1271
- };
1272
-
1273
- let quiet = feed(0, 1000, &|_| 0);
1274
- assert_eq!(quiet.queue_delay, Duration::ZERO);
1275
- // 51 packets of 1200 bytes arrived in the last 500 ms, both ends in.
1276
- assert_eq!(quiet.delivered_kbps, 979);
1277
- // A frame's burst lifts the packets at its tail, not the ones before.
1278
- let burst = feed(1000, 1100, &|ms| if ms >= 1070 { 40 } else { 0 });
1279
- assert_eq!(burst.queue_delay, Duration::ZERO);
1280
- // A link that drains slower than it is fed lifts every packet: the
1281
- // queue is the least any packet of the last 200 ms waited.
1282
- let standing = feed(1100, 1400, &|ms| 30 + (ms - 1100) / 10);
1283
- assert_eq!(standing.queue_delay, Duration::from_millis(41));
1284
- }
1285
-
1286
- struct NoopHandler;
1287
-
1288
- impl PeerConnectionEventHandler for NoopHandler {}
1289
-
1290
- async fn answerer(offer: &str) -> Arc<dyn PeerConnection> {
1291
- answerer_with(offer, vec![vp9_codec()]).await
1292
- }
1293
-
1294
- /// A receiver that decodes `codecs`, in its order of preference.
1295
- pub(crate) async fn answerer_with(
1296
- offer: &str,
1297
- codecs: Vec<RTCRtpCodecParameters>,
1298
- ) -> Arc<dyn PeerConnection> {
1299
- let mut media_engine = MediaEngine::default();
1300
- for codec in codecs {
1301
- media_engine
1302
- .register_codec(codec, RtpCodecKind::Video)
1303
- .expect("the codec registers");
1304
- }
1305
- let registry = register_default_interceptors(Registry::new(), &mut media_engine)
1306
- .expect("interceptors register");
1307
- let peer = PeerConnectionBuilder::<std::net::SocketAddr>::new()
1308
- .with_media_engine(media_engine)
1309
- .with_interceptor_registry(registry)
1310
- .with_runtime(default_runtime().expect("a runtime"))
1311
- .with_handler(Arc::new(NoopHandler))
1312
- .with_udp_addrs(udp_addrs())
1313
- .build()
1314
- .await
1315
- .expect("an answering peer");
1316
- peer.set_remote_description(
1317
- RTCSessionDescription::offer(offer.to_owned()).expect("a valid offer"),
1318
- )
1319
- .await
1320
- .expect("the offer is accepted");
1321
- Arc::new(peer)
1322
- }
1323
-
1324
- #[tokio::test]
1325
- async fn an_encoded_offer_carries_the_streams_own_profile_and_level() {
1326
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1327
- let (_peer, offer) = VideoPeer::offer(
1328
- TransportOptions {
1329
- ice_servers: Vec::new(),
1330
- loopback_tcp: false,
1331
- pace_bps: 20_000_000.0,
1332
- },
1333
- events,
1334
- VideoCodec::H264 {
1335
- profile_level_id: String::from("42c029"),
1336
- },
1337
- )
1338
- .await
1339
- .expect("a peer connection");
1340
- assert!(
1341
- offer.contains("H264/90000"),
1342
- "the encoded track must be offered as H.264: {offer}",
1343
- );
1344
- assert!(
1345
- offer.contains("packetization-mode=1;profile-level-id=42c029"),
1346
- "the offer must name the fed SPS's own profile-level-id: {offer}",
1347
- );
1348
- assert!(
1349
- !offer.contains("VP9"),
1350
- "an encoded source must not also offer the codec it cannot encode: {offer}",
1351
- );
1352
- }
1353
-
1354
1236
  fn transport() -> TransportOptions {
1355
1237
  TransportOptions {
1356
1238
  ice_servers: Vec::new(),
@@ -1359,321 +1241,6 @@ pub(crate) mod tests {
1359
1241
  }
1360
1242
  }
1361
1243
 
1362
- /// The answer `codecs` make to a `Vp9OrH264` offer, applied to its peer.
1363
- pub(crate) async fn answered(codecs: Vec<RTCRtpCodecParameters>) -> (VideoPeer, String) {
1364
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1365
- let (peer, offer) = VideoPeer::offer(transport(), events, VideoCodec::Vp9OrH264)
1366
- .await
1367
- .expect("a peer connection");
1368
- let other = answerer_with(&offer, codecs).await;
1369
- let answer = other.create_answer(None).await.expect("an answer");
1370
- other
1371
- .set_local_description(answer.clone())
1372
- .await
1373
- .expect("a local description");
1374
- peer.accept_answer(answer.sdp.clone())
1375
- .await
1376
- .expect("the answer is accepted");
1377
- (peer, answer.sdp)
1378
- }
1379
-
1380
- pub(crate) fn h264_receiver_codec() -> RTCRtpCodecParameters {
1381
- h264_codec(crate::h264::PROFILE_LEVEL_ID)
1382
- }
1383
-
1384
- #[tokio::test]
1385
- async fn a_captured_offer_puts_vp9_first_and_h264_second() {
1386
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1387
- let (_peer, offer) = VideoPeer::offer(transport(), events, VideoCodec::Vp9OrH264)
1388
- .await
1389
- .expect("a peer connection");
1390
- let video = offer
1391
- .lines()
1392
- .find(|line| line.starts_with("m=video "))
1393
- .expect("a video section");
1394
- assert!(
1395
- video.ends_with(&format!(" {VP9_PAYLOAD_TYPE} {H264_PAYLOAD_TYPE}")),
1396
- "VP9 is the default a receiver with no preference answers with: {video}",
1397
- );
1398
- assert!(
1399
- offer.contains(&format!(
1400
- "a=fmtp:{H264_PAYLOAD_TYPE} level-asymmetry-allowed=1;packetization-mode=1;profile-level-id={}",
1401
- crate::h264::PROFILE_LEVEL_ID
1402
- )),
1403
- "H.264 is offered as the Constrained Baseline every backend emits: {offer}",
1404
- );
1405
- }
1406
-
1407
- #[tokio::test]
1408
- async fn the_receivers_first_codec_is_the_one_sent() {
1409
- let (peer, answer) = answered(vec![vp9_codec(), h264_receiver_codec()]).await;
1410
- assert!(
1411
- !peer.sends_h264(),
1412
- "a receiver that keeps the offer's order gets VP9: {answer}"
1413
- );
1414
- let (peer, answer) = answered(vec![h264_receiver_codec()]).await;
1415
- assert!(peer.sends_h264(), "an H.264 receiver gets H.264: {answer}");
1416
- assert_eq!(
1417
- peer.payload_type.load(Ordering::SeqCst),
1418
- H264_PAYLOAD_TYPE,
1419
- "frames go out on the payload type the answer kept",
1420
- );
1421
- }
1422
-
1423
- #[test]
1424
- fn an_answer_is_read_for_its_video_sections_first_known_codec() {
1425
- let answer = |formats: &str| {
1426
- format!(
1427
- "v=0\r\no=- 1 1 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\n\
1428
- m=video 9 UDP/TLS/RTP/SAVPF {formats}\r\nc=IN IP4 0.0.0.0\r\n\
1429
- a=rtpmap:96 rtx/90000\r\na=rtpmap:98 VP9/90000\r\na=rtpmap:102 H264/90000\r\n\
1430
- m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\na=rtpmap:5 H264/90000\r\n"
1431
- )
1432
- };
1433
- assert!(answer_prefers_h264(&answer("102 98")));
1434
- assert!(answer_prefers_h264(&answer("96 102 98")), "rtx is skipped");
1435
- assert!(!answer_prefers_h264(&answer("98 102")));
1436
- assert!(!answer_prefers_h264(&answer("98")));
1437
- assert!(!answer_prefers_h264("v=0\r\n"), "no video, no H.264");
1438
- }
1439
-
1440
- #[tokio::test]
1441
- async fn the_offer_asks_the_receiver_to_show_frames_without_playout_delay() {
1442
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1443
- let (_peer, offer) = VideoPeer::offer(
1444
- TransportOptions {
1445
- ice_servers: Vec::new(),
1446
- loopback_tcp: false,
1447
- pace_bps: 20_000_000.0,
1448
- },
1449
- events,
1450
- VideoCodec::Vp9,
1451
- )
1452
- .await
1453
- .expect("a peer connection");
1454
- assert!(
1455
- offer
1456
- .lines()
1457
- .any(|line| line.starts_with("a=extmap:") && line.ends_with(PLAYOUT_DELAY_URI)),
1458
- "the video must negotiate the playout-delay extension its packets carry: {offer}",
1459
- );
1460
- }
1461
-
1462
- #[test]
1463
- fn the_profile_level_id_comes_from_the_sps_in_the_stream() {
1464
- // A leading zero byte, a four-byte start code and a three-byte one, so
1465
- // the scanner is exercised on both forms.
1466
- let mut stream = vec![0x00, 0x00, 0x00, 0x01];
1467
- stream.extend_from_slice(&[0x67, 0x42, 0xc0, 0x29, 0x8c, 0x8d]);
1468
- stream.extend_from_slice(&[0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80]);
1469
- stream.extend_from_slice(&[0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x00]);
1470
- assert_eq!(
1471
- h264_profile_level_id(&stream).as_deref(),
1472
- Some("42c029"),
1473
- "profile_idc, constraint flags and level_idc, as the SDP needs them",
1474
- );
1475
- assert_eq!(
1476
- h264_profile_level_id(&[0x00, 0x00, 0x01, 0x65, 0x88]),
1477
- None,
1478
- "a stream with no SPS cannot start a session",
1479
- );
1480
- let mut adjacent = vec![0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01];
1481
- adjacent.extend_from_slice(&[0x67, 0x42, 0xc0, 0x29]);
1482
- assert_eq!(
1483
- h264_profile_level_id(&adjacent).as_deref(),
1484
- Some("42c029"),
1485
- "an empty NAL between start codes is skipped, not fatal to the scan",
1486
- );
1487
- }
1488
-
1489
- #[test]
1490
- fn a_fed_keyframe_is_packetized_as_parameter_sets_then_a_slice() {
1491
- let mut stream = vec![0x00, 0x00, 0x00, 0x01];
1492
- stream.extend_from_slice(&[0x67, 0x42, 0xc0, 0x29, 0x8c, 0x8d]);
1493
- stream.extend_from_slice(&[0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80]);
1494
- stream.extend_from_slice(&[0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x00]);
1495
- let payloader = h264_codec("42c029")
1496
- .rtp_codec
1497
- .payloader()
1498
- .expect("the media engine has an H.264 payloader");
1499
- let mut packetizer = H264Packetizer::new(payloader);
1500
- let packets = packetizer
1501
- .packetize(&stream, Instant::now(), 7, H264_PAYLOAD_TYPE)
1502
- .expect("a keyframe packetizes");
1503
- assert_eq!(
1504
- packets.len(),
1505
- 2,
1506
- "one STAP-A for the parameter sets, one slice"
1507
- );
1508
- assert_eq!(
1509
- packets[0].payload[0], 0x78,
1510
- "the parameter sets travel as a STAP-A",
1511
- );
1512
- assert_eq!(packets[1].payload[0], 0x65, "then the IDR slice itself");
1513
- assert!(
1514
- !packets[0].header.marker && packets[1].header.marker,
1515
- "the marker ends the access unit, not the first fragment of it",
1516
- );
1517
- assert_eq!(
1518
- packets[0].header.timestamp, packets[1].header.timestamp,
1519
- "an access unit is one instant",
1520
- );
1521
- assert_eq!(packets[0].header.payload_type, H264_PAYLOAD_TYPE);
1522
- }
1523
-
1524
- #[tokio::test]
1525
- async fn a_candidate_that_arrives_before_the_answer_is_not_refused() {
1526
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1527
- let (peer, _offer) = VideoPeer::offer(
1528
- TransportOptions {
1529
- ice_servers: Vec::new(),
1530
- loopback_tcp: false,
1531
- pace_bps: 20_000_000.0,
1532
- },
1533
- events,
1534
- VideoCodec::Vp9,
1535
- )
1536
- .await
1537
- .expect("a peer connection");
1538
-
1539
- // The engine is the offerer, so the remote description only arrives with
1540
- // the answer; this candidate must be held, not refused.
1541
- peer.add_candidate(
1542
- String::from("candidate:1 1 udp 2113937151 192.0.2.1 40000 typ host"),
1543
- Some(String::from("0")),
1544
- Some(0),
1545
- )
1546
- .await
1547
- .expect("a candidate that arrives before the answer is buffered");
1548
- }
1549
-
1550
- #[tokio::test]
1551
- async fn packets_ask_for_no_playout_delay_only_when_the_answer_accepts_it() {
1552
- for accepts in [false, true] {
1553
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1554
- let (peer, offer) = VideoPeer::offer(
1555
- TransportOptions {
1556
- ice_servers: Vec::new(),
1557
- loopback_tcp: false,
1558
- pace_bps: 20_000_000.0,
1559
- },
1560
- events,
1561
- VideoCodec::Vp9,
1562
- )
1563
- .await
1564
- .expect("a peer connection");
1565
- // This answerer knows no playout-delay extension, so it declines it.
1566
- let other = answerer(&offer).await;
1567
- let answer = other.create_answer(None).await.expect("an answer");
1568
- let sdp = if accepts {
1569
- answer.sdp.replacen(
1570
- "a=mid:0\r\n",
1571
- &format!("a=mid:0\r\na=extmap:1 {PLAYOUT_DELAY_URI}\r\n"),
1572
- 1,
1573
- )
1574
- } else {
1575
- answer.sdp
1576
- };
1577
- assert_eq!(sdp.contains(PLAYOUT_DELAY_URI), accepts);
1578
- peer.accept_answer(sdp)
1579
- .await
1580
- .expect("the answer is accepted");
1581
- assert_eq!(
1582
- peer.playout_delay.load(Ordering::SeqCst),
1583
- accepts,
1584
- "a packet may carry the extension only when the answer accepted it",
1585
- );
1586
- }
1587
- }
1588
-
1589
- #[tokio::test]
1590
- async fn held_candidates_reach_the_peer_when_the_answer_arrives() {
1591
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1592
- let (peer, offer) = VideoPeer::offer(
1593
- TransportOptions {
1594
- ice_servers: Vec::new(),
1595
- loopback_tcp: false,
1596
- pace_bps: 20_000_000.0,
1597
- },
1598
- events,
1599
- VideoCodec::Vp9,
1600
- )
1601
- .await
1602
- .expect("a peer connection");
1603
-
1604
- let other = answerer(&offer).await;
1605
- let answer = other.create_answer(None).await.expect("an answer");
1606
- other
1607
- .set_local_description(answer.clone())
1608
- .await
1609
- .expect("a local description");
1610
-
1611
- peer.add_candidate(
1612
- String::from("candidate:1 1 udp 2113937151 192.0.2.1 40000 typ host"),
1613
- Some(String::from("0")),
1614
- Some(0),
1615
- )
1616
- .await
1617
- .expect("a candidate before the answer is buffered");
1618
-
1619
- let applied = peer
1620
- .accept_answer(answer.sdp)
1621
- .await
1622
- .expect("the answer is accepted");
1623
- assert_eq!(applied, 1, "the held candidate must reach the peer");
1624
- }
1625
-
1626
- #[tokio::test]
1627
- async fn an_ice_restart_offers_fresh_credentials_on_the_same_peer() {
1628
- let (events, _events_rx) = tokio::sync::mpsc::unbounded_channel();
1629
- let (peer, offer) = VideoPeer::offer(
1630
- TransportOptions {
1631
- ice_servers: Vec::new(),
1632
- loopback_tcp: false,
1633
- pace_bps: 20_000_000.0,
1634
- },
1635
- events,
1636
- VideoCodec::Vp9,
1637
- )
1638
- .await
1639
- .expect("a peer connection");
1640
-
1641
- let other = answerer(&offer).await;
1642
- let answer = other.create_answer(None).await.expect("an answer");
1643
- other
1644
- .set_local_description(answer.clone())
1645
- .await
1646
- .expect("a local description");
1647
- peer.accept_answer(answer.sdp)
1648
- .await
1649
- .expect("the answer is accepted");
1650
-
1651
- let ufrag = |sdp: &str| {
1652
- sdp.lines()
1653
- .find_map(|line| line.strip_prefix("a=ice-ufrag:"))
1654
- .expect("an ICE username fragment")
1655
- .to_owned()
1656
- };
1657
- let restart = peer.restart_ice().await.expect("a restart offer");
1658
- assert!(
1659
- !restart.is_empty() && ufrag(&restart) != ufrag(&offer),
1660
- "the restart must turn over the ICE generation"
1661
- );
1662
- // The far end answers the restart as usual, on the same peer.
1663
- other
1664
- .set_remote_description(RTCSessionDescription::offer(restart).expect("a valid offer"))
1665
- .await
1666
- .expect("the restart offer applies");
1667
- let reanswer = other.create_answer(None).await.expect("an answer");
1668
- other
1669
- .set_local_description(reanswer.clone())
1670
- .await
1671
- .expect("a local description");
1672
- peer.accept_answer(reanswer.sdp)
1673
- .await
1674
- .expect("the restart answer is accepted");
1675
- }
1676
-
1677
1244
  /// How long the restarted generation may take to nominate and reconnect,
1678
1245
  /// measured from the answered offer: two `check_interval` ticks. The vendored
1679
1246
  /// agent ticks every 200 ms (`vendor/rtc-ice/src/agent/agent_config.rs`), and
@@ -149,78 +149,3 @@ fn choose_cursor_mode(available: BitFlags<CursorMode>) -> Result<CursorMode> {
149
149
  .find(|mode| available.contains(*mode))
150
150
  .context("cursor-unavailable: portal cannot leave the cursor out of capture")
151
151
  }
152
-
153
- #[cfg(all(test, target_os = "linux"))]
154
- mod tests {
155
- use super::*;
156
- use std::process::{Child, Command, Stdio};
157
- use zbus::{connection::Builder, message::Type, MessageStream};
158
-
159
- struct Bus(Child);
160
-
161
- impl Drop for Bus {
162
- fn drop(&mut self) {
163
- let _ = self.0.kill();
164
- let _ = self.0.wait();
165
- }
166
- }
167
-
168
- /// A private session bus whose `org.freedesktop.portal.Desktop` owner
169
- /// answers every property read the way a stale xdg-desktop-portal answers
170
- /// for ScreenCast: no such interface.
171
- #[tokio::test]
172
- async fn open_names_the_fix_when_the_portal_exports_no_screencast() {
173
- use zbus::export::futures_core::Stream;
174
-
175
- let mut daemon = Command::new("dbus-daemon")
176
- .args(["--session", "--nofork", "--print-address=1"])
177
- .stdout(Stdio::piped())
178
- .spawn()
179
- .expect("dbus-daemon");
180
- let mut address = String::new();
181
- std::io::BufRead::read_line(
182
- &mut std::io::BufReader::new(daemon.stdout.take().unwrap()),
183
- &mut address,
184
- )
185
- .unwrap();
186
- let _bus = Bus(daemon);
187
- let address = address.trim().to_owned();
188
-
189
- let fake = Builder::address(address.as_str())
190
- .unwrap()
191
- .name("org.freedesktop.portal.Desktop")
192
- .unwrap()
193
- .build()
194
- .await
195
- .unwrap();
196
- let service = fake.clone();
197
- let answering = tokio::spawn(async move {
198
- let mut messages = MessageStream::from(&service);
199
- while let Some(Ok(message)) =
200
- std::future::poll_fn(|cx| std::pin::Pin::new(&mut messages).poll_next(cx)).await
201
- {
202
- if message.message_type() != Type::MethodCall {
203
- continue;
204
- }
205
- let _ = service
206
- .reply_dbus_error(
207
- &message.header(),
208
- zbus::fdo::Error::InvalidArgs(
209
- "No such interface \u{201c}org.freedesktop.portal.ScreenCast\u{201d}".into(),
210
- ),
211
- )
212
- .await;
213
- }
214
- });
215
- // Screencast::new reads the session bus from the environment.
216
- std::env::set_var("DBUS_SESSION_BUS_ADDRESS", &address);
217
-
218
- let error = match open(None, crate::protocol::CursorMode::Embedded).await {
219
- Ok(_) => panic!("a portal without ScreenCast must not open a session"),
220
- Err(error) => format!("{error:#}"),
221
- };
222
- answering.abort();
223
- assert!(error.starts_with("compositor has no ScreenCast portal"), "{error}");
224
- assert!(error.contains("systemctl --user restart xdg-desktop-portal"), "{error}");
225
- }
226
- }