@flashphoner/sfusdk-examples 2.0.185 → 2.0.188
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/package.json +1 -1
- package/src/commons/js/config.js +2 -1
- package/src/commons/js/display.js +181 -71
- package/src/commons/js/util.js +3 -2
- package/src/player/player.js +104 -13
- package/src/two-way-streaming/config.json +4 -2
- package/src/two-way-streaming/two-way-streaming.js +159 -60
- package/src/webrtc-abr-player/player.js +91 -9
package/package.json
CHANGED
package/src/commons/js/config.js
CHANGED
|
@@ -29,15 +29,25 @@ const initLocalDisplay = function(localDisplayElement){
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
}
|
|
32
|
+
}
|
|
33
33
|
|
|
34
|
-
const
|
|
34
|
+
const onMuteClick = function(button, stream, type) {
|
|
35
|
+
if (stream.getAudioTracks().length > 0) {
|
|
36
|
+
stream.getAudioTracks()[0].enabled = !(stream.getAudioTracks()[0].enabled);
|
|
37
|
+
button.innerHTML = audioStateText(stream) + " " + type;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const add = function(id, name, stream, type) {
|
|
35
42
|
if (stream.getAudioTracks().length > 0) {
|
|
36
43
|
let videoElement = getAudioContainer();
|
|
37
44
|
if (videoElement) {
|
|
38
45
|
let track = stream.getAudioTracks()[0];
|
|
39
46
|
videoElement.video.srcObject.addTrack(track);
|
|
40
|
-
videoElement.audioStateDisplay.innerHTML = audioStateText(stream);
|
|
47
|
+
videoElement.audioStateDisplay.innerHTML = audioStateText(stream) + " " + type;
|
|
48
|
+
videoElement.audioStateDisplay.addEventListener("click", function() {
|
|
49
|
+
onMuteClick(videoElement.audioStateDisplay, stream, type);
|
|
50
|
+
});
|
|
41
51
|
track.addEventListener("ended", function() {
|
|
42
52
|
videoElement.video.srcObject.removeTrack(track);
|
|
43
53
|
videoElement.audioStateDisplay.innerHTML = "No audio";
|
|
@@ -53,31 +63,15 @@ const initLocalDisplay = function(localDisplayElement){
|
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
const coreDisplay =
|
|
57
|
-
coreDisplay.setAttribute("class","text-center");
|
|
58
|
-
coreDisplay.setAttribute("style","width: auto; height: auto;");
|
|
66
|
+
const coreDisplay = createContainer(null);
|
|
59
67
|
coreDisplay.id = stream.id;
|
|
60
|
-
const publisherNameDisplay =
|
|
61
|
-
publisherNameDisplay.innerHTML = "Name: " + name;
|
|
62
|
-
publisherNameDisplay.setAttribute("class","text-center");
|
|
63
|
-
publisherNameDisplay.setAttribute("style","width: auto; height: auto;");
|
|
64
|
-
coreDisplay.appendChild(publisherNameDisplay);
|
|
68
|
+
const publisherNameDisplay = createInfoDisplay(coreDisplay, name + " " + type);
|
|
65
69
|
|
|
66
70
|
const audioStateDisplay = document.createElement("button");
|
|
67
|
-
audioStateDisplay.innerHTML = audioStateText(stream);
|
|
68
|
-
audioStateDisplay.addEventListener('click', function(){
|
|
69
|
-
if (stream.getAudioTracks().length > 0) {
|
|
70
|
-
stream.getAudioTracks()[0].enabled = !(stream.getAudioTracks()[0].enabled);
|
|
71
|
-
audioStateDisplay.innerHTML = audioStateText(stream);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
71
|
coreDisplay.appendChild(audioStateDisplay);
|
|
75
72
|
|
|
76
|
-
const streamDisplay =
|
|
73
|
+
const streamDisplay = createContainer(coreDisplay);
|
|
77
74
|
streamDisplay.id = "stream-" + id;
|
|
78
|
-
streamDisplay.setAttribute("class","text-center");
|
|
79
|
-
streamDisplay.setAttribute("style","width: auto; height: auto;");
|
|
80
|
-
coreDisplay.appendChild(streamDisplay);
|
|
81
75
|
const video = document.createElement("video");
|
|
82
76
|
video.muted = true;
|
|
83
77
|
if(Browser().isSafariWebRTC()) {
|
|
@@ -101,10 +95,21 @@ const initLocalDisplay = function(localDisplayElement){
|
|
|
101
95
|
removeLocalDisplay(id);
|
|
102
96
|
});
|
|
103
97
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
if (stream.getVideoTracks().length > 0) {
|
|
99
|
+
// Resize only if video displayed
|
|
100
|
+
video.addEventListener('resize', function (event) {
|
|
101
|
+
publisherNameDisplay.innerHTML = name + " " + type + " " + video.videoWidth + "x" + video.videoHeight;
|
|
102
|
+
resizeVideo(event.target);
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
// Hide audio only container
|
|
106
|
+
hideItem(streamDisplay);
|
|
107
|
+
// Set up mute button for audio only stream
|
|
108
|
+
audioStateDisplay.innerHTML = audioStateText(stream) + " " + type;
|
|
109
|
+
audioStateDisplay.addEventListener("click", function() {
|
|
110
|
+
onMuteClick(audioStateDisplay, stream, type);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
108
113
|
localDisplays[id] = coreDisplay;
|
|
109
114
|
localDisplayDiv.appendChild(coreDisplay);
|
|
110
115
|
return coreDisplay;
|
|
@@ -150,7 +155,7 @@ const initRemoteDisplay = function(options) {
|
|
|
150
155
|
let mainDiv = options.div;
|
|
151
156
|
let room = options.room;
|
|
152
157
|
let peerConnection = options.peerConnection;
|
|
153
|
-
let displayOptions = options.displayOptions || {publisher: true, quality: true};
|
|
158
|
+
let displayOptions = options.displayOptions || {publisher: true, quality: true, type: true};
|
|
154
159
|
|
|
155
160
|
room.on(constants.SFU_ROOM_EVENT.ADD_TRACKS, function(e) {
|
|
156
161
|
console.log("Received ADD_TRACKS");
|
|
@@ -180,6 +185,7 @@ const initRemoteDisplay = function(options) {
|
|
|
180
185
|
continue;
|
|
181
186
|
}
|
|
182
187
|
display.audioMid = pTrack.mid;
|
|
188
|
+
display.setTrackInfo(pTrack);
|
|
183
189
|
createDisplay = false;
|
|
184
190
|
break;
|
|
185
191
|
}
|
|
@@ -194,6 +200,7 @@ const initRemoteDisplay = function(options) {
|
|
|
194
200
|
display.setTrackInfo(pTrack);
|
|
195
201
|
} else if (pTrack.type === "AUDIO") {
|
|
196
202
|
display.audioMid = pTrack.mid;
|
|
203
|
+
display.setTrackInfo(pTrack);
|
|
197
204
|
}
|
|
198
205
|
}
|
|
199
206
|
}).on(constants.SFU_ROOM_EVENT.REMOVE_TRACKS, function(e) {
|
|
@@ -264,35 +271,34 @@ const initRemoteDisplay = function(options) {
|
|
|
264
271
|
mainDiv.appendChild(cell);
|
|
265
272
|
let publisherNameDisplay;
|
|
266
273
|
let currentQualityDisplay;
|
|
274
|
+
let videoTypeDisplay;
|
|
267
275
|
if (displayOptions.publisher) {
|
|
268
|
-
publisherNameDisplay =
|
|
269
|
-
publisherNameDisplay.innerHTML = "Published by: " + name;
|
|
270
|
-
publisherNameDisplay.setAttribute("style","width:auto; height:30px;");
|
|
271
|
-
publisherNameDisplay.setAttribute("class","text-center");
|
|
272
|
-
cell.appendChild(publisherNameDisplay);
|
|
276
|
+
publisherNameDisplay = createInfoDisplay(cell, "Published by: " + name);
|
|
273
277
|
}
|
|
274
278
|
if (displayOptions.quality) {
|
|
275
|
-
currentQualityDisplay =
|
|
276
|
-
currentQualityDisplay.innerHTML = "";
|
|
277
|
-
currentQualityDisplay.setAttribute("style","width:auto; height:30px;");
|
|
278
|
-
currentQualityDisplay.setAttribute("class","text-center");
|
|
279
|
-
cell.appendChild(currentQualityDisplay);
|
|
279
|
+
currentQualityDisplay = createInfoDisplay(cell, "");
|
|
280
280
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
cell
|
|
281
|
+
if (displayOptions.type) {
|
|
282
|
+
videoTypeDisplay = createInfoDisplay(cell, "");
|
|
283
|
+
}
|
|
284
|
+
const qualitySwitchDisplay = createInfoDisplay(cell, "");
|
|
285
285
|
|
|
286
286
|
let qualityDivs = [];
|
|
287
|
+
let contentType = "";
|
|
288
|
+
|
|
289
|
+
const rootDisplay = createContainer(cell);
|
|
290
|
+
const streamDisplay = createContainer(rootDisplay);
|
|
291
|
+
const audioDisplay = createContainer(rootDisplay);
|
|
292
|
+
const audioTypeDisplay = createInfoDisplay(audioDisplay);
|
|
293
|
+
const audioTrackDisplay = createContainer(audioDisplay);
|
|
294
|
+
const audioStateButton = AudioStateButton();
|
|
287
295
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
streamDisplay.setAttribute("class","text-center");
|
|
295
|
-
rootDisplay.appendChild(streamDisplay);
|
|
296
|
+
hideItem(streamDisplay);
|
|
297
|
+
hideItem(audioDisplay);
|
|
298
|
+
hideItem(publisherNameDisplay);
|
|
299
|
+
hideItem(currentQualityDisplay);
|
|
300
|
+
hideItem(videoTypeDisplay);
|
|
301
|
+
hideItem(qualitySwitchDisplay);
|
|
296
302
|
|
|
297
303
|
let audio = null;
|
|
298
304
|
let video = null;
|
|
@@ -316,6 +322,7 @@ const initRemoteDisplay = function(options) {
|
|
|
316
322
|
this.audioMid = undefined;
|
|
317
323
|
return;
|
|
318
324
|
}
|
|
325
|
+
showItem(audioDisplay);
|
|
319
326
|
audio = document.createElement("audio");
|
|
320
327
|
audio.controls = "controls";
|
|
321
328
|
audio.muted = true;
|
|
@@ -327,7 +334,8 @@ const initRemoteDisplay = function(options) {
|
|
|
327
334
|
} else {
|
|
328
335
|
this.setEventHandlers(audio);
|
|
329
336
|
}
|
|
330
|
-
|
|
337
|
+
audioTrackDisplay.appendChild(audio);
|
|
338
|
+
audioStateButton.makeButton(audioTypeDisplay, audio);
|
|
331
339
|
audio.srcObject = stream;
|
|
332
340
|
audio.onloadedmetadata = function (e) {
|
|
333
341
|
audio.play().then(function() {
|
|
@@ -335,6 +343,7 @@ const initRemoteDisplay = function(options) {
|
|
|
335
343
|
console.warn("Audio track should be manually unmuted in iOS Safari");
|
|
336
344
|
} else {
|
|
337
345
|
audio.muted = false;
|
|
346
|
+
audioStateButton.setButtonState();
|
|
338
347
|
}
|
|
339
348
|
});
|
|
340
349
|
};
|
|
@@ -356,6 +365,7 @@ const initRemoteDisplay = function(options) {
|
|
|
356
365
|
qualityDivs = [];
|
|
357
366
|
return;
|
|
358
367
|
}
|
|
368
|
+
showItem(streamDisplay);
|
|
359
369
|
video = document.createElement("video");
|
|
360
370
|
video.controls = "controls";
|
|
361
371
|
video.muted = true;
|
|
@@ -372,31 +382,47 @@ const initRemoteDisplay = function(options) {
|
|
|
372
382
|
this.setResizeHandler(video);
|
|
373
383
|
},
|
|
374
384
|
setTrackInfo: function(trackInfo) {
|
|
375
|
-
if (trackInfo
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (qualityDivs[c].style.color !== "red") {
|
|
389
|
-
qualityDivs[c].style.color = "gray";
|
|
385
|
+
if (trackInfo) {
|
|
386
|
+
if (trackInfo.quality) {
|
|
387
|
+
showItem(qualitySwitchDisplay);
|
|
388
|
+
for (let i = 0; i < trackInfo.quality.length; i++) {
|
|
389
|
+
const qualityDiv = document.createElement("button");
|
|
390
|
+
qualityDivs.push(qualityDiv);
|
|
391
|
+
qualityDiv.innerText = trackInfo.quality[i];
|
|
392
|
+
qualityDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
393
|
+
qualityDiv.style.color = "red";
|
|
394
|
+
qualityDiv.addEventListener('click', function(){
|
|
395
|
+
console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfo.id);
|
|
396
|
+
if (qualityDiv.style.color === "red") {
|
|
397
|
+
return;
|
|
390
398
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
399
|
+
for (let c = 0; c < qualityDivs.length; c++) {
|
|
400
|
+
if (qualityDivs[c].style.color !== "red") {
|
|
401
|
+
qualityDivs[c].style.color = "gray";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
qualityDiv.style.color = "blue";
|
|
405
|
+
room.changeQuality(trackInfo.id, trackInfo.quality[i]);
|
|
406
|
+
});
|
|
407
|
+
qualitySwitchDisplay.appendChild(qualityDiv);
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
hideItem(qualitySwitchDisplay);
|
|
411
|
+
}
|
|
412
|
+
if (trackInfo.type) {
|
|
413
|
+
contentType = trackInfo.contentType || "";
|
|
414
|
+
if (trackInfo.type == "VIDEO" && displayOptions.type && contentType !== "") {
|
|
415
|
+
showItem(videoTypeDisplay);
|
|
416
|
+
videoTypeDisplay.innerHTML = contentType;
|
|
417
|
+
}
|
|
418
|
+
if (trackInfo.type == "AUDIO") {
|
|
419
|
+
audioStateButton.setContentType(contentType);
|
|
420
|
+
}
|
|
396
421
|
}
|
|
397
422
|
}
|
|
398
423
|
},
|
|
399
424
|
updateQualityInfo: function(videoQuality) {
|
|
425
|
+
showItem(qualitySwitchDisplay);
|
|
400
426
|
for (const qualityInfo of videoQuality) {
|
|
401
427
|
for (const qualityDiv of qualityDivs) {
|
|
402
428
|
if (qualityDiv.innerText === qualityInfo.quality){
|
|
@@ -416,9 +442,11 @@ const initRemoteDisplay = function(options) {
|
|
|
416
442
|
setResizeHandler: function(video) {
|
|
417
443
|
video.addEventListener("resize", function (event) {
|
|
418
444
|
if (displayOptions.publisher) {
|
|
445
|
+
showItem(publisherNameDisplay);
|
|
419
446
|
publisherNameDisplay.innerHTML = "Published by: " + name;
|
|
420
447
|
}
|
|
421
448
|
if (displayOptions.quality) {
|
|
449
|
+
showItem(currentQualityDisplay);
|
|
422
450
|
currentQualityDisplay.innerHTML = video.videoWidth + "x" + video.videoHeight;
|
|
423
451
|
}
|
|
424
452
|
resizeVideo(event.target);
|
|
@@ -506,6 +534,52 @@ const initRemoteDisplay = function(options) {
|
|
|
506
534
|
}
|
|
507
535
|
}
|
|
508
536
|
|
|
537
|
+
const AudioStateButton = function() {
|
|
538
|
+
let button = {
|
|
539
|
+
audio: null,
|
|
540
|
+
contentType: "",
|
|
541
|
+
displayButton: null,
|
|
542
|
+
makeButton: function(parent, audio) {
|
|
543
|
+
button.setAudio(audio);
|
|
544
|
+
button.displayButton = document.createElement("button");
|
|
545
|
+
button.displayButton.innerHTML = button.audioState();
|
|
546
|
+
button.displayButton.addEventListener("click", function() {
|
|
547
|
+
button.audio.muted = !button.audio.muted;
|
|
548
|
+
button.displayButton.innerHTML = button.audioState();
|
|
549
|
+
});
|
|
550
|
+
parent.appendChild(button.displayButton);
|
|
551
|
+
|
|
552
|
+
},
|
|
553
|
+
setAudio: function(audio) {
|
|
554
|
+
button.audio = audio;
|
|
555
|
+
},
|
|
556
|
+
setButtonState: function() {
|
|
557
|
+
if (button.displayButton) {
|
|
558
|
+
button.displayButton.innerHTML = button.audioState();
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
setContentType: function(type) {
|
|
562
|
+
button.contentType = type;
|
|
563
|
+
button.setButtonState();
|
|
564
|
+
},
|
|
565
|
+
audioState: function() {
|
|
566
|
+
let state = "";
|
|
567
|
+
if (button.audio) {
|
|
568
|
+
if (button.audio.muted) {
|
|
569
|
+
state = "Unmute";
|
|
570
|
+
} else {
|
|
571
|
+
state = "Mute";
|
|
572
|
+
}
|
|
573
|
+
if (button.contentType) {
|
|
574
|
+
state = state + " " + button.contentType;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return (state);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
return button;
|
|
581
|
+
}
|
|
582
|
+
|
|
509
583
|
return {
|
|
510
584
|
stop: stop
|
|
511
585
|
}
|
|
@@ -557,4 +631,40 @@ const downScaleToFitSize = function(videoWidth, videoHeight, dstWidth, dstHeight
|
|
|
557
631
|
w: newWidth,
|
|
558
632
|
h: newHeight
|
|
559
633
|
};
|
|
560
|
-
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const createInfoDisplay = function(parent, text) {
|
|
637
|
+
const div = document.createElement("div");
|
|
638
|
+
if (text) {
|
|
639
|
+
div.innerHTML = text;
|
|
640
|
+
}
|
|
641
|
+
div.setAttribute("style","width:auto; height:30px;");
|
|
642
|
+
div.setAttribute("class","text-center");
|
|
643
|
+
if (parent) {
|
|
644
|
+
parent.appendChild(div);
|
|
645
|
+
}
|
|
646
|
+
return div;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const createContainer = function(parent) {
|
|
650
|
+
const div = document.createElement("div");
|
|
651
|
+
div.setAttribute("style","width:auto; height:auto;");
|
|
652
|
+
div.setAttribute("class","text-center");
|
|
653
|
+
if (parent) {
|
|
654
|
+
parent.appendChild(div);
|
|
655
|
+
}
|
|
656
|
+
return div;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Helper functions to display/hide an element
|
|
660
|
+
const showItem = function(tag) {
|
|
661
|
+
if (tag) {
|
|
662
|
+
tag.style.display = "block";
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const hideItem = function(tag) {
|
|
667
|
+
if (tag) {
|
|
668
|
+
tag.style.display = "none";
|
|
669
|
+
}
|
|
670
|
+
}
|
package/src/commons/js/util.js
CHANGED
|
@@ -110,6 +110,7 @@ const createUUID = function (length) {
|
|
|
110
110
|
return uuid.substring(0, length);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// Browser detection
|
|
113
114
|
const Browser = function() {
|
|
114
115
|
const isIE = function () {
|
|
115
116
|
return /*@cc_on!@*/false || !!document.documentMode;
|
|
@@ -176,7 +177,7 @@ const Browser = function() {
|
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
|
|
180
|
+
// A workaround to play audio automatically in Safari
|
|
180
181
|
const playFirstSound = function (parent, preloader) {
|
|
181
182
|
return new Promise(function (resolve, reject) {
|
|
182
183
|
let audio = document.createElement("audio");
|
|
@@ -199,4 +200,4 @@ const playFirstSound = function (parent, preloader) {
|
|
|
199
200
|
}
|
|
200
201
|
resolve();
|
|
201
202
|
});
|
|
202
|
-
}
|
|
203
|
+
}
|
package/src/player/player.js
CHANGED
|
@@ -5,7 +5,9 @@ let remoteDisplay;
|
|
|
5
5
|
let playState;
|
|
6
6
|
const PLAY = "play";
|
|
7
7
|
const STOP = "stop";
|
|
8
|
-
const PRELOADER_URL="../commons/media/silence.mp3"
|
|
8
|
+
const PRELOADER_URL="../commons/media/silence.mp3";
|
|
9
|
+
const MAX_AWAIT_MS=5000;
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Default publishing config
|
|
@@ -28,15 +30,31 @@ const CurrentState = function(prefix) {
|
|
|
28
30
|
pc: null,
|
|
29
31
|
session: null,
|
|
30
32
|
room: null,
|
|
33
|
+
roomEnded: false,
|
|
34
|
+
timeout: null,
|
|
35
|
+
timer: null,
|
|
36
|
+
promise: null,
|
|
31
37
|
set: function(pc, session, room) {
|
|
32
38
|
state.pc = pc;
|
|
33
39
|
state.session = session;
|
|
34
40
|
state.room = room;
|
|
41
|
+
state.roomEnded = false;
|
|
42
|
+
state.timeout = null;
|
|
43
|
+
state.timer = null;
|
|
44
|
+
state.promise = null;
|
|
35
45
|
},
|
|
36
46
|
clear: function() {
|
|
47
|
+
state.stopWaiting();
|
|
37
48
|
state.room = null;
|
|
38
49
|
state.session = null;
|
|
39
50
|
state.pc = null;
|
|
51
|
+
state.roomEnded = false;
|
|
52
|
+
state.timeout = null;
|
|
53
|
+
state.timer = null;
|
|
54
|
+
state.promise = null;
|
|
55
|
+
},
|
|
56
|
+
setRoomEnded: function() {
|
|
57
|
+
state.roomEnded = true;
|
|
40
58
|
},
|
|
41
59
|
buttonId: function() {
|
|
42
60
|
return state.prefix + "Btn";
|
|
@@ -58,6 +76,52 @@ const CurrentState = function(prefix) {
|
|
|
58
76
|
},
|
|
59
77
|
is: function(value) {
|
|
60
78
|
return (prefix === value);
|
|
79
|
+
},
|
|
80
|
+
isActive: function() {
|
|
81
|
+
return (state.room && !state.roomEnded && state.pc);
|
|
82
|
+
},
|
|
83
|
+
isConnected: function() {
|
|
84
|
+
return (state.session && state.session.state() == constants.SFU_STATE.CONNECTED);
|
|
85
|
+
},
|
|
86
|
+
isRoomEnded: function() {
|
|
87
|
+
return state.roomEnded;
|
|
88
|
+
},
|
|
89
|
+
waitFor: async function(promise, ms) {
|
|
90
|
+
// Create a promise that rejects in <ms> milliseconds
|
|
91
|
+
state.promise = promise;
|
|
92
|
+
state.timeout = new Promise((resolve, reject) => {
|
|
93
|
+
state.resolve = resolve;
|
|
94
|
+
state.timer = setTimeout(() => {
|
|
95
|
+
clearTimeout(state.timer);
|
|
96
|
+
state.timer = null;
|
|
97
|
+
state.promise = null;
|
|
98
|
+
state.timeout = null;
|
|
99
|
+
reject('Operation timed out in '+ ms + ' ms.')
|
|
100
|
+
}, ms)
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Returns a race between our timeout and the passed in promise
|
|
104
|
+
Promise.race([
|
|
105
|
+
state.promise,
|
|
106
|
+
state.timeout
|
|
107
|
+
]).then(() => {
|
|
108
|
+
state.stopWaiting();
|
|
109
|
+
}).catch((e) => {
|
|
110
|
+
setStatus(state.errInfoId(), e, "red");
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
stopWaiting: function() {
|
|
114
|
+
if (state.timer) {
|
|
115
|
+
clearTimeout(state.timer);
|
|
116
|
+
state.timer = null;
|
|
117
|
+
}
|
|
118
|
+
if (state.timeout) {
|
|
119
|
+
state.resolve();
|
|
120
|
+
state.timeout = null;
|
|
121
|
+
}
|
|
122
|
+
if (state.promise) {
|
|
123
|
+
state.promise = null;
|
|
124
|
+
}
|
|
61
125
|
}
|
|
62
126
|
};
|
|
63
127
|
return state;
|
|
@@ -120,20 +184,38 @@ const connect = function(state) {
|
|
|
120
184
|
});
|
|
121
185
|
}
|
|
122
186
|
|
|
123
|
-
const onConnected = function(state) {
|
|
187
|
+
const onConnected = async function(state) {
|
|
124
188
|
$("#" + state.buttonId()).text("Stop").off('click').click(function () {
|
|
125
189
|
onStopClick(state);
|
|
126
|
-
})
|
|
190
|
+
});
|
|
127
191
|
$('#url').prop('disabled', true);
|
|
128
192
|
$("#roomName").prop('disabled', true);
|
|
129
193
|
$("#" + state.inputId()).prop('disabled', true);
|
|
130
194
|
// Add errors displaying
|
|
131
195
|
state.room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
|
|
132
196
|
setStatus(state.errInfoId(), e, "red");
|
|
197
|
+
state.setRoomEnded();
|
|
198
|
+
state.stopWaiting();
|
|
199
|
+
onStopClick(state);
|
|
133
200
|
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
134
201
|
setStatus(state.errInfoId(), e.operation + " failed: " + e.error, "red");
|
|
202
|
+
state.setRoomEnded();
|
|
203
|
+
state.stopWaiting();
|
|
204
|
+
onStopClick(state);
|
|
205
|
+
}).on(constants.SFU_ROOM_EVENT.ENDED, function (e) {
|
|
206
|
+
setStatus(state.errInfoId(), "Room "+state.room.name()+" has ended", "red");
|
|
207
|
+
state.setRoomEnded();
|
|
208
|
+
state.stopWaiting();
|
|
209
|
+
onStopClick(state);
|
|
210
|
+
}).on(constants.SFU_ROOM_EVENT.DROPPED, function (e) {
|
|
211
|
+
setStatus(state.errInfoId(), "Dropped from the room "+state.room.name()+" due to network issues", "red");
|
|
212
|
+
state.setRoomEnded();
|
|
213
|
+
state.stopWaiting();
|
|
214
|
+
onStopClick(state);
|
|
135
215
|
});
|
|
136
|
-
playStreams(state);
|
|
216
|
+
await playStreams(state);
|
|
217
|
+
// Enable button after starting playback #WCS-3635
|
|
218
|
+
$("#" + state.buttonId()).prop('disabled', false);
|
|
137
219
|
}
|
|
138
220
|
|
|
139
221
|
const onDisconnected = function(state) {
|
|
@@ -158,20 +240,29 @@ const onStartClick = function(state) {
|
|
|
158
240
|
}
|
|
159
241
|
}
|
|
160
242
|
|
|
161
|
-
const onStopClick = function(state) {
|
|
243
|
+
const onStopClick = async function(state) {
|
|
162
244
|
$("#" + state.buttonId()).prop('disabled', true);
|
|
163
245
|
stopStreams(state);
|
|
164
|
-
state.
|
|
246
|
+
if (state.isConnected()) {
|
|
247
|
+
state.waitFor(state.session.disconnect(), MAX_AWAIT_MS);
|
|
248
|
+
}
|
|
165
249
|
}
|
|
166
250
|
|
|
167
|
-
const playStreams = function(state) {
|
|
251
|
+
const playStreams = async function(state) {
|
|
168
252
|
//create remote display item to show remote streams
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
253
|
+
try {
|
|
254
|
+
remoteDisplay = initRemoteDisplay({
|
|
255
|
+
div: document.getElementById("remoteVideo"),
|
|
256
|
+
room: state.room,
|
|
257
|
+
peerConnection: state.pc
|
|
258
|
+
});
|
|
259
|
+
// Start WebRTC negotiation
|
|
260
|
+
state.waitFor(state.room.join(state.pc), MAX_AWAIT_MS);
|
|
261
|
+
} catch(e) {
|
|
262
|
+
console.error("Failed to play streams: " + e);
|
|
263
|
+
setStatus(state.errInfoId(), e.name, "red");
|
|
264
|
+
onStopClick(state);
|
|
265
|
+
}
|
|
175
266
|
}
|
|
176
267
|
|
|
177
268
|
const stopStreams = function(state) {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"audio": {
|
|
10
10
|
"tracks": [{
|
|
11
11
|
"source": "mic",
|
|
12
|
-
"channels": 2
|
|
12
|
+
"channels": 2,
|
|
13
|
+
"type": "mic1"
|
|
13
14
|
}]
|
|
14
15
|
},
|
|
15
16
|
"video": {
|
|
@@ -26,7 +27,8 @@
|
|
|
26
27
|
{ "rid": "720p", "active": true, "maxBitrate": 900000 },
|
|
27
28
|
{ "rid": "360p", "active": true, "maxBitrate": 500000, "scaleResolutionDownBy": 2 },
|
|
28
29
|
{ "rid": "180p", "active": true, "maxBitrate": 200000, "scaleResolutionDownBy": 4 }
|
|
29
|
-
]
|
|
30
|
+
],
|
|
31
|
+
"type": "cam1"
|
|
30
32
|
}
|
|
31
33
|
]
|
|
32
34
|
}
|
|
@@ -8,7 +8,8 @@ let playState;
|
|
|
8
8
|
const PUBLISH = "publish";
|
|
9
9
|
const PLAY = "play";
|
|
10
10
|
const STOP = "stop";
|
|
11
|
-
const PRELOADER_URL="../commons/media/silence.mp3"
|
|
11
|
+
const PRELOADER_URL="../commons/media/silence.mp3";
|
|
12
|
+
const MAX_AWAIT_MS=5000;
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -56,36 +57,32 @@ const CurrentState = function(prefix) {
|
|
|
56
57
|
pc: null,
|
|
57
58
|
session: null,
|
|
58
59
|
room: null,
|
|
60
|
+
roomEnded: false,
|
|
61
|
+
timeout: null,
|
|
59
62
|
timer: null,
|
|
63
|
+
promise: null,
|
|
64
|
+
starting: false,
|
|
60
65
|
set: function(pc, session, room) {
|
|
61
66
|
state.pc = pc;
|
|
62
67
|
state.session = session;
|
|
63
68
|
state.room = room;
|
|
69
|
+
state.roomEnded = false;
|
|
70
|
+
state.timeout = null;
|
|
71
|
+
state.timer = null;
|
|
72
|
+
state.promise = null;
|
|
64
73
|
},
|
|
65
74
|
clear: function() {
|
|
66
75
|
state.stopWaiting();
|
|
67
76
|
state.room = null;
|
|
68
77
|
state.session = null;
|
|
69
78
|
state.pc = null;
|
|
79
|
+
state.roomEnded = false;
|
|
80
|
+
state.timeout = null;
|
|
81
|
+
state.timer = null;
|
|
82
|
+
state.promise = null;
|
|
70
83
|
},
|
|
71
|
-
|
|
72
|
-
state.
|
|
73
|
-
state.timer = setTimeout(function () {
|
|
74
|
-
if (div.innerHTML !== "") {
|
|
75
|
-
// Enable stop button
|
|
76
|
-
$("#" + state.buttonId()).prop('disabled', false);
|
|
77
|
-
}
|
|
78
|
-
else if (state.isConnected()) {
|
|
79
|
-
setStatus(state.errInfoId(), "No media capturing started in " + timeout + " ms, stopping", "red");
|
|
80
|
-
onStopClick(state);
|
|
81
|
-
}
|
|
82
|
-
}, timeout);
|
|
83
|
-
},
|
|
84
|
-
stopWaiting: function() {
|
|
85
|
-
if (state.timer) {
|
|
86
|
-
clearTimeout(state.timer);
|
|
87
|
-
state.timer = null;
|
|
88
|
-
}
|
|
84
|
+
setRoomEnded: function() {
|
|
85
|
+
state.roomEnded = true;
|
|
89
86
|
},
|
|
90
87
|
buttonId: function() {
|
|
91
88
|
return state.prefix + "Btn";
|
|
@@ -109,10 +106,56 @@ const CurrentState = function(prefix) {
|
|
|
109
106
|
return (prefix === value);
|
|
110
107
|
},
|
|
111
108
|
isActive: function() {
|
|
112
|
-
return (state.room && state.pc);
|
|
109
|
+
return (state.room && !state.roomEnded && state.pc);
|
|
113
110
|
},
|
|
114
111
|
isConnected: function() {
|
|
115
112
|
return (state.session && state.session.state() == constants.SFU_STATE.CONNECTED);
|
|
113
|
+
},
|
|
114
|
+
isRoomEnded: function() {
|
|
115
|
+
return state.roomEnded;
|
|
116
|
+
},
|
|
117
|
+
waitFor: async function(promise, ms) {
|
|
118
|
+
// Create a promise that rejects in <ms> milliseconds
|
|
119
|
+
state.promise = promise;
|
|
120
|
+
state.timeout = new Promise((resolve, reject) => {
|
|
121
|
+
state.resolve = resolve;
|
|
122
|
+
state.timer = setTimeout(() => {
|
|
123
|
+
clearTimeout(state.timer);
|
|
124
|
+
state.timer = null;
|
|
125
|
+
state.promise = null;
|
|
126
|
+
state.timeout = null;
|
|
127
|
+
reject('Operation timed out in '+ ms + ' ms.')
|
|
128
|
+
}, ms)
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Returns a race between our timeout and the passed in promise
|
|
132
|
+
Promise.race([
|
|
133
|
+
state.promise,
|
|
134
|
+
state.timeout
|
|
135
|
+
]).then(() => {
|
|
136
|
+
state.stopWaiting();
|
|
137
|
+
}).catch((e) => {
|
|
138
|
+
setStatus(state.errInfoId(), e, "red");
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
stopWaiting: function() {
|
|
142
|
+
if (state.timer) {
|
|
143
|
+
clearTimeout(state.timer);
|
|
144
|
+
state.timer = null;
|
|
145
|
+
}
|
|
146
|
+
if (state.timeout) {
|
|
147
|
+
state.resolve();
|
|
148
|
+
state.timeout = null;
|
|
149
|
+
}
|
|
150
|
+
if (state.promise) {
|
|
151
|
+
state.promise = null;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
setStarting: function(value) {
|
|
155
|
+
state.starting = value;
|
|
156
|
+
},
|
|
157
|
+
isStarting: function() {
|
|
158
|
+
return state.starting;
|
|
116
159
|
}
|
|
117
160
|
};
|
|
118
161
|
return state;
|
|
@@ -154,7 +197,7 @@ const init = function() {
|
|
|
154
197
|
*/
|
|
155
198
|
const connect = function(state) {
|
|
156
199
|
//create peer connection
|
|
157
|
-
pc = new RTCPeerConnection();
|
|
200
|
+
let pc = new RTCPeerConnection();
|
|
158
201
|
//get config object for room creation
|
|
159
202
|
const roomConfig = getRoomConfig(mainConfig);
|
|
160
203
|
roomConfig.url = $("#url").val();
|
|
@@ -191,10 +234,24 @@ const onConnected = function(state) {
|
|
|
191
234
|
// Add errors displaying
|
|
192
235
|
state.room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
|
|
193
236
|
setStatus(state.errInfoId(), e, "red");
|
|
194
|
-
|
|
237
|
+
state.setRoomEnded();
|
|
238
|
+
state.stopWaiting();
|
|
239
|
+
onStopClick(state);
|
|
195
240
|
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
196
241
|
setStatus(state.errInfoId(), e.operation + " failed: " + e.error, "red");
|
|
197
|
-
|
|
242
|
+
state.setRoomEnded();
|
|
243
|
+
state.stopWaiting();
|
|
244
|
+
onStopClick(state);
|
|
245
|
+
}).on(constants.SFU_ROOM_EVENT.ENDED, function () {
|
|
246
|
+
setStatus(state.errInfoId(), "Room "+state.room.name()+" has ended", "red");
|
|
247
|
+
state.setRoomEnded();
|
|
248
|
+
state.stopWaiting();
|
|
249
|
+
onStopClick(state);
|
|
250
|
+
}).on(constants.SFU_ROOM_EVENT.DROPPED, function () {
|
|
251
|
+
setStatus(state.errInfoId(), "Dropped from the room "+state.room.name()+" due to network issues", "red");
|
|
252
|
+
state.setRoomEnded();
|
|
253
|
+
state.stopWaiting();
|
|
254
|
+
onStopClick(state);
|
|
198
255
|
});
|
|
199
256
|
startStreaming(state);
|
|
200
257
|
}
|
|
@@ -204,18 +261,27 @@ const onDisconnected = function(state) {
|
|
|
204
261
|
onStartClick(state);
|
|
205
262
|
}).prop('disabled', false);
|
|
206
263
|
$("#" + state.inputId()).prop('disabled', false);
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
264
|
+
// Enable other session buttons
|
|
265
|
+
let otherState = getOtherState(state);
|
|
266
|
+
if (!otherState.session) {
|
|
267
|
+
$("#" + otherState.buttonId()).prop('disabled', false);
|
|
268
|
+
$("#" + otherState.inputId()).prop('disabled', false);
|
|
269
|
+
$('#url').prop('disabled', false);
|
|
270
|
+
$("#roomName").prop('disabled', false);
|
|
211
271
|
}
|
|
212
|
-
$('#url').prop('disabled', false);
|
|
213
|
-
$("#roomName").prop('disabled', false);
|
|
214
272
|
}
|
|
215
273
|
|
|
216
274
|
const onStartClick = function(state) {
|
|
217
|
-
if (validateForm("connectionForm"
|
|
275
|
+
if (validateForm("connectionForm", state.errInfoId())
|
|
276
|
+
&& validateForm(state.formId(), state.errInfoId())
|
|
277
|
+
&& validateName(state, state.errInfoId())) {
|
|
278
|
+
state.setStarting(true);
|
|
279
|
+
let otherState = getOtherState(state);
|
|
218
280
|
$("#" + state.buttonId()).prop('disabled', true);
|
|
281
|
+
// Disable other session button to prevent a simultaneous connections
|
|
282
|
+
if (!otherState.isStarting()) {
|
|
283
|
+
$("#" + otherState.buttonId()).prop('disabled', true);
|
|
284
|
+
}
|
|
219
285
|
if (state.is(PLAY) && Browser().isSafariWebRTC()) {
|
|
220
286
|
playFirstSound(document.getElementById("main"), PRELOADER_URL).then(function () {
|
|
221
287
|
connect(state);
|
|
@@ -227,28 +293,35 @@ const onStartClick = function(state) {
|
|
|
227
293
|
}
|
|
228
294
|
|
|
229
295
|
const onStopClick = function(state) {
|
|
296
|
+
state.setStarting(false);
|
|
230
297
|
$("#" + state.buttonId()).prop('disabled', true);
|
|
231
298
|
stopStreaming(state);
|
|
232
|
-
if (state.isConnected()) {
|
|
233
|
-
state.session.disconnect();
|
|
234
|
-
}
|
|
235
299
|
}
|
|
236
300
|
|
|
237
|
-
const startStreaming = function(state) {
|
|
301
|
+
const startStreaming = async function(state) {
|
|
238
302
|
if (state.is(PUBLISH)) {
|
|
239
|
-
publishStreams(state);
|
|
303
|
+
await publishStreams(state);
|
|
240
304
|
} else if (state.is(PLAY)) {
|
|
241
|
-
playStreams(state);
|
|
305
|
+
await playStreams(state);
|
|
306
|
+
}
|
|
307
|
+
state.setStarting(false);
|
|
308
|
+
// Enable session buttons
|
|
309
|
+
let otherState = getOtherState(state);
|
|
310
|
+
$("#" + state.buttonId()).prop('disabled', false);
|
|
311
|
+
if (!otherState.isStarting()) {
|
|
312
|
+
$("#" + otherState.buttonId()).prop('disabled', false);
|
|
242
313
|
}
|
|
243
314
|
}
|
|
244
315
|
|
|
245
|
-
const stopStreaming = function(state) {
|
|
246
|
-
state.stopWaiting();
|
|
316
|
+
const stopStreaming = async function(state) {
|
|
247
317
|
if (state.is(PUBLISH)) {
|
|
248
318
|
unPublishStreams(state);
|
|
249
319
|
} else if (state.is(PLAY)) {
|
|
250
320
|
stopStreams(state);
|
|
251
321
|
}
|
|
322
|
+
if (state.isConnected()) {
|
|
323
|
+
state.waitFor(state.session.disconnect(), MAX_AWAIT_MS);
|
|
324
|
+
}
|
|
252
325
|
}
|
|
253
326
|
|
|
254
327
|
const publishStreams = async function(state) {
|
|
@@ -265,28 +338,23 @@ const publishStreams = async function(state) {
|
|
|
265
338
|
let config = {};
|
|
266
339
|
//add our local streams to the room (to PeerConnection)
|
|
267
340
|
streams.forEach(function (s) {
|
|
341
|
+
let contentType = s.type || s.source;
|
|
268
342
|
//add local stream to local display
|
|
269
|
-
localDisplay.add(s.stream.id, $("#" + state.inputId()).val(), s.stream);
|
|
343
|
+
localDisplay.add(s.stream.id, $("#" + state.inputId()).val(), s.stream, contentType);
|
|
270
344
|
//add each track to PeerConnection
|
|
271
345
|
s.stream.getTracks().forEach((track) => {
|
|
272
|
-
|
|
273
|
-
config[track.id] = s.source;
|
|
274
|
-
}
|
|
346
|
+
config[track.id] = contentType;
|
|
275
347
|
addTrackToPeerConnection(state.pc, s.stream, track, s.encodings);
|
|
276
348
|
subscribeTrackToEndedEvent(state.room, track, state.pc);
|
|
277
349
|
});
|
|
278
350
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
state.waitFor(document.getElementById("localVideo"), 3000);
|
|
351
|
+
//start WebRTC negotiation
|
|
352
|
+
state.waitFor(state.room.join(state.pc, null, config), MAX_AWAIT_MS);
|
|
282
353
|
}
|
|
283
354
|
} catch(e) {
|
|
284
355
|
console.error("Failed to capture streams: " + e);
|
|
285
356
|
setStatus(state.errInfoId(), e.name, "red");
|
|
286
|
-
state
|
|
287
|
-
if (state.isConnected()) {
|
|
288
|
-
onStopClick(state);
|
|
289
|
-
}
|
|
357
|
+
onStopClick(state);
|
|
290
358
|
}
|
|
291
359
|
}
|
|
292
360
|
}
|
|
@@ -297,17 +365,23 @@ const unPublishStreams = function(state) {
|
|
|
297
365
|
}
|
|
298
366
|
}
|
|
299
367
|
|
|
300
|
-
const playStreams = function(state) {
|
|
368
|
+
const playStreams = async function(state) {
|
|
301
369
|
if (state.isConnected() && state.isActive()) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
370
|
+
try {
|
|
371
|
+
//create remote display item to show remote streams
|
|
372
|
+
remoteDisplay = initRemoteDisplay({
|
|
373
|
+
div: document.getElementById("remoteVideo"),
|
|
374
|
+
room: state.room,
|
|
375
|
+
peerConnection: state.pc
|
|
376
|
+
});
|
|
377
|
+
//start WebRTC negotiation
|
|
378
|
+
state.waitFor(state.room.join(state.pc), MAX_AWAIT_MS);
|
|
379
|
+
} catch(e) {
|
|
380
|
+
console.error("Failed to play streams: " + e);
|
|
381
|
+
setStatus(state.errInfoId(), e.name, "red");
|
|
382
|
+
onStopClick(state);
|
|
383
|
+
}
|
|
309
384
|
}
|
|
310
|
-
$("#" + state.buttonId()).prop('disabled', false);
|
|
311
385
|
}
|
|
312
386
|
|
|
313
387
|
const stopStreams = function(state) {
|
|
@@ -351,14 +425,17 @@ const setStatus = function (status, text, color) {
|
|
|
351
425
|
field.innerText = text;
|
|
352
426
|
}
|
|
353
427
|
|
|
354
|
-
const validateForm = function (formId) {
|
|
355
|
-
|
|
428
|
+
const validateForm = function (formId, errorInfoId) {
|
|
429
|
+
let valid = true;
|
|
430
|
+
// Validate empty fields
|
|
356
431
|
$('#' + formId + ' :text').each(function () {
|
|
357
432
|
if (!$(this).val()) {
|
|
358
433
|
highlightInput($(this));
|
|
359
434
|
valid = false;
|
|
435
|
+
setStatus(errorInfoId, "Fields cannot be empty", "red");
|
|
360
436
|
} else {
|
|
361
437
|
removeHighlight($(this));
|
|
438
|
+
setStatus(errorInfoId, "");
|
|
362
439
|
}
|
|
363
440
|
});
|
|
364
441
|
return valid;
|
|
@@ -372,7 +449,29 @@ const validateForm = function (formId) {
|
|
|
372
449
|
}
|
|
373
450
|
}
|
|
374
451
|
|
|
452
|
+
const validateName = function (state) {
|
|
453
|
+
let valid = true;
|
|
454
|
+
// Validate other nickname
|
|
455
|
+
let nameToCheck = $("#" + state.inputId()).val();
|
|
456
|
+
let otherState = getOtherState(state);
|
|
457
|
+
|
|
458
|
+
if (nameToCheck === $("#" + otherState.inputId()).val()) {
|
|
459
|
+
if (otherState.isActive() || otherState.isConnected()) {
|
|
460
|
+
valid = false;
|
|
461
|
+
setStatus(state.errInfoId(), "Cannot connect with the same name", "red");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return valid;
|
|
465
|
+
}
|
|
466
|
+
|
|
375
467
|
const buttonText = function (string) {
|
|
376
468
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
377
469
|
}
|
|
378
470
|
|
|
471
|
+
const getOtherState = function(state) {
|
|
472
|
+
if (state.is(PUBLISH)) {
|
|
473
|
+
return playState;
|
|
474
|
+
} else if (state.is(PLAY)) {
|
|
475
|
+
return publishState;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const constants = SFU.constants;
|
|
2
2
|
const sfu = SFU;
|
|
3
|
-
const PRELOADER_URL="../commons/media/silence.mp3"
|
|
3
|
+
const PRELOADER_URL="../commons/media/silence.mp3";
|
|
4
|
+
const MAX_AWAIT_MS=5000;
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Current state object
|
|
@@ -11,15 +13,40 @@ const CurrentState = function() {
|
|
|
11
13
|
session: null,
|
|
12
14
|
room: null,
|
|
13
15
|
remoteDisplay: null,
|
|
16
|
+
roomEnded: false,
|
|
17
|
+
timeout: null,
|
|
18
|
+
timer: null,
|
|
19
|
+
promise: null,
|
|
14
20
|
set: function(pc, session, room) {
|
|
15
21
|
state.pc = pc;
|
|
16
22
|
state.session = session;
|
|
17
23
|
state.room = room;
|
|
24
|
+
state.roomEnded = false;
|
|
25
|
+
state.timeout = null;
|
|
26
|
+
state.timer = null;
|
|
27
|
+
state.promise = null;
|
|
18
28
|
},
|
|
19
29
|
clear: function() {
|
|
30
|
+
state.stopWaiting();
|
|
20
31
|
state.room = null;
|
|
21
32
|
state.session = null;
|
|
22
33
|
state.pc = null;
|
|
34
|
+
state.roomEnded = false;
|
|
35
|
+
state.timeout = null;
|
|
36
|
+
state.timer = null;
|
|
37
|
+
state.promise = null;
|
|
38
|
+
},
|
|
39
|
+
setRoomEnded: function() {
|
|
40
|
+
state.roomEnded = true;
|
|
41
|
+
},
|
|
42
|
+
isRoomEnded: function() {
|
|
43
|
+
return state.roomEnded;
|
|
44
|
+
},
|
|
45
|
+
isConnected: function() {
|
|
46
|
+
return (state.session && state.session.state() == constants.SFU_STATE.CONNECTED);
|
|
47
|
+
},
|
|
48
|
+
isActive: function() {
|
|
49
|
+
return (state.room && !state.roomEnded && state.pc);
|
|
23
50
|
},
|
|
24
51
|
setDisplay: function(display) {
|
|
25
52
|
state.remoteDisplay = display;
|
|
@@ -29,7 +56,45 @@ const CurrentState = function() {
|
|
|
29
56
|
state.remoteDisplay.stop();
|
|
30
57
|
state.remoteDisplay = null;
|
|
31
58
|
}
|
|
59
|
+
},
|
|
60
|
+
waitFor: async function(promise, ms) {
|
|
61
|
+
// Create a promise that rejects in <ms> milliseconds
|
|
62
|
+
state.promise = promise;
|
|
63
|
+
state.timeout = new Promise((resolve, reject) => {
|
|
64
|
+
state.resolve = resolve;
|
|
65
|
+
state.timer = setTimeout(() => {
|
|
66
|
+
clearTimeout(state.timer);
|
|
67
|
+
state.timer = null;
|
|
68
|
+
state.promise = null;
|
|
69
|
+
state.timeout = null;
|
|
70
|
+
reject('Operation timed out in '+ ms + ' ms.')
|
|
71
|
+
}, ms)
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Returns a race between our timeout and the passed in promise
|
|
75
|
+
Promise.race([
|
|
76
|
+
state.promise,
|
|
77
|
+
state.timeout
|
|
78
|
+
]).then(() => {
|
|
79
|
+
state.stopWaiting();
|
|
80
|
+
}).catch((e) => {
|
|
81
|
+
setStatus("playStatus", e, "red");
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
stopWaiting: function() {
|
|
85
|
+
if (state.timer) {
|
|
86
|
+
clearTimeout(state.timer);
|
|
87
|
+
state.timer = null;
|
|
88
|
+
}
|
|
89
|
+
if (state.timeout) {
|
|
90
|
+
state.resolve();
|
|
91
|
+
state.timeout = null;
|
|
92
|
+
}
|
|
93
|
+
if (state.promise) {
|
|
94
|
+
state.promise = null;
|
|
95
|
+
}
|
|
32
96
|
}
|
|
97
|
+
|
|
33
98
|
};
|
|
34
99
|
return state;
|
|
35
100
|
}
|
|
@@ -83,10 +148,10 @@ const connect = function(state) {
|
|
|
83
148
|
});
|
|
84
149
|
}
|
|
85
150
|
|
|
86
|
-
const onConnected = function(state) {
|
|
151
|
+
const onConnected = async function(state) {
|
|
87
152
|
$("#playBtn").text("Stop").off('click').click(function () {
|
|
88
153
|
onStopClick(state);
|
|
89
|
-
})
|
|
154
|
+
});
|
|
90
155
|
$('#url').prop('disabled', true);
|
|
91
156
|
$("#streamName").prop('disabled', true);
|
|
92
157
|
// Add room event handling
|
|
@@ -106,12 +171,25 @@ const onConnected = function(state) {
|
|
|
106
171
|
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
107
172
|
// Display the operation failed
|
|
108
173
|
setStatus("playErrorInfo", e.operation + " failed: " + e.error, "red");
|
|
174
|
+
state.setRoomEnded();
|
|
175
|
+
state.stopWaiting();
|
|
176
|
+
onStopClick(state);
|
|
109
177
|
}).on(constants.SFU_ROOM_EVENT.ENDED, function () {
|
|
110
178
|
// Publishing is stopped, dispose playback and close connection
|
|
111
179
|
setStatus("playErrorInfo", "ABR stream is stopped", "red");
|
|
180
|
+
state.setRoomEnded();
|
|
181
|
+
state.stopWaiting();
|
|
182
|
+
onStopClick(state);
|
|
183
|
+
}).on(constants.SFU_ROOM_EVENT.DROPPED, function () {
|
|
184
|
+
// Client dropped from the room, dispose playback and close connection
|
|
185
|
+
setStatus("playErrorInfo", "Playback is dropped due to network issues", "red");
|
|
186
|
+
state.setRoomEnded();
|
|
187
|
+
state.stopWaiting();
|
|
112
188
|
onStopClick(state);
|
|
113
189
|
});
|
|
114
|
-
playStreams(state);
|
|
190
|
+
await playStreams(state);
|
|
191
|
+
// Enable button after starting playback #WCS-3635
|
|
192
|
+
$("#playBtn").prop('disabled', false);
|
|
115
193
|
}
|
|
116
194
|
|
|
117
195
|
const onDisconnected = function(state) {
|
|
@@ -136,13 +214,15 @@ const onStartClick = function(state) {
|
|
|
136
214
|
}
|
|
137
215
|
}
|
|
138
216
|
|
|
139
|
-
const onStopClick = function(state) {
|
|
217
|
+
const onStopClick = async function(state) {
|
|
140
218
|
$("#playBtn").prop('disabled', true);
|
|
141
219
|
stopStreams(state);
|
|
142
|
-
state.
|
|
220
|
+
if (state.isConnected()) {
|
|
221
|
+
state.waitFor(state.session.disconnect(), MAX_AWAIT_MS);
|
|
222
|
+
}
|
|
143
223
|
}
|
|
144
224
|
|
|
145
|
-
const playStreams = function(state) {
|
|
225
|
+
const playStreams = async function(state) {
|
|
146
226
|
// Create remote display item to show remote streams
|
|
147
227
|
state.setDisplay(initRemoteDisplay({
|
|
148
228
|
div: document.getElementById("remoteVideo"),
|
|
@@ -150,10 +230,12 @@ const playStreams = function(state) {
|
|
|
150
230
|
peerConnection: state.pc,
|
|
151
231
|
displayOptions: {
|
|
152
232
|
publisher: false,
|
|
153
|
-
quality: true
|
|
233
|
+
quality: true,
|
|
234
|
+
type: false
|
|
154
235
|
}
|
|
155
236
|
}));
|
|
156
|
-
|
|
237
|
+
// Start WebRTC negotiation
|
|
238
|
+
state.waitFor(state.room.join(state.pc), MAX_AWAIT_MS);
|
|
157
239
|
}
|
|
158
240
|
|
|
159
241
|
const stopStreams = function(state) {
|