@reactoo/watchtogether-sdk-js 2.7.89 → 2.7.91

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.
@@ -251,14 +251,15 @@ class RoomSession {
251
251
  this.privateId = null;
252
252
  this.constructId = constructId || RoomSession.randomString(16);
253
253
  this.sessionId = null;
254
+ this.apisecret = null;
254
255
  this.handleId = null;
255
256
  this.ws = null;
257
+ this.isSupposeToBeConnected = false;
256
258
  this.isRestarting = false;
257
259
  this.isConnecting = false;
258
260
  this.isDisconnecting = false;
259
261
  this.isConnected = false;
260
262
  this.isPublished = false;
261
- this.isReclaiming = false;
262
263
  this.isStreaming = false;
263
264
  this.isMuted = [];
264
265
  this.isVideoEnabled = false;
@@ -276,6 +277,9 @@ class RoomSession {
276
277
  this._sendMessageTimeout = 10000;
277
278
  this._retries = 0;
278
279
  this._maxRetries = 5;
280
+ this._keepAlivePeriod = 25000;
281
+ this._longPollTimeout = 60000;
282
+ this._maxev = 10;
279
283
  this._keepAliveId = null;
280
284
  this._participants = [];
281
285
  this._restrictSubscribeToUserIds = []; // all if empty
@@ -297,6 +301,86 @@ class RoomSession {
297
301
  }
298
302
  }
299
303
 
304
+ _httpAPICall = function(url, options) {
305
+ return new Promise((resolve, reject) => {
306
+ const xhr = new XMLHttpRequest();
307
+
308
+ xhr.open(options.verb || 'POST', url);
309
+
310
+ // Set default headers
311
+ xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
312
+ if (options.verb === "POST") {
313
+ xhr.setRequestHeader('Content-Type', 'application/json');
314
+ }
315
+
316
+ // Handle credentials
317
+ if (options.withCredentials) {
318
+ xhr.withCredentials = true;
319
+ }
320
+
321
+ // Set timeout if specified
322
+ if (options.timeout) {
323
+ xhr.timeout = options.timeout;
324
+ }
325
+
326
+ // Setup handlers
327
+ xhr.onload = function() {
328
+ if (xhr.status >= 200 && xhr.status < 300) {
329
+ let response;
330
+ try {
331
+ response = JSON.parse(xhr.responseText);
332
+ } catch(e) {
333
+ if (options.error) {
334
+ options.error('Invalid JSON', xhr.responseText);
335
+ }
336
+ reject('Invalid JSON');
337
+ return;
338
+ }
339
+ if (options.success) {
340
+ options.success(response);
341
+ }
342
+ resolve(response);
343
+ } else {
344
+ let errorText = xhr.statusText || 'HTTP Error';
345
+ if (options.error) {
346
+ options.error(errorText, xhr.responseText);
347
+ }
348
+ reject(errorText);
349
+ }
350
+ };
351
+
352
+ xhr.onerror = function() {
353
+ let errorText = 'Network error';
354
+ if (options.error) {
355
+ options.error(errorText, 'Is the server down?');
356
+ }
357
+ reject(errorText);
358
+ };
359
+
360
+ xhr.ontimeout = function() {
361
+ let errorText = 'Request timed out';
362
+ if (options.error) {
363
+ options.error(errorText, 'Is the server down?');
364
+ }
365
+ reject(errorText);
366
+ };
367
+
368
+ // Send request
369
+ try {
370
+ if (options.body) {
371
+ xhr.send(JSON.stringify(options.body));
372
+ } else {
373
+ xhr.send();
374
+ }
375
+ } catch(e) {
376
+ if (options.error) {
377
+ options.error('Error sending request', e);
378
+ }
379
+ reject(e);
380
+ }
381
+ });
382
+ }
383
+
300
384
  _pushToRemoteUsersCache(userId, streams, id) {
301
385
  const existingIndex = this._remoteUsersCache.findIndex(u => u.userId === userId);
302
386
  if (existingIndex > -1) {
@@ -431,13 +515,34 @@ class RoomSession {
431
515
  })
432
516
  }
433
517
 
434
- _send(request = {}, ignoreResponse = false, dontResolveOnAck = false, retry = 0) {
435
-
518
+ _sendHTTP(request = {}, ignoreResponse = false, dontResolveOnAck = false, retry = 0) {
436
519
  let transaction = RoomSession.randomString(12);
437
520
  let requestData = {
438
521
  ...request,
439
522
  transaction,
440
- token: this.token, ...((this.sessionId && {'session_id': this.sessionId}) || {})
523
+ token: this.token,
524
+ ...((this.sessionId && {'session_id': this.sessionId}) || {}),
525
+ ...((this.apisecret && {'apisecret': this.apisecret}) || {})
526
+ };
527
+ this._log(requestData);
528
+ return this._httpAPICall(this.server, {body:requestData})
529
+ .then(json => {
530
+ if(json['janus'] !== 'success' && json['janus'] !== 'ack') {
531
+ // TODO: check if this can stay like this
532
+ // not a success not an ack ... should be error
533
+ return Promise.reject(json["error"])
534
+ }
535
+ return json;
536
+ })
537
+ }
538
+ _sendWebsockets(request = {}, ignoreResponse = false, dontResolveOnAck = false, retry = 0) {
539
+ let transaction = RoomSession.randomString(12);
540
+ let requestData = {
541
+ ...request,
542
+ transaction,
543
+ token: this.token,
544
+ ...((this.sessionId && {'session_id': this.sessionId}) || {}),
545
+ ...((this.apisecret && {'apisecret': this.apisecret}) || {})
441
546
  };
442
547
  this._log(requestData);
443
548
  const op = () => new Promise((resolve, reject) => {
@@ -503,6 +608,50 @@ class RoomSession {
503
608
  }
504
609
  })
505
610
  }
611
+ _send(request = {}, ignoreResponse = false, dontResolveOnAck = false, retry = 0) {
612
+ if(this.useWebsockets) {
613
+ return this._sendWebsockets(request, ignoreResponse, dontResolveOnAck, retry);
614
+ }
615
+ else {
616
+ return this._sendHTTP(request, ignoreResponse, dontResolveOnAck, retry)
617
+ }
618
+ }
619
+
620
+ _longPoll() {
621
+
622
+ if(!this.isSupposeToBeConnected) {
623
+ return;
624
+ }
625
+
626
+
627
+ let longpoll = this.server + "/" + this.sessionId + "?rid=" + new Date().getTime();
628
+ if(this._maxev)
629
+ longpoll = longpoll + "&maxev=" + this._maxev;
630
+ if(this.token)
631
+ longpoll = longpoll + "&token=" + encodeURIComponent(this.token);
632
+ if(this.apisecret)
633
+ longpoll = longpoll + "&apisecret=" + encodeURIComponent(this.apisecret);
634
+
635
+ this._httpAPICall(longpoll, {
636
+ verb: 'GET',
637
+ timeout: this._longPollTimeout
638
+ })
639
+ .then(this._handleWsEvents.bind(this))
640
+ .catch(() => {
641
+
642
+ this._retries++;
643
+ if(this._retries > this._maxRetries) {
644
+ if (this.sessiontype === 'reactooroom') {
645
+ this.disconnect(true);
646
+ } else if (this.sessiontype === 'streaming') {
647
+ this.stopStream();
648
+ }
649
+ this.emit('error', {type: 'error', id: 33, message: 'Lost connection to server', data: null});
650
+ }
651
+
652
+ this._longPoll();
653
+ })
654
+ }
506
655
 
507
656
  _connectionClosed() {
508
657
 
@@ -555,16 +704,36 @@ class RoomSession {
555
704
 
556
705
  this._keepAliveId = setTimeout(() => {
557
706
  this._startKeepAlive();
558
- }, 10000);
707
+ }, this._keepAlivePeriod);
559
708
  }
560
709
 
561
710
  _stopKeepAlive() {
562
711
  clearTimeout(this._keepAliveId);
563
712
  }
564
713
 
565
- _handleWsEvents(event) {
714
+ _handleWsEvents(event, skipPoll = false) {
715
+
716
+ this._retries = 0;
717
+
718
+ // we have a response from long poll, that means we need to run it again
719
+ if(!this.useWebsockets && !skipPoll) {
720
+ this._longPoll();
721
+ }
722
+
723
+ if(!this.useWebsockets && skipPoll) {
724
+ // we need to fire those events into the wild
725
+ this.emit('longPollEvent', event);
726
+ }
727
+
728
+ if(Array.isArray(event)) {
729
+ event.forEach(ev => this._handleWsEvents({data:ev}, true));
730
+ return;
731
+ }
732
+
733
+ let json = typeof event.data === 'string'
734
+ ? JSON.parse(event.data)
735
+ : event.data;
566
736
 
567
- let json = JSON.parse(event.data);
568
737
  var sender = json["sender"];
569
738
  var type = json["janus"];
570
739
 
@@ -1274,14 +1443,15 @@ class RoomSession {
1274
1443
  : Promise.resolve();
1275
1444
  }
1276
1445
 
1277
- // internal reconnect
1278
-
1279
- _reconnect() {
1446
+ _webSocketConnection(reclaim = false) {
1280
1447
 
1281
- if (this.isReclaiming) {
1282
- return Promise.resolve();
1448
+ if (this.isConnecting) {
1449
+ return Promise.reject({type: 'warning', id: 16, message: 'connection establishment already in progress'});
1283
1450
  }
1284
1451
 
1452
+ this.isConnecting = true;
1453
+ this.emit('joining', true);
1454
+
1285
1455
  if (this.ws) {
1286
1456
  this._wipeListeners();
1287
1457
  if (this.ws.readyState === 1) {
@@ -1289,60 +1459,153 @@ class RoomSession {
1289
1459
  }
1290
1460
  }
1291
1461
  this._stopKeepAlive();
1292
- this.isReclaiming = true;
1293
- this.emit('joining', true);
1462
+
1294
1463
  return new Promise((resolve, reject) => {
1295
1464
 
1296
- let abortReconnect = () => {
1297
- this._abortController.signal.removeEventListener('abort', abortReconnect);
1465
+ this.__connectionClosedBoundFn = this._connectionClosed.bind(this);
1466
+ this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);
1467
+
1468
+ let abortConnect = () => {
1469
+ this._abortController.signal.removeEventListener('abort', abortConnect);
1298
1470
  this.ws.removeEventListener('close', this.__connectionClosedBoundFn);
1299
1471
  this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);
1300
1472
  this.ws.onopen = null;
1301
1473
  this.ws.onerror = null;
1302
- this.isReclaiming = false;
1474
+ this.isConnecting = false;
1303
1475
  this.emit('joining', false);
1304
- reject({type: 'warning', id: 17, message: 'Connection cancelled', data: e});
1476
+ reject({type: 'warning', id: 17, message: 'Connection cancelled'});
1305
1477
  };
1306
1478
 
1307
1479
  this.ws = new WebSocket(this.server, 'janus-protocol');
1308
- this.__connectionClosedBoundFn = this._connectionClosed.bind(this);
1309
- this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);
1310
1480
  this.ws.addEventListener('close', this.__connectionClosedBoundFn);
1311
1481
  this.ws.addEventListener('message', this.__handleWsEventsBoundFn);
1482
+
1312
1483
  this.ws.onopen = () => {
1313
- this._abortController.signal.removeEventListener('abort', abortReconnect);
1314
- this._send({"janus": "claim"})
1315
- .then(json => {
1316
- this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1317
- this._startKeepAlive();
1318
- this.isReclaiming = false;
1319
- this.emit('joining', false);
1320
- this._retries = 0;
1321
- resolve(json);
1322
- })
1323
- .catch(error => {
1324
- this.isReclaiming = false;
1325
- this.emit('joining', false);
1326
- reject({type: 'error', id: 11, message: 'reconnection error', data: error})
1327
- });
1484
+ this._abortController.signal.removeEventListener('abort', abortConnect);
1485
+
1486
+ if(!reclaim) {
1487
+
1488
+ this._send({"janus": "create"})
1489
+ .then(json => {
1490
+ this._retries = 0;
1491
+ this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1492
+ this._startKeepAlive();
1493
+ return 1;
1494
+ })
1495
+ .then(() => this._createParticipant(this.userId))
1496
+ .then(handle => {
1497
+ this.handleId = handle.handleId;
1498
+ return 1
1499
+ })
1500
+ .then(() => this._joinRoom(this.roomId, this.pin, this.userId, this.display))
1501
+ .then(() => {
1502
+ this._enableStatsWatch();
1503
+ this._enableSubstreamAutoSelect();
1504
+ this.isConnecting = false;
1505
+ this.emit('joining', false);
1506
+ resolve(this);
1507
+ })
1508
+ .catch(error => {
1509
+ this.isConnecting = false;
1510
+ this.emit('joining', false);
1511
+ reject({type: error?.type === 'warning' ? 'warning' : 'error', id: 13, message: 'connection error', data: error})
1512
+ });
1513
+
1514
+ }
1515
+
1516
+ else {
1517
+ this._send({"janus": "claim"})
1518
+ .then(json => {
1519
+ this._retries = 0;
1520
+ this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1521
+ this._startKeepAlive();
1522
+ this.isConnecting = false;
1523
+ this.emit('joining', false);
1524
+ resolve(json);
1525
+ })
1526
+ .catch(error => {
1527
+ this.isConnecting = false;
1528
+ this.emit('joining', false);
1529
+ reject({type: 'error', id: 11, message: 'reconnection error', data: error})
1530
+ });
1531
+ }
1532
+
1328
1533
  };
1329
1534
 
1330
- // this is called before 'close' event callback so it doesn't break reconnect loop
1331
1535
  this.ws.onerror = (e) => {
1332
- this._abortController.signal.removeEventListener('abort', abortReconnect);
1333
- this.isReclaiming = false;
1536
+ this._abortController.signal.removeEventListener('abort', abortConnect);
1537
+ this.isConnecting = false;
1334
1538
  this.emit('joining', false);
1335
- reject({type: 'warning', id: 12, message: 'ws reconnection error', data: e});
1539
+ reject({type: 'error', id: 14, message: 'ws connection error', data: e});
1336
1540
  }
1337
1541
 
1338
- this._abortController.signal.addEventListener('abort', abortReconnect);
1542
+ this._abortController.signal.addEventListener('abort', abortConnect);
1543
+
1339
1544
  });
1340
1545
  }
1341
1546
 
1547
+ _httpConnection(reclaim = false) {
1548
+ if(!reclaim) {
1549
+ return this._send({"janus": "create"})
1550
+ .then(json => {
1551
+ this._retries = 0;
1552
+ this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1553
+ this._startKeepAlive();
1554
+ return 1;
1555
+ })
1556
+ .then(() => this._createParticipant(this.userId))
1557
+ .then(handle => {
1558
+ this.handleId = handle.handleId;
1559
+ return 1
1560
+ })
1561
+ .then(() => this._joinRoom(this.roomId, this.pin, this.userId, this.display))
1562
+ .then(() => {
1563
+ this._enableStatsWatch();
1564
+ this._enableSubstreamAutoSelect();
1565
+ this._longPoll();
1566
+ this.isConnecting = false;
1567
+ this.emit('joining', false);
1568
+ return Promise.resolve(this);
1569
+ })
1570
+ .catch(error => {
1571
+ this.isConnecting = false;
1572
+ this.emit('joining', false);
1573
+ return Promise.reject({type: error?.type === 'warning' ? 'warning' : 'error', id: 13, message: 'connection error', data: error})
1574
+ });
1575
+ }
1576
+ else {
1577
+ return this._send({"janus": "claim"})
1578
+ .then(json => {
1579
+ this._retries = 0;
1580
+ this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1581
+ this._startKeepAlive();
1582
+ this.isConnecting = false;
1583
+ this.emit('joining', false);
1584
+ return Promise.resolve(json);
1585
+ })
1586
+ .catch(error => {
1587
+ this.isConnecting = false;
1588
+ this.emit('joining', false);
1589
+ return Promise.reject({type: 'error', id: 11, message: 'reconnection error', data: error})
1590
+ });
1591
+ }
1592
+ }
1593
+
1594
+
1595
+ // internal reconnect
1596
+ _reconnect() {
1597
+ if(this.useWebsockets) {
1598
+ return this._webSocketConnection(true)
1599
+ }
1600
+ else {
1601
+ return this._httpConnection(true)
1602
+ }
1603
+ }
1342
1604
  connect(
1343
1605
  roomId,
1344
1606
  pin,
1345
1607
  server,
1608
+ protocol,
1346
1609
  iceServers,
1347
1610
  token,
1348
1611
  display,
@@ -1355,18 +1618,17 @@ class RoomSession {
1355
1618
  enableDtx = false
1356
1619
  ) {
1357
1620
 
1621
+ this.isSupposeToBeConnected = true;
1622
+
1358
1623
  if (this.isConnecting) {
1359
1624
  return Promise.reject({type: 'warning', id: 16, message: 'connection already in progress'});
1360
1625
  }
1361
1626
 
1362
- if (this.ws) {
1363
- this._wipeListeners();
1364
- }
1365
- this._stopKeepAlive();
1366
1627
  this._abortController = new AbortController();
1367
1628
 
1368
1629
  this.sessionId = null;
1369
1630
  this.server = server;
1631
+ this.protocol = protocol;
1370
1632
  this.iceServers = iceServers;
1371
1633
  this.token = token;
1372
1634
  this.roomId = roomId;
@@ -1376,10 +1638,10 @@ class RoomSession {
1376
1638
  this.webrtcVersion = webrtcVersion;
1377
1639
  this.initialBitrate = initialBitrate;
1378
1640
  this.recordingFilename = recordingFilename;
1379
- this.isConnecting = true;
1380
1641
  this.enableDtx = enableDtx;
1381
1642
  this.simulcast = simulcast;
1382
1643
  this.simulcastSettings = structuredClone(simulcastSettings);
1644
+ this.useWebsockets = this.protocol === 'ws' || this.protocol === 'wss';
1383
1645
 
1384
1646
  // sort simulcast bitrates
1385
1647
  if(this.simulcastSettings && typeof this.simulcastSettings === 'object' && Object.keys(this.simulcastSettings).length) {
@@ -1393,69 +1655,18 @@ class RoomSession {
1393
1655
  });
1394
1656
  }
1395
1657
 
1396
- this.emit('joining', true);
1397
- return new Promise((resolve, reject) => {
1398
-
1399
- this.ws = new WebSocket(this.server, 'janus-protocol');
1400
- this.__connectionClosedBoundFn = this._connectionClosed.bind(this);
1401
- this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);
1402
-
1403
- let abortConnect = () => {
1404
- this._abortController.signal.removeEventListener('abort', abortConnect);
1405
- this.ws.removeEventListener('close', this.__connectionClosedBoundFn);
1406
- this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);
1407
- this.ws.onopen = null;
1408
- this.ws.onerror = null;
1409
- this.isConnecting = false;
1410
- this.emit('joining', false);
1411
- reject({type: 'warning', id: 17, message: 'Connection cancelled'});
1412
- };
1413
-
1414
- this.ws.addEventListener('close', this.__connectionClosedBoundFn);
1415
- this.ws.addEventListener('message', this.__handleWsEventsBoundFn);
1416
- this.ws.onopen = () => {
1417
- this._abortController.signal.removeEventListener('abort', abortConnect);
1418
- this._retries = 0;
1419
- this._send({"janus": "create"})
1420
- .then(json => {
1421
- this.sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
1422
- this._startKeepAlive();
1423
- return 1;
1424
- })
1425
- .then(() => this._createParticipant(userId))
1426
- .then(handle => {
1427
- this.handleId = handle.handleId;
1428
- return 1
1429
- })
1430
- .then(() => this._joinRoom(roomId, pin, userId, display))
1431
- .then(() => {
1432
- this._enableStatsWatch();
1433
- this._enableSubstreamAutoSelect();
1434
- this.isConnecting = false;
1435
- this.emit('joining', false);
1436
- resolve(this);
1437
- })
1438
- .catch(error => {
1439
- this.isConnecting = false;
1440
- this.emit('joining', false);
1441
- reject({type: error?.type === 'warning' ? 'warning' : 'error', id: 13, message: 'connection error', data: error})
1442
- });
1443
- };
1444
-
1445
- this.ws.onerror = (e) => {
1446
- this._abortController.signal.removeEventListener('abort', abortConnect);
1447
- this.isConnecting = false;
1448
- this.emit('joining', false);
1449
- reject({type: 'error', id: 14, message: 'ws connection error', data: e});
1450
- }
1451
-
1452
- this._abortController.signal.addEventListener('abort', abortConnect);
1453
-
1454
- });
1658
+ if(this.useWebsockets) {
1659
+ return this._webSocketConnection(false)
1660
+ }
1661
+ else {
1662
+ return this._httpConnection(false)
1663
+ }
1455
1664
  }
1456
1665
 
1457
1666
  disconnect() {
1458
1667
 
1668
+ this.isSupposeToBeConnected = false;
1669
+
1459
1670
  if (this.isDisconnecting) {
1460
1671
  return Promise.resolve();
1461
1672
  }
@@ -1488,6 +1699,8 @@ class RoomSession {
1488
1699
 
1489
1700
  startStream(streamId, server, iceServers, token, userId) {
1490
1701
 
1702
+ this.isSupposeToBeConnected = true;
1703
+
1491
1704
  if (this.isConnecting) {
1492
1705
  return Promise.reject({type: 'warning', id: 16, message: 'connection error', data: 'Connection is in progress'});
1493
1706
  }
@@ -1789,7 +2002,7 @@ class RoomSession {
1789
2002
 
1790
2003
  _parseVideoStats(participantsStats) {
1791
2004
 
1792
- let statsToEmitt = [];
2005
+ let statsToEmit = [];
1793
2006
 
1794
2007
  for (const sourceStats of participantsStats) {
1795
2008
 
@@ -1865,7 +2078,7 @@ class RoomSession {
1865
2078
  handle.webrtcStuff.stats[mid].shift();
1866
2079
  }
1867
2080
 
1868
- statsToEmitt.push({
2081
+ statsToEmit.push({
1869
2082
  handleId: participantStats.handleId,
1870
2083
  stats,
1871
2084
  userId: decodeJanusDisplay(handle.userId)?.userId,
@@ -1875,7 +2088,7 @@ class RoomSession {
1875
2088
  }
1876
2089
  }
1877
2090
 
1878
- this.emit('rtcStats', statsToEmitt);
2091
+ this.emit('rtcStats', statsToEmit);
1879
2092
  }
1880
2093
 
1881
2094
  _getStats(type = null) {
@@ -2125,6 +2338,7 @@ class RoomSession {
2125
2338
  if(event.track.kind === 'video') {
2126
2339
  this.requestKeyFrame(handle.handleId, mid);
2127
2340
  }
2341
+
2128
2342
  this.emit(this._getAddParticipantEventName(handle.handleId), {
2129
2343
  tid: generateUUID(),
2130
2344
  mid,
@@ -2831,28 +3045,11 @@ class RoomSession {
2831
3045
  config.pc.addTrack(stream.getVideoTracks()[0], config.stream);
2832
3046
  }
2833
3047
  else {
2834
- if(adapter.browserDetails.browser !== 'firefox') {
2835
-
2836
- // standard
2837
- config.pc.addTransceiver(stream.getVideoTracks()[0], {
2838
- direction: 'sendonly',
2839
- streams: [config.stream],
2840
- sendEncodings: structuredClone(simulcastConfigForSource?.bitrates)
2841
- })
2842
- }
2843
- else {
2844
- // firefox
2845
- let transceiver = config.pc.addTransceiver(stream.getVideoTracks()[0], {
2846
- direction: 'sendonly',
2847
- streams: [config.stream]
2848
- });
2849
- let sender = transceiver ? transceiver.sender : null;
2850
- if(sender) {
2851
- let parameters = sender.getParameters() || {};
2852
- parameters.encodings = stream.getVideoTracks()[0].sendEncodings || structuredClone(simulcastConfigForSource?.bitrates);
2853
- sender.setParameters(parameters);
2854
- }
2855
- }
3048
+ config.pc.addTransceiver(stream.getVideoTracks()[0], {
3049
+ direction: 'sendonly',
3050
+ streams: [config.stream],
3051
+ sendEncodings: structuredClone(simulcastConfigForSource?.bitrates)
3052
+ })
2856
3053
  }
2857
3054
  needsNegotiation = true;
2858
3055
  }
@@ -3224,23 +3421,31 @@ class RoomSession {
3224
3421
  let config = handle.webrtcStuff;
3225
3422
  return new Promise((resolve, reject) => {
3226
3423
  let messageTimeoutId;
3227
- let abortResponse = () => {
3424
+
3425
+ let clearListeners = () => {
3228
3426
  clearTimeout(messageTimeoutId);
3229
3427
  this._abortController.signal.removeEventListener('abort', abortResponse);
3230
- this.ws.removeEventListener('message', parseResponse);
3428
+ this.off('longPollEvent', parseResponse);
3429
+ this.ws?.removeEventListener('message', parseResponse);
3430
+ };
3431
+
3432
+ let abortResponse = () => {
3433
+ clearListeners();
3231
3434
  reject('aborted');
3232
3435
  };
3436
+
3233
3437
  let parseResponse = (event) => {
3234
- let json = JSON.parse(event.data);
3438
+ let json = typeof event.data === 'string'
3439
+ ? JSON.parse(event.data)
3440
+ : event.data;
3441
+
3235
3442
  var sender = json["sender"];
3236
3443
  if(sender === handleId) {
3237
3444
  let plugindata = json["plugindata"] || {};
3238
3445
  let msg = plugindata["data"] || {};
3239
3446
  let substream = msg["substream"];
3240
3447
  if(substream !== undefined && substream !== null && (mid !== undefined ? msg["mid"] === mid : true)) {
3241
- clearTimeout(messageTimeoutId);
3242
- this._abortController.signal.removeEventListener('abort', abortResponse);
3243
- this.ws.removeEventListener('message', parseResponse);
3448
+ clearListeners();
3244
3449
  resolve({substream, sender});
3245
3450
  }
3246
3451
  }
@@ -3280,11 +3485,11 @@ class RoomSession {
3280
3485
  config.overriddenSimulcastMode[mid]['mode'] = "manual";
3281
3486
  }
3282
3487
 
3283
- this.ws.addEventListener('message', parseResponse);
3488
+ this.on('longPollEvent', parseResponse);
3489
+ this.ws?.addEventListener('message', parseResponse);
3284
3490
  this._abortController.signal.addEventListener('abort', abortResponse);
3285
3491
  messageTimeoutId = setTimeout(() => {
3286
- this._abortController.signal.removeEventListener('abort', abortResponse);
3287
- this.ws.removeEventListener('message', parseResponse);
3492
+ clearListeners();
3288
3493
  reject('timeout');
3289
3494
  }, 10000);
3290
3495