@flashphoner/sfusdk-examples 2.0.244 → 2.0.248
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 +2 -2
- package/src/client/chat.js +15 -1
- package/src/client/config.json +2 -2
- package/src/client/controls.js +4 -2
- package/src/client/main.html +3 -2
- package/src/client/main.js +49 -47
- package/src/commons/js/display.js +1481 -635
- package/src/commons/js/util.js +4 -0
- package/src/player/player.js +3 -7
- package/src/track-test/track-test.css +26 -0
- package/src/track-test/track-test.html +89 -0
- package/src/track-test/track-test.js +646 -0
- package/src/two-way-streaming/config.json +3 -3
- package/src/two-way-streaming/two-way-streaming.js +86 -89
- package/src/webrtc-abr-player/player.js +16 -23
- package/src/client/display.js +0 -502
- package/src/client/util.js +0 -67
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
const constants = SFU.constants;
|
|
2
|
+
const sfu = SFU;
|
|
3
|
+
let mainConfig;
|
|
4
|
+
let publishState;
|
|
5
|
+
let test1State;
|
|
6
|
+
let test2State;
|
|
7
|
+
let test3State;
|
|
8
|
+
|
|
9
|
+
const PUBLISH = "publish";
|
|
10
|
+
const TEST1 = "test1";
|
|
11
|
+
const TEST2 = "test2";
|
|
12
|
+
const TEST3 = "test3";
|
|
13
|
+
const PRELOADER_URL = "../commons/media/silence.mp3";
|
|
14
|
+
|
|
15
|
+
const trackCount = 30;
|
|
16
|
+
/**
|
|
17
|
+
* Default publishing config
|
|
18
|
+
*/
|
|
19
|
+
const defaultConfig = {
|
|
20
|
+
room: {
|
|
21
|
+
url: "wss://127.0.0.1:8888",
|
|
22
|
+
name: "ROOM1",
|
|
23
|
+
pin: "1234",
|
|
24
|
+
nickName: "User1",
|
|
25
|
+
failedProbesThreshold: 5,
|
|
26
|
+
pingInterval: 5000
|
|
27
|
+
},
|
|
28
|
+
media: {
|
|
29
|
+
video: {
|
|
30
|
+
tracks: Array(trackCount).fill({
|
|
31
|
+
source: "camera",
|
|
32
|
+
width: 1280,
|
|
33
|
+
height: 720,
|
|
34
|
+
codec: "H264",
|
|
35
|
+
constraints: {
|
|
36
|
+
frameRate: 25
|
|
37
|
+
},
|
|
38
|
+
encodings: [
|
|
39
|
+
{rid: "180p", active: true, maxBitrate: 2000000, scaleResolutionDownBy: 4}
|
|
40
|
+
],
|
|
41
|
+
type: "cam1"
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Current state object
|
|
49
|
+
*/
|
|
50
|
+
const CurrentState = function (prefix) {
|
|
51
|
+
let state = {
|
|
52
|
+
prefix: prefix,
|
|
53
|
+
pc: null,
|
|
54
|
+
session: null,
|
|
55
|
+
room: null,
|
|
56
|
+
display: null,
|
|
57
|
+
roomEnded: false,
|
|
58
|
+
starting: false,
|
|
59
|
+
set: function (pc, session, room) {
|
|
60
|
+
state.pc = pc;
|
|
61
|
+
state.session = session;
|
|
62
|
+
state.room = room;
|
|
63
|
+
state.roomEnded = false;
|
|
64
|
+
},
|
|
65
|
+
clear: function () {
|
|
66
|
+
state.room = null;
|
|
67
|
+
state.session = null;
|
|
68
|
+
state.pc = null;
|
|
69
|
+
state.roomEnded = false;
|
|
70
|
+
},
|
|
71
|
+
setRoomEnded: function () {
|
|
72
|
+
state.roomEnded = true;
|
|
73
|
+
},
|
|
74
|
+
buttonId: function () {
|
|
75
|
+
return state.prefix + "Btn";
|
|
76
|
+
},
|
|
77
|
+
buttonText: function () {
|
|
78
|
+
return (state.prefix.charAt(0).toUpperCase() + state.prefix.slice(1));
|
|
79
|
+
},
|
|
80
|
+
inputId: function () {
|
|
81
|
+
return state.prefix + "Name";
|
|
82
|
+
},
|
|
83
|
+
statusId: function () {
|
|
84
|
+
return state.prefix + "Status";
|
|
85
|
+
},
|
|
86
|
+
formId: function () {
|
|
87
|
+
return state.prefix + "Form";
|
|
88
|
+
},
|
|
89
|
+
errInfoId: function () {
|
|
90
|
+
return state.prefix + "ErrorInfo";
|
|
91
|
+
},
|
|
92
|
+
is: function (value) {
|
|
93
|
+
return (prefix === value);
|
|
94
|
+
},
|
|
95
|
+
isActive: function () {
|
|
96
|
+
return (state.room && !state.roomEnded && state.pc);
|
|
97
|
+
},
|
|
98
|
+
isConnected: function () {
|
|
99
|
+
return (state.session && state.session.state() === constants.SFU_STATE.CONNECTED);
|
|
100
|
+
},
|
|
101
|
+
isRoomEnded: function () {
|
|
102
|
+
return state.roomEnded;
|
|
103
|
+
},
|
|
104
|
+
setStarting: function (value) {
|
|
105
|
+
state.starting = value;
|
|
106
|
+
},
|
|
107
|
+
isStarting: function () {
|
|
108
|
+
return state.starting;
|
|
109
|
+
},
|
|
110
|
+
setDisplay: function (display) {
|
|
111
|
+
state.display = display;
|
|
112
|
+
},
|
|
113
|
+
disposeDisplay: function () {
|
|
114
|
+
if (state.display) {
|
|
115
|
+
state.display.stop();
|
|
116
|
+
state.display = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
return state;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* load config and set default values
|
|
125
|
+
*/
|
|
126
|
+
const init = function () {
|
|
127
|
+
$("#publishBtn").prop('disabled', true);
|
|
128
|
+
$("#test1Btn").prop('disabled', true);
|
|
129
|
+
$("#test2Btn").prop('disabled', true);
|
|
130
|
+
$("#test3Btn").prop('disabled', true);
|
|
131
|
+
$("#url").prop('disabled', true);
|
|
132
|
+
$("#roomName").prop('disabled', true);
|
|
133
|
+
$("#publishName").prop('disabled', true);
|
|
134
|
+
publishState = CurrentState(PUBLISH);
|
|
135
|
+
test1State = CurrentState(TEST1);
|
|
136
|
+
test2State = CurrentState(TEST2);
|
|
137
|
+
test3State = CurrentState(TEST3);
|
|
138
|
+
mainConfig = defaultConfig;
|
|
139
|
+
onDisconnected(publishState);
|
|
140
|
+
onDisconnected(test1State);
|
|
141
|
+
onDisconnected(test2State);
|
|
142
|
+
onDisconnected(test3State);
|
|
143
|
+
$("#url").val(setURL());
|
|
144
|
+
$("#roomName").val("ROOM1-" + createUUID(4));
|
|
145
|
+
$("#publishName").val("Publisher1-" + createUUID(4));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* connect to server
|
|
150
|
+
*/
|
|
151
|
+
const connect = async function (state) {
|
|
152
|
+
//create peer connection
|
|
153
|
+
let pc = new RTCPeerConnection();
|
|
154
|
+
//get config object for room creation
|
|
155
|
+
const roomConfig = getRoomConfig(mainConfig);
|
|
156
|
+
roomConfig.url = $("#url").val();
|
|
157
|
+
roomConfig.roomName = $("#roomName").val();
|
|
158
|
+
roomConfig.nickname = createUUID(5);
|
|
159
|
+
// clean state display items
|
|
160
|
+
setStatus(state.statusId(), "");
|
|
161
|
+
setStatus(state.errInfoId(), "");
|
|
162
|
+
// connect to server and create a room if not
|
|
163
|
+
try {
|
|
164
|
+
const session = await sfu.createRoom(roomConfig);
|
|
165
|
+
// Set up session ending events
|
|
166
|
+
session.on(constants.SFU_EVENT.DISCONNECTED, function () {
|
|
167
|
+
onStopClick(state);
|
|
168
|
+
onDisconnected(state);
|
|
169
|
+
setStatus(state.statusId(), "DISCONNECTED", "green");
|
|
170
|
+
}).on(constants.SFU_EVENT.FAILED, function (e) {
|
|
171
|
+
onStopClick(state);
|
|
172
|
+
onDisconnected(state);
|
|
173
|
+
setStatus(state.statusId(), "FAILED", "red");
|
|
174
|
+
if (e.status && e.statusText) {
|
|
175
|
+
setStatus(state.errInfoId(), e.status + " " + e.statusText, "red");
|
|
176
|
+
} else if (e.type && e.info) {
|
|
177
|
+
setStatus(state.errInfoId(), e.type + ": " + e.info, "red");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// Connected successfully
|
|
181
|
+
onConnected(state, pc, session);
|
|
182
|
+
setStatus(state.statusId(), "ESTABLISHED", "green");
|
|
183
|
+
} catch (e) {
|
|
184
|
+
onDisconnected(state);
|
|
185
|
+
setStatus(state.statusId(), "FAILED", "red");
|
|
186
|
+
setStatus(state.errInfoId(), e, "red");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const onConnected = function (state, pc, session) {
|
|
191
|
+
state.set(pc, session, session.room());
|
|
192
|
+
$("#" + state.buttonId()).text("Stop").off('click').click(function () {
|
|
193
|
+
onStopClick(state);
|
|
194
|
+
});
|
|
195
|
+
$('#url').prop('disabled', true);
|
|
196
|
+
$("#roomName").prop('disabled', true);
|
|
197
|
+
$("#" + state.inputId()).prop('disabled', true);
|
|
198
|
+
// Add errors displaying
|
|
199
|
+
state.room.on(constants.SFU_ROOM_EVENT.FAILED, function (e) {
|
|
200
|
+
setStatus(state.errInfoId(), e, "red");
|
|
201
|
+
state.setRoomEnded();
|
|
202
|
+
onStopClick(state);
|
|
203
|
+
}).on(constants.SFU_ROOM_EVENT.OPERATION_FAILED, function (e) {
|
|
204
|
+
onOperationFailed(state, e);
|
|
205
|
+
}).on(constants.SFU_ROOM_EVENT.ENDED, function () {
|
|
206
|
+
setStatus(state.errInfoId(), "Room " + state.room.name() + " has ended", "red");
|
|
207
|
+
state.setRoomEnded();
|
|
208
|
+
onStopClick(state);
|
|
209
|
+
}).on(constants.SFU_ROOM_EVENT.DROPPED, function () {
|
|
210
|
+
setStatus(state.errInfoId(), "Dropped from the room " + state.room.name() + " due to network issues", "red");
|
|
211
|
+
state.setRoomEnded();
|
|
212
|
+
onStopClick(state);
|
|
213
|
+
});
|
|
214
|
+
startStreaming(state);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const onDisconnected = function (state) {
|
|
218
|
+
state.clear();
|
|
219
|
+
$("#" + state.buttonId()).text(state.buttonText()).off('click').click(function () {
|
|
220
|
+
onStartClick(state);
|
|
221
|
+
}).prop('disabled', false);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const onStartClick = function (state) {
|
|
225
|
+
if (validateForm("connectionForm", state.errInfoId())
|
|
226
|
+
&& validateForm(state.formId(), state.errInfoId())) {
|
|
227
|
+
state.setStarting(true);
|
|
228
|
+
if (!state.is(PUBLISH) && Browser().isSafariWebRTC()) {
|
|
229
|
+
playFirstSound(document.getElementById("main"), PRELOADER_URL).then(function () {
|
|
230
|
+
connect(state);
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
connect(state);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const onOperationFailed = function (state, event) {
|
|
239
|
+
if (event.operation && event.error) {
|
|
240
|
+
setStatus(state.errInfoId(), event.operation + " failed: " + event.error, "red");
|
|
241
|
+
} else {
|
|
242
|
+
setStatus(state.errInfoId(), event, "red");
|
|
243
|
+
}
|
|
244
|
+
state.setRoomEnded();
|
|
245
|
+
onStopClick(state);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const onStopClick = async function (state) {
|
|
249
|
+
state.setStarting(false);
|
|
250
|
+
disposeStateDisplay(state);
|
|
251
|
+
if (state.isConnected()) {
|
|
252
|
+
$("#" + state.buttonId()).prop('disabled', true);
|
|
253
|
+
await state.session.disconnect();
|
|
254
|
+
onDisconnected(state);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const startStreaming = async function (state) {
|
|
259
|
+
if (state.is(PUBLISH)) {
|
|
260
|
+
await publishStreams(state);
|
|
261
|
+
} else {
|
|
262
|
+
await playStreams(state);
|
|
263
|
+
}
|
|
264
|
+
state.setStarting(false);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const publishStreams = async function (state) {
|
|
268
|
+
if (state.isConnected()) {
|
|
269
|
+
//create local display item to show local streams
|
|
270
|
+
const localDisplay = initLocalDisplay(document.getElementById("localVideo"));
|
|
271
|
+
state.setDisplay(localDisplay);
|
|
272
|
+
try {
|
|
273
|
+
//get configured local video streams
|
|
274
|
+
let streams = await getVideoStreams(mainConfig);
|
|
275
|
+
let audioStreams = await getAudioStreams(mainConfig);
|
|
276
|
+
if (state.isConnected() && state.isActive()) {
|
|
277
|
+
//combine local video streams with audio streams
|
|
278
|
+
streams.push.apply(streams, audioStreams);
|
|
279
|
+
let config = {};
|
|
280
|
+
//add our local streams to the room (to PeerConnection)
|
|
281
|
+
streams.forEach(function (s) {
|
|
282
|
+
let contentType = s.type || s.source;
|
|
283
|
+
//add local stream to local display
|
|
284
|
+
localDisplay.add(s.stream.id, $("#" + state.inputId()).val(), s.stream, contentType);
|
|
285
|
+
//add each track to PeerConnection
|
|
286
|
+
s.stream.getTracks().forEach((track) => {
|
|
287
|
+
config[track.id] = contentType;
|
|
288
|
+
addTrackToPeerConnection(state.pc, s.stream, track, s.encodings);
|
|
289
|
+
subscribeTrackToEndedEvent(state.room, track, state.pc);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
//start WebRTC negotiation
|
|
293
|
+
await state.room.join(state.pc, null, config);
|
|
294
|
+
}
|
|
295
|
+
} catch (e) {
|
|
296
|
+
if (e.type === constants.SFU_ROOM_EVENT.OPERATION_FAILED) {
|
|
297
|
+
onOperationFailed(state, e);
|
|
298
|
+
} else {
|
|
299
|
+
console.error("Failed to capture streams: " + e);
|
|
300
|
+
setStatus(state.errInfoId(), e.name, "red");
|
|
301
|
+
onStopClick(state);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const playStreams = async function (state) {
|
|
308
|
+
if (state.isConnected() && state.isActive()) {
|
|
309
|
+
try {
|
|
310
|
+
if (state.is(TEST1)) {
|
|
311
|
+
const display = initRemoteDisplay(state.room, document.getElementById("remoteVideo"), null, null,
|
|
312
|
+
createDefaultMeetingController,
|
|
313
|
+
createDelayedMeetingModel,
|
|
314
|
+
createDefaultMeetingView,
|
|
315
|
+
oneToOneParticipantFactory(remoteTrackProvider(state.room)));
|
|
316
|
+
state.setDisplay(display);
|
|
317
|
+
} else if (state.is(TEST2)) {
|
|
318
|
+
const display = initRemoteDisplay(state.room, document.getElementById("remoteVideo"), null, null,
|
|
319
|
+
createDefaultMeetingController,
|
|
320
|
+
createDefaultMeetingModel,
|
|
321
|
+
createDefaultMeetingView,
|
|
322
|
+
createParticipantFactory(remoteTrackProvider(state.room), createAutoMuteParticipantView, createOneToManyParticipantModel));
|
|
323
|
+
state.setDisplay(display);
|
|
324
|
+
} else if (state.is(TEST3)) {
|
|
325
|
+
const display = initRemoteDisplay(state.room, document.getElementById("remoteVideo"), null, null,
|
|
326
|
+
createDefaultMeetingController,
|
|
327
|
+
createTest3MeetingModel,
|
|
328
|
+
createDefaultMeetingView,
|
|
329
|
+
oneToOneParticipantFactory(remoteTrackProvider(state.room)));
|
|
330
|
+
state.setDisplay(display);
|
|
331
|
+
}
|
|
332
|
+
//start WebRTC negotiation
|
|
333
|
+
await state.room.join(state.pc, null, null, 10);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
if (e.type === constants.SFU_ROOM_EVENT.OPERATION_FAILED) {
|
|
336
|
+
onOperationFailed(state, e);
|
|
337
|
+
} else {
|
|
338
|
+
console.error("Failed to play streams: " + e);
|
|
339
|
+
setStatus(state.errInfoId(), e.name, "red");
|
|
340
|
+
onStopClick(state);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
const disposeStateDisplay = function (state) {
|
|
348
|
+
state.disposeDisplay();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const subscribeTrackToEndedEvent = function (room, track, pc) {
|
|
352
|
+
track.addEventListener("ended", async function () {
|
|
353
|
+
//track ended, see if we need to cleanup
|
|
354
|
+
let negotiate = false;
|
|
355
|
+
for (const sender of pc.getSenders()) {
|
|
356
|
+
if (sender.track === track) {
|
|
357
|
+
pc.removeTrack(sender);
|
|
358
|
+
//track found, set renegotiation flag
|
|
359
|
+
negotiate = true;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (negotiate) {
|
|
364
|
+
//kickoff renegotiation
|
|
365
|
+
await room.updateState();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const addTrackToPeerConnection = function (pc, stream, track, encodings) {
|
|
371
|
+
pc.addTransceiver(track, {
|
|
372
|
+
direction: "sendonly",
|
|
373
|
+
streams: [stream],
|
|
374
|
+
sendEncodings: encodings ? encodings : [] //passing encoding types for video simulcast tracks
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const setStatus = function (status, text, color) {
|
|
379
|
+
const field = document.getElementById(status);
|
|
380
|
+
if (color) {
|
|
381
|
+
field.style.color = color;
|
|
382
|
+
}
|
|
383
|
+
field.innerText = text;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const validateForm = function (formId, errorInfoId) {
|
|
387
|
+
let valid = true;
|
|
388
|
+
// Validate empty fields
|
|
389
|
+
$('#' + formId + ' :text').each(function () {
|
|
390
|
+
if (!$(this).val()) {
|
|
391
|
+
highlightInput($(this));
|
|
392
|
+
valid = false;
|
|
393
|
+
setStatus(errorInfoId, "Fields cannot be empty", "red");
|
|
394
|
+
} else {
|
|
395
|
+
removeHighlight($(this));
|
|
396
|
+
setStatus(errorInfoId, "");
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return valid;
|
|
400
|
+
|
|
401
|
+
function highlightInput(input) {
|
|
402
|
+
input.closest('.input-group').addClass("has-error");
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function removeHighlight(input) {
|
|
406
|
+
input.closest('.input-group').removeClass("has-error");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function timeout(ms) {
|
|
411
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const createDelayedMeetingModel = function (meetingView, participantFactory, displayOptions, abrFactory) {
|
|
415
|
+
return {
|
|
416
|
+
participants: new Map(),
|
|
417
|
+
meetingName: null,
|
|
418
|
+
ended: false,
|
|
419
|
+
addParticipant: function (userId, participantName) {
|
|
420
|
+
if (this.participants.get(userId)) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const [participantModel, participantView, participant] = participantFactory.createParticipant(userId, participantName, displayOptions, abrFactory);
|
|
424
|
+
this.participants.set(userId, participant);
|
|
425
|
+
meetingView.addParticipant(userId, participantName, participantView.rootDiv);
|
|
426
|
+
},
|
|
427
|
+
removeParticipant: function (userId) {
|
|
428
|
+
const participant = this.participants.get(userId);
|
|
429
|
+
if (participant) {
|
|
430
|
+
this.participants.delete(userId);
|
|
431
|
+
meetingView.removeParticipant(userId);
|
|
432
|
+
participant.dispose();
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
addTracks: async function (userId, tracks) {
|
|
436
|
+
const participant = this.participants.get(userId);
|
|
437
|
+
if (!participant) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
for (let i = 0; i < 10 && i < tracks.length; i++) {
|
|
442
|
+
if (this.ended) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (tracks[i].type === "VIDEO") {
|
|
446
|
+
participant.addVideoTrack(tracks[i]);
|
|
447
|
+
await timeout(1000);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
removeTracks: function (userId, tracks) {
|
|
452
|
+
const participant = this.participants.get(userId);
|
|
453
|
+
if (!participant) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
for (const track of tracks) {
|
|
457
|
+
if (track.type === "VIDEO") {
|
|
458
|
+
participant.removeVideoTrack(track);
|
|
459
|
+
} else if (track.type === "AUDIO") {
|
|
460
|
+
participant.removeAudioTrack(track);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
updateQualityInfo: function (userId, tracksInfo) {
|
|
465
|
+
const participant = this.participants.get(userId);
|
|
466
|
+
if (!participant) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
participant.updateQualityInfo(tracksInfo);
|
|
470
|
+
},
|
|
471
|
+
end: function () {
|
|
472
|
+
console.log("Meeting " + this.meetingName + " ended")
|
|
473
|
+
meetingView.end();
|
|
474
|
+
this.participants.forEach((participant, id) => {
|
|
475
|
+
participant.dispose();
|
|
476
|
+
});
|
|
477
|
+
this.participants.clear();
|
|
478
|
+
},
|
|
479
|
+
setMeetingName: function (id) {
|
|
480
|
+
this.meetingName = id;
|
|
481
|
+
meetingView.setMeetingName(id);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const createAutoMuteParticipantView = function () {
|
|
487
|
+
|
|
488
|
+
const participantDiv = createContainer(null);
|
|
489
|
+
|
|
490
|
+
const participantNicknameDisplay = createInfoDisplay(participantDiv, "Name: ");
|
|
491
|
+
const muteStatus = createInfoDisplay(participantDiv, "unMuted");
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
const player = createVideoPlayer(participantDiv);
|
|
495
|
+
const timer = setInterval(() => {
|
|
496
|
+
if (player.muteButton) {
|
|
497
|
+
player.muteButton.click();
|
|
498
|
+
}
|
|
499
|
+
}, 10000);
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
rootDiv: participantDiv,
|
|
503
|
+
dispose: function () {
|
|
504
|
+
player.dispose();
|
|
505
|
+
if (timer) {
|
|
506
|
+
clearInterval(timer);
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
addVideoTrack: function (track, requestVideoTrack) {
|
|
510
|
+
|
|
511
|
+
},
|
|
512
|
+
removeVideoTrack: function (track) {
|
|
513
|
+
|
|
514
|
+
},
|
|
515
|
+
addVideoSource: function (remoteVideoTrack, track, onResize, muteHandler) {
|
|
516
|
+
player.setVideoSource(remoteVideoTrack, onResize, async (mute) => {
|
|
517
|
+
const startDate = Date.now();
|
|
518
|
+
if (mute) {
|
|
519
|
+
muteStatus.innerText = "muting";
|
|
520
|
+
return muteHandler(mute).then(() => {
|
|
521
|
+
const delay = Date.now() - startDate;
|
|
522
|
+
muteStatus.innerText = "muted";
|
|
523
|
+
muteStatus.innerHTML = muteStatus.innerHTML + " in " + delay + "ms";
|
|
524
|
+
|
|
525
|
+
});
|
|
526
|
+
} else {
|
|
527
|
+
muteStatus.innerText = "unMuting";
|
|
528
|
+
return muteHandler(mute).then(() => {
|
|
529
|
+
const delay = Date.now() - startDate;
|
|
530
|
+
muteStatus.innerText = "unMuted";
|
|
531
|
+
muteStatus.innerHTML = muteStatus.innerHTML + " in " + delay + "ms";
|
|
532
|
+
|
|
533
|
+
});
|
|
534
|
+
}});
|
|
535
|
+
if (player.muteButton) {
|
|
536
|
+
hideItem(player.muteButton);
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
removeVideoSource: function (track) {
|
|
540
|
+
player.removeVideoSource(track);
|
|
541
|
+
},
|
|
542
|
+
showVideoTrack: function (track) {
|
|
543
|
+
player.showVideoTrack(track);
|
|
544
|
+
},
|
|
545
|
+
addAudioTrack: function (track, audioTrack, show) {
|
|
546
|
+
|
|
547
|
+
},
|
|
548
|
+
removeAudioTrack: function (track) {
|
|
549
|
+
|
|
550
|
+
},
|
|
551
|
+
setNickname: function (nickname) {
|
|
552
|
+
participantNicknameDisplay.innerText = "Name: " + nickname;
|
|
553
|
+
},
|
|
554
|
+
updateQuality: function (track, qualityName, available) {
|
|
555
|
+
player.updateQuality(qualityName, available);
|
|
556
|
+
},
|
|
557
|
+
addQuality: function (track, qualityName, available, onQualityPick) {
|
|
558
|
+
player.addQuality(qualityName, available, onQualityPick);
|
|
559
|
+
},
|
|
560
|
+
clearQualityState: function (track) {
|
|
561
|
+
player.clearQualityState();
|
|
562
|
+
},
|
|
563
|
+
pickQuality: function (track, qualityName) {
|
|
564
|
+
player.pickQuality(qualityName);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const createTest3MeetingModel = function (meetingView, participantFactory, displayOptions, abrFactory) {
|
|
570
|
+
return {
|
|
571
|
+
participants: new Map(),
|
|
572
|
+
meetingName: null,
|
|
573
|
+
ended: false,
|
|
574
|
+
addParticipant: function (userId, participantName) {
|
|
575
|
+
if (this.participants.get(userId)) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const [participantModel, participantView, participant] = participantFactory.createParticipant(userId, participantName, displayOptions, abrFactory);
|
|
579
|
+
this.participants.set(userId, participant);
|
|
580
|
+
meetingView.addParticipant(userId, participantName, participantView.rootDiv);
|
|
581
|
+
},
|
|
582
|
+
removeParticipant: function (userId) {
|
|
583
|
+
const participant = this.participants.get(userId);
|
|
584
|
+
if (participant) {
|
|
585
|
+
this.participants.delete(userId);
|
|
586
|
+
meetingView.removeParticipant(userId);
|
|
587
|
+
participant.dispose();
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
addTracks: async function (userId, tracks) {
|
|
591
|
+
const participant = this.participants.get(userId);
|
|
592
|
+
if (!participant) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const videoTracks = tracks.filter((t) => t.type === "VIDEO");
|
|
596
|
+
for (let i = 0; i < videoTracks.length;i+=5) {
|
|
597
|
+
console.log("add 5 tracks");
|
|
598
|
+
for(let j = i; j< i+5; j++) {
|
|
599
|
+
participant.addVideoTrack(videoTracks[j]);
|
|
600
|
+
}
|
|
601
|
+
await timeout(5000);
|
|
602
|
+
if (this.ended) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const tracksToRemove = []
|
|
606
|
+
console.log("remove 5 tracks");
|
|
607
|
+
for(let j = i; j< i+5; j++) {
|
|
608
|
+
tracksToRemove.push(videoTracks[j]);
|
|
609
|
+
}
|
|
610
|
+
this.removeTracks(userId, tracksToRemove);
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
removeTracks: function (userId, tracks) {
|
|
614
|
+
const participant = this.participants.get(userId);
|
|
615
|
+
if (!participant) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
for (const track of tracks) {
|
|
619
|
+
if (track.type === "VIDEO") {
|
|
620
|
+
participant.removeVideoTrack(track);
|
|
621
|
+
} else if (track.type === "AUDIO") {
|
|
622
|
+
participant.removeAudioTrack(track);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
updateQualityInfo: function (userId, tracksInfo) {
|
|
627
|
+
const participant = this.participants.get(userId);
|
|
628
|
+
if (!participant) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
participant.updateQualityInfo(tracksInfo);
|
|
632
|
+
},
|
|
633
|
+
end: function () {
|
|
634
|
+
console.log("Meeting " + this.meetingName + " ended")
|
|
635
|
+
meetingView.end();
|
|
636
|
+
this.participants.forEach((participant, id) => {
|
|
637
|
+
participant.dispose();
|
|
638
|
+
});
|
|
639
|
+
this.participants.clear();
|
|
640
|
+
},
|
|
641
|
+
setMeetingName: function (id) {
|
|
642
|
+
this.meetingName = id;
|
|
643
|
+
meetingView.setMeetingName(id);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"frameRate": 25
|
|
27
27
|
},
|
|
28
28
|
"encodings": [
|
|
29
|
-
{ "rid": "
|
|
29
|
+
{ "rid": "180p", "active": true, "maxBitrate": 200000, "scaleResolutionDownBy": 4 },
|
|
30
30
|
{ "rid": "360p", "active": true, "maxBitrate": 500000, "scaleResolutionDownBy": 2 },
|
|
31
|
-
{ "rid": "
|
|
31
|
+
{ "rid": "720p", "active": true, "maxBitrate": 900000 }
|
|
32
32
|
],
|
|
33
33
|
"type": "cam1"
|
|
34
|
-
}
|
|
34
|
+
}
|
|
35
35
|
]
|
|
36
36
|
}
|
|
37
37
|
}
|