@reactoo/watchtogether-sdk-js 2.5.82 → 2.5.85

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.
@@ -166,6 +166,7 @@ class RoomSession {
166
166
  constructor(constructId = null, type = 'reactooroom', options = {}) {
167
167
 
168
168
  Object.assign(this, emitter());
169
+ this.options = {...options};
169
170
  this.defaultDataChannelLabel = 'JanusDataChannel'
170
171
  this.server = null;
171
172
  this.iceServers = null;
@@ -176,9 +177,14 @@ class RoomSession {
176
177
  this.userId = null;
177
178
  this.sessiontype = type;
178
179
  this.initialBitrate = 0;
180
+ this.simulcast = false;
181
+ this.simulcastBitrates = {
182
+ high: 900000,
183
+ medium: 300000,
184
+ low: 100000
185
+ };
179
186
  this.recordingFilename = null;
180
187
  this.pluginName = RoomSession.sessionTypes[type];
181
- this.options = options;
182
188
  this.id = null;
183
189
  this.privateId = null;
184
190
  this.constructId = constructId || RoomSession.randomString(16);
@@ -186,7 +192,6 @@ class RoomSession {
186
192
  this.handleId = null;
187
193
  this.ws = null;
188
194
  this.isRestarting = false;
189
-
190
195
  this.isConnecting = false;
191
196
  this.isDisconnecting = false;
192
197
  this.isConnected = false;
@@ -206,7 +211,6 @@ class RoomSession {
206
211
  this.isVideoMuted = false;
207
212
  this.isVideoEnabled = false;
208
213
  this.isAudioEnabed = false;
209
- this.isUnifiedPlan = RoomSession.checkUnifiedPlan();
210
214
 
211
215
  this.subscriptionRules = {
212
216
  ...RoomSession.subscriptionRules,
@@ -220,35 +224,6 @@ class RoomSession {
220
224
 
221
225
  }
222
226
 
223
- // Check if this browser supports Unified Plan and transceivers
224
- // Based on https://codepen.io/anon/pen/ZqLwWV?editors=0010
225
- static checkUnifiedPlan() {
226
- let unifiedPlan = false;
227
- if (adapter.browserDetails.browser === 'firefox' &&
228
- adapter.browserDetails.version >= 59) {
229
- // Firefox definitely does, starting from version 59
230
- unifiedPlan = true;
231
- } else if (adapter.browserDetails.browser === 'chrome' &&
232
- adapter.browserDetails.version >= 72) {
233
- // Chrome does, but it's only usable from version 72 on
234
- unifiedPlan = true;
235
- } else if (!window.RTCRtpTransceiver || !('currentDirection' in RTCRtpTransceiver.prototype)) {
236
- // Safari supports addTransceiver() but not Unified Plan when
237
- // currentDirection is not defined (see codepen above).
238
- unifiedPlan = false;
239
- } else {
240
- // Check if addTransceiver() throws an exception
241
- const tempPc = new RTCPeerConnection();
242
- try {
243
- tempPc.addTransceiver('audio');
244
- unifiedPlan = true;
245
- } catch (e) {
246
- }
247
- tempPc.close();
248
- }
249
- return unifiedPlan;
250
- }
251
-
252
227
  _participantShouldSubscribe(userId) {
253
228
  const myUser = decodeJanusDisplay(this.display);
254
229
  const remoteUser = decodeJanusDisplay(userId);
@@ -564,7 +539,8 @@ class RoomSession {
564
539
  "request": "join",
565
540
  "room": this.roomId,
566
541
  "ptype": "subscriber",
567
- "feed": id,
542
+ streams: streams.map(stream => ({feed: stream.id, mid: stream.mid})),
543
+ //"feed": id,
568
544
  "private_id": this.privateId,
569
545
  ...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
570
546
  pin: this.pin
@@ -576,7 +552,8 @@ class RoomSession {
576
552
  })
577
553
  }
578
554
  }
579
- } else if (event === "event") {
555
+ }
556
+ else if (event === "event") {
580
557
 
581
558
  if (msg["streams"] !== undefined && msg["streams"] !== null) {
582
559
  this._log('Got my own streams back', msg["streams"]);
@@ -600,7 +577,7 @@ class RoomSession {
600
577
  "request": "join",
601
578
  "room": this.roomId,
602
579
  "ptype": "subscriber",
603
- "feed": id,
580
+ streams: streams.map(stream => ({feed: stream.id, mid: stream.mid})),
604
581
  "private_id": this.privateId,
605
582
  ...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
606
583
  pin: this.pin
@@ -679,6 +656,11 @@ class RoomSession {
679
656
 
680
657
  }
681
658
  } else if (type === "webrtcup") {
659
+
660
+ if(this.simulcast) {
661
+ return;
662
+ }
663
+
682
664
  this._log('Configuring bitrate: ' + this.initialBitrate);
683
665
  if (this.initialBitrate > 0) {
684
666
  this.sendMessage(this.handleId, {
@@ -700,6 +682,7 @@ class RoomSession {
700
682
  let jsep = json["jsep"];
701
683
  let event = msg["videoroom"];
702
684
  let error = msg["error"];
685
+ let substream = msg["substream"];
703
686
 
704
687
  if (event === "attached") {
705
688
  this._log('Remote have successfully joined Room', msg);
@@ -982,7 +965,7 @@ class RoomSession {
982
965
  });
983
966
  }
984
967
 
985
- connect(roomId, pin, server, iceServers, token, display, userId, webrtcVersion = 0, initialBitrate = 0, recordingFilename) {
968
+ connect(roomId, pin, server, iceServers, token, display, userId, webrtcVersion = 0, initialBitrate = 0, recordingFilename, simulcast = false, simulcastBitrates = {}) {
986
969
 
987
970
  if (this.isConnecting) {
988
971
  return Promise.reject({type: 'warning', id: 16, message: 'connection already in progress'});
@@ -993,6 +976,7 @@ class RoomSession {
993
976
  }
994
977
  this._stopKeepAlive();
995
978
  this._abortController = new AbortController();
979
+
996
980
  this.sessionId = null;
997
981
  this.server = server;
998
982
  this.iceServers = iceServers;
@@ -1005,6 +989,9 @@ class RoomSession {
1005
989
  this.initialBitrate = initialBitrate;
1006
990
  this.recordingFilename = recordingFilename;
1007
991
  this.isConnecting = true;
992
+ this.simulcast = simulcast;
993
+ this.simulcastBitrates = {...this.simulcastBitrates, ...simulcastBitrates};
994
+
1008
995
  this.emit('joining', true);
1009
996
  return new Promise((resolve, reject) => {
1010
997
 
@@ -1263,7 +1250,7 @@ class RoomSession {
1263
1250
  if (!config.pc) {
1264
1251
  let pc_config = {"iceServers": this.iceServers, "iceTransportPolicy": 'all', "bundlePolicy": undefined};
1265
1252
 
1266
- pc_config["sdpSemantics"] = this.isUnifiedPlan ? "unified-plan" : "plan-b";
1253
+ pc_config["sdpSemantics"] = "unified-plan";
1267
1254
 
1268
1255
  let pc_constraints = {};
1269
1256
 
@@ -1281,6 +1268,7 @@ class RoomSession {
1281
1268
  }
1282
1269
  this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);
1283
1270
  if(handleId !== this.handleId && config.pc.connectionState === 'connected') {
1271
+
1284
1272
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1285
1273
  tid: generateUUID(),
1286
1274
  id: handle.handleId,
@@ -1376,12 +1364,9 @@ class RoomSession {
1376
1364
 
1377
1365
  event.track.onended = (ev) => {
1378
1366
 
1379
- let mid = ev.target.id;
1380
- if(this.isUnifiedPlan) {
1381
- let transceiver = config.pc.getTransceivers().find(
1382
- t => t.receiver.track === ev.target);
1383
- mid = transceiver.mid;
1384
- }
1367
+ let transceiver = config.pc?.getTransceivers()?.find(
1368
+ t => t.receiver.track === ev.target);
1369
+ let mid = transceiver?.mid || ev.target.id;
1385
1370
 
1386
1371
  if (config.stream) {
1387
1372
  config.stream && config.stream.removeTrack(ev.target);
@@ -1406,12 +1391,9 @@ class RoomSession {
1406
1391
  event.track.onmute = (ev) => {
1407
1392
  this._log('remoteTrackMuted', 'onmute');
1408
1393
 
1409
- let mid = ev.target.id;
1410
- if(this.isUnifiedPlan) {
1411
- let transceiver = config.pc.getTransceivers().find(
1412
- t => t.receiver.track === ev.target);
1413
- mid = transceiver.mid;
1414
- }
1394
+ let transceiver = config.pc.getTransceivers().find(
1395
+ t => t.receiver.track === ev.target);
1396
+ let mid = transceiver.mid || ev.target.id;
1415
1397
 
1416
1398
  this.emit('remoteTrackMuted', {
1417
1399
  id: handle.handleId,
@@ -1428,12 +1410,9 @@ class RoomSession {
1428
1410
  event.track.onunmute = (ev) => {
1429
1411
  this._log('remoteTrackMuted', 'onunmute');
1430
1412
 
1431
- let mid = ev.target.id;
1432
- if(this.isUnifiedPlan) {
1433
- let transceiver = config.pc.getTransceivers().find(
1434
- t => t.receiver.track === ev.target);
1435
- mid = transceiver.mid;
1436
- }
1413
+ let transceiver = config.pc.getTransceivers().find(
1414
+ t => t.receiver.track === ev.target);
1415
+ let mid = transceiver.mid || ev.target.id;
1437
1416
 
1438
1417
  this.emit('remoteTrackMuted', {
1439
1418
  id: handle.handleId,
@@ -1552,7 +1531,8 @@ class RoomSession {
1552
1531
  config.isIceRestarting = true;
1553
1532
  let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);
1554
1533
  let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);
1555
- this._createAO('offer', handleId, true, [hasAudio, false, hasVideo, false])
1534
+ this._setupTransceivers([hasAudio, false, hasVideo, false]);
1535
+ this._createAO('offer', handleId, true )
1556
1536
  .then((jsep) => {
1557
1537
  if (!jsep) {
1558
1538
  return null;
@@ -1568,7 +1548,7 @@ class RoomSession {
1568
1548
  })
1569
1549
  .catch((e) => {
1570
1550
  config.isIceRestarting = false;
1571
- this.emit('warning', {type: 'error', id: 28, message: 'iceRestart failed', data: e});
1551
+ this.emit('error', {type: 'warning', id: 28, message: 'iceRestart failed', data: e});
1572
1552
  });
1573
1553
  } else {
1574
1554
  this._log('Performing remote ICE restart', handleId);
@@ -1584,148 +1564,139 @@ class RoomSession {
1584
1564
 
1585
1565
  }
1586
1566
 
1587
- _createAO(type = 'offer', handleId, iceRestart = false, [audioSend, audioRecv, videoSend, videoRecv]) {
1567
+ _setupTransceivers(handleId, [audioSend, audioRecv, videoSend, videoRecv]) {
1588
1568
 
1589
1569
  let handle = this._getHandle(handleId);
1590
1570
  if (!handle) {
1591
- return Promise.reject({
1592
- type: 'warning',
1593
- id: 15,
1594
- message: 'id non-existent',
1595
- data: [handleId, 'createAO', type]
1596
- });
1571
+ return null;
1597
1572
  }
1598
1573
 
1599
- let methodName = null;
1600
- if (type === 'offer') {
1601
- methodName = 'createOffer'
1602
- } else {
1603
- methodName = 'createAnswer'
1574
+ let config = handle.webrtcStuff;
1575
+ let audioTransceiver = null, videoTransceiver = null;
1576
+ let transceivers = config.pc.getTransceivers();
1577
+ if (transceivers && transceivers.length > 0) {
1578
+ for (var i in transceivers) {
1579
+ var t = transceivers[i];
1580
+ if ((t.sender && t.sender.track && t.sender.track.kind === "audio") ||
1581
+ (t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) {
1582
+ if (!audioTransceiver)
1583
+ audioTransceiver = t;
1584
+ continue;
1585
+ }
1586
+ if ((t.sender && t.sender.track && t.sender.track.kind === "video") ||
1587
+ (t.receiver && t.receiver.track && t.receiver.track.kind === "video")) {
1588
+ if (!videoTransceiver)
1589
+ videoTransceiver = t;
1590
+ continue;
1591
+ }
1592
+ }
1604
1593
  }
1605
1594
 
1606
- var config = handle.webrtcStuff;
1607
-
1608
- // https://code.google.com/p/webrtc/issues/detail?id=3508
1609
- var mediaConstraints = {};
1610
- if (this.isUnifiedPlan) {
1611
- var audioTransceiver = null, videoTransceiver = null;
1612
- var transceivers = config.pc.getTransceivers();
1613
- if (transceivers && transceivers.length > 0) {
1614
- for (var i in transceivers) {
1615
- var t = transceivers[i];
1616
- if ((t.sender && t.sender.track && t.sender.track.kind === "audio" && t.stopped === false) ||
1617
- (t.receiver && t.receiver.track && t.receiver.track.kind === "audio" && t.stopped === false)) {
1618
- if (!audioTransceiver)
1619
- audioTransceiver = t;
1620
- continue;
1621
- }
1622
- if ((t.sender && t.sender.track && t.sender.track.kind === "video" && t.stopped === false) ||
1623
- (t.receiver && t.receiver.track && t.receiver.track.kind === "video" && t.stopped === false)) {
1624
- if (!videoTransceiver)
1625
- videoTransceiver = t;
1626
- continue;
1627
- }
1595
+ // Handle audio (and related changes, if any)
1596
+ if (!audioSend && !audioRecv) {
1597
+ // Audio disabled: have we removed it?
1598
+ if (audioTransceiver) {
1599
+ if (audioTransceiver.setDirection) {
1600
+ audioTransceiver.setDirection("inactive");
1601
+ } else {
1602
+ audioTransceiver.direction = "inactive";
1628
1603
  }
1629
1604
  }
1630
-
1631
- // Handle audio (and related changes, if any)
1632
- if (!audioSend && !audioRecv) {
1633
- // Audio disabled: have we removed it?
1605
+ } else {
1606
+ // Take care of audio m-line
1607
+ if (audioSend && audioRecv) {
1634
1608
  if (audioTransceiver) {
1635
1609
  if (audioTransceiver.setDirection) {
1636
- audioTransceiver.setDirection("inactive");
1610
+ audioTransceiver.setDirection("sendrecv");
1637
1611
  } else {
1638
- audioTransceiver.direction = "inactive";
1612
+ audioTransceiver.direction = "sendrecv";
1639
1613
  }
1640
1614
  }
1641
- } else {
1642
- // Take care of audio m-line
1643
- if (audioSend && audioRecv) {
1644
- if (audioTransceiver) {
1645
- if (audioTransceiver.setDirection) {
1646
- audioTransceiver.setDirection("sendrecv");
1647
- } else {
1648
- audioTransceiver.direction = "sendrecv";
1649
- }
1650
- }
1651
- } else if (audioSend && !audioRecv) {
1652
- if (audioTransceiver) {
1653
- if (audioTransceiver.setDirection) {
1654
- audioTransceiver.setDirection("sendonly");
1655
- } else {
1656
- audioTransceiver.direction = "sendonly";
1657
- }
1615
+ } else if (audioSend && !audioRecv) {
1616
+ if (audioTransceiver) {
1617
+ if (audioTransceiver.setDirection) {
1618
+ audioTransceiver.setDirection("sendonly");
1619
+ } else {
1620
+ audioTransceiver.direction = "sendonly";
1658
1621
  }
1659
- } else if (!audioSend && audioRecv) {
1660
- if (audioTransceiver) {
1661
- if (audioTransceiver.setDirection) {
1662
- audioTransceiver.setDirection("recvonly");
1663
- } else {
1664
- audioTransceiver.direction = "recvonly";
1665
- }
1622
+ }
1623
+ } else if (!audioSend && audioRecv) {
1624
+ if (audioTransceiver) {
1625
+ if (audioTransceiver.setDirection) {
1626
+ audioTransceiver.setDirection("recvonly");
1666
1627
  } else {
1667
- // In theory, this is the only case where we might not have a transceiver yet
1668
- audioTransceiver = config.pc.addTransceiver("audio", {direction: "recvonly"});
1628
+ audioTransceiver.direction = "recvonly";
1669
1629
  }
1630
+ } else {
1631
+ // In theory, this is the only case where we might not have a transceiver yet
1632
+ config.pc.addTransceiver("audio", {direction: "recvonly"});
1633
+ }
1634
+ }
1635
+ }
1636
+ // Handle video (and related changes, if any)
1637
+ if (!videoSend && !videoRecv) {
1638
+ if (videoTransceiver) {
1639
+ if (videoTransceiver.setDirection) {
1640
+ videoTransceiver.setDirection("inactive");
1641
+ } else {
1642
+ videoTransceiver.direction = "inactive";
1670
1643
  }
1671
1644
  }
1672
- // Handle video (and related changes, if any)
1673
- if (!videoSend && !videoRecv) {
1645
+ } else {
1646
+ // Take care of video m-line
1647
+ if (videoSend && videoRecv) {
1674
1648
  if (videoTransceiver) {
1675
1649
  if (videoTransceiver.setDirection) {
1676
- videoTransceiver.setDirection("inactive");
1650
+ videoTransceiver.setDirection("sendrecv");
1677
1651
  } else {
1678
- videoTransceiver.direction = "inactive";
1652
+ videoTransceiver.direction = "sendrecv";
1679
1653
  }
1680
1654
  }
1681
- } else {
1682
- // Take care of video m-line
1683
- if (videoSend && videoRecv) {
1684
- if (videoTransceiver) {
1685
- if (videoTransceiver.setDirection) {
1686
- videoTransceiver.setDirection("sendrecv");
1687
- } else {
1688
- videoTransceiver.direction = "sendrecv";
1689
- }
1690
- }
1691
- } else if (videoSend && !videoRecv) {
1692
- if (videoTransceiver) {
1693
- if (videoTransceiver.setDirection) {
1694
- videoTransceiver.setDirection("sendonly");
1695
- } else {
1696
- videoTransceiver.direction = "sendonly";
1697
- }
1655
+ } else if (videoSend && !videoRecv) {
1656
+ if (videoTransceiver) {
1657
+ if (videoTransceiver.setDirection) {
1658
+ videoTransceiver.setDirection("sendonly");
1659
+ } else {
1660
+ videoTransceiver.direction = "sendonly";
1698
1661
  }
1699
- } else if (!videoSend && videoRecv) {
1700
- if (videoTransceiver) {
1701
- if (videoTransceiver.setDirection) {
1702
- videoTransceiver.setDirection("recvonly");
1703
- } else {
1704
- videoTransceiver.direction = "recvonly";
1705
- }
1662
+ }
1663
+ } else if (!videoSend && videoRecv) {
1664
+ if (videoTransceiver) {
1665
+ if (videoTransceiver.setDirection) {
1666
+ videoTransceiver.setDirection("recvonly");
1706
1667
  } else {
1707
- // In theory, this is the only case where we might not have a transceiver yet
1708
- videoTransceiver = config.pc.addTransceiver("video", {direction: "recvonly"});
1668
+ videoTransceiver.direction = "recvonly";
1709
1669
  }
1670
+ } else {
1671
+ // In theory, this is the only case where we might not have a transceiver yet
1672
+ config.pc.addTransceiver("video", {direction: "recvonly"});
1710
1673
  }
1711
1674
  }
1712
- } else {
1675
+ }
1676
+ }
1713
1677
 
1714
- if (adapter.browserDetails.browser === "firefox" || adapter.browserDetails.browser === "edge") {
1715
- mediaConstraints = {
1716
- offerToReceiveAudio: audioRecv,
1717
- offerToReceiveVideo: videoRecv
1718
- };
1719
- } else {
1720
- mediaConstraints = {
1721
- mandatory: {
1722
- OfferToReceiveAudio: audioRecv,
1723
- OfferToReceiveVideo: videoRecv
1724
- }
1725
- };
1726
- }
1678
+ _createAO(type = 'offer', handleId, iceRestart = false) {
1679
+
1680
+ let handle = this._getHandle(handleId);
1681
+ if (!handle) {
1682
+ return Promise.reject({
1683
+ type: 'warning',
1684
+ id: 15,
1685
+ message: 'id non-existent',
1686
+ data: [handleId, 'createAO', type]
1687
+ });
1727
1688
  }
1728
1689
 
1690
+ let methodName = null;
1691
+ if (type === 'offer') {
1692
+ methodName = 'createOffer'
1693
+ } else {
1694
+ methodName = 'createAnswer'
1695
+ }
1696
+
1697
+ let config = handle.webrtcStuff;
1698
+ let mediaConstraints = {};
1699
+
1729
1700
  if (iceRestart) {
1730
1701
  mediaConstraints["iceRestart"] = true;
1731
1702
  }
@@ -1795,8 +1766,11 @@ class RoomSession {
1795
1766
  }
1796
1767
  config.candidates = [];
1797
1768
  }
1769
+
1770
+ this._setupTransceivers(handleId, [false, true, false, true]);
1771
+
1798
1772
  // Create the answer now
1799
- return this._createAO('answer', handleId, false, [false, true, false, true])
1773
+ return this._createAO('answer', handleId, false)
1800
1774
  .then(_jsep => {
1801
1775
  if (!_jsep) {
1802
1776
  this.emit('error', {
@@ -1827,7 +1801,7 @@ class RoomSession {
1827
1801
 
1828
1802
  //Public methods
1829
1803
 
1830
- publishLocal(stream, {keepAudio = false, keepVideo = false} = {}) {
1804
+ publishLocal(stream = null) {
1831
1805
 
1832
1806
  if(this.isDisconnecting || !this.isConnected) {
1833
1807
  return Promise.reject({
@@ -1852,122 +1826,130 @@ class RoomSession {
1852
1826
  this._webrtc(this.handleId);
1853
1827
 
1854
1828
  let config = handle.webrtcStuff;
1829
+ let audioTrackReplacePromise = Promise.resolve();
1830
+ let videoTrackReplacePromise = Promise.resolve();
1855
1831
 
1856
- if (stream) {
1857
- if (!config.stream) {
1858
- config.stream = stream;
1859
- stream.getTracks().forEach(function (track) {
1860
- config.pc.addTrack(track, stream);
1861
- });
1862
-
1863
- } else {
1864
-
1865
- /* UPDATE Audio */
1866
-
1867
- let replaceAudio = stream.getAudioTracks().length;
1832
+ if (!config.stream) {
1833
+ config.stream = stream;
1834
+ stream?.getTracks()?.forEach( (track) => {
1868
1835
 
1869
- if (replaceAudio || !keepAudio) {
1870
- //this will stop existing tracks
1871
- let oldAudioStream = config.stream.getAudioTracks()[0];
1872
- if (oldAudioStream) {
1873
- config.stream.removeTrack(oldAudioStream);
1874
- try {
1875
- oldAudioStream.stop();
1876
- } catch (e) {
1877
- this._log(e);
1836
+ if(track.kind === 'audio' || !this.simulcast) {
1837
+ config.pc.addTrack(track, stream);
1838
+ }
1839
+ else {
1840
+ // adding simulcast streams
1841
+ let bitRates = this.simulcastBitrates;
1842
+ if(adapter.browserDetails.browser !== 'firefox') {
1843
+ // standard
1844
+
1845
+ config.pc.addTransceiver(track, {
1846
+ direction: 'sendonly',
1847
+ streams: [config.stream],
1848
+ sendEncodings: [
1849
+ { rid: 'h', active: true, maxBitrate: bitRates.high },
1850
+ { rid: 'm', active: true, maxBitrate: bitRates.medium, scaleResolutionDownBy: 2 },
1851
+ { rid: 'l', active: true, maxBitrate: bitRates.low, scaleResolutionDownBy: 4 }
1852
+ ]
1853
+ })
1854
+ }
1855
+ else {
1856
+ // firefox
1857
+ let transceiver = config.pc.addTransceiver(track, {
1858
+ direction: 'sendonly',
1859
+ streams: [config.stream]
1860
+ });
1861
+ let sender = transceiver ? transceiver.sender : null;
1862
+ if(sender) {
1863
+ let parameters = sender.getParameters() || {};
1864
+ parameters.encodings = track.sendEncodings || [
1865
+ { rid: 'h', active: true, maxBitrate: bitRates.high },
1866
+ { rid: 'm', active: true, maxBitrate: bitRates.medium, scaleResolutionDownBy: 2 },
1867
+ { rid: 'l', active: true, maxBitrate: bitRates.low, scaleResolutionDownBy: 4 }
1868
+ ];
1869
+ sender.setParameters(parameters);
1878
1870
  }
1879
1871
  }
1880
1872
  }
1873
+ });
1881
1874
 
1882
- if (config.pc.getSenders() && config.pc.getSenders().length) {
1883
- if (replaceAudio && this.isUnifiedPlan) {
1884
- //using replace
1885
- } else {
1886
- for (let index in config.pc.getSenders()) {
1887
- let s = config.pc.getSenders()[index];
1888
- if (s && s.track && s.track.kind === "audio") {
1889
- config.pc.removeTrack(s);
1890
- }
1891
- }
1875
+ } else {
1876
+
1877
+ let transceivers = config.pc.getTransceivers();
1878
+ let audioTransceiver = null;
1879
+ let videoTransceiver = null;
1880
+ if (transceivers && transceivers.length > 0) {
1881
+ for (let i in transceivers) {
1882
+ let t = transceivers[i];
1883
+ if ((t.sender && t.sender.track && t.sender.track.kind === "audio") ||
1884
+ (t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) {
1885
+ audioTransceiver = t;
1886
+ break;
1892
1887
  }
1893
1888
  }
1894
-
1895
- if (replaceAudio) {
1896
- config.stream.addTrack(stream.getAudioTracks()[0]);
1897
- var audioTransceiver = null;
1898
- if (this.isUnifiedPlan) {
1899
- let transceivers = config.pc.getTransceivers();
1900
- if (transceivers && transceivers.length > 0) {
1901
- for (let i in transceivers) {
1902
- let t = transceivers[i];
1903
- if ((t.sender && t.sender.track && t.sender.track.kind === "audio" && t.stopped === false) ||
1904
- (t.receiver && t.receiver.track && t.receiver.track.kind === "audio" && t.stopped === false)) {
1905
- audioTransceiver = t;
1906
- break;
1907
- }
1908
- }
1909
- }
1889
+ for (let i in transceivers) {
1890
+ let t = transceivers[i];
1891
+ if ((t.sender && t.sender.track && t.sender.track.kind === "video") ||
1892
+ (t.receiver && t.receiver.track && t.receiver.track.kind === "video")) {
1893
+ videoTransceiver = t;
1894
+ break;
1910
1895
  }
1896
+ }
1897
+ }
1911
1898
 
1912
- if (audioTransceiver && audioTransceiver.sender) {
1913
- audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);
1914
- } else {
1915
- config.pc.addTrack(stream.getAudioTracks()[0], stream);
1916
- }
1899
+ /* UPDATE Audio */
1900
+ // stopping existing tracks
1917
1901
 
1902
+ let oldAudioStream = config?.stream?.getAudioTracks()?.[0];
1903
+ if (oldAudioStream) {
1904
+ config.stream.removeTrack(oldAudioStream);
1905
+ try {
1906
+ oldAudioStream.stop();
1907
+ } catch (e) {
1908
+ this._log(e);
1918
1909
  }
1910
+ }
1919
1911
 
1920
- /* UPDATE Video */
1912
+ let replaceAudio = stream?.getAudioTracks()?.length;
1913
+ if (replaceAudio) {
1914
+ config.stream.addTrack(stream.getAudioTracks()[0]);
1915
+ if (audioTransceiver && audioTransceiver.sender) {
1916
+ audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);
1917
+ } else {
1918
+ config.pc.addTrack(stream.getAudioTracks()[0], stream);
1919
+ }
1920
+ }
1921
+ else {
1922
+ if (audioTransceiver && audioTransceiver.sender) {
1923
+ audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(null);
1924
+ }
1925
+ }
1921
1926
 
1922
- let replaceVideo = stream.getVideoTracks().length;
1927
+ /* UPDATE Video */
1923
1928
 
1924
- if (replaceVideo || !keepVideo) {
1925
- let oldVideoStream = config.stream.getVideoTracks()[0];
1926
- if (oldVideoStream) {
1927
- config.stream.removeTrack(oldVideoStream);
1928
- try {
1929
- oldVideoStream.stop();
1930
- } catch (e) {
1931
- this._log(e);
1932
- }
1933
- }
1934
- }
1929
+ // stopping existing tracks
1935
1930
 
1936
- if (config.pc.getSenders() && config.pc.getSenders().length) {
1937
- if (replaceVideo && this.isUnifiedPlan) {
1938
- //using replace
1939
- } else {
1940
- for (let index in config.pc.getSenders()) {
1941
- let s = config.pc.getSenders()[index];
1942
- if (s && s.track && s.track.kind === "video") {
1943
- config.pc.removeTrack(s);
1944
- }
1945
- }
1946
- }
1931
+ let oldVideoStream = config?.stream?.getVideoTracks()?.[0];
1932
+ if (oldVideoStream) {
1933
+ config.stream.removeTrack(oldVideoStream);
1934
+ try {
1935
+ oldVideoStream.stop();
1936
+ } catch (e) {
1937
+ this._log(e);
1947
1938
  }
1939
+ }
1948
1940
 
1949
- if (replaceVideo) {
1950
- config.stream.addTrack(stream.getVideoTracks()[0]);
1951
- var videoTransceiver = null;
1952
- if (this.isUnifiedPlan) {
1953
- let transceivers = config.pc.getTransceivers();
1954
- if (transceivers && transceivers.length > 0) {
1955
- for (let i in transceivers) {
1956
- let t = transceivers[i];
1957
- if ((t.sender && t.sender.track && t.sender.track.kind === "video" && t.stopped === false) ||
1958
- (t.receiver && t.receiver.track && t.receiver.track.kind === "video" && t.stopped === false)) {
1959
- videoTransceiver = t;
1960
- break;
1961
- }
1962
- }
1963
- }
1964
- }
1965
- if (videoTransceiver && videoTransceiver.sender) {
1966
- //TODO: check if t.stopped === false still gets us videoTransceiver
1967
- videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);
1968
- } else {
1969
- config.pc.addTrack(stream.getVideoTracks()[0], stream);
1970
- }
1941
+ let replaceVideo = stream?.getVideoTracks()?.length;
1942
+ if (replaceVideo) {
1943
+ config.stream.addTrack(stream.getVideoTracks()[0]);
1944
+ if (videoTransceiver && videoTransceiver.sender) {
1945
+ videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);
1946
+ } else {
1947
+ config.pc.addTrack(stream.getVideoTracks()[0], stream);
1948
+ }
1949
+ }
1950
+ else {
1951
+ if (videoTransceiver && videoTransceiver.sender) {
1952
+ videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(null);
1971
1953
  }
1972
1954
  }
1973
1955
  }
@@ -1982,7 +1964,16 @@ class RoomSession {
1982
1964
  this.isAudioMuted = isAudioMuted;
1983
1965
  this.isVideoMuted = isVideoMuted;
1984
1966
 
1985
- return this._createAO('offer', this.handleId, false, [hasAudio, false, hasVideo, false])
1967
+ this._setupTransceivers(this.handleId, [hasAudio, false, hasVideo, false]);
1968
+
1969
+ // this should be enough
1970
+ if(this.isPublished) {
1971
+ return Promise.resolve();
1972
+ }
1973
+
1974
+ return Promise
1975
+ .all([audioTrackReplacePromise, videoTrackReplacePromise])
1976
+ .then(() => this._createAO('offer', this.handleId, false))
1986
1977
  .then((jsep) => {
1987
1978
  if (!jsep) {
1988
1979
  return null;
@@ -2117,20 +2108,24 @@ class RoomSession {
2117
2108
  return Promise.reject({type: 'error', id: 21, message: 'no local id, connect first', data: null})
2118
2109
  }
2119
2110
  let config = handle.webrtcStuff;
2120
- if (this.isUnifiedPlan) {
2121
- let transceiver = config.pc.getTransceivers()
2122
- .find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && t.stopped === false && (mid ? t.mid === mid : true));
2123
- if (transceiver) {
2124
- transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
2125
- }
2126
- this.isAudioMuted = !transceiver || !transceiver.sender.track.enabled;
2127
- } else {
2128
- if (config.stream && config.stream.getAudioTracks().length) {
2129
- config.stream.getAudioTracks()[0].enabled = value !== null ? !!value : !config.stream.getAudioTracks()[0].enabled;
2130
- }
2131
- this.isAudioMuted = config.stream.getAudioTracks().length === 0 || !config.stream.getAudioTracks()[0].enabled;
2111
+
2112
+ let transceiver = config.pc.getTransceivers()
2113
+ .find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (mid ? t.mid === mid : true));
2114
+
2115
+ if (transceiver) {
2116
+ transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
2132
2117
  }
2118
+
2119
+ this.isAudioMuted = !transceiver || !transceiver.sender.track.enabled;
2120
+
2121
+ // if (config.stream && config.stream.getAudioTracks().length) {
2122
+ // config.stream.getAudioTracks()[0].enabled = value !== null ? !!value : !config.stream.getAudioTracks()[0].enabled;
2123
+ // }
2124
+ // this.isAudioMuted = config.stream.getAudioTracks().length === 0 || !config.stream.getAudioTracks()[0].enabled;
2125
+ //
2126
+
2133
2127
  this.emit('localMuted', {type: 'audio', value: this.isAudioMuted, mid});
2128
+
2134
2129
  }
2135
2130
 
2136
2131
  toggleVideo(value = null, mid) {
@@ -2140,28 +2135,37 @@ class RoomSession {
2140
2135
  }
2141
2136
  let config = handle.webrtcStuff;
2142
2137
 
2143
- if (this.isUnifiedPlan) {
2144
- let transceiver = config.pc.getTransceivers()
2145
- .find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && t.stopped === false && (mid ? t.mid === mid : true));
2146
- if (transceiver) {
2147
- transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
2148
- }
2149
- this.isVideoMuted = !transceiver || !transceiver.sender.track.enabled;
2150
- }
2151
- else {
2152
- if (config.stream && config.stream.getVideoTracks().length) {
2153
- config.stream.getVideoTracks()[0].enabled = value !== null ? !!value : !config.stream.getVideoTracks()[0].enabled;
2154
- }
2155
- this.isVideoMuted = config.stream.getVideoTracks().length === 0 || !config.stream.getVideoTracks()[0].enabled;
2138
+ let transceiver = config.pc.getTransceivers()
2139
+ .find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (mid ? t.mid === mid : true));
2140
+ if (transceiver) {
2141
+ transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
2156
2142
  }
2143
+
2144
+ this.isVideoMuted = !transceiver || !transceiver.sender.track.enabled;
2145
+
2146
+ // if (config.stream && config.stream.getVideoTracks().length) {
2147
+ // config.stream.getVideoTracks()[0].enabled = value !== null ? !!value : !config.stream.getVideoTracks()[0].enabled;
2148
+ // }
2149
+ // this.isVideoMuted = config.stream.getVideoTracks().length === 0 || !config.stream.getVideoTracks()[0].enabled;
2150
+
2157
2151
  this.emit('localMuted', {type: 'video', value: this.isVideoMuted, mid});
2158
2152
  }
2159
2153
 
2154
+ selectSubStream(handleId, substream = 2) {
2155
+ this.sendMessage(this.handleId, {
2156
+ "body": {
2157
+ "request": "configure",
2158
+ "substream": substream
2159
+ }
2160
+ }).catch(() => null)
2161
+ }
2162
+
2160
2163
  setRoomType(type = 'watchparty') {
2161
2164
  this._roomType = type;
2162
2165
  return this._roomType;
2163
2166
  }
2164
2167
 
2168
+
2165
2169
  }
2166
2170
 
2167
2171
  export default Room;