@reactoo/watchtogether-sdk-js 2.5.19 → 2.5.22

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.
@@ -5,7 +5,7 @@ import emitter from './wt-emitter';
5
5
  import {generateUUID} from "./wt-utils";
6
6
 
7
7
  class Room {
8
-
8
+
9
9
  constructor(debug) {
10
10
  this.debug = debug;
11
11
  this.sessions = [];
@@ -22,16 +22,16 @@ class Room {
22
22
  // Let's get it started
23
23
  this.whenInitialized = this.initialize();
24
24
  }
25
-
25
+
26
26
  initialize() {
27
27
  return this.safariVp8TestPromise
28
28
  .then(() => this);
29
29
  }
30
-
30
+
31
31
  createSession(constructId = null, type = 'reactooroom', options = {}) {
32
32
  return new RoomSession(constructId, type, {debug: this.debug, ...options});
33
33
  }
34
-
34
+
35
35
  static testSafariVp8() {
36
36
  return new Promise(resolve => {
37
37
  if (adapter.browserDetails.browser === 'safari' &&
@@ -61,7 +61,7 @@ class Room {
61
61
  } else resolve(false);
62
62
  });
63
63
  }
64
-
64
+
65
65
  static isWebrtcSupported() {
66
66
  return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null &&
67
67
  navigator.mediaDevices !== undefined && navigator.mediaDevices !== null &&
@@ -70,9 +70,9 @@ class Room {
70
70
  }
71
71
 
72
72
  class RoomSession {
73
-
73
+
74
74
  static noop() {
75
-
75
+
76
76
  }
77
77
 
78
78
  static randomString(len) {
@@ -84,11 +84,11 @@ class RoomSession {
84
84
  }
85
85
  return randomString;
86
86
  }
87
-
87
+
88
88
  //TODO: solve
89
89
  // #eventList = ['error', 'kicked', 'addLocalParticipant', ,'addRemoteInstructor','addRemoteParticipant','addRemoteTalkback', 'addRemoteObserver', 'removeRemoteInstructor', 'removeLocalParticipant', 'removeRemoteParticipant', 'removeRemoteTalkback', 'removeRemoteObserver', 'localMuted', 'localHasVideo', 'localHasAudio', 'data', 'iceState', 'connectionState', 'joined', 'joining', 'dataChannel', 'disconnect', 'observerIds', 'talkbackIds', 'instructorId', 'published', 'publishing', 'remoteTrackMuted', 'streamingStatus', 'streaming', 'streamStarting'];
90
90
  //
91
-
91
+
92
92
  static sessionTypes = {
93
93
  'reactooroom': 'janus.plugin.reactooroom',
94
94
  'streaming': 'janus.plugin.streaming'
@@ -116,11 +116,11 @@ class RoomSession {
116
116
  videoWall: [],
117
117
  },
118
118
  };
119
-
119
+
120
120
  constructor(constructId = null, type = 'reactooroom', options = {}) {
121
121
 
122
122
  Object.assign(this, emitter());
123
-
123
+ this.defaultDataChannelLabel = 'JanusDataChannel'
124
124
  this.server = null;
125
125
  this.iceServers = null;
126
126
  this.token = null;
@@ -147,7 +147,7 @@ class RoomSession {
147
147
  // double click prevention
148
148
  this.connectingPromise = null;
149
149
  this.disconnectingPromise = null;
150
-
150
+
151
151
  this._ipv6Support = false;
152
152
  this._retries = 0;
153
153
  this._maxRetries = 3;
@@ -177,7 +177,7 @@ class RoomSession {
177
177
  }
178
178
 
179
179
  }
180
-
180
+
181
181
  // Check if this browser supports Unified Plan and transceivers
182
182
  // Based on https://codepen.io/anon/pen/ZqLwWV?editors=0010
183
183
  static checkUnifiedPlan() {
@@ -288,7 +288,7 @@ class RoomSession {
288
288
  }
289
289
  return eventName
290
290
  }
291
-
291
+
292
292
  sendMessage(handleId, message = {body: 'Example Body'}, dontWait = false, dontResolveOnAck = false) {
293
293
  return this._send({
294
294
  "janus": "message",
@@ -311,7 +311,7 @@ class RoomSession {
311
311
  }
312
312
  })
313
313
  }
314
-
314
+
315
315
  _send(request = {}, ignoreResponse = false, dontResolveOnAck = false) {
316
316
  let transaction = RoomSession.randomString(12);
317
317
  let requestData = {
@@ -320,7 +320,7 @@ class RoomSession {
320
320
  token: this.token, ...((this.sessionId && {'session_id': this.sessionId}) || {})
321
321
  };
322
322
  let timeouId = null;
323
-
323
+
324
324
  return new Promise((resolve, reject) => {
325
325
  let parseResponse = (event) => {
326
326
  let json = JSON.parse(event.data);
@@ -338,13 +338,13 @@ class RoomSession {
338
338
  }
339
339
  }
340
340
  };
341
-
341
+
342
342
  if (ignoreResponse) {
343
343
  if (this.ws && this.ws.readyState === 1) {
344
344
  this.ws.send(JSON.stringify(requestData));
345
345
  }
346
346
  resolve();
347
-
347
+
348
348
  } else {
349
349
  if (this.ws && this.ws.readyState === 1) {
350
350
  this.ws.addEventListener('message', parseResponse);
@@ -359,12 +359,12 @@ class RoomSession {
359
359
  }
360
360
  })
361
361
  }
362
-
362
+
363
363
  _connectionClosed() {
364
364
  if (this.disconnectingPromise || this.connectingPromise) {
365
365
  return;
366
366
  }
367
-
367
+
368
368
  if (this._retries < this._maxRetries) {
369
369
  setTimeout(() => {
370
370
  this._retries++;
@@ -378,18 +378,18 @@ class RoomSession {
378
378
  } else if (this.sessiontype === 'streaming') {
379
379
  this.stopStream();
380
380
  }
381
-
381
+
382
382
  this.emit('error', {type: 'error', id: 4, message: 'Lost connection to WebSockets', data: null});
383
383
  }
384
384
  }
385
-
385
+
386
386
  _wipeListeners() {
387
387
  if (this.ws) {
388
388
  this.ws.removeEventListener('close', this.__connectionClosedBoundFn);
389
389
  this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);
390
390
  }
391
391
  }
392
-
392
+
393
393
  _startKeepAlive() {
394
394
  this._send({"janus": "keepalive"})
395
395
  .then(json => {
@@ -406,27 +406,27 @@ class RoomSession {
406
406
  this.emit('error', {type: 'warning', id: 6, message: 'keepalive dead', data: e});
407
407
  this._connectionClosed();
408
408
  });
409
-
409
+
410
410
  this._keepAliveId = setTimeout(() => {
411
411
  this._startKeepAlive();
412
412
  }, 30000);
413
413
  }
414
-
414
+
415
415
  _stopKeepAlive() {
416
416
  clearTimeout(this._keepAliveId);
417
417
  }
418
418
 
419
419
  _handleWsEvents(event) {
420
-
420
+
421
421
  let json = JSON.parse(event.data);
422
422
  var sender = json["sender"];
423
423
  var type = json["janus"];
424
-
424
+
425
425
  let handle = this._getHandle(sender);
426
426
  if (!handle) {
427
427
  return;
428
428
  }
429
-
429
+
430
430
  if (type === "trickle") {
431
431
  let candidate = json["candidate"];
432
432
  let config = handle.webrtcStuff;
@@ -451,9 +451,9 @@ class RoomSession {
451
451
  this._log('detached on', handle.handleId);
452
452
  this._removeParticipant(handle.handleId, null, true);
453
453
  } else if (type === "media") {
454
- this._log('Media event:', handle.handleId, json["type"], json["receiving"]);
454
+ this._log('Media event:', handle.handleId, json["type"], json["receiving"], json["mid"]);
455
455
  } else if (type === "slowlink") {
456
- this._log('Slowlink', handle.handleId, json["uplink"], json["nacks"]);
456
+ this._log('Slowlink', handle.handleId, json["uplink"], json["lost"], json["mid"]);
457
457
  } else if (type === "event") {
458
458
  //none universal
459
459
  } else if (type === 'timeout') {
@@ -463,16 +463,16 @@ class RoomSession {
463
463
  } else {
464
464
  this._log(`Unknown event: ${type} on session: ${this.sessionId}`);
465
465
  }
466
-
467
-
466
+
467
+
468
468
  // LOCAL
469
-
469
+
470
470
  if (sender === this.handleId) {
471
471
  if (type === "event") {
472
472
  var plugindata = json["plugindata"] || {};
473
473
  var msg = plugindata["data"] || {};
474
474
  var jsep = json["jsep"];
475
-
475
+
476
476
  let result = msg["result"] || null;
477
477
  let event = msg["videoroom"] || null;
478
478
  let list = msg["publishers"] || {};
@@ -480,7 +480,7 @@ class RoomSession {
480
480
  //let joining = msg["joining"];
481
481
  let unpublished = msg["unpublished"];
482
482
  let error = msg["error"];
483
-
483
+
484
484
  if (event === "joined") {
485
485
  this.id = msg["id"];
486
486
  this.privateId = msg["private_id"];
@@ -517,11 +517,11 @@ class RoomSession {
517
517
  }
518
518
  }
519
519
  } else if (event === "event") {
520
-
520
+
521
521
  if (msg["streams"] !== undefined && msg["streams"] !== null) {
522
522
  this._log('Got my own streams back', msg["streams"]);
523
523
  }
524
-
524
+
525
525
  for (let f in list) {
526
526
  let userId = list[f]["display"];
527
527
  let streams = list[f]["streams"] || [];
@@ -551,7 +551,7 @@ class RoomSession {
551
551
  })
552
552
  }
553
553
  }
554
-
554
+
555
555
  if (leaving === 'ok') {
556
556
  this._log('leaving', this.handleId, 'this is us');
557
557
  this._removeParticipant(this.handleId);
@@ -564,7 +564,7 @@ class RoomSession {
564
564
  this._log('leaving', leaving);
565
565
  this._removeParticipant(null, leaving);
566
566
  }
567
-
567
+
568
568
  if (unpublished === 'ok') {
569
569
  this._log('unpublished', this.handleId, 'this is us');
570
570
  this._removeParticipant(this.handleId, null, false); // we do just hangup
@@ -572,7 +572,7 @@ class RoomSession {
572
572
  this._log('unpublished', unpublished);
573
573
  this._removeParticipant(null, unpublished, true); // we do hangup and detach
574
574
  }
575
-
575
+
576
576
  if (error) {
577
577
  this.emit('error', {
578
578
  type: 'error',
@@ -582,7 +582,7 @@ class RoomSession {
582
582
  });
583
583
  }
584
584
  }
585
-
585
+
586
586
  // Streaming related
587
587
  else if (result && result["status"]) {
588
588
  this.emit('streamingStatus', result["status"]);
@@ -594,9 +594,9 @@ class RoomSession {
594
594
  this._isStreaming = true;
595
595
  }
596
596
  }
597
-
597
+
598
598
  if (jsep !== undefined && jsep !== null) {
599
-
599
+
600
600
  if (this.sessiontype === 'reactooroom') {
601
601
  this._webrtcPeer(this.handleId, jsep)
602
602
  .catch(err => {
@@ -608,7 +608,7 @@ class RoomSession {
608
608
  this.emit('error', err);
609
609
  });
610
610
  }
611
-
611
+
612
612
  }
613
613
  } else if (type === "webrtcup") {
614
614
  this._log('Configuring bitrate: ' + this.initialBitrate);
@@ -622,17 +622,17 @@ class RoomSession {
622
622
  }
623
623
  }
624
624
  }
625
-
625
+
626
626
  //REMOTE
627
-
627
+
628
628
  else {
629
-
629
+
630
630
  let plugindata = json["plugindata"] || {};
631
631
  let msg = plugindata["data"] || {};
632
632
  let jsep = json["jsep"];
633
633
  let event = msg["videoroom"];
634
634
  let error = msg["error"];
635
-
635
+
636
636
  if (event === "attached") {
637
637
  this._log('Remote have successfully joined Room', msg);
638
638
  this.emit(this._getAddParticipantEventName(handle.handleId), {
@@ -648,11 +648,11 @@ class RoomSession {
648
648
  hasVideoTrack: false
649
649
  });
650
650
  }
651
-
651
+
652
652
  if (error) {
653
653
  this.emit('error', {type: 'warning', id: 8, message: 'remote participant error', data: [sender, msg]});
654
654
  }
655
-
655
+
656
656
  if (jsep) {
657
657
  this._publishRemote(handle.handleId, jsep)
658
658
  .catch(err => {
@@ -661,24 +661,24 @@ class RoomSession {
661
661
  }
662
662
  }
663
663
  }
664
-
664
+
665
665
  _handleDataEvents(handleId, type, data) {
666
-
666
+
667
667
  let handle = this._getHandle(handleId);
668
-
668
+
669
669
  if (type === 'state') {
670
- this._log(` - Data channel status - `, `UID: ${handleId}`, `STATUS: ${data}`, `ME: ${handleId === this.handleId}`)
670
+ this._log(` - Data channel status - `, `UID: ${handleId}`, `STATUS: ${JSON.stringify(data)}`, `ME: ${handleId === this.handleId}`)
671
671
  if (handle) {
672
672
  let config = handle.webrtcStuff;
673
- config.dataChannelOpen = data === 'open';
673
+ config.dataChannelOpen = this.defaultDataChannelLabel === data?.label && data?.state === 'open';
674
674
  }
675
-
675
+
676
676
  if (handleId === this.handleId) {
677
- this._isDataChannelOpen = data === 'open';
678
- this.emit('dataChannel', data === 'open');
677
+ this._isDataChannelOpen = this.defaultDataChannelLabel === data?.label && data?.state === 'open';
678
+ this.emit('dataChannel', this.defaultDataChannelLabel === data?.label && data?.state === 'open');
679
679
  }
680
680
  }
681
-
681
+
682
682
  if (type === 'error') {
683
683
 
684
684
  this.emit('error', {
@@ -687,21 +687,23 @@ class RoomSession {
687
687
  message: 'data event warning',
688
688
  data: [handleId, data]
689
689
  });
690
-
690
+
691
691
  if (handle) {
692
692
  let config = handle.webrtcStuff;
693
- config.dataChannelOpen = false;
693
+ if(this.defaultDataChannelLabel === data.label) {
694
+ config.dataChannelOpen = false;
695
+ }
694
696
  }
695
- if (handleId === this.handleId) {
697
+ if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {
696
698
  this._isDataChannelOpen = false;
697
699
  this.emit('dataChannel', false);
698
700
  }
699
701
  }
700
-
702
+
701
703
  if (handleId === this.handleId && type === 'message') {
702
-
704
+
703
705
  let d = null;
704
-
706
+
705
707
  try {
706
708
  d = JSON.parse(data)
707
709
  } catch (e) {
@@ -710,19 +712,19 @@ class RoomSession {
710
712
  }
711
713
  this.emit('data', d);
712
714
  }
713
-
715
+
714
716
  }
715
-
717
+
716
718
  //removeHandle === true -> hangup, detach, removeHandle === false -> hangup
717
719
  _removeParticipant(handleId, rfid, removeHandle = true) {
718
720
  let handle = this._getHandle(handleId, rfid);
719
-
721
+
720
722
  if (!handle) {
721
723
  return Promise.resolve();
722
724
  } else {
723
725
  handleId = handle.handleId;
724
726
  }
725
-
727
+
726
728
  return this._send({"janus": "hangup", "handle_id": handleId,}, true)
727
729
  .then(() => (removeHandle ? this._send({
728
730
  "janus": "detach",
@@ -738,10 +740,12 @@ class RoomSession {
738
740
  }
739
741
  handle.webrtcStuff.stream = null;
740
742
  if (handle.webrtcStuff.dataChannel) {
741
- handle.webrtcStuff.dataChannel.onmessage = null;
742
- handle.webrtcStuff.dataChannel.onopen = null;
743
- handle.webrtcStuff.dataChannel.onclose = null;
744
- handle.webrtcStuff.dataChannel.onerror = null;
743
+ Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {
744
+ handle.webrtcStuff.dataChannel[label].onmessage = null;
745
+ handle.webrtcStuff.dataChannel[label].onopen = null;
746
+ handle.webrtcStuff.dataChannel[label].onclose = null;
747
+ handle.webrtcStuff.dataChannel[label].onerror = null;
748
+ })
745
749
  }
746
750
  if (handle.webrtcStuff.pc) {
747
751
  handle.webrtcStuff.pc.onicecandidate = null;
@@ -766,7 +770,7 @@ class RoomSession {
766
770
  trickle: true,
767
771
  iceDone: false,
768
772
  };
769
-
773
+
770
774
  if (handleId === this.handleId) {
771
775
  this._isDataChannelOpen = false;
772
776
  this._isPublished = false;
@@ -780,12 +784,12 @@ class RoomSession {
780
784
  let handleIndex = this._participants.findIndex(p => p.handleId === handleId);
781
785
  this._participants.splice(handleIndex, 1);
782
786
  }
783
-
787
+
784
788
  return true;
785
789
  });
786
-
790
+
787
791
  }
788
-
792
+
789
793
  _createParticipant(userId = null, rfid = null) {
790
794
  return this._send({
791
795
  "janus": "attach",
@@ -812,7 +816,7 @@ class RoomSession {
812
816
  return handle;
813
817
  })
814
818
  }
815
-
819
+
816
820
  _joinRoom(roomId, pin, userId) {
817
821
  return this.sendMessage(this.handleId, {
818
822
  body: {
@@ -820,7 +824,7 @@ class RoomSession {
820
824
  }
821
825
  }, false, true)
822
826
  }
823
-
827
+
824
828
  _watchStream(id) {
825
829
  return this.sendMessage(this.handleId, {
826
830
  body: {
@@ -828,7 +832,7 @@ class RoomSession {
828
832
  }
829
833
  }, false, true)
830
834
  }
831
-
835
+
832
836
  _leaveRoom(dontWait = false) {
833
837
  return this._hasJoined
834
838
  ? this.sendMessage(this.handleId, {body: {"request": "leave"}}, dontWait)
@@ -838,15 +842,15 @@ class RoomSession {
838
842
  })
839
843
  : Promise.resolve();
840
844
  }
841
-
845
+
842
846
  // internal reconnect
843
-
847
+
844
848
  _reconnect() {
845
-
849
+
846
850
  if (this.connectingPromise) {
847
851
  return this.connectingPromise;
848
852
  }
849
-
853
+
850
854
  if (this.ws) {
851
855
  this._wipeListeners();
852
856
  if (this.ws.readyState === 1) {
@@ -877,7 +881,7 @@ class RoomSession {
877
881
  reject({type: 'error', id: 11, message: 'reconnection error', data: error})
878
882
  });
879
883
  };
880
-
884
+
881
885
  // this is called before 'close' event callback so it doesn't break reconnect loop
882
886
  this.ws.onerror = (e) => {
883
887
  this.connectingPromise = null;
@@ -887,13 +891,13 @@ class RoomSession {
887
891
  });
888
892
  return this.connectingPromise;
889
893
  }
890
-
894
+
891
895
  connect(roomId, pin, server, iceServers, token, userId, webrtcVersion = 0, initialBitrate = 0, isMonitor, recordingFilename) {
892
-
896
+
893
897
  if (this.connectingPromise) {
894
898
  return this.connectingPromise;
895
899
  }
896
-
900
+
897
901
  this.emit('joined', false);
898
902
  if (this.ws) {
899
903
  this._wipeListeners();
@@ -951,12 +955,12 @@ class RoomSession {
951
955
  });
952
956
  return this.connectingPromise;
953
957
  }
954
-
958
+
955
959
  disconnect() {
956
960
  if (this.disconnectingPromise) {
957
961
  return this.disconnectingPromise;
958
962
  }
959
-
963
+
960
964
  this._stopKeepAlive();
961
965
  this.disconnectingPromise = Promise.all(this._participants.map(p => this._removeParticipant(p.handleId)))
962
966
  .finally(() => {
@@ -976,13 +980,13 @@ class RoomSession {
976
980
  })
977
981
  return this.disconnectingPromise;
978
982
  }
979
-
983
+
980
984
  startStream(streamId, server, iceServers, token, userId) {
981
-
985
+
982
986
  if (this.connectingPromise) {
983
987
  return this.connectingPromise
984
988
  }
985
-
989
+
986
990
  this.emit('streaming', false);
987
991
  if (this.ws) {
988
992
  this._wipeListeners();
@@ -995,7 +999,7 @@ class RoomSession {
995
999
  this.token = token;
996
1000
  this.streamId = streamId;
997
1001
  this.userId = userId;
998
-
1002
+
999
1003
  this.connectingPromise = new Promise((resolve, reject) => {
1000
1004
  this.emit('streamStarting', true);
1001
1005
  this.ws = new WebSocket(this.server, 'janus-protocol');
@@ -1036,7 +1040,7 @@ class RoomSession {
1036
1040
  });
1037
1041
  return this.connectingPromise;
1038
1042
  }
1039
-
1043
+
1040
1044
  stopStream() {
1041
1045
  if (this.disconnectingPromise) {
1042
1046
  return this.disconnectingPromise;
@@ -1062,13 +1066,13 @@ class RoomSession {
1062
1066
  this.disconnectingPromise = null;
1063
1067
  return Promise.resolve('Disconnected');
1064
1068
  });
1065
-
1069
+
1066
1070
  return this.disconnectingPromise;
1067
1071
  }
1068
-
1069
-
1072
+
1073
+
1070
1074
  destroy() {
1071
-
1075
+
1072
1076
  if (this.sessiontype === 'reactooroom') {
1073
1077
  return this.disconnect()
1074
1078
  .then(() => {
@@ -1083,7 +1087,7 @@ class RoomSession {
1083
1087
  })
1084
1088
  }
1085
1089
  }
1086
-
1090
+
1087
1091
  _enableDebug() {
1088
1092
  this._log = console.log.bind(console);
1089
1093
  }
@@ -1091,7 +1095,7 @@ class RoomSession {
1091
1095
  _getHandle(handleId, rfid = null) {
1092
1096
  return this._participants.find(p => p.handleId === handleId || (rfid && p.rfid === rfid));
1093
1097
  }
1094
-
1098
+
1095
1099
  _getStats(type = null) {
1096
1100
  return Promise.all(this._participants.map(participant => {
1097
1101
  let mediaTrack = null;
@@ -1105,7 +1109,7 @@ class RoomSession {
1105
1109
  .catch(e => Promise.resolve({handle: participant, stats: e}))
1106
1110
  }))
1107
1111
  }
1108
-
1112
+
1109
1113
  _sendTrickleCandidate(handleId, candidate) {
1110
1114
  return this._send({
1111
1115
  "janus": "trickle",
@@ -1113,9 +1117,9 @@ class RoomSession {
1113
1117
  "handle_id": handleId
1114
1118
  })
1115
1119
  }
1116
-
1120
+
1117
1121
  _webrtc(handleId, enableOntrack = false) {
1118
-
1122
+
1119
1123
  let handle = this._getHandle(handleId);
1120
1124
  if (!handle) {
1121
1125
  this.emit('error', {
@@ -1125,13 +1129,13 @@ class RoomSession {
1125
1129
  data: [handleId, 'create rtc connection']
1126
1130
  });
1127
1131
  }
1128
-
1132
+
1129
1133
  let config = handle.webrtcStuff;
1130
1134
  if (!config.pc) {
1131
1135
  let pc_config = {"iceServers": this.iceServers, "iceTransportPolicy": 'all', "bundlePolicy": undefined};
1132
-
1136
+
1133
1137
  pc_config["sdpSemantics"] = this.isUnifiedPlan ? "unified-plan" : "plan-b";
1134
-
1138
+
1135
1139
  let pc_constraints = {
1136
1140
  "optional": [{"DtlsSrtpKeyAgreement": true}]
1137
1141
  };
@@ -1142,9 +1146,9 @@ class RoomSession {
1142
1146
  // This is Edge, enable BUNDLE explicitly
1143
1147
  pc_config.bundlePolicy = "max-bundle";
1144
1148
  }
1145
-
1149
+
1146
1150
  this._log('new RTCPeerConnection', pc_config, pc_constraints);
1147
-
1151
+
1148
1152
  config.pc = new RTCPeerConnection(pc_config, pc_constraints);
1149
1153
  config.pc.onconnectionstatechange = () => {
1150
1154
  if (config.pc.connectionState === 'failed') {
@@ -1157,10 +1161,11 @@ class RoomSession {
1157
1161
  id: handle.handleId,
1158
1162
  userId: handle.userId,
1159
1163
  stream: config.stream,
1164
+ track: null,
1160
1165
  optional: true,
1161
1166
  constructId: this.constructId,
1162
1167
  metaData: this.options.metaData,
1163
- adding: true,
1168
+ adding: false,
1164
1169
  hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
1165
1170
  hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
1166
1171
  })
@@ -1177,10 +1182,11 @@ class RoomSession {
1177
1182
  id: handle.handleId,
1178
1183
  userId: handle.userId,
1179
1184
  stream: config.stream,
1185
+ track: null,
1180
1186
  optional: true,
1181
1187
  constructId: this.constructId,
1182
1188
  metaData: this.options.metaData,
1183
- adding: true,
1189
+ adding: false,
1184
1190
  hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
1185
1191
  hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
1186
1192
  })
@@ -1201,30 +1207,31 @@ class RoomSession {
1201
1207
  "sdpMid": event.candidate.sdpMid,
1202
1208
  "sdpMLineIndex": event.candidate.sdpMLineIndex
1203
1209
  };
1204
-
1210
+
1205
1211
  this._sendTrickleCandidate(handleId, candidate)
1206
1212
  .catch(e => {
1207
1213
  this.emit('error', e);
1208
1214
  });
1209
1215
  }
1210
1216
  };
1211
-
1217
+
1212
1218
  if (enableOntrack) {
1213
1219
  config.pc.ontrack = (event) => {
1214
-
1215
- // if(!event.streams)
1216
- // return;
1217
-
1220
+
1221
+ if(!event.streams)
1222
+ return;
1223
+
1218
1224
  //config.stream = event.streams[0];
1219
-
1225
+
1220
1226
  if (!config.stream) {
1221
1227
  config.stream = new MediaStream();
1222
1228
  }
1223
-
1224
1229
  if (event.track) {
1230
+ let mid = event.transceiver ? event.transceiver.mid : event.track.id;
1225
1231
  config.stream.addTrack(event.track);
1226
1232
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1227
1233
  tid: generateUUID(),
1234
+ mid,
1228
1235
  id: handle.handleId,
1229
1236
  userId: handle.userId,
1230
1237
  stream: config.stream,
@@ -1235,32 +1242,51 @@ class RoomSession {
1235
1242
  hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
1236
1243
  hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
1237
1244
  });
1238
-
1245
+
1239
1246
  if (event.track.onended)
1240
1247
  return;
1241
-
1248
+
1242
1249
  event.track.onended = (ev) => {
1250
+
1251
+ let mid = ev.target.id;
1252
+ if(this.isUnifiedPlan) {
1253
+ let transceiver = config.pc.getTransceivers().find(
1254
+ t => t.receiver.track === ev.target);
1255
+ mid = transceiver.mid;
1256
+ }
1257
+
1243
1258
  if (config.stream) {
1244
1259
  config.stream && config.stream.removeTrack(ev.target);
1245
1260
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1246
1261
  tid: generateUUID(),
1247
1262
  id: handle.handleId,
1263
+ mid,
1248
1264
  userId: handle.userId,
1249
1265
  stream: config.stream,
1250
1266
  track: ev.target,
1251
1267
  constructId: this.constructId,
1252
1268
  metaData: this.options.metaData,
1253
1269
  adding: false,
1270
+ removing: true,
1254
1271
  hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
1255
1272
  hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
1256
1273
  });
1257
1274
  }
1258
1275
  };
1259
-
1276
+
1260
1277
  event.track.onmute = (ev) => {
1261
1278
  this._log('remoteTrackMuted', 'onmute');
1279
+
1280
+ let mid = ev.target.id;
1281
+ if(this.isUnifiedPlan) {
1282
+ let transceiver = config.pc.getTransceivers().find(
1283
+ t => t.receiver.track === ev.target);
1284
+ mid = transceiver.mid;
1285
+ }
1286
+
1262
1287
  this.emit('remoteTrackMuted', {
1263
1288
  id: handle.handleId,
1289
+ mid,
1264
1290
  userId: handle.userId,
1265
1291
  stream: config.stream,
1266
1292
  kind: ev.target.kind,
@@ -1268,11 +1294,20 @@ class RoomSession {
1268
1294
  muted: true
1269
1295
  });
1270
1296
  };
1271
-
1297
+
1272
1298
  event.track.onunmute = (ev) => {
1273
1299
  this._log('remoteTrackMuted', 'onunmute');
1300
+
1301
+ let mid = ev.target.id;
1302
+ if(this.isUnifiedPlan) {
1303
+ let transceiver = config.pc.getTransceivers().find(
1304
+ t => t.receiver.track === ev.target);
1305
+ mid = transceiver.mid;
1306
+ }
1307
+
1274
1308
  this.emit('remoteTrackMuted', {
1275
1309
  id: handle.handleId,
1310
+ mid,
1276
1311
  userId: handle.userId,
1277
1312
  stream: config.stream,
1278
1313
  kind: ev.target.kind,
@@ -1280,55 +1315,71 @@ class RoomSession {
1280
1315
  muted: false
1281
1316
  });
1282
1317
  };
1283
-
1318
+
1284
1319
  }
1285
-
1320
+
1286
1321
  };
1287
1322
  }
1288
1323
  }
1289
-
1290
- let defaultDataChannelLabel = 'JanusDataChannel';
1291
-
1324
+
1292
1325
  if (!config.dataChannel || !config.dataChannelOpen) {
1293
-
1326
+
1327
+ config.dataChannel = {};
1328
+
1294
1329
  var onDataChannelMessage = (event) => {
1295
1330
  this._handleDataEvents(handleId, 'message', event.data);
1296
1331
  };
1297
1332
  var onDataChannelStateChange = (event) => {
1298
- this._handleDataEvents(handleId, 'state', config.dataChannel.readyState);
1333
+ let label = event.target.label;
1334
+ let protocol = event.target.protocol;
1335
+ let state = config.dataChannel[label] ? config.dataChannel[label].readyState : "null";
1336
+ this._handleDataEvents(handleId, 'state', {state, label} );
1299
1337
  };
1338
+ //TODO: check this
1300
1339
  var onDataChannelError = (error) => {
1301
- this._handleDataEvents(handleId, 'error', error);
1340
+ this._handleDataEvents(handleId, 'error', {label: error?.channel?.label, error});
1302
1341
  };
1303
- // Until we implement the proxying of open requests within the Janus core, we open a channel ourselves whatever the case
1304
- config.dataChannel = config.pc.createDataChannel(defaultDataChannelLabel, {ordered: true});
1305
- config.dataChannel.onmessage = onDataChannelMessage;
1306
- config.dataChannel.onopen = onDataChannelStateChange;
1307
- config.dataChannel.onclose = onDataChannelStateChange;
1308
- config.dataChannel.onerror = onDataChannelError;
1309
-
1342
+
1343
+ const createDataChannel = (label, protocol, incoming) => {
1344
+ let options = {ordered: true};
1345
+ if(!incoming) {
1346
+ if(protocol) {
1347
+ options = {...options, protocol}
1348
+ }
1349
+ config.dataChannel[label] = config.pc.createDataChannel(label, options);
1350
+ }
1351
+ else {
1352
+ config.dataChannel[label] = incoming;
1353
+ }
1354
+
1355
+ config.dataChannel[label].onmessage = onDataChannelMessage;
1356
+ config.dataChannel[label].onopen = onDataChannelStateChange;
1357
+ config.dataChannel[label].onclose = onDataChannelStateChange;
1358
+ config.dataChannel[label].onerror = onDataChannelError;
1359
+ }
1360
+
1361
+ createDataChannel(this.defaultDataChannelLabel, null, null)
1310
1362
  config.pc.ondatachannel = function (event) {
1311
- //TODO: implement this
1312
- console.log('Janus is creating data channel');
1363
+ createDataChannel(event.channel.label, event.channel.protocol, event.channel)
1313
1364
  };
1314
1365
  }
1315
1366
  }
1316
-
1367
+
1317
1368
  _webrtcPeer(handleId, jsep) {
1318
-
1369
+
1319
1370
  let handle = this._getHandle(handleId);
1320
1371
  if (!handle) {
1321
1372
  return Promise.reject({type: 'warning', id: 15, message: 'id non-existent', data: [handleId, 'rtc peer']});
1322
1373
  }
1323
-
1374
+
1324
1375
  var config = handle.webrtcStuff;
1325
-
1376
+
1326
1377
  if (jsep !== undefined && jsep !== null) {
1327
1378
  if (config.pc === null) {
1328
1379
  this._log("No PeerConnection: if this is an answer, use createAnswer and not _webrtcPeer");
1329
1380
  return Promise.resolve(null);
1330
1381
  }
1331
-
1382
+
1332
1383
  return config.pc.setRemoteDescription(jsep)
1333
1384
  .then(() => {
1334
1385
  config.remoteSdp = jsep.sdp;
@@ -1351,20 +1402,20 @@ class RoomSession {
1351
1402
  return Promise.reject({type: 'warning', id: 22, message: 'rtc peer', data: [handleId, 'invalid jsep']});
1352
1403
  }
1353
1404
  }
1354
-
1405
+
1355
1406
  _iceRestart(handleId) {
1356
-
1407
+
1357
1408
  let handle = this._getHandle(handleId);
1358
1409
  if (!handle) {
1359
1410
  return;
1360
1411
  }
1361
1412
  var config = handle.webrtcStuff;
1362
-
1413
+
1363
1414
  // Already restarting;
1364
1415
  if (config.isIceRestarting) {
1365
1416
  return;
1366
1417
  }
1367
-
1418
+
1368
1419
  if (this.handleId === handleId) {
1369
1420
  this._log('Performing local ICE restart');
1370
1421
  config.isIceRestarting = true;
@@ -1399,11 +1450,11 @@ class RoomSession {
1399
1450
  config.isIceRestarting = false;
1400
1451
  });
1401
1452
  }
1402
-
1453
+
1403
1454
  }
1404
-
1455
+
1405
1456
  _createAO(type = 'offer', handleId, iceRestart = false, [audioSend, audioRecv, videoSend, videoRecv]) {
1406
-
1457
+
1407
1458
  let handle = this._getHandle(handleId);
1408
1459
  if (!handle) {
1409
1460
  return Promise.reject({
@@ -1413,16 +1464,16 @@ class RoomSession {
1413
1464
  data: [handleId, 'createAO', type]
1414
1465
  });
1415
1466
  }
1416
-
1467
+
1417
1468
  let methodName = null;
1418
1469
  if (type === 'offer') {
1419
1470
  methodName = 'createOffer'
1420
1471
  } else {
1421
1472
  methodName = 'createAnswer'
1422
1473
  }
1423
-
1474
+
1424
1475
  var config = handle.webrtcStuff;
1425
-
1476
+
1426
1477
  // https://code.google.com/p/webrtc/issues/detail?id=3508
1427
1478
  var mediaConstraints = {};
1428
1479
  if (this.isUnifiedPlan) {
@@ -1445,7 +1496,7 @@ class RoomSession {
1445
1496
  }
1446
1497
  }
1447
1498
  }
1448
-
1499
+
1449
1500
  // Handle audio (and related changes, if any)
1450
1501
  if (!audioSend && !audioRecv) {
1451
1502
  // Audio disabled: have we removed it?
@@ -1528,7 +1579,7 @@ class RoomSession {
1528
1579
  }
1529
1580
  }
1530
1581
  } else {
1531
-
1582
+
1532
1583
  if (adapter.browserDetails.browser === "firefox" || adapter.browserDetails.browser === "edge") {
1533
1584
  mediaConstraints = {
1534
1585
  offerToReceiveAudio: audioRecv,
@@ -1543,11 +1594,11 @@ class RoomSession {
1543
1594
  };
1544
1595
  }
1545
1596
  }
1546
-
1597
+
1547
1598
  if (iceRestart) {
1548
1599
  mediaConstraints["iceRestart"] = true;
1549
1600
  }
1550
-
1601
+
1551
1602
  return config.pc[methodName](mediaConstraints)
1552
1603
  .then(function (response) {
1553
1604
  config.mySdp = response.sdp;
@@ -1565,21 +1616,21 @@ class RoomSession {
1565
1616
  // Don't do anything until we have all candidates
1566
1617
  return Promise.resolve(null);
1567
1618
  }
1568
-
1619
+
1569
1620
  // JSON.stringify doesn't work on some WebRTC objects anymore
1570
1621
  // See https://code.google.com/p/chromium/issues/detail?id=467366
1571
1622
  var jsep = {
1572
1623
  "type": response.type,
1573
1624
  "sdp": response.sdp
1574
1625
  };
1575
-
1626
+
1576
1627
  return _p.then(() => jsep)
1577
1628
  }, (e) => {
1578
1629
  return Promise.reject({type: 'warning', id: 25, message: methodName, data: [handleId, e]})
1579
1630
  });
1580
-
1631
+
1581
1632
  }
1582
-
1633
+
1583
1634
  _publishRemote(handleId, jsep) {
1584
1635
  let handle = this._getHandle(handleId);
1585
1636
  if (!handle) {
@@ -1590,11 +1641,11 @@ class RoomSession {
1590
1641
  data: [handleId, 'publish remote participant']
1591
1642
  })
1592
1643
  }
1593
-
1644
+
1594
1645
  this._webrtc(handleId, true);
1595
-
1646
+
1596
1647
  let config = handle.webrtcStuff;
1597
-
1648
+
1598
1649
  if (jsep) {
1599
1650
  return config.pc.setRemoteDescription(jsep)
1600
1651
  .then(() => {
@@ -1636,19 +1687,19 @@ class RoomSession {
1636
1687
  message: 'setRemoteDescription',
1637
1688
  data: [handleId, e]
1638
1689
  }));
1639
-
1690
+
1640
1691
  } else {
1641
1692
  return Promise.resolve();
1642
1693
  }
1643
-
1694
+
1644
1695
  }
1645
-
1696
+
1646
1697
  //Public methods
1647
-
1698
+
1648
1699
  publishLocal(stream, {keepAudio = false, keepVideo = false} = {}) {
1649
-
1700
+
1650
1701
  this.emit('publishing', true);
1651
-
1702
+
1652
1703
  let handle = this._getHandle(this.handleId);
1653
1704
  if (!handle) {
1654
1705
  return Promise.reject({
@@ -1658,24 +1709,24 @@ class RoomSession {
1658
1709
  data: null
1659
1710
  })
1660
1711
  }
1661
-
1712
+
1662
1713
  this._webrtc(this.handleId);
1663
-
1714
+
1664
1715
  let config = handle.webrtcStuff;
1665
-
1716
+
1666
1717
  if (stream) {
1667
1718
  if (!config.stream) {
1668
1719
  config.stream = stream;
1669
1720
  stream.getTracks().forEach(function (track) {
1670
1721
  config.pc.addTrack(track, stream);
1671
1722
  });
1672
-
1723
+
1673
1724
  } else {
1674
-
1725
+
1675
1726
  /* UPDATE Audio */
1676
-
1727
+
1677
1728
  let replaceAudio = stream.getAudioTracks().length;
1678
-
1729
+
1679
1730
  if (replaceAudio || !keepAudio) {
1680
1731
  //this will stop existing tracks
1681
1732
  let oldAudioStream = config.stream.getAudioTracks()[0];
@@ -1688,7 +1739,7 @@ class RoomSession {
1688
1739
  }
1689
1740
  }
1690
1741
  }
1691
-
1742
+
1692
1743
  if (config.pc.getSenders() && config.pc.getSenders().length) {
1693
1744
  if (replaceAudio && this.isUnifiedPlan) {
1694
1745
  //using replace
@@ -1701,7 +1752,7 @@ class RoomSession {
1701
1752
  }
1702
1753
  }
1703
1754
  }
1704
-
1755
+
1705
1756
  if (replaceAudio) {
1706
1757
  config.stream.addTrack(stream.getAudioTracks()[0]);
1707
1758
  var audioTransceiver = null;
@@ -1718,19 +1769,19 @@ class RoomSession {
1718
1769
  }
1719
1770
  }
1720
1771
  }
1721
-
1772
+
1722
1773
  if (audioTransceiver && audioTransceiver.sender) {
1723
1774
  audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);
1724
1775
  } else {
1725
1776
  config.pc.addTrack(stream.getAudioTracks()[0], stream);
1726
1777
  }
1727
-
1778
+
1728
1779
  }
1729
-
1780
+
1730
1781
  /* UPDATE Video */
1731
-
1782
+
1732
1783
  let replaceVideo = stream.getVideoTracks().length;
1733
-
1784
+
1734
1785
  if (replaceVideo || !keepVideo) {
1735
1786
  let oldVideoStream = config.stream.getVideoTracks()[0];
1736
1787
  if (oldVideoStream) {
@@ -1742,7 +1793,7 @@ class RoomSession {
1742
1793
  }
1743
1794
  }
1744
1795
  }
1745
-
1796
+
1746
1797
  if (config.pc.getSenders() && config.pc.getSenders().length) {
1747
1798
  if (replaceVideo && this.isUnifiedPlan) {
1748
1799
  //using replace
@@ -1755,7 +1806,7 @@ class RoomSession {
1755
1806
  }
1756
1807
  }
1757
1808
  }
1758
-
1809
+
1759
1810
  if (replaceVideo) {
1760
1811
  config.stream.addTrack(stream.getVideoTracks()[0]);
1761
1812
  var videoTransceiver = null;
@@ -1781,17 +1832,17 @@ class RoomSession {
1781
1832
  }
1782
1833
  }
1783
1834
  }
1784
-
1835
+
1785
1836
  let hasAudio = !!(stream && stream.getAudioTracks().length > 0);
1786
1837
  let hasVideo = !!(stream && stream.getVideoTracks().length > 0);
1787
1838
  let isAudioMuted = !stream || stream.getAudioTracks().length === 0 || !stream.getAudioTracks()[0].enabled;
1788
1839
  let isVideoMuted = !stream || stream.getVideoTracks().length === 0 || !stream.getVideoTracks()[0].enabled;
1789
-
1840
+
1790
1841
  this.isAudioEnabed = hasAudio;
1791
1842
  this.isVideoEnabled = hasVideo;
1792
1843
  this.isAudioMuted = isAudioMuted;
1793
1844
  this.isVideoMuted = isVideoMuted;
1794
-
1845
+
1795
1846
  return this._createAO('offer', this.handleId, false, [hasAudio, false, hasVideo, false])
1796
1847
  .then((jsep) => {
1797
1848
  if (!jsep) {
@@ -1828,7 +1879,54 @@ class RoomSession {
1828
1879
  })
1829
1880
  .then(r => {
1830
1881
  this._isPublished = true;
1831
- this.emit('addLocalParticipant', {tid:generateUUID(), id: handle.handleId, userId: handle.userId, stream: config.stream});
1882
+ if(config.stream) {
1883
+ let tracks = config.stream.getTracks();
1884
+ tracks.forEach(track => {
1885
+ // used as a flag to not emit tracks that been already emitted
1886
+ if(!track.onended) {
1887
+ this.emit('addLocalParticipant', {
1888
+ tid: generateUUID(),
1889
+ id: handle.handleId,
1890
+ userId: handle.userId,
1891
+ track,
1892
+ stream: config.stream,
1893
+ adding: true,
1894
+ constructId: this.constructId,
1895
+ metaData: this.options.metaData,
1896
+ hasAudioTrack: hasAudio,
1897
+ hasVideoTrack: hasVideo
1898
+ });
1899
+ track.onended = (ev) => {
1900
+ this.emit('addLocalParticipant', {
1901
+ tid:generateUUID(),
1902
+ id: handle.handleId,
1903
+ userId: handle.userId,
1904
+ track: ev.target,
1905
+ stream: config.stream,
1906
+ adding: false,
1907
+ removing: true,
1908
+ constructId: this.constructId,
1909
+ metaData: this.options.metaData,
1910
+ hasAudioTrack: hasAudio,
1911
+ hasVideoTrack: hasVideo
1912
+ });
1913
+ }
1914
+ }
1915
+ })
1916
+ } else {
1917
+ this.emit('addLocalParticipant', {
1918
+ tid: generateUUID(),
1919
+ id: handle.handleId,
1920
+ userId: handle.userId,
1921
+ stream: null,
1922
+ adding: false,
1923
+ constructId: this.constructId,
1924
+ metaData: this.options.metaData,
1925
+ hasAudioTrack: hasAudio,
1926
+ hasVideoTrack: hasVideo
1927
+ });
1928
+ }
1929
+
1832
1930
  this.emit('published', {status: true, hasStream: !!config.stream});
1833
1931
  this.emit('publishing', false);
1834
1932
  this.emit('localHasVideo', hasVideo);
@@ -1842,7 +1940,7 @@ class RoomSession {
1842
1940
  return Promise.reject(e);
1843
1941
  })
1844
1942
  }
1845
-
1943
+
1846
1944
  unpublishLocal(dontWait = false) {
1847
1945
  return this._isPublished
1848
1946
  ? this.sendMessage(this.handleId, {body: {"request": "unpublish"}}, dontWait)
@@ -1853,8 +1951,8 @@ class RoomSession {
1853
1951
  })
1854
1952
  : Promise.resolve()
1855
1953
  }
1856
-
1857
- toggleAudio(value = null) {
1954
+
1955
+ toggleAudio(value = null, mid) {
1858
1956
  let handle = this._getHandle(this.handleId);
1859
1957
  if (!handle) {
1860
1958
  return Promise.reject({type: 'error', id: 21, message: 'no local id, connect first', data: null})
@@ -1862,7 +1960,7 @@ class RoomSession {
1862
1960
  let config = handle.webrtcStuff;
1863
1961
  if (this.isUnifiedPlan) {
1864
1962
  let transceiver = config.pc.getTransceivers()
1865
- .find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && t.stopped === false);
1963
+ .find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && t.stopped === false && (mid ? t.mid === mid : true));
1866
1964
  if (transceiver) {
1867
1965
  transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
1868
1966
  }
@@ -1873,51 +1971,51 @@ class RoomSession {
1873
1971
  }
1874
1972
  this.isAudioMuted = config.stream.getAudioTracks().length === 0 || !config.stream.getAudioTracks()[0].enabled;
1875
1973
  }
1876
- this.emit('localMuted', {type: 'audio', value: this.isAudioMuted});
1974
+ this.emit('localMuted', {type: 'audio', value: this.isAudioMuted, mid});
1877
1975
  }
1878
-
1879
- toggleVideo() {
1976
+
1977
+ toggleVideo(value = null, mid) {
1880
1978
  let handle = this._getHandle(this.handleId);
1881
1979
  if (!handle) {
1882
1980
  return Promise.reject({type: 'error', id: 21, message: 'no local id, connect first', data: null})
1883
1981
  }
1884
1982
  let config = handle.webrtcStuff;
1885
-
1983
+
1886
1984
  if (this.isUnifiedPlan) {
1887
1985
  let transceiver = config.pc.getTransceivers()
1888
- .find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && t.stopped === false);
1986
+ .find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && t.stopped === false && (mid ? t.mid === mid : true));
1889
1987
  if (transceiver) {
1890
- transceiver.sender.track.enabled = !transceiver.sender.track.enabled;
1988
+ transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
1891
1989
  }
1892
1990
  this.isVideoMuted = !transceiver || !transceiver.sender.track.enabled;
1893
1991
  }
1894
1992
  else {
1895
1993
  if (config.stream && config.stream.getVideoTracks().length) {
1896
- config.stream.getVideoTracks()[0].enabled = !config.stream.getVideoTracks()[0].enabled;
1994
+ config.stream.getVideoTracks()[0].enabled = value !== null ? !!value : !config.stream.getVideoTracks()[0].enabled;
1897
1995
  }
1898
1996
  this.isVideoMuted = config.stream.getVideoTracks().length === 0 || !config.stream.getVideoTracks()[0].enabled;
1899
1997
  }
1900
- this.emit('localMuted', {type: 'video', value: this.isVideoMuted});
1998
+ this.emit('localMuted', {type: 'video', value: this.isVideoMuted, mid});
1901
1999
  }
1902
-
2000
+
1903
2001
  setInstructorId(instructorId = null) {
1904
2002
  this._instuctorId = instructorId;
1905
2003
  this.emit('instructorId', this._instuctorId);
1906
2004
  return this._instuctorId;
1907
2005
  }
1908
-
2006
+
1909
2007
  setObserverIds(observerIds = []) {
1910
2008
  this._observerIds = observerIds;
1911
2009
  this.emit('observerIds', this._observerIds);
1912
2010
  return this._observerIds;
1913
2011
  }
1914
-
2012
+
1915
2013
  setTalkbackIds(talkbackIds = []) {
1916
2014
  this._talkbackIds = talkbackIds;
1917
2015
  this.emit('talkbackIds', this._talkbackIds);
1918
2016
  return this._talkbackIds;
1919
2017
  }
1920
-
2018
+
1921
2019
  }
1922
2020
 
1923
2021
  export default Room;