@flashphoner/sfusdk-examples 2.0.190 → 2.0.193
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/client/chat.js +2 -2
- package/src/client/controls.js +133 -120
- package/src/client/display.js +4 -4
- package/src/client/main.html +1 -1
- package/src/client/main.js +153 -54
- package/src/commons/js/display.js +2 -2
- package/src/player/player.js +18 -12
- package/src/sfu.ts +2 -2
- package/src/two-way-streaming/two-way-streaming.js +24 -16
- package/src/webrtc-abr-player/player.js +20 -14
package/package.json
CHANGED
package/src/client/chat.js
CHANGED
|
@@ -22,10 +22,10 @@ const createChat = function(room, messages, input, sendButton) {
|
|
|
22
22
|
}, chatOtherColour, chatEventColour);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
const sendMessage = function() {
|
|
25
|
+
const sendMessage = async function() {
|
|
26
26
|
let message = input.value;
|
|
27
27
|
input.value = "";
|
|
28
|
-
room.sendMessage(message);
|
|
28
|
+
await room.sendMessage(message);
|
|
29
29
|
appendMessage({
|
|
30
30
|
nickName: nickName.value,
|
|
31
31
|
message: message
|
package/src/client/controls.js
CHANGED
|
@@ -70,64 +70,62 @@ const createControls = function(config) {
|
|
|
70
70
|
controls.entrance.roomPin.value = config.room.pin;
|
|
71
71
|
controls.entrance.nickName.value = config.room.nickName;
|
|
72
72
|
|
|
73
|
-
const addAudioTrackRow = function(track) {
|
|
74
|
-
getMedia([track])
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
const addAudioTrackRow = async function(track) {
|
|
74
|
+
const stream = await getMedia([track]);
|
|
75
|
+
let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
|
|
76
|
+
const row = controls.tables.audio.row.add({
|
|
77
|
+
source: track.source,
|
|
78
|
+
channels: track.channels,
|
|
79
|
+
action: button,
|
|
80
|
+
stream: stream
|
|
81
|
+
}).node();
|
|
82
|
+
controls.tables.audio.draw();
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
84
|
+
$('#' + stream.id + "-button").on('click', function(){
|
|
85
|
+
//terminate stream
|
|
86
|
+
console.log("terminate audio stream " + stream.id);
|
|
87
|
+
let track = stream.getAudioTracks()[0];
|
|
88
|
+
track.stop();
|
|
89
|
+
track.dispatchEvent(new Event("ended"));
|
|
90
|
+
}).prop('disabled', true);
|
|
91
|
+
stream.getTracks()[0].onended = function() {
|
|
92
|
+
controls.tables.audio.row(row).remove().draw();
|
|
93
|
+
}
|
|
94
|
+
trackCallback({
|
|
95
|
+
stream: stream,
|
|
96
|
+
encodings: track.encodings,
|
|
97
|
+
source: track.source
|
|
99
98
|
});
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
const addVideoTrackRow = function(track) {
|
|
103
|
-
getMedia([track])
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
101
|
+
const addVideoTrackRow = async function(track) {
|
|
102
|
+
const stream = await getMedia([track]);
|
|
103
|
+
let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
|
|
104
|
+
const row = controls.tables.video.row.add({
|
|
105
|
+
source: track.source,
|
|
106
|
+
width: track.width,
|
|
107
|
+
height: track.height,
|
|
108
|
+
codec: track.codec,
|
|
109
|
+
action: button,
|
|
110
|
+
stream: stream,
|
|
111
|
+
encodings: track.encodings,
|
|
112
|
+
}).node();
|
|
113
|
+
controls.tables.video.draw();
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
115
|
+
$('#' + stream.id + "-button").on('click', function(){
|
|
116
|
+
//terminate stream
|
|
117
|
+
console.log("terminate video stream " + stream.id);
|
|
118
|
+
let track = stream.getVideoTracks()[0];
|
|
119
|
+
track.stop();
|
|
120
|
+
track.dispatchEvent(new Event("ended"));
|
|
121
|
+
}).prop('disabled', true);
|
|
122
|
+
stream.getTracks()[0].addEventListener("ended", function() {
|
|
123
|
+
controls.tables.video.row(row).remove().draw();
|
|
124
|
+
});
|
|
125
|
+
trackCallback({
|
|
126
|
+
stream: stream,
|
|
127
|
+
encodings: track.encodings,
|
|
128
|
+
source: track.source
|
|
131
129
|
});
|
|
132
130
|
}
|
|
133
131
|
|
|
@@ -148,28 +146,6 @@ const createControls = function(config) {
|
|
|
148
146
|
return details;
|
|
149
147
|
}
|
|
150
148
|
|
|
151
|
-
// Add event listener for opening and closing details
|
|
152
|
-
$('#videoTracksTableBody').on('click', 'td.details-control', function () {
|
|
153
|
-
let tr = $(this).closest('tr');
|
|
154
|
-
let row = controls.tables.video.row(tr);
|
|
155
|
-
if (row.child.isShown()) {
|
|
156
|
-
// This row is already open - close it
|
|
157
|
-
row.child.hide();
|
|
158
|
-
tr.removeClass('shown');
|
|
159
|
-
} else {
|
|
160
|
-
// Open this row
|
|
161
|
-
row.child(format(row.data())).show();
|
|
162
|
-
tr.addClass('shown');
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
config.media.audio.tracks.forEach(function(track){
|
|
167
|
-
addAudioTrackRow(track);
|
|
168
|
-
})
|
|
169
|
-
config.media.video.tracks.forEach(function(track){
|
|
170
|
-
addVideoTrackRow(track);
|
|
171
|
-
})
|
|
172
|
-
|
|
173
149
|
const muteForm = function(form) {
|
|
174
150
|
for (const [key, value] of Object.entries(form)) {
|
|
175
151
|
value.disabled = true;
|
|
@@ -220,63 +196,100 @@ const createControls = function(config) {
|
|
|
220
196
|
return streams;
|
|
221
197
|
}
|
|
222
198
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
let encoding = this.data();
|
|
227
|
-
encodings.push({
|
|
228
|
-
rid: encoding.rid,
|
|
229
|
-
active: encoding.active,
|
|
230
|
-
maxBitrate: encoding.maxBitrate,
|
|
231
|
-
scaleResolutionDownBy: encoding.resolutionScale
|
|
232
|
-
})
|
|
233
|
-
});
|
|
234
|
-
let track = {
|
|
235
|
-
source: controls.addVideoTrack.source.value,
|
|
236
|
-
width: controls.addVideoTrack.width.value,
|
|
237
|
-
height: controls.addVideoTrack.height.value,
|
|
238
|
-
codec: controls.addVideoTrack.codec.value,
|
|
239
|
-
encodings: encodings
|
|
240
|
-
}
|
|
241
|
-
addVideoTrackRow(track);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
$("#videoTrackEncodingsTable").on("click", ".remove", function(){
|
|
245
|
-
controls.tables.encodings.row($(this).parents('tr')).remove().draw();
|
|
246
|
-
});
|
|
199
|
+
const onTrack = function(callback) {
|
|
200
|
+
trackCallback = callback;
|
|
201
|
+
}
|
|
247
202
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
203
|
+
const displayTables = async function() {
|
|
204
|
+
// Add event listener for opening and closing details
|
|
205
|
+
$('#videoTracksTableBody').on('click', 'td.details-control', function () {
|
|
206
|
+
let tr = $(this).closest('tr');
|
|
207
|
+
let row = controls.tables.video.row(tr);
|
|
208
|
+
if (row.child.isShown()) {
|
|
209
|
+
// This row is already open - close it
|
|
210
|
+
row.child.hide();
|
|
211
|
+
tr.removeClass('shown');
|
|
212
|
+
} else {
|
|
213
|
+
// Open this row
|
|
214
|
+
row.child(format(row.data())).show();
|
|
215
|
+
tr.addClass('shown');
|
|
216
|
+
}
|
|
217
|
+
});
|
|
258
218
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
219
|
+
// Add preconfigured audio and video tracks
|
|
220
|
+
for (const track of config.media.audio.tracks) {
|
|
221
|
+
await addAudioTrackRow(track);
|
|
222
|
+
}
|
|
223
|
+
for (const track of config.media.video.tracks) {
|
|
224
|
+
await addVideoTrackRow(track);
|
|
265
225
|
}
|
|
266
|
-
addAudioTrackRow(track);
|
|
267
|
-
});
|
|
268
226
|
|
|
269
|
-
|
|
270
|
-
|
|
227
|
+
// Click event listener to add a new video track
|
|
228
|
+
document.getElementById("addVideoTrack").addEventListener("click", function(e){
|
|
229
|
+
let encodings = [];
|
|
230
|
+
controls.tables.encodings.rows().every(function() {
|
|
231
|
+
let encoding = this.data();
|
|
232
|
+
encodings.push({
|
|
233
|
+
rid: encoding.rid,
|
|
234
|
+
active: encoding.active,
|
|
235
|
+
maxBitrate: encoding.maxBitrate,
|
|
236
|
+
scaleResolutionDownBy: encoding.resolutionScale
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
let track = {
|
|
240
|
+
source: controls.addVideoTrack.source.value,
|
|
241
|
+
width: controls.addVideoTrack.width.value,
|
|
242
|
+
height: controls.addVideoTrack.height.value,
|
|
243
|
+
codec: controls.addVideoTrack.codec.value,
|
|
244
|
+
encodings: encodings
|
|
245
|
+
}
|
|
246
|
+
addVideoTrackRow(track);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Click event listener to remove video quality
|
|
250
|
+
$("#videoTrackEncodingsTable").on("click", ".remove", function(){
|
|
251
|
+
controls.tables.encodings.row($(this).parents('tr')).remove().draw();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Click event listener to add video quality
|
|
255
|
+
document.getElementById("addVideoTrackEncoding").addEventListener("click", function(){
|
|
256
|
+
let button = '<button class="btn btn-primary remove">Delete</button>';
|
|
257
|
+
controls.tables.encodings.row.add({
|
|
258
|
+
rid: controls.addVideoEncoding.rid.value,
|
|
259
|
+
active: controls.addVideoEncoding.active.value,
|
|
260
|
+
maxBitrate: controls.addVideoEncoding.maxBitrate.value,
|
|
261
|
+
resolutionScale: controls.addVideoEncoding.resolutionScale.value,
|
|
262
|
+
action: button
|
|
263
|
+
}).draw();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Click event listener to add a new audio track
|
|
267
|
+
document.getElementById("addAudioTrack").addEventListener("click", function(e){
|
|
268
|
+
let encodings = [];
|
|
269
|
+
let track = {
|
|
270
|
+
source: controls.addAudioTrack.source.value,
|
|
271
|
+
channels: controls.addAudioTrack.channels.value,
|
|
272
|
+
encodings: encodings
|
|
273
|
+
}
|
|
274
|
+
addAudioTrackRow(track);
|
|
275
|
+
});
|
|
276
|
+
|
|
271
277
|
}
|
|
272
278
|
|
|
279
|
+
const cleanTables = function() {
|
|
280
|
+
controls.tables.video.rows().remove().draw();
|
|
281
|
+
controls.tables.audio.rows().remove().draw();
|
|
282
|
+
controls.tables.encodings.rows().remove().draw();
|
|
283
|
+
}
|
|
273
284
|
|
|
274
285
|
return {
|
|
275
286
|
muteInput: muteInput,
|
|
276
287
|
roomConfig: roomConfig,
|
|
288
|
+
displayTables: displayTables,
|
|
277
289
|
getAudioStreams: getAudioStreams,
|
|
278
290
|
getVideoStreams: getVideoStreams,
|
|
279
|
-
onTrack: onTrack
|
|
291
|
+
onTrack: onTrack,
|
|
292
|
+
cleanTables: cleanTables
|
|
280
293
|
}
|
|
281
294
|
}
|
|
282
295
|
|
package/src/client/display.js
CHANGED
|
@@ -327,7 +327,7 @@ const initRemoteDisplay = function(room, mainDiv, peerConnection) {
|
|
|
327
327
|
qualityDiv.innerText = trackInfo.quality[i];
|
|
328
328
|
qualityDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
329
329
|
qualityDiv.style.color = "red";
|
|
330
|
-
qualityDiv.addEventListener('click', function(){
|
|
330
|
+
qualityDiv.addEventListener('click', async function(){
|
|
331
331
|
console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfo.id);
|
|
332
332
|
if (qualityDiv.style.color === "red") {
|
|
333
333
|
return;
|
|
@@ -338,7 +338,7 @@ const initRemoteDisplay = function(room, mainDiv, peerConnection) {
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
qualityDiv.style.color = "blue";
|
|
341
|
-
room.changeQuality(trackInfo.id, trackInfo.quality[i]);
|
|
341
|
+
await room.changeQuality(trackInfo.id, trackInfo.quality[i]);
|
|
342
342
|
});
|
|
343
343
|
qualityDisplay.appendChild(qualityDiv);
|
|
344
344
|
}
|
|
@@ -348,13 +348,13 @@ const initRemoteDisplay = function(room, mainDiv, peerConnection) {
|
|
|
348
348
|
tidDiv.innerText = "TID"+i;
|
|
349
349
|
tidDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
350
350
|
tidDiv.style.color = "gray";
|
|
351
|
-
tidDiv.addEventListener('click', function(){
|
|
351
|
+
tidDiv.addEventListener('click', async function(){
|
|
352
352
|
console.log("Clicked on TID " + i + " trackId " + trackInfo.id);
|
|
353
353
|
for (let c = 0; c < tidDivs.length; c++) {
|
|
354
354
|
tidDivs[c].style.color = "gray";
|
|
355
355
|
}
|
|
356
356
|
tidDiv.style.color = "blue";
|
|
357
|
-
room.changeQuality(trackInfo.id, null, i);
|
|
357
|
+
await room.changeQuality(trackInfo.id, null, i);
|
|
358
358
|
});
|
|
359
359
|
tidDisplay.appendChild(tidDiv);
|
|
360
360
|
}
|
package/src/client/main.html
CHANGED
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
</div>
|
|
211
211
|
</div>
|
|
212
212
|
<div class="modal-footer">
|
|
213
|
-
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
213
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="cancel()">Close</button>
|
|
214
214
|
<button class="btn btn-primary" id="startButton" onclick="connect()">Enter</button>
|
|
215
215
|
</div>
|
|
216
216
|
</div>
|
package/src/client/main.js
CHANGED
|
@@ -2,7 +2,6 @@ const constants = SFU.constants;
|
|
|
2
2
|
const sfu = SFU;
|
|
3
3
|
let localDisplay;
|
|
4
4
|
let cControls;
|
|
5
|
-
let pc;
|
|
6
5
|
|
|
7
6
|
const defaultConfig = {
|
|
8
7
|
room: {
|
|
@@ -38,7 +37,7 @@ const defaultConfig = {
|
|
|
38
37
|
};
|
|
39
38
|
|
|
40
39
|
/**
|
|
41
|
-
*
|
|
40
|
+
* Load track configuration and show entrance modal
|
|
42
41
|
*/
|
|
43
42
|
const init = function() {
|
|
44
43
|
//read config
|
|
@@ -48,44 +47,51 @@ const init = function() {
|
|
|
48
47
|
//use default config
|
|
49
48
|
cControls = createControls(defaultConfig);
|
|
50
49
|
});
|
|
51
|
-
//create local display to show local streams
|
|
52
|
-
localDisplay = initLocalDisplay(document.getElementById("localDisplay"));
|
|
53
50
|
//open entrance modal
|
|
54
51
|
$('#entranceModal').modal('show');
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
/**
|
|
58
|
-
*
|
|
55
|
+
* Connect to server and publish preconfigured streams
|
|
59
56
|
*/
|
|
60
|
-
function connect() {
|
|
61
|
-
//hide modal
|
|
57
|
+
async function connect() {
|
|
58
|
+
// hide modal
|
|
62
59
|
$('#entranceModal').modal('hide');
|
|
63
|
-
//disable controls
|
|
60
|
+
// disable controls
|
|
64
61
|
cControls.muteInput();
|
|
65
62
|
//create peer connection
|
|
66
|
-
pc = new RTCPeerConnection();
|
|
63
|
+
const pc = new RTCPeerConnection();
|
|
67
64
|
//get config object for room creation
|
|
68
65
|
const roomConfig = cControls.roomConfig();
|
|
69
66
|
//kick off connect to server and local room creation
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
try {
|
|
68
|
+
const session = await sfu.createRoom(roomConfig);
|
|
69
|
+
// Now we connected to the server (if no exception was thrown)
|
|
70
|
+
session.on(constants.SFU_EVENT.FAILED, function(e) {
|
|
71
|
+
displayError("CONNECTION FAILED: "+ e +". Refresh the page to enter the room again");
|
|
72
|
+
}).on(constants.SFU_EVENT.DISCONNECTED, function(e) {
|
|
73
|
+
displayError("DISCONNECTED. Refresh the page to enter the room again");
|
|
74
|
+
});
|
|
72
75
|
const room = session.room();
|
|
73
|
-
|
|
76
|
+
room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
|
|
77
|
+
displayError(e);
|
|
78
|
+
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
79
|
+
displayError(e.operation + " failed: " + e.error);
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// create local display to show local streams
|
|
83
|
+
localDisplay = initLocalDisplay(document.getElementById("localDisplay"));
|
|
84
|
+
// display audio and video control tables
|
|
85
|
+
await cControls.displayTables();
|
|
86
|
+
cControls.onTrack(async function (s) {
|
|
87
|
+
await publishNewTrack(room, pc, s);
|
|
88
|
+
});
|
|
89
|
+
//create and bind chat to the new room
|
|
74
90
|
const chatDiv = document.getElementById('messages');
|
|
75
91
|
const chatInput = document.getElementById('localMessage');
|
|
76
92
|
const chatButton = document.getElementById('sendMessage');
|
|
77
|
-
//create and bind chat to the new room
|
|
78
93
|
createChat(room, chatDiv, chatInput, chatButton);
|
|
79
94
|
|
|
80
|
-
room.on(constants.SFU_ROOM_EVENT.FAILED, function(e) {
|
|
81
|
-
const errField = document.getElementById("errorMsg");
|
|
82
|
-
errField.style.color = "red";
|
|
83
|
-
errField.innerText = e;
|
|
84
|
-
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
85
|
-
const errField = document.getElementById("errorMsg");
|
|
86
|
-
errField.style.color = "red";
|
|
87
|
-
errField.innerText = e.operation + " failed: " + e.error;
|
|
88
|
-
})
|
|
89
95
|
//setup remote display for showing remote audio/video tracks
|
|
90
96
|
const remoteDisplay = document.getElementById("display");
|
|
91
97
|
initRemoteDisplay(room, remoteDisplay, pc);
|
|
@@ -94,6 +100,25 @@ function connect() {
|
|
|
94
100
|
let streams = cControls.getVideoStreams();
|
|
95
101
|
//combine local video streams with audio streams
|
|
96
102
|
streams.push.apply(streams, cControls.getAudioStreams());
|
|
103
|
+
|
|
104
|
+
// Publish preconfigured streams
|
|
105
|
+
publishPreconfiguredStreams(room, pc, streams);
|
|
106
|
+
} catch(e) {
|
|
107
|
+
console.error(e);
|
|
108
|
+
displayError(e);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Publish streams after entyering room according to configuration file
|
|
115
|
+
*
|
|
116
|
+
* @param {*} room
|
|
117
|
+
* @param {*} pc
|
|
118
|
+
* @param {*} streams
|
|
119
|
+
*/
|
|
120
|
+
const publishPreconfiguredStreams = async function(room, pc, streams) {
|
|
121
|
+
try {
|
|
97
122
|
let config = {};
|
|
98
123
|
//add our local streams to the room (to PeerConnection)
|
|
99
124
|
streams.forEach(function (s) {
|
|
@@ -108,50 +133,124 @@ function connect() {
|
|
|
108
133
|
subscribeTrackToEndedEvent(room, track, pc);
|
|
109
134
|
});
|
|
110
135
|
});
|
|
111
|
-
//add callback for the new local stream to the local controls
|
|
112
|
-
cControls.onTrack(function (s) {
|
|
113
|
-
let config = {};
|
|
114
|
-
//add local stream to local display
|
|
115
|
-
localDisplay.add(s.stream.id, "local", s.stream);
|
|
116
|
-
//add each track to PeerConnection
|
|
117
|
-
s.stream.getTracks().forEach((track) => {
|
|
118
|
-
if (s.source === "screen") {
|
|
119
|
-
config[track.id] = s.source;
|
|
120
|
-
}
|
|
121
|
-
addTrackToPeerConnection(pc, s.stream, track, s.encodings);
|
|
122
|
-
subscribeTrackToEndedEvent(room, track, pc);
|
|
123
|
-
});
|
|
124
|
-
//kickoff renegotiation
|
|
125
|
-
room.updateState(config);
|
|
126
|
-
});
|
|
127
136
|
//join room
|
|
128
|
-
room.join(pc, null, config);
|
|
129
|
-
|
|
137
|
+
await room.join(pc, null, config);
|
|
138
|
+
// Enable Delete button for each preconfigured stream #WCS-3689
|
|
139
|
+
streams.forEach(function (s) {
|
|
140
|
+
$('#' + s.stream.id + "-button").prop('disabled', false);
|
|
141
|
+
});
|
|
142
|
+
} catch(e) {
|
|
143
|
+
console.error("Failed to publish a preconfigured streams: " + e);
|
|
144
|
+
displayError(e);
|
|
145
|
+
// Enable Delete button for each preconfigured stream #WCS-3689
|
|
146
|
+
streams.forEach(function (s) {
|
|
147
|
+
$('#' + s.stream.id + "-button").prop('disabled', false);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
130
150
|
}
|
|
131
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Publish a new media track to the room
|
|
154
|
+
*
|
|
155
|
+
* @param {*} room
|
|
156
|
+
* @param {*} pc
|
|
157
|
+
* @param {*} media
|
|
158
|
+
*/
|
|
159
|
+
const publishNewTrack = async function(room, pc, media) {
|
|
160
|
+
try {
|
|
161
|
+
let config = {};
|
|
162
|
+
//add local stream to local display
|
|
163
|
+
localDisplay.add(media.stream.id, "local", media.stream);
|
|
164
|
+
//add each track to PeerConnection
|
|
165
|
+
media.stream.getTracks().forEach((track) => {
|
|
166
|
+
if (media.source === "screen") {
|
|
167
|
+
config[track.id] = media.source;
|
|
168
|
+
}
|
|
169
|
+
addTrackToPeerConnection(pc, media.stream, track, media.encodings);
|
|
170
|
+
subscribeTrackToEndedEvent(room, track, pc);
|
|
171
|
+
});
|
|
172
|
+
// Clean error message
|
|
173
|
+
displayError("");
|
|
174
|
+
//kickoff renegotiation
|
|
175
|
+
await room.updateState(config);
|
|
176
|
+
// Enable Delete button for a new stream #WCS-3689
|
|
177
|
+
$('#' + media.stream.id + "-button").prop('disabled', false);
|
|
178
|
+
} catch(e) {
|
|
179
|
+
console.error("Failed to publish a new track: " + e);
|
|
180
|
+
displayError(e);
|
|
181
|
+
// Enable Delete button for a new stream #WCS-3689
|
|
182
|
+
$('#' + media.stream.id + "-button").prop('disabled', false);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Subscribe to track ended event to renegotiate WebRTC connection
|
|
188
|
+
*
|
|
189
|
+
* @param {*} room
|
|
190
|
+
* @param {*} track
|
|
191
|
+
* @param {*} pc
|
|
192
|
+
*/
|
|
132
193
|
const subscribeTrackToEndedEvent = function(room, track, pc) {
|
|
133
|
-
track.addEventListener("ended", function() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
194
|
+
track.addEventListener("ended", async function() {
|
|
195
|
+
try {
|
|
196
|
+
//track ended, see if we need to cleanup
|
|
197
|
+
let negotiate = false;
|
|
198
|
+
for (const sender of pc.getSenders()) {
|
|
199
|
+
if (sender.track === track) {
|
|
200
|
+
pc.removeTrack(sender);
|
|
201
|
+
//track found, set renegotiation flag
|
|
202
|
+
negotiate = true;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
142
205
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
206
|
+
// Clean error message
|
|
207
|
+
displayError("");
|
|
208
|
+
if (negotiate) {
|
|
209
|
+
//kickoff renegotiation
|
|
210
|
+
await room.updateState();
|
|
211
|
+
}
|
|
212
|
+
} catch(e) {
|
|
213
|
+
displayError(e);
|
|
214
|
+
console.error("Failed to update room state: " + e);
|
|
147
215
|
}
|
|
148
216
|
});
|
|
149
|
-
}
|
|
217
|
+
}
|
|
150
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Add track to WebRTC PeerConnection
|
|
221
|
+
*
|
|
222
|
+
* @param {*} pc
|
|
223
|
+
* @param {*} stream
|
|
224
|
+
* @param {*} track
|
|
225
|
+
* @param {*} encodings
|
|
226
|
+
*/
|
|
151
227
|
const addTrackToPeerConnection = function(pc, stream, track, encodings) {
|
|
152
228
|
pc.addTransceiver(track, {
|
|
153
229
|
direction: "sendonly",
|
|
154
230
|
streams: [stream],
|
|
155
231
|
sendEncodings: encodings ? encodings : [] //passing encoding types for video simulcast tracks
|
|
156
232
|
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Display error message
|
|
237
|
+
*
|
|
238
|
+
* @param {*} text
|
|
239
|
+
*/
|
|
240
|
+
const displayError = function(text) {
|
|
241
|
+
const errField = document.getElementById("errorMsg");
|
|
242
|
+
errField.style.color = "red";
|
|
243
|
+
errField.innerText = text;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Entrance modal cancelled, we do not enter to a room
|
|
248
|
+
*/
|
|
249
|
+
const cancel = function() {
|
|
250
|
+
//hide modal
|
|
251
|
+
$('#entranceModal').modal('hide');
|
|
252
|
+
//disable controls
|
|
253
|
+
cControls.muteInput();
|
|
254
|
+
// display the error message
|
|
255
|
+
displayError("Please refresh the page, fill the entrance modal and enter a room to publish or play streams");
|
|
157
256
|
}
|
|
@@ -391,7 +391,7 @@ const initRemoteDisplay = function(options) {
|
|
|
391
391
|
qualityDiv.innerText = trackInfo.quality[i];
|
|
392
392
|
qualityDiv.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
393
393
|
qualityDiv.style.color = "red";
|
|
394
|
-
qualityDiv.addEventListener('click', function(){
|
|
394
|
+
qualityDiv.addEventListener('click', async function(){
|
|
395
395
|
console.log("Clicked on quality " + trackInfo.quality[i] + " trackId " + trackInfo.id);
|
|
396
396
|
if (qualityDiv.style.color === "red") {
|
|
397
397
|
return;
|
|
@@ -402,7 +402,7 @@ const initRemoteDisplay = function(options) {
|
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
404
|
qualityDiv.style.color = "blue";
|
|
405
|
-
room.changeQuality(trackInfo.id, trackInfo.quality[i]);
|
|
405
|
+
await room.changeQuality(trackInfo.id, trackInfo.quality[i]);
|
|
406
406
|
});
|
|
407
407
|
qualitySwitchDisplay.appendChild(qualityDiv);
|
|
408
408
|
}
|
package/src/player/player.js
CHANGED
|
@@ -155,7 +155,7 @@ const init = function() {
|
|
|
155
155
|
/**
|
|
156
156
|
* connect to server
|
|
157
157
|
*/
|
|
158
|
-
const connect = function(state) {
|
|
158
|
+
const connect = async function(state) {
|
|
159
159
|
//create peer connection
|
|
160
160
|
pc = new RTCPeerConnection();
|
|
161
161
|
//get config object for room creation
|
|
@@ -167,21 +167,27 @@ const connect = function(state) {
|
|
|
167
167
|
setStatus(state.statusId(), "");
|
|
168
168
|
setStatus(state.errInfoId(), "");
|
|
169
169
|
// connect to server and create a room if not
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
try {
|
|
171
|
+
const session = await sfu.createRoom(roomConfig);
|
|
172
|
+
// Set up session ending events
|
|
173
|
+
session.on(constants.SFU_EVENT.DISCONNECTED, function() {
|
|
174
|
+
state.clear();
|
|
175
|
+
onDisconnected(state);
|
|
176
|
+
setStatus(state.statusId(), "DISCONNECTED", "green");
|
|
177
|
+
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
178
|
+
state.clear();
|
|
179
|
+
onDisconnected(state);
|
|
180
|
+
setStatus(state.statusId(), "FAILED", "red");
|
|
181
|
+
setStatus(state.errInfoId(), e.status + " " + e.statusText, "red");
|
|
182
|
+
});
|
|
183
|
+
// Connected successfully
|
|
172
184
|
state.set(pc, session, session.room());
|
|
173
185
|
onConnected(state);
|
|
174
186
|
setStatus(state.statusId(), "ESTABLISHED", "green");
|
|
175
|
-
}
|
|
176
|
-
state.clear();
|
|
177
|
-
onDisconnected(state);
|
|
178
|
-
setStatus(state.statusId(), "DISCONNECTED", "green");
|
|
179
|
-
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
180
|
-
state.clear();
|
|
181
|
-
onDisconnected(state);
|
|
187
|
+
} catch(e) {
|
|
182
188
|
setStatus(state.statusId(), "FAILED", "red");
|
|
183
|
-
setStatus(state.errInfoId(), e
|
|
184
|
-
}
|
|
189
|
+
setStatus(state.errInfoId(), e, "red");
|
|
190
|
+
}
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
const onConnected = async function(state) {
|
package/src/sfu.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {Sfu, RoomEvent, SfuEvent, State} from "@flashphoner/sfusdk";
|
|
2
2
|
|
|
3
|
-
export function createRoom(options: {
|
|
3
|
+
export async function createRoom(options: {
|
|
4
4
|
url: string,
|
|
5
5
|
roomName: string,
|
|
6
6
|
pin: string,
|
|
7
7
|
nickname: string
|
|
8
8
|
}) {
|
|
9
9
|
const sfu = new Sfu();
|
|
10
|
-
sfu.connect({
|
|
10
|
+
await sfu.connect({
|
|
11
11
|
url: options.url,
|
|
12
12
|
nickname: options.nickname,
|
|
13
13
|
logGroup: options.roomName
|
|
@@ -195,7 +195,7 @@ const init = function() {
|
|
|
195
195
|
/**
|
|
196
196
|
* connect to server
|
|
197
197
|
*/
|
|
198
|
-
const connect = function(state) {
|
|
198
|
+
const connect = async function(state) {
|
|
199
199
|
//create peer connection
|
|
200
200
|
let pc = new RTCPeerConnection();
|
|
201
201
|
//get config object for room creation
|
|
@@ -207,21 +207,27 @@ const connect = function(state) {
|
|
|
207
207
|
setStatus(state.statusId(), "");
|
|
208
208
|
setStatus(state.errInfoId(), "");
|
|
209
209
|
// connect to server and create a room if not
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
try {
|
|
211
|
+
const session = await sfu.createRoom(roomConfig);
|
|
212
|
+
// Set up session ending events
|
|
213
|
+
session.on(constants.SFU_EVENT.DISCONNECTED, function() {
|
|
214
|
+
state.clear();
|
|
215
|
+
onDisconnected(state);
|
|
216
|
+
setStatus(state.statusId(), "DISCONNECTED", "green");
|
|
217
|
+
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
218
|
+
state.clear();
|
|
219
|
+
onDisconnected(state);
|
|
220
|
+
setStatus(state.statusId(), "FAILED", "red");
|
|
221
|
+
setStatus(state.errInfoId(), e.status + " " + e.statusText, "red");
|
|
222
|
+
});
|
|
223
|
+
// Connected successfully
|
|
212
224
|
state.set(pc, session, session.room());
|
|
213
225
|
onConnected(state);
|
|
214
226
|
setStatus(state.statusId(), "ESTABLISHED", "green");
|
|
215
|
-
}
|
|
216
|
-
state.clear();
|
|
217
|
-
onDisconnected(state);
|
|
218
|
-
setStatus(state.statusId(), "DISCONNECTED", "green");
|
|
219
|
-
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
220
|
-
state.clear();
|
|
221
|
-
onDisconnected(state);
|
|
227
|
+
} catch(e) {
|
|
222
228
|
setStatus(state.statusId(), "FAILED", "red");
|
|
223
|
-
setStatus(state.errInfoId(), e
|
|
224
|
-
}
|
|
229
|
+
setStatus(state.errInfoId(), e, "red");
|
|
230
|
+
}
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
const onConnected = function(state) {
|
|
@@ -345,7 +351,7 @@ const publishStreams = async function(state) {
|
|
|
345
351
|
s.stream.getTracks().forEach((track) => {
|
|
346
352
|
config[track.id] = contentType;
|
|
347
353
|
addTrackToPeerConnection(state.pc, s.stream, track, s.encodings);
|
|
348
|
-
subscribeTrackToEndedEvent(state
|
|
354
|
+
subscribeTrackToEndedEvent(state, track);
|
|
349
355
|
});
|
|
350
356
|
});
|
|
351
357
|
//start WebRTC negotiation
|
|
@@ -390,8 +396,10 @@ const stopStreams = function(state) {
|
|
|
390
396
|
}
|
|
391
397
|
}
|
|
392
398
|
|
|
393
|
-
const subscribeTrackToEndedEvent = function(
|
|
394
|
-
|
|
399
|
+
const subscribeTrackToEndedEvent = function(state, track) {
|
|
400
|
+
let room = state.room;
|
|
401
|
+
let pc = state.pc;
|
|
402
|
+
track.addEventListener("ended", async function() {
|
|
395
403
|
//track ended, see if we need to cleanup
|
|
396
404
|
let negotiate = false;
|
|
397
405
|
for (const sender of pc.getSenders()) {
|
|
@@ -404,7 +412,7 @@ const subscribeTrackToEndedEvent = function(room, track, pc) {
|
|
|
404
412
|
}
|
|
405
413
|
if (negotiate) {
|
|
406
414
|
//kickoff renegotiation
|
|
407
|
-
room.updateState();
|
|
415
|
+
state.waitFor(room.updateState(), MAX_AWAIT_MS);
|
|
408
416
|
}
|
|
409
417
|
});
|
|
410
418
|
};
|
|
@@ -113,9 +113,9 @@ const init = function() {
|
|
|
113
113
|
/**
|
|
114
114
|
* Connect to server
|
|
115
115
|
*/
|
|
116
|
-
const connect = function(state) {
|
|
116
|
+
const connect = async function(state) {
|
|
117
117
|
// Create peer connection
|
|
118
|
-
|
|
118
|
+
const pc = new RTCPeerConnection();
|
|
119
119
|
// Create a config to connect to SFU room
|
|
120
120
|
const roomConfig = {
|
|
121
121
|
// Server websocket URL
|
|
@@ -130,22 +130,28 @@ const connect = function(state) {
|
|
|
130
130
|
// Clean state display items
|
|
131
131
|
setStatus("playStatus", "");
|
|
132
132
|
setStatus("playErrorInfo", "");
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
try {
|
|
134
|
+
// Connect to the server (room should already exist)
|
|
135
|
+
const session = await sfu.createRoom(roomConfig);
|
|
136
|
+
// Set up session ending events
|
|
137
|
+
session.on(constants.SFU_EVENT.DISCONNECTED, function() {
|
|
138
|
+
state.clear();
|
|
139
|
+
onDisconnected(state);
|
|
140
|
+
setStatus("playStatus", "DISCONNECTED", "green");
|
|
141
|
+
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
142
|
+
state.clear();
|
|
143
|
+
onDisconnected(state);
|
|
144
|
+
setStatus("playStatus", "FAILED", "red");
|
|
145
|
+
setStatus("playErrorInfo", e.status + " " + e.statusText, "red");
|
|
146
|
+
});
|
|
147
|
+
// Connected successfully
|
|
136
148
|
state.set(pc, session, session.room());
|
|
137
149
|
onConnected(state);
|
|
138
150
|
setStatus("playStatus", "CONNECTING...", "black");
|
|
139
|
-
}
|
|
140
|
-
state.clear();
|
|
141
|
-
onDisconnected(state);
|
|
142
|
-
setStatus("playStatus", "DISCONNECTED", "green");
|
|
143
|
-
}).on(constants.SFU_EVENT.FAILED, function(e) {
|
|
144
|
-
state.clear();
|
|
145
|
-
onDisconnected(state);
|
|
151
|
+
} catch(e) {
|
|
146
152
|
setStatus("playStatus", "FAILED", "red");
|
|
147
|
-
setStatus("playErrorInfo", e
|
|
148
|
-
}
|
|
153
|
+
setStatus("playErrorInfo", e, "red");
|
|
154
|
+
}
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
const onConnected = async function(state) {
|