@reactoo/watchtogether-sdk-js 2.6.13 → 2.6.20
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.
- package/dist/watchtogether-sdk.js +733 -411
- package/dist/watchtogether-sdk.min.js +2 -2
- package/example/index.html +60 -27
- package/package.json +1 -3
- package/src/models/iot.js +1 -1
- package/src/models/room-session.js +7 -10
- package/src/models/utils.js +18 -3
- package/src/modules/wt-fingerprint.js +46 -0
- package/src/modules/wt-room.js +530 -333
- package/src/modules/wt-utils.js +48 -45
package/src/modules/wt-room.js
CHANGED
|
@@ -206,9 +206,7 @@ class RoomSession {
|
|
|
206
206
|
this._roomType = 'watchparty';
|
|
207
207
|
this._isDataChannelOpen = false;
|
|
208
208
|
this._abortController = null;
|
|
209
|
-
|
|
210
|
-
this.isAudioMuted = false;
|
|
211
|
-
this.isVideoMuted = false;
|
|
209
|
+
this.isMuted = [];
|
|
212
210
|
this.isVideoEnabled = false;
|
|
213
211
|
this.isAudioEnabed = false;
|
|
214
212
|
|
|
@@ -221,7 +219,6 @@ class RoomSession {
|
|
|
221
219
|
if (this.options.debug) {
|
|
222
220
|
this._enableDebug();
|
|
223
221
|
}
|
|
224
|
-
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
_participantShouldSubscribe(userId) {
|
|
@@ -242,6 +239,7 @@ class RoomSession {
|
|
|
242
239
|
message: 'id non-existent',
|
|
243
240
|
data: [handleId, 'getParticipantEventName']
|
|
244
241
|
});
|
|
242
|
+
return;
|
|
245
243
|
}
|
|
246
244
|
|
|
247
245
|
const participantRole = decodeJanusDisplay(handle.userId)?.role;
|
|
@@ -278,6 +276,7 @@ class RoomSession {
|
|
|
278
276
|
message: 'id non-existent',
|
|
279
277
|
data: [handleId, 'getParticipantEventName']
|
|
280
278
|
});
|
|
279
|
+
return;
|
|
281
280
|
}
|
|
282
281
|
|
|
283
282
|
const participantRole = decodeJanusDisplay(handle.userId)?.role;
|
|
@@ -501,10 +500,10 @@ class RoomSession {
|
|
|
501
500
|
|
|
502
501
|
if (sender === this.handleId) {
|
|
503
502
|
if (type === "event") {
|
|
503
|
+
|
|
504
504
|
var plugindata = json["plugindata"] || {};
|
|
505
505
|
var msg = plugindata["data"] || {};
|
|
506
506
|
var jsep = json["jsep"];
|
|
507
|
-
|
|
508
507
|
let result = msg["result"] || null;
|
|
509
508
|
let event = msg["videoroom"] || null;
|
|
510
509
|
let list = msg["publishers"] || {};
|
|
@@ -539,9 +538,9 @@ class RoomSession {
|
|
|
539
538
|
"request": "join",
|
|
540
539
|
"room": this.roomId,
|
|
541
540
|
"ptype": "subscriber",
|
|
542
|
-
streams: streams.map(stream => ({feed: stream.id, mid: stream.mid})),
|
|
543
|
-
//"feed": id,
|
|
544
541
|
"private_id": this.privateId,
|
|
542
|
+
streams: streams.filter(s => !s.disabled).map(stream => ({feed: stream.id, mid: stream.mid})),
|
|
543
|
+
//"feed": id,
|
|
545
544
|
...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
|
|
546
545
|
pin: this.pin
|
|
547
546
|
}
|
|
@@ -560,6 +559,7 @@ class RoomSession {
|
|
|
560
559
|
}
|
|
561
560
|
|
|
562
561
|
for (let f in list) {
|
|
562
|
+
|
|
563
563
|
let userId = list[f]["display"];
|
|
564
564
|
let streams = list[f]["streams"] || [];
|
|
565
565
|
let id = list[f]["id"];
|
|
@@ -569,24 +569,42 @@ class RoomSession {
|
|
|
569
569
|
}
|
|
570
570
|
this._log('Remote userId: ', userId);
|
|
571
571
|
if (this._participantShouldSubscribe(userId)) {
|
|
572
|
-
|
|
573
|
-
this.
|
|
574
|
-
|
|
575
|
-
|
|
572
|
+
|
|
573
|
+
let handle = this._getHandle(null, id);
|
|
574
|
+
if(handle) {
|
|
575
|
+
let subscribe = streams.filter(stream => !stream.disabled && handle.webrtcStuff.tracksMap.filter(t => t.active).findIndex(t => t.mid === stream.mid) === -1).map(s => ({feed: s.id, mid: s.mid}));
|
|
576
|
+
let unsubscribe = streams.filter(stream => stream.disabled).map(s => ({feed: s.id, mid: s.mid}));
|
|
577
|
+
this._log('Already subscribed to user: ', userId, 'Update streams', subscribe, unsubscribe);
|
|
578
|
+
if(subscribe.length || unsubscribe.length) {
|
|
579
|
+
this.sendMessage(handle.handleId, {
|
|
576
580
|
body: {
|
|
577
|
-
"request": "
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
streams: streams.map(stream => ({feed: stream.id, mid: stream.mid})),
|
|
581
|
-
"private_id": this.privateId,
|
|
582
|
-
...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
|
|
583
|
-
pin: this.pin
|
|
581
|
+
"request": "update",
|
|
582
|
+
...(subscribe.length ? {subscribe}: {}),
|
|
583
|
+
...(unsubscribe.length ? {unsubscribe}: {})
|
|
584
584
|
}
|
|
585
585
|
})
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
this._log('Creating user: ', userId, streams);
|
|
590
|
+
this._createParticipant(userId, id)
|
|
591
|
+
.then(handle => {
|
|
592
|
+
return this.sendMessage(handle.handleId, {
|
|
593
|
+
body: {
|
|
594
|
+
"request": "join",
|
|
595
|
+
"room": this.roomId,
|
|
596
|
+
"ptype": "subscriber",
|
|
597
|
+
"private_id": this.privateId,
|
|
598
|
+
streams: streams.filter(s => !s.disabled).map(stream => ({feed: stream.id, mid: stream.mid})),
|
|
599
|
+
...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
|
|
600
|
+
pin: this.pin
|
|
601
|
+
}
|
|
602
|
+
})
|
|
603
|
+
})
|
|
604
|
+
.catch(err => {
|
|
605
|
+
this.emit('error', err);
|
|
606
|
+
})
|
|
607
|
+
}
|
|
590
608
|
}
|
|
591
609
|
}
|
|
592
610
|
|
|
@@ -598,7 +616,7 @@ class RoomSession {
|
|
|
598
616
|
this.disconnect().catch(() => {});
|
|
599
617
|
}
|
|
600
618
|
} else if (leaving) {
|
|
601
|
-
//TODO:
|
|
619
|
+
//TODO: in 1 PeerConnection case we only unsubscribe from streams
|
|
602
620
|
this._log('leaving', leaving);
|
|
603
621
|
this._removeParticipant(null, leaving, true);
|
|
604
622
|
}
|
|
@@ -607,6 +625,7 @@ class RoomSession {
|
|
|
607
625
|
this._log('unpublished', this.handleId, 'this is us');
|
|
608
626
|
this._removeParticipant(this.handleId, null, false); // we do just hangup
|
|
609
627
|
} else if (unpublished) {
|
|
628
|
+
//TODO: in 1 PeerConnection case we only unsubscribe from streams
|
|
610
629
|
this._log('unpublished', unpublished);
|
|
611
630
|
this._removeParticipant(null, unpublished, true); // we do hangup and detach
|
|
612
631
|
}
|
|
@@ -684,18 +703,25 @@ class RoomSession {
|
|
|
684
703
|
let error = msg["error"];
|
|
685
704
|
let substream = msg["substream"];
|
|
686
705
|
|
|
706
|
+
if (event === "updated") {
|
|
707
|
+
this._log('Remote has updated tracks', msg);
|
|
708
|
+
this._updateParticipantsTrackData(handle.handleId, msg["streams"] || []);
|
|
709
|
+
}
|
|
710
|
+
|
|
687
711
|
if (event === "attached") {
|
|
688
712
|
this._log('Remote have successfully joined Room', msg);
|
|
713
|
+
this._updateParticipantsTrackData(handle.handleId, msg["streams"] || []);
|
|
689
714
|
this.emit(this._getAddParticipantEventName(handle.handleId), {
|
|
690
715
|
tid: generateUUID(),
|
|
691
716
|
id: handle.handleId,
|
|
692
717
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
693
718
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
694
719
|
stream: null,
|
|
720
|
+
streamMap: handle.webrtcStuff?.streamMap,
|
|
721
|
+
source: null,
|
|
695
722
|
track: null,
|
|
696
723
|
adding: false,
|
|
697
724
|
constructId: this.constructId,
|
|
698
|
-
metaData: this.options.metaData,
|
|
699
725
|
hasAudioTrack: false,
|
|
700
726
|
hasVideoTrack: false
|
|
701
727
|
});
|
|
@@ -783,6 +809,7 @@ class RoomSession {
|
|
|
783
809
|
"handle_id": handleId
|
|
784
810
|
}, true) : Promise.resolve()))
|
|
785
811
|
.finally(() => {
|
|
812
|
+
|
|
786
813
|
try {
|
|
787
814
|
if (handle.webrtcStuff.stream) {
|
|
788
815
|
if(!this.isRestarting) {
|
|
@@ -795,7 +822,11 @@ class RoomSession {
|
|
|
795
822
|
} catch (e) {
|
|
796
823
|
// Do nothing
|
|
797
824
|
}
|
|
825
|
+
|
|
826
|
+
handle.webrtcStuff.clearTrackRemovedListener?.();
|
|
827
|
+
handle.webrtcStuff.stream.onremovetrack = null;
|
|
798
828
|
handle.webrtcStuff.stream = null;
|
|
829
|
+
|
|
799
830
|
if (handle.webrtcStuff.dataChannel) {
|
|
800
831
|
Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {
|
|
801
832
|
handle.webrtcStuff.dataChannel[label].onmessage = null;
|
|
@@ -818,6 +849,8 @@ class RoomSession {
|
|
|
818
849
|
}
|
|
819
850
|
handle.webrtcStuff = {
|
|
820
851
|
stream: null,
|
|
852
|
+
streamMap: {},
|
|
853
|
+
tracksMap: [],
|
|
821
854
|
mySdp: null,
|
|
822
855
|
mediaConstraints: null,
|
|
823
856
|
pc: null,
|
|
@@ -826,15 +859,24 @@ class RoomSession {
|
|
|
826
859
|
dtmfSender: null,
|
|
827
860
|
trickle: true,
|
|
828
861
|
iceDone: false,
|
|
862
|
+
clearTrackRemovedListener: null,
|
|
829
863
|
};
|
|
830
864
|
|
|
831
865
|
if (handleId === this.handleId) {
|
|
832
866
|
this._isDataChannelOpen = false;
|
|
833
867
|
this.isPublished = false;
|
|
834
868
|
this.emit('published', {status: false, hasStream: false});
|
|
835
|
-
this.emit('removeLocalParticipant', {
|
|
869
|
+
this.emit('removeLocalParticipant', {
|
|
870
|
+
id: handleId,
|
|
871
|
+
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
872
|
+
role: decodeJanusDisplay(handle.userId)?.role}
|
|
873
|
+
);
|
|
836
874
|
} else {
|
|
837
|
-
this.emit(this._getRemoveParticipantEventName(handleId), {
|
|
875
|
+
this.emit(this._getRemoveParticipantEventName(handleId), {
|
|
876
|
+
id: handleId,
|
|
877
|
+
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
878
|
+
role: decodeJanusDisplay(handle.userId)?.role}
|
|
879
|
+
);
|
|
838
880
|
}
|
|
839
881
|
|
|
840
882
|
if (removeHandle) {
|
|
@@ -858,6 +900,8 @@ class RoomSession {
|
|
|
858
900
|
rfid, userId,
|
|
859
901
|
webrtcStuff: {
|
|
860
902
|
stream: null,
|
|
903
|
+
streamMap: {},
|
|
904
|
+
tracksMap: [],
|
|
861
905
|
mySdp: null,
|
|
862
906
|
mediaConstraints: null,
|
|
863
907
|
pc: null,
|
|
@@ -874,6 +918,54 @@ class RoomSession {
|
|
|
874
918
|
})
|
|
875
919
|
}
|
|
876
920
|
|
|
921
|
+
_updateParticipantsTrackData(handleId, streams) {
|
|
922
|
+
this._log('Updating participants track data', handleId, streams);
|
|
923
|
+
let handle = this._getHandle(handleId);
|
|
924
|
+
if (!handle) {
|
|
925
|
+
this.emit('error', {
|
|
926
|
+
type: 'warning',
|
|
927
|
+
id: 15,
|
|
928
|
+
message: 'id non-existent',
|
|
929
|
+
data: [handleId, 'updateParticipantsTrackData']
|
|
930
|
+
});
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
let config = handle.webrtcStuff;
|
|
934
|
+
config.tracksMap = structuredClone(streams);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
_updateRemoteParticipantStreamMap(handleId) {
|
|
938
|
+
this._log('Updating participants stream map', handleId);
|
|
939
|
+
let handle = this._getHandle(handleId);
|
|
940
|
+
if (!handle) {
|
|
941
|
+
this.emit('error', {
|
|
942
|
+
type: 'warning',
|
|
943
|
+
id: 15,
|
|
944
|
+
message: 'id non-existent',
|
|
945
|
+
data: [handleId, 'updateRemoteParticipantStreamMap']
|
|
946
|
+
});
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
let config = handle.webrtcStuff;
|
|
950
|
+
config.streamMap = {};
|
|
951
|
+
config.tracksMap.forEach(tItem => {
|
|
952
|
+
if(tItem.type === 'data') {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if(tItem.active === false) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if(!config.streamMap[tItem.feed_description]) {
|
|
959
|
+
config.streamMap[tItem.feed_description] = [];
|
|
960
|
+
}
|
|
961
|
+
let trackId = config.pc.getTransceivers().find(t => t.mid === tItem.mid).receiver.track.id;
|
|
962
|
+
if(trackId) {
|
|
963
|
+
config.streamMap[tItem.feed_description].push(trackId);
|
|
964
|
+
}
|
|
965
|
+
})
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
|
|
877
969
|
_joinRoom(roomId, pin, userId, display) {
|
|
878
970
|
return this.sendMessage(this.handleId, {
|
|
879
971
|
body: {
|
|
@@ -1239,11 +1331,12 @@ class RoomSession {
|
|
|
1239
1331
|
let handle = this._getHandle(handleId);
|
|
1240
1332
|
if (!handle) {
|
|
1241
1333
|
this.emit('error', {
|
|
1242
|
-
type: '
|
|
1334
|
+
type: 'error',
|
|
1243
1335
|
id: 15,
|
|
1244
1336
|
message: 'id non-existent',
|
|
1245
1337
|
data: [handleId, 'create rtc connection']
|
|
1246
1338
|
});
|
|
1339
|
+
return;
|
|
1247
1340
|
}
|
|
1248
1341
|
|
|
1249
1342
|
let config = handle.webrtcStuff;
|
|
@@ -1266,8 +1359,15 @@ class RoomSession {
|
|
|
1266
1359
|
this._log('new RTCPeerConnection', pc_config, pc_constraints);
|
|
1267
1360
|
|
|
1268
1361
|
config.pc = new RTCPeerConnection(pc_config, pc_constraints);
|
|
1362
|
+
|
|
1363
|
+
config.pc.onnegotiationneeded = () => {
|
|
1364
|
+
this._log('onnegotiationneeded');
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1269
1367
|
config.pc.onconnectionstatechange = () => {
|
|
1368
|
+
//TODO: check if this isnt fired prematurely when we switch internet connection
|
|
1270
1369
|
if (config.pc.connectionState === 'failed') {
|
|
1370
|
+
this._log('connectionState failed');
|
|
1271
1371
|
this._iceRestart(handleId);
|
|
1272
1372
|
}
|
|
1273
1373
|
this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);
|
|
@@ -1279,10 +1379,11 @@ class RoomSession {
|
|
|
1279
1379
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1280
1380
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1281
1381
|
stream: config.stream,
|
|
1382
|
+
streamMap: config.streamMap,
|
|
1282
1383
|
track: null,
|
|
1384
|
+
source: null,
|
|
1283
1385
|
optional: true,
|
|
1284
1386
|
constructId: this.constructId,
|
|
1285
|
-
metaData: this.options.metaData,
|
|
1286
1387
|
adding: false,
|
|
1287
1388
|
hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
|
|
1288
1389
|
hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
|
|
@@ -1301,10 +1402,11 @@ class RoomSession {
|
|
|
1301
1402
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1302
1403
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1303
1404
|
stream: config.stream,
|
|
1405
|
+
streamMap: config.streamMap,
|
|
1304
1406
|
track: null,
|
|
1407
|
+
source: null,
|
|
1305
1408
|
optional: true,
|
|
1306
1409
|
constructId: this.constructId,
|
|
1307
|
-
metaData: this.options.metaData,
|
|
1308
1410
|
adding: false,
|
|
1309
1411
|
hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
|
|
1310
1412
|
hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
|
|
@@ -1340,14 +1442,44 @@ class RoomSession {
|
|
|
1340
1442
|
if(!event.streams)
|
|
1341
1443
|
return;
|
|
1342
1444
|
|
|
1343
|
-
//config.stream = event.streams[0];
|
|
1344
|
-
|
|
1345
1445
|
if (!config.stream) {
|
|
1346
1446
|
config.stream = new MediaStream();
|
|
1347
1447
|
}
|
|
1448
|
+
|
|
1449
|
+
if(!event.streams?.[0]?.onremovetrack) {
|
|
1450
|
+
event.streams[0].onremovetrack = (ev) => {
|
|
1451
|
+
this._log('Remote track removed', ev);
|
|
1452
|
+
let transceiver = config.pc?.getTransceivers()?.find(
|
|
1453
|
+
t => t.receiver.track === ev.track);
|
|
1454
|
+
|
|
1455
|
+
let mid = transceiver?.mid || ev.track.id;
|
|
1456
|
+
let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.track.id));
|
|
1457
|
+
config.stream?.removeTrack(ev.track);
|
|
1458
|
+
this._updateRemoteParticipantStreamMap(handle.handleId);
|
|
1459
|
+
this.emit(this._getAddParticipantEventName(handle.handleId), {
|
|
1460
|
+
tid: generateUUID(),
|
|
1461
|
+
id: handle.handleId,
|
|
1462
|
+
mid,
|
|
1463
|
+
constructId: this.constructId,
|
|
1464
|
+
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1465
|
+
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1466
|
+
stream: config.stream,
|
|
1467
|
+
streamMap: config.streamMap,
|
|
1468
|
+
source,
|
|
1469
|
+
track: ev.track,
|
|
1470
|
+
adding: false,
|
|
1471
|
+
removing: true,
|
|
1472
|
+
hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
|
|
1473
|
+
hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
|
|
1474
|
+
});
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1348
1478
|
if (event.track) {
|
|
1349
1479
|
let mid = event.transceiver ? event.transceiver.mid : event.track.id;
|
|
1350
|
-
config.
|
|
1480
|
+
let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(event.track.id));
|
|
1481
|
+
config.stream?.addTrack(event.track);
|
|
1482
|
+
this._updateRemoteParticipantStreamMap(handle.handleId);
|
|
1351
1483
|
this.emit(this._getAddParticipantEventName(handle.handleId), {
|
|
1352
1484
|
tid: generateUUID(),
|
|
1353
1485
|
mid,
|
|
@@ -1355,9 +1487,10 @@ class RoomSession {
|
|
|
1355
1487
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1356
1488
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1357
1489
|
stream: config.stream,
|
|
1490
|
+
streamMap: config.streamMap,
|
|
1491
|
+
source,
|
|
1358
1492
|
track: event.track,
|
|
1359
1493
|
constructId: this.constructId,
|
|
1360
|
-
metaData: this.options.metaData,
|
|
1361
1494
|
adding: true,
|
|
1362
1495
|
hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
|
|
1363
1496
|
hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
|
|
@@ -1367,45 +1500,49 @@ class RoomSession {
|
|
|
1367
1500
|
return;
|
|
1368
1501
|
|
|
1369
1502
|
event.track.onended = (ev) => {
|
|
1503
|
+
this._log('Remote track ended');
|
|
1370
1504
|
|
|
1371
1505
|
let transceiver = config.pc?.getTransceivers()?.find(
|
|
1372
1506
|
t => t.receiver.track === ev.target);
|
|
1373
1507
|
|
|
1374
1508
|
let mid = transceiver?.mid || ev.target.id;
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
}
|
|
1509
|
+
let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));
|
|
1510
|
+
config.stream?.removeTrack(ev.target);
|
|
1511
|
+
this._updateRemoteParticipantStreamMap(handle.handleId);
|
|
1512
|
+
this.emit(this._getAddParticipantEventName(handle.handleId), {
|
|
1513
|
+
tid: generateUUID(),
|
|
1514
|
+
id: handle.handleId,
|
|
1515
|
+
mid,
|
|
1516
|
+
constructId: this.constructId,
|
|
1517
|
+
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1518
|
+
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1519
|
+
stream: config.stream,
|
|
1520
|
+
streamMap: config.streamMap,
|
|
1521
|
+
source,
|
|
1522
|
+
track: ev.target,
|
|
1523
|
+
adding: false,
|
|
1524
|
+
removing: true,
|
|
1525
|
+
hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),
|
|
1526
|
+
hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)
|
|
1527
|
+
});
|
|
1394
1528
|
};
|
|
1395
1529
|
|
|
1396
1530
|
event.track.onmute = (ev) => {
|
|
1397
|
-
this._log('
|
|
1531
|
+
this._log('Remote track muted');
|
|
1398
1532
|
|
|
1399
1533
|
let transceiver = config.pc.getTransceivers().find(
|
|
1400
1534
|
t => t.receiver.track === ev.target);
|
|
1401
1535
|
let mid = transceiver.mid || ev.target.id;
|
|
1402
|
-
|
|
1536
|
+
let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));
|
|
1403
1537
|
this.emit('remoteTrackMuted', {
|
|
1404
1538
|
id: handle.handleId,
|
|
1405
1539
|
mid,
|
|
1540
|
+
constructId: this.constructId,
|
|
1406
1541
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1407
1542
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1408
1543
|
stream: config.stream,
|
|
1544
|
+
streamMap: config.streamMap,
|
|
1545
|
+
source,
|
|
1409
1546
|
kind: ev.target.kind,
|
|
1410
1547
|
track: ev.target,
|
|
1411
1548
|
muted: true
|
|
@@ -1413,26 +1550,27 @@ class RoomSession {
|
|
|
1413
1550
|
};
|
|
1414
1551
|
|
|
1415
1552
|
event.track.onunmute = (ev) => {
|
|
1416
|
-
this._log('
|
|
1553
|
+
this._log('Remote track unmuted');
|
|
1417
1554
|
|
|
1418
1555
|
let transceiver = config.pc.getTransceivers().find(
|
|
1419
1556
|
t => t.receiver.track === ev.target);
|
|
1420
1557
|
let mid = transceiver.mid || ev.target.id;
|
|
1421
|
-
|
|
1558
|
+
let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));
|
|
1422
1559
|
this.emit('remoteTrackMuted', {
|
|
1423
1560
|
id: handle.handleId,
|
|
1424
1561
|
mid,
|
|
1562
|
+
constructId: this.constructId,
|
|
1425
1563
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1426
1564
|
role: decodeJanusDisplay(handle.userId)?.role,
|
|
1427
1565
|
stream: config.stream,
|
|
1566
|
+
streamMap: config.streamMap,
|
|
1567
|
+
source,
|
|
1428
1568
|
kind: ev.target.kind,
|
|
1429
1569
|
track: ev.target,
|
|
1430
1570
|
muted: false
|
|
1431
1571
|
});
|
|
1432
1572
|
};
|
|
1433
|
-
|
|
1434
1573
|
}
|
|
1435
|
-
|
|
1436
1574
|
};
|
|
1437
1575
|
}
|
|
1438
1576
|
}
|
|
@@ -1450,7 +1588,6 @@ class RoomSession {
|
|
|
1450
1588
|
let state = config.dataChannel[label] ? config.dataChannel[label].readyState : "null";
|
|
1451
1589
|
this._handleDataEvents(handleId, 'state', {state, label} );
|
|
1452
1590
|
};
|
|
1453
|
-
//TODO: check this
|
|
1454
1591
|
var onDataChannelError = (error) => {
|
|
1455
1592
|
this._handleDataEvents(handleId, 'error', {label: error?.channel?.label, error});
|
|
1456
1593
|
};
|
|
@@ -1536,7 +1673,6 @@ class RoomSession {
|
|
|
1536
1673
|
config.isIceRestarting = true;
|
|
1537
1674
|
let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);
|
|
1538
1675
|
let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);
|
|
1539
|
-
this._setupTransceivers(handleId,[hasAudio, false, hasVideo, false]);
|
|
1540
1676
|
this._createAO('offer', handleId, true )
|
|
1541
1677
|
.then((jsep) => {
|
|
1542
1678
|
if (!jsep) {
|
|
@@ -1569,118 +1705,91 @@ class RoomSession {
|
|
|
1569
1705
|
|
|
1570
1706
|
}
|
|
1571
1707
|
|
|
1572
|
-
_setupTransceivers(handleId, [audioSend, audioRecv, videoSend, videoRecv]) {
|
|
1708
|
+
_setupTransceivers(handleId, [audioSend, audioRecv, videoSend, videoRecv, audioTransceiver = null, videoTransceiver = null]) {
|
|
1709
|
+
|
|
1710
|
+
//TODO: this should be refactored to use handle's trackMap so we dont have to pass any parameters
|
|
1573
1711
|
|
|
1574
1712
|
let handle = this._getHandle(handleId);
|
|
1575
1713
|
if (!handle) {
|
|
1576
1714
|
return null;
|
|
1577
1715
|
}
|
|
1578
|
-
|
|
1579
1716
|
let config = handle.webrtcStuff;
|
|
1580
|
-
let audioTransceiver = null, videoTransceiver = null;
|
|
1581
|
-
let transceivers = config.pc.getTransceivers();
|
|
1582
|
-
if (transceivers && transceivers.length > 0) {
|
|
1583
|
-
for (var i in transceivers) {
|
|
1584
|
-
var t = transceivers[i];
|
|
1585
|
-
if ((t.sender && t.sender.track && t.sender.track.kind === "audio") ||
|
|
1586
|
-
(t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) {
|
|
1587
|
-
if (!audioTransceiver)
|
|
1588
|
-
audioTransceiver = t;
|
|
1589
|
-
continue;
|
|
1590
|
-
}
|
|
1591
|
-
if ((t.sender && t.sender.track && t.sender.track.kind === "video") ||
|
|
1592
|
-
(t.receiver && t.receiver.track && t.receiver.track.kind === "video")) {
|
|
1593
|
-
if (!videoTransceiver)
|
|
1594
|
-
videoTransceiver = t;
|
|
1595
|
-
continue;
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
1717
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
} else {
|
|
1607
|
-
audioTransceiver.direction = "inactive";
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
} else {
|
|
1611
|
-
// Take care of audio m-line
|
|
1612
|
-
if (audioSend && audioRecv) {
|
|
1613
|
-
if (audioTransceiver) {
|
|
1614
|
-
if (audioTransceiver.setDirection) {
|
|
1615
|
-
audioTransceiver.setDirection("sendrecv");
|
|
1718
|
+
const setTransceiver = (transceiver, send, recv) => {
|
|
1719
|
+
if (!send && !recv) {
|
|
1720
|
+
// disabled: have we removed it?
|
|
1721
|
+
if (transceiver) {
|
|
1722
|
+
if (transceiver.setDirection) {
|
|
1723
|
+
transceiver.setDirection("inactive");
|
|
1616
1724
|
} else {
|
|
1617
|
-
|
|
1725
|
+
transceiver.direction = "inactive";
|
|
1618
1726
|
}
|
|
1619
1727
|
}
|
|
1620
|
-
} else
|
|
1621
|
-
if (
|
|
1622
|
-
if (
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1728
|
+
} else {
|
|
1729
|
+
if (send && recv) {
|
|
1730
|
+
if (transceiver) {
|
|
1731
|
+
if (transceiver.setDirection) {
|
|
1732
|
+
transceiver.setDirection("sendrecv");
|
|
1733
|
+
} else {
|
|
1734
|
+
transceiver.direction = "sendrecv";
|
|
1735
|
+
}
|
|
1626
1736
|
}
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1737
|
+
} else if (send && !recv) {
|
|
1738
|
+
if (transceiver) {
|
|
1739
|
+
if (transceiver.setDirection) {
|
|
1740
|
+
transceiver.setDirection("sendonly");
|
|
1741
|
+
} else {
|
|
1742
|
+
transceiver.direction = "sendonly";
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
} else if (!send && recv) {
|
|
1746
|
+
if (transceiver) {
|
|
1747
|
+
if (transceiver.setDirection) {
|
|
1748
|
+
transceiver.setDirection("recvonly");
|
|
1749
|
+
} else {
|
|
1750
|
+
transceiver.direction = "recvonly";
|
|
1751
|
+
}
|
|
1632
1752
|
} else {
|
|
1633
|
-
|
|
1753
|
+
// In theory, this is the only case where we might not have a transceiver yet
|
|
1754
|
+
config.pc.addTransceiver("audio", {direction: "recvonly"});
|
|
1634
1755
|
}
|
|
1635
|
-
} else {
|
|
1636
|
-
// In theory, this is the only case where we might not have a transceiver yet
|
|
1637
|
-
config.pc.addTransceiver("audio", {direction: "recvonly"});
|
|
1638
1756
|
}
|
|
1639
1757
|
}
|
|
1640
1758
|
}
|
|
1641
|
-
|
|
1642
|
-
if
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
} else {
|
|
1647
|
-
videoTransceiver.direction = "inactive";
|
|
1648
|
-
}
|
|
1759
|
+
|
|
1760
|
+
// if we're passing any transceivers, we work only on them, doesn't matter if one of them is null
|
|
1761
|
+
if(audioTransceiver || videoTransceiver) {
|
|
1762
|
+
if(audioTransceiver) {
|
|
1763
|
+
setTransceiver(audioTransceiver, audioSend, audioRecv);
|
|
1649
1764
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
videoTransceiver.direction = "sendonly";
|
|
1765
|
+
if(videoTransceiver) {
|
|
1766
|
+
setTransceiver(videoTransceiver, videoSend, videoRecv);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
// else we work on all transceivers
|
|
1770
|
+
else {
|
|
1771
|
+
let transceivers = config.pc.getTransceivers();
|
|
1772
|
+
if (transceivers && transceivers.length > 0) {
|
|
1773
|
+
for (let i in transceivers) {
|
|
1774
|
+
let t = transceivers[i];
|
|
1775
|
+
if (
|
|
1776
|
+
(t.sender && t.sender.track && t.sender.track.kind === "audio") ||
|
|
1777
|
+
(t.receiver && t.receiver.track && t.receiver.track.kind === "audio")
|
|
1778
|
+
) {
|
|
1779
|
+
setTransceiver(t, audioSend, audioRecv);
|
|
1666
1780
|
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
} else {
|
|
1673
|
-
videoTransceiver.direction = "recvonly";
|
|
1781
|
+
if (
|
|
1782
|
+
(t.sender && t.sender.track && t.sender.track.kind === "video") ||
|
|
1783
|
+
(t.receiver && t.receiver.track && t.receiver.track.kind === "video")
|
|
1784
|
+
) {
|
|
1785
|
+
setTransceiver(t, videoSend, videoRecv);
|
|
1674
1786
|
}
|
|
1675
|
-
} else {
|
|
1676
|
-
// In theory, this is the only case where we might not have a transceiver yet
|
|
1677
|
-
config.pc.addTransceiver("video", {direction: "recvonly"});
|
|
1678
1787
|
}
|
|
1679
1788
|
}
|
|
1680
1789
|
}
|
|
1681
1790
|
}
|
|
1682
1791
|
|
|
1683
|
-
_createAO(type = 'offer', handleId, iceRestart = false) {
|
|
1792
|
+
_createAO(type = 'offer', handleId, iceRestart = false, ) {
|
|
1684
1793
|
|
|
1685
1794
|
let handle = this._getHandle(handleId);
|
|
1686
1795
|
if (!handle) {
|
|
@@ -1707,7 +1816,7 @@ class RoomSession {
|
|
|
1707
1816
|
}
|
|
1708
1817
|
|
|
1709
1818
|
return config.pc[methodName](mediaConstraints)
|
|
1710
|
-
.then(
|
|
1819
|
+
.then( (response) => {
|
|
1711
1820
|
config.mySdp = response.sdp;
|
|
1712
1821
|
let _p = config.pc.setLocalDescription(response)
|
|
1713
1822
|
.catch((e) => {
|
|
@@ -1805,8 +1914,35 @@ class RoomSession {
|
|
|
1805
1914
|
}
|
|
1806
1915
|
|
|
1807
1916
|
//Public methods
|
|
1917
|
+
_republishOnTrackEnded(source) {
|
|
1918
|
+
let handle = this._getHandle(this.handleId);
|
|
1919
|
+
if (!handle) {
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
let config = handle.webrtcStuff;
|
|
1923
|
+
if (!config.stream) {
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
let sourceTrackIds = (config.streamMap[source] || []);
|
|
1927
|
+
let remainingTracks = [];
|
|
1928
|
+
for(let i = 0; i < sourceTrackIds.length; i++) {
|
|
1929
|
+
let foundTrack = config.stream.getTracks().find(t => t.id === sourceTrackIds[i]);
|
|
1930
|
+
if(foundTrack) {
|
|
1931
|
+
remainingTracks.push(foundTrack);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
if (remainingTracks.length) {
|
|
1935
|
+
let stream = new MediaStream();
|
|
1936
|
+
remainingTracks.forEach(track => stream.addTrack(track));
|
|
1937
|
+
return this.publishLocal(stream, source);
|
|
1938
|
+
}
|
|
1939
|
+
else {
|
|
1940
|
+
return this.publishLocal(null, source);
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
|
|
1808
1944
|
|
|
1809
|
-
publishLocal(stream = null) {
|
|
1945
|
+
publishLocal(stream = null, source = 'camera0') {
|
|
1810
1946
|
|
|
1811
1947
|
if(this.isDisconnecting || !this.isConnected) {
|
|
1812
1948
|
return Promise.reject({
|
|
@@ -1816,7 +1952,23 @@ class RoomSession {
|
|
|
1816
1952
|
})
|
|
1817
1953
|
}
|
|
1818
1954
|
|
|
1819
|
-
|
|
1955
|
+
if(stream?.getVideoTracks()?.length > 1) {
|
|
1956
|
+
return Promise.reject({
|
|
1957
|
+
type: 'warning',
|
|
1958
|
+
id: 30,
|
|
1959
|
+
message: 'multiple video tracks not supported',
|
|
1960
|
+
data: null
|
|
1961
|
+
})
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
if(stream?.getAudioTracks()?.length > 1) {
|
|
1965
|
+
return Promise.reject({
|
|
1966
|
+
type: 'warning',
|
|
1967
|
+
id: 30,
|
|
1968
|
+
message: 'multiple audio tracks not supported',
|
|
1969
|
+
data: null
|
|
1970
|
+
})
|
|
1971
|
+
}
|
|
1820
1972
|
|
|
1821
1973
|
let handle = this._getHandle(this.handleId);
|
|
1822
1974
|
if (!handle) {
|
|
@@ -1828,26 +1980,105 @@ class RoomSession {
|
|
|
1828
1980
|
})
|
|
1829
1981
|
}
|
|
1830
1982
|
|
|
1983
|
+
this.emit('publishing', true);
|
|
1984
|
+
|
|
1831
1985
|
this._webrtc(this.handleId);
|
|
1832
1986
|
|
|
1833
1987
|
let config = handle.webrtcStuff;
|
|
1988
|
+
|
|
1989
|
+
if (!config.stream) {
|
|
1990
|
+
config.stream = new MediaStream();
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
let needsNegotiation = false;
|
|
1994
|
+
let transceivers = config.pc.getTransceivers();
|
|
1995
|
+
let existingTracks = [...(config.streamMap[source] || [])];
|
|
1996
|
+
config.streamMap[source] = stream?.getTracks()?.map(track => track.id) || [];
|
|
1997
|
+
|
|
1998
|
+
// remove old audio track related to this source
|
|
1999
|
+
let oldAudioStream = config?.stream?.getAudioTracks()?.find(track => existingTracks.includes(track.id));
|
|
2000
|
+
if (oldAudioStream) {
|
|
2001
|
+
try {
|
|
2002
|
+
oldAudioStream.stop();
|
|
2003
|
+
|
|
2004
|
+
} catch (e) {
|
|
2005
|
+
this._log(e);
|
|
2006
|
+
}
|
|
2007
|
+
config.stream.removeTrack(oldAudioStream);
|
|
2008
|
+
config.stream.dispatchEvent(new Event("trackremoved", {detail: oldAudioStream}))
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// remove old video track related to this source
|
|
2012
|
+
let oldVideoStream = config?.stream?.getVideoTracks()?.find(track => existingTracks.includes(track.id));
|
|
2013
|
+
if (oldVideoStream) {
|
|
2014
|
+
try {
|
|
2015
|
+
oldVideoStream.stop();
|
|
2016
|
+
} catch (e) {
|
|
2017
|
+
this._log(e);
|
|
2018
|
+
}
|
|
2019
|
+
config.stream.removeTrack(oldVideoStream);
|
|
2020
|
+
config.stream.dispatchEvent(new Event("trackremoved", {detail: oldVideoStream}))
|
|
2021
|
+
}
|
|
2022
|
+
|
|
1834
2023
|
let audioTrackReplacePromise = Promise.resolve();
|
|
1835
2024
|
let videoTrackReplacePromise = Promise.resolve();
|
|
1836
2025
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2026
|
+
let audioTransceiver = null;
|
|
2027
|
+
let videoTransceiver = null;
|
|
2028
|
+
let replaceAudio = stream?.getAudioTracks()?.length;
|
|
2029
|
+
let replaceVideo = stream?.getVideoTracks()?.length;
|
|
2030
|
+
|
|
2031
|
+
for(const transceiver of transceivers) {
|
|
2032
|
+
if(['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && transceiver.sender?.track?.kind === 'audio' && existingTracks.includes(transceiver.sender?.track?.id)) {
|
|
2033
|
+
audioTransceiver = transceiver;
|
|
2034
|
+
}
|
|
2035
|
+
else if(['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && transceiver.sender?.track?.kind === 'video' && existingTracks.includes(transceiver.sender?.track?.id)) {
|
|
2036
|
+
videoTransceiver = transceiver;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
// Reusing existing transceivers
|
|
2040
|
+
// TODO: if we start using different codecs for different sources, we need to check for that here
|
|
2041
|
+
|
|
2042
|
+
else if(transceiver.currentDirection === 'inactive' && transceiver.sender?.getParameters()?.codecs?.find(c => c.mimeType.indexOf('audio') > -1) && replaceAudio && !audioTransceiver) {
|
|
2043
|
+
audioTransceiver = transceiver;
|
|
2044
|
+
needsNegotiation = true;
|
|
2045
|
+
}
|
|
2046
|
+
else if(transceiver.currentDirection === 'inactive' && transceiver.sender?.getParameters()?.codecs?.find(c => c.mimeType.indexOf('video') > -1) && replaceVideo && !videoTransceiver) {
|
|
2047
|
+
videoTransceiver = transceiver;
|
|
2048
|
+
needsNegotiation = true;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
if (replaceAudio) {
|
|
2053
|
+
config.stream.addTrack(stream.getAudioTracks()[0]);
|
|
2054
|
+
if (audioTransceiver && audioTransceiver.sender) {
|
|
2055
|
+
audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);
|
|
2056
|
+
} else {
|
|
2057
|
+
config.pc.addTrack(stream.getAudioTracks()[0], config.stream);
|
|
2058
|
+
needsNegotiation = true;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
else {
|
|
2062
|
+
if (audioTransceiver && audioTransceiver.sender) {
|
|
2063
|
+
audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(null);
|
|
2064
|
+
needsNegotiation = true;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
1840
2067
|
|
|
1841
|
-
|
|
1842
|
-
|
|
2068
|
+
if (replaceVideo) {
|
|
2069
|
+
config.stream.addTrack(stream.getVideoTracks()[0]);
|
|
2070
|
+
if (videoTransceiver && videoTransceiver.sender) {
|
|
2071
|
+
videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);
|
|
2072
|
+
} else {
|
|
2073
|
+
if(!this.simulcast) {
|
|
2074
|
+
config.pc.addTrack(stream.getVideoTracks()[0], config.stream);
|
|
1843
2075
|
}
|
|
1844
2076
|
else {
|
|
1845
|
-
// adding simulcast streams
|
|
1846
2077
|
let bitRates = this.simulcastBitrates;
|
|
1847
2078
|
if(adapter.browserDetails.browser !== 'firefox') {
|
|
1848
2079
|
// standard
|
|
1849
2080
|
|
|
1850
|
-
config.pc.addTransceiver(
|
|
2081
|
+
config.pc.addTransceiver(stream.getVideoTracks()[0], {
|
|
1851
2082
|
direction: 'sendonly',
|
|
1852
2083
|
streams: [config.stream],
|
|
1853
2084
|
sendEncodings: [
|
|
@@ -1859,14 +2090,14 @@ class RoomSession {
|
|
|
1859
2090
|
}
|
|
1860
2091
|
else {
|
|
1861
2092
|
// firefox
|
|
1862
|
-
let transceiver = config.pc.addTransceiver(
|
|
2093
|
+
let transceiver = config.pc.addTransceiver(stream.getVideoTracks()[0], {
|
|
1863
2094
|
direction: 'sendonly',
|
|
1864
2095
|
streams: [config.stream]
|
|
1865
2096
|
});
|
|
1866
2097
|
let sender = transceiver ? transceiver.sender : null;
|
|
1867
2098
|
if(sender) {
|
|
1868
2099
|
let parameters = sender.getParameters() || {};
|
|
1869
|
-
parameters.encodings =
|
|
2100
|
+
parameters.encodings = stream.getVideoTracks()[0].sendEncodings || [
|
|
1870
2101
|
{ rid: 'h', active: true, maxBitrate: bitRates.high },
|
|
1871
2102
|
{ rid: 'm', active: true, maxBitrate: bitRates.medium, scaleResolutionDownBy: 2 },
|
|
1872
2103
|
{ rid: 'l', active: true, maxBitrate: bitRates.low, scaleResolutionDownBy: 4 }
|
|
@@ -1875,109 +2106,72 @@ class RoomSession {
|
|
|
1875
2106
|
}
|
|
1876
2107
|
}
|
|
1877
2108
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
} else {
|
|
1881
|
-
|
|
1882
|
-
let transceivers = config.pc.getTransceivers();
|
|
1883
|
-
let audioTransceiver = null;
|
|
1884
|
-
let videoTransceiver = null;
|
|
1885
|
-
if (transceivers && transceivers.length > 0) {
|
|
1886
|
-
for (let i in transceivers) {
|
|
1887
|
-
let t = transceivers[i];
|
|
1888
|
-
if ((t.sender && t.sender.track && t.sender.track.kind === "audio") ||
|
|
1889
|
-
(t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) {
|
|
1890
|
-
audioTransceiver = t;
|
|
1891
|
-
break;
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
for (let i in transceivers) {
|
|
1895
|
-
let t = transceivers[i];
|
|
1896
|
-
if ((t.sender && t.sender.track && t.sender.track.kind === "video") ||
|
|
1897
|
-
(t.receiver && t.receiver.track && t.receiver.track.kind === "video")) {
|
|
1898
|
-
videoTransceiver = t;
|
|
1899
|
-
break;
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
2109
|
+
needsNegotiation = true;
|
|
1902
2110
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
if (oldAudioStream) {
|
|
1909
|
-
config.stream.removeTrack(oldAudioStream);
|
|
1910
|
-
try {
|
|
1911
|
-
oldAudioStream.stop();
|
|
1912
|
-
} catch (e) {
|
|
1913
|
-
this._log(e);
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
let replaceAudio = stream?.getAudioTracks()?.length;
|
|
1918
|
-
if (replaceAudio) {
|
|
1919
|
-
config.stream.addTrack(stream.getAudioTracks()[0]);
|
|
1920
|
-
if (audioTransceiver && audioTransceiver.sender) {
|
|
1921
|
-
audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);
|
|
1922
|
-
} else {
|
|
1923
|
-
config.pc.addTrack(stream.getAudioTracks()[0], stream);
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
else {
|
|
1927
|
-
if (audioTransceiver && audioTransceiver.sender) {
|
|
1928
|
-
audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(null);
|
|
1929
|
-
}
|
|
2111
|
+
}
|
|
2112
|
+
else {
|
|
2113
|
+
if (videoTransceiver && videoTransceiver.sender) {
|
|
2114
|
+
videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(null);
|
|
2115
|
+
needsNegotiation = true;
|
|
1930
2116
|
}
|
|
2117
|
+
}
|
|
1931
2118
|
|
|
1932
|
-
/* UPDATE Video */
|
|
1933
2119
|
|
|
1934
|
-
|
|
2120
|
+
this.isAudioEnabed = !!(config.stream && config.stream.getAudioTracks().length > 0);
|
|
2121
|
+
this.isVideoEnabled = !!(config.stream && config.stream.getVideoTracks().length > 0);
|
|
1935
2122
|
|
|
1936
|
-
|
|
1937
|
-
if (oldVideoStream) {
|
|
1938
|
-
config.stream.removeTrack(oldVideoStream);
|
|
1939
|
-
try {
|
|
1940
|
-
oldVideoStream.stop();
|
|
1941
|
-
} catch (e) {
|
|
1942
|
-
this._log(e);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
2123
|
+
// we possibly created new transceivers, so we need to get them again
|
|
1945
2124
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
else {
|
|
1956
|
-
if (videoTransceiver && videoTransceiver.sender) {
|
|
1957
|
-
videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(null);
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
2125
|
+
transceivers = config.pc.getTransceivers();
|
|
2126
|
+
existingTracks = [...(config.streamMap[source] || [])];
|
|
2127
|
+
if(!audioTransceiver) {
|
|
2128
|
+
audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && existingTracks.includes(transceiver.sender.track.id))
|
|
2129
|
+
}
|
|
2130
|
+
if(!videoTransceiver) {
|
|
2131
|
+
videoTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && existingTracks.includes(transceiver.sender.track.id))
|
|
1960
2132
|
}
|
|
1961
2133
|
|
|
1962
2134
|
let hasAudio = !!(stream && stream.getAudioTracks().length > 0);
|
|
1963
2135
|
let hasVideo = !!(stream && stream.getVideoTracks().length > 0);
|
|
1964
|
-
|
|
1965
|
-
let isVideoMuted = !stream || stream.getVideoTracks().length === 0 || !stream.getVideoTracks()[0].enabled;
|
|
1966
|
-
|
|
1967
|
-
this.isAudioEnabed = hasAudio;
|
|
1968
|
-
this.isVideoEnabled = hasVideo;
|
|
1969
|
-
this.isAudioMuted = isAudioMuted;
|
|
1970
|
-
this.isVideoMuted = isVideoMuted;
|
|
2136
|
+
this._setupTransceivers(this.handleId, [hasAudio, false, hasVideo, false, audioTransceiver, videoTransceiver]);
|
|
1971
2137
|
|
|
1972
|
-
this._setupTransceivers(this.handleId, [hasAudio, false, hasVideo, false]);
|
|
1973
2138
|
|
|
1974
2139
|
const emitEvents = () => {
|
|
1975
2140
|
this.isPublished = true;
|
|
1976
|
-
|
|
1977
|
-
|
|
2141
|
+
|
|
2142
|
+
if(!config.stream.onremovetrack) {
|
|
2143
|
+
config.stream.onremovetrack = (ev) => {};
|
|
2144
|
+
|
|
2145
|
+
let trackremoved = (ev) => {
|
|
2146
|
+
this.emit('addLocalParticipant', {
|
|
2147
|
+
tid: generateUUID(),
|
|
2148
|
+
id: handle.handleId,
|
|
2149
|
+
constructId: this.constructId,
|
|
2150
|
+
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
2151
|
+
role: decodeJanusDisplay(this.display)?.role,
|
|
2152
|
+
track: ev.detail,
|
|
2153
|
+
stream: config.stream,
|
|
2154
|
+
streamMap: config.streamMap,
|
|
2155
|
+
source,
|
|
2156
|
+
adding: false,
|
|
2157
|
+
removing: true,
|
|
2158
|
+
hasAudioTrack: hasAudio,
|
|
2159
|
+
hasVideoTrack: hasVideo,
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
config.stream.addEventListener('trackremoved', trackremoved);
|
|
2164
|
+
config.clearTrackRemovedListener = () => {
|
|
2165
|
+
config.stream.removeEventListener('trackremoved', trackremoved);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
let tracks = config.stream.getTracks();
|
|
2170
|
+
if(tracks.length) {
|
|
1978
2171
|
tracks.forEach(track => {
|
|
1979
2172
|
// used as a flag to not emit tracks that been already emitted
|
|
1980
2173
|
if(!track.onended) {
|
|
2174
|
+
|
|
1981
2175
|
this.emit('addLocalParticipant', {
|
|
1982
2176
|
tid: generateUUID(),
|
|
1983
2177
|
id: handle.handleId,
|
|
@@ -1985,31 +2179,24 @@ class RoomSession {
|
|
|
1985
2179
|
role: decodeJanusDisplay(this.display)?.role,
|
|
1986
2180
|
track,
|
|
1987
2181
|
stream: config.stream,
|
|
2182
|
+
streamMap: config.streamMap,
|
|
2183
|
+
source,
|
|
1988
2184
|
adding: true,
|
|
1989
2185
|
constructId: this.constructId,
|
|
1990
|
-
metaData: this.options.metaData,
|
|
1991
2186
|
hasAudioTrack: hasAudio,
|
|
1992
2187
|
hasVideoTrack: hasVideo
|
|
1993
2188
|
});
|
|
2189
|
+
|
|
1994
2190
|
track.onended = (ev) => {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
1999
|
-
role: decodeJanusDisplay(this.display)?.role,
|
|
2000
|
-
track: ev.target,
|
|
2001
|
-
stream: config.stream,
|
|
2002
|
-
adding: false,
|
|
2003
|
-
removing: true,
|
|
2004
|
-
constructId: this.constructId,
|
|
2005
|
-
metaData: this.options.metaData,
|
|
2006
|
-
hasAudioTrack: hasAudio,
|
|
2007
|
-
hasVideoTrack: hasVideo
|
|
2008
|
-
});
|
|
2191
|
+
config.stream.removeTrack(track);
|
|
2192
|
+
config.stream.dispatchEvent(new Event("trackremoved", {detail: track}))
|
|
2193
|
+
this._republishOnTrackEnded(source);
|
|
2009
2194
|
}
|
|
2010
2195
|
}
|
|
2011
2196
|
})
|
|
2012
2197
|
}
|
|
2198
|
+
|
|
2199
|
+
//TODO: legacy event, remove in future
|
|
2013
2200
|
else {
|
|
2014
2201
|
this.emit('addLocalParticipant', {
|
|
2015
2202
|
tid: generateUUID(),
|
|
@@ -2017,24 +2204,34 @@ class RoomSession {
|
|
|
2017
2204
|
userId: decodeJanusDisplay(handle.userId)?.userId,
|
|
2018
2205
|
role: decodeJanusDisplay(this.display)?.role,
|
|
2019
2206
|
stream: null,
|
|
2207
|
+
streamMap: config.streamMap,
|
|
2208
|
+
source,
|
|
2020
2209
|
adding: false,
|
|
2021
2210
|
constructId: this.constructId,
|
|
2022
|
-
metaData: this.options.metaData,
|
|
2023
2211
|
hasAudioTrack: hasAudio,
|
|
2024
2212
|
hasVideoTrack: hasVideo
|
|
2025
2213
|
});
|
|
2026
2214
|
}
|
|
2027
|
-
|
|
2215
|
+
|
|
2216
|
+
this.isMuted = [];
|
|
2217
|
+
for(const source of Object.keys(config.streamMap)) {
|
|
2218
|
+
const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2219
|
+
const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2220
|
+
this.isMuted.push({type: 'audio', value: !audioTrack || !audioTrack.enabled, source, mid: transceivers.find(transceiver => transceiver.sender?.track?.kind === 'audio' && transceiver.sender?.track?.id === audioTrack?.id)?.mid});
|
|
2221
|
+
this.isMuted.push({type: 'video', value: !videoTrack || !videoTrack.enabled, source, mid: transceivers.find(transceiver => transceiver.sender?.track?.kind === 'video' && transceiver.sender?.track?.id === videoTrack?.id)?.mid});
|
|
2222
|
+
this.emit('localHasVideo', !!videoTrack, source);
|
|
2223
|
+
this.emit('localHasAudio', !!audioTrack, source);
|
|
2224
|
+
}
|
|
2225
|
+
for(const val of this.isMuted) {
|
|
2226
|
+
this.emit('localMuted', {...val});
|
|
2227
|
+
}
|
|
2228
|
+
this.emit('published', {status: true, hasStream: tracks.length > 0});
|
|
2028
2229
|
this.emit('publishing', false);
|
|
2029
|
-
this.emit('localHasVideo', hasVideo);
|
|
2030
|
-
this.emit('localHasAudio', hasAudio);
|
|
2031
|
-
this.emit('localMuted', {type: 'video', value: isVideoMuted});
|
|
2032
|
-
this.emit('localMuted', {type: 'audio', value: isAudioMuted});
|
|
2033
2230
|
return this;
|
|
2034
2231
|
};
|
|
2035
2232
|
|
|
2036
2233
|
// this should be enough
|
|
2037
|
-
if(
|
|
2234
|
+
if(!needsNegotiation) {
|
|
2038
2235
|
return Promise
|
|
2039
2236
|
.all([audioTrackReplacePromise, videoTrackReplacePromise])
|
|
2040
2237
|
.then(() => emitEvents())
|
|
@@ -2051,47 +2248,29 @@ class RoomSession {
|
|
|
2051
2248
|
if (jsep.sdp && jsep.sdp.indexOf("\r\na=ice-ufrag") === -1) {
|
|
2052
2249
|
jsep.sdp = jsep.sdp.replace("\na=ice-ufrag", "\r\na=ice-ufrag");
|
|
2053
2250
|
}
|
|
2054
|
-
return this.sendMessage(this.handleId, {
|
|
2055
|
-
body: {"request": "configure", "audio": hasAudio, "video": hasVideo, "data": true, ...(this.recordingFilename ? {filename: this.recordingFilename} : {})},
|
|
2056
|
-
jsep
|
|
2057
|
-
});
|
|
2058
|
-
})
|
|
2059
|
-
.then((r) => {
|
|
2060
|
-
if (this._isDataChannelOpen) {
|
|
2061
|
-
return Promise.resolve(r)
|
|
2062
|
-
} else {
|
|
2063
|
-
return new Promise((resolve, reject) => {
|
|
2064
|
-
|
|
2065
|
-
let dataChannelTimeoutId = null;
|
|
2066
|
-
|
|
2067
|
-
let _resolve = (val) => {
|
|
2068
|
-
if (val) {
|
|
2069
|
-
clearTimeout(dataChannelTimeoutId);
|
|
2070
|
-
this._abortController.signal.removeEventListener('abort', _rejectAbort);
|
|
2071
|
-
this.off('dataChannel', _resolve, this);
|
|
2072
|
-
resolve(this);
|
|
2073
|
-
}
|
|
2074
|
-
};
|
|
2075
2251
|
|
|
2076
|
-
let _rejectTimeout = () => {
|
|
2077
|
-
this.off('dataChannel', _resolve, this);
|
|
2078
|
-
this._abortController.signal.removeEventListener('abort', _rejectAbort);
|
|
2079
|
-
reject({type: 'error', id: 27, message: 'Data channel did not open', data: null});
|
|
2080
|
-
}
|
|
2081
2252
|
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2253
|
+
let descriptions = [];
|
|
2254
|
+
Object.keys(config.streamMap).forEach(source => {
|
|
2255
|
+
config.streamMap[source].forEach(trackId => {
|
|
2256
|
+
let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId)
|
|
2257
|
+
if(t) {
|
|
2258
|
+
descriptions.push({mid: t.mid, description: source});
|
|
2087
2259
|
}
|
|
2260
|
+
})
|
|
2261
|
+
});
|
|
2088
2262
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
this.
|
|
2093
|
-
|
|
2094
|
-
|
|
2263
|
+
return this.sendMessage(this.handleId, {
|
|
2264
|
+
body: {
|
|
2265
|
+
"request": "configure",
|
|
2266
|
+
"audio": this.isAudioEnabed,
|
|
2267
|
+
"video": this.isVideoEnabled,
|
|
2268
|
+
"data": true,
|
|
2269
|
+
...(this.recordingFilename ? {filename: this.recordingFilename} : {}),
|
|
2270
|
+
descriptions: descriptions
|
|
2271
|
+
},
|
|
2272
|
+
jsep
|
|
2273
|
+
});
|
|
2095
2274
|
})
|
|
2096
2275
|
.then(() => emitEvents())
|
|
2097
2276
|
.catch(e => {
|
|
@@ -2111,53 +2290,71 @@ class RoomSession {
|
|
|
2111
2290
|
: Promise.resolve()
|
|
2112
2291
|
}
|
|
2113
2292
|
|
|
2114
|
-
toggleAudio(value = null, mid) {
|
|
2293
|
+
toggleAudio(value = null, source = 'camera0', mid) {
|
|
2115
2294
|
let handle = this._getHandle(this.handleId);
|
|
2116
2295
|
if (!handle) {
|
|
2117
2296
|
return Promise.reject({type: 'error', id: 21, message: 'no local id, connect first', data: null})
|
|
2118
2297
|
}
|
|
2119
2298
|
let config = handle.webrtcStuff;
|
|
2120
|
-
|
|
2121
|
-
let transceiver =
|
|
2122
|
-
|
|
2123
|
-
|
|
2299
|
+
let transceivers = config.pc.getTransceivers().filter(t => t.currentDirection !== 'inactive');
|
|
2300
|
+
let transceiver = null;
|
|
2301
|
+
if(source) {
|
|
2302
|
+
transceiver = transceivers
|
|
2303
|
+
.find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (config.streamMap[source] || []).includes(t.sender.track.id));
|
|
2304
|
+
}
|
|
2305
|
+
else {
|
|
2306
|
+
transceiver = transceivers
|
|
2307
|
+
.find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (mid ? t.mid === mid : true));
|
|
2308
|
+
}
|
|
2124
2309
|
if (transceiver) {
|
|
2125
2310
|
transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
|
|
2126
2311
|
}
|
|
2127
2312
|
|
|
2128
|
-
this.
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2313
|
+
this.isMuted = [];
|
|
2314
|
+
for(const source of Object.keys(config.streamMap)) {
|
|
2315
|
+
const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2316
|
+
const audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === audioTrack?.id);
|
|
2317
|
+
const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2318
|
+
const videoTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === videoTrack?.id);
|
|
2319
|
+
this.isMuted.push({type: 'audio', value: !audioTrack || !audioTransceiver || !audioTransceiver?.sender?.track?.enabled , source, mid: audioTransceiver?.mid});
|
|
2320
|
+
this.isMuted.push({type: 'video', value: !videoTrack || !videoTransceiver || !videoTransceiver?.sender?.track?.enabled, source, mid: videoTransceiver?.mid});
|
|
2321
|
+
}
|
|
2322
|
+
for(let val of this.isMuted) {
|
|
2323
|
+
this.emit('localMuted', {...val});
|
|
2324
|
+
}
|
|
2138
2325
|
}
|
|
2139
2326
|
|
|
2140
|
-
toggleVideo(value = null, mid) {
|
|
2327
|
+
toggleVideo(value = null, source = 'camera0', mid) {
|
|
2141
2328
|
let handle = this._getHandle(this.handleId);
|
|
2142
2329
|
if (!handle) {
|
|
2143
2330
|
return Promise.reject({type: 'error', id: 21, message: 'no local id, connect first', data: null})
|
|
2144
2331
|
}
|
|
2145
2332
|
let config = handle.webrtcStuff;
|
|
2146
|
-
|
|
2147
|
-
let transceiver =
|
|
2148
|
-
|
|
2333
|
+
let transceivers = config.pc.getTransceivers().filter(t => t.currentDirection !== 'inactive');
|
|
2334
|
+
let transceiver = null;
|
|
2335
|
+
if(source) {
|
|
2336
|
+
transceiver = transceivers
|
|
2337
|
+
.find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (config.streamMap[source] || []).includes(t.sender.track.id));
|
|
2338
|
+
}
|
|
2339
|
+
else {
|
|
2340
|
+
transceiver = transceivers
|
|
2341
|
+
.find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (mid ? t.mid === mid : true));
|
|
2342
|
+
}
|
|
2149
2343
|
if (transceiver) {
|
|
2150
2344
|
transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
|
|
2151
2345
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2346
|
+
this.isMuted = [];
|
|
2347
|
+
for(const source of Object.keys(config.streamMap)) {
|
|
2348
|
+
const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2349
|
+
const audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === audioTrack?.id);
|
|
2350
|
+
const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[source].includes(track.id));
|
|
2351
|
+
const videoTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === videoTrack?.id);
|
|
2352
|
+
this.isMuted.push({type: 'audio', value: !audioTrack || !audioTransceiver || !audioTransceiver?.sender?.track?.enabled , source, mid: audioTransceiver?.mid});
|
|
2353
|
+
this.isMuted.push({type: 'video', value: !videoTrack || !videoTransceiver || !videoTransceiver?.sender?.track?.enabled, source, mid: videoTransceiver?.mid});
|
|
2354
|
+
}
|
|
2355
|
+
for(let val of this.isMuted) {
|
|
2356
|
+
this.emit('localMuted', {...val});
|
|
2357
|
+
}
|
|
2161
2358
|
}
|
|
2162
2359
|
|
|
2163
2360
|
selectSubStream(handleId, substream = 2) {
|