@flashphoner/sfusdk-examples 2.0.264 → 2.0.269
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/config.json +6 -3
- package/src/client/controls.js +48 -42
- package/src/client/main.html +7 -0
- package/src/client/main.js +79 -1
- package/src/commons/js/display.js +354 -50
- package/src/controller/parser.js +5 -3
package/package.json
CHANGED
package/src/client/config.json
CHANGED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
"source": "camera",
|
|
16
16
|
"width": 1280,
|
|
17
17
|
"height": 720,
|
|
18
|
-
"codec": "
|
|
18
|
+
"codec": "vp9",
|
|
19
19
|
"encodings": [
|
|
20
|
-
{
|
|
21
|
-
|
|
20
|
+
{
|
|
21
|
+
"rid": "nonsense",
|
|
22
|
+
"active": true,
|
|
23
|
+
"scalabilityMode": "L1T3"
|
|
24
|
+
}
|
|
22
25
|
]
|
|
23
26
|
}]
|
|
24
27
|
}
|
package/src/client/controls.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
const createControls = function(config) {
|
|
1
|
+
const createControls = function (config) {
|
|
2
2
|
|
|
3
|
-
let trackCallback = function(){
|
|
3
|
+
let trackCallback = function () {
|
|
4
|
+
};
|
|
4
5
|
|
|
5
6
|
const controls = {
|
|
6
7
|
entrance: {
|
|
@@ -24,16 +25,17 @@ const createControls = function(config) {
|
|
|
24
25
|
rid: document.getElementById("addVideoTrackEncodingRid"),
|
|
25
26
|
active: document.getElementById("addVideoTrackEncodingActive"),
|
|
26
27
|
maxBitrate: document.getElementById("addVideoTrackEncodingMaxBitrate"),
|
|
27
|
-
resolutionScale: document.getElementById("addVideoTrackEncodingResolutionScale")
|
|
28
|
+
resolutionScale: document.getElementById("addVideoTrackEncodingResolutionScale"),
|
|
29
|
+
scalabilityMode: document.getElementById("addVideoTrackScalabilityMode")
|
|
28
30
|
},
|
|
29
31
|
tables: {
|
|
30
32
|
video: $('#videoTracksTable').DataTable({
|
|
31
33
|
"sDom": 't',
|
|
32
34
|
"columns": [
|
|
33
35
|
{
|
|
34
|
-
"className":
|
|
35
|
-
"orderable":
|
|
36
|
-
"data":
|
|
36
|
+
"className": 'details-control',
|
|
37
|
+
"orderable": false,
|
|
38
|
+
"data": null,
|
|
37
39
|
"defaultContent": ''
|
|
38
40
|
},
|
|
39
41
|
{"data": "source"},
|
|
@@ -58,6 +60,7 @@ const createControls = function(config) {
|
|
|
58
60
|
{"data": "active"},
|
|
59
61
|
{"data": "maxBitrate"},
|
|
60
62
|
{"data": "resolutionScale"},
|
|
63
|
+
{"data": "scalabilityMode"},
|
|
61
64
|
{"data": "action"}
|
|
62
65
|
]
|
|
63
66
|
})
|
|
@@ -70,7 +73,7 @@ const createControls = function(config) {
|
|
|
70
73
|
controls.entrance.roomPin.value = config.room.pin;
|
|
71
74
|
controls.entrance.nickName.value = config.room.nickName;
|
|
72
75
|
|
|
73
|
-
const addAudioTrackRow = async function(track) {
|
|
76
|
+
const addAudioTrackRow = async function (track) {
|
|
74
77
|
const stream = await getMedia([track]);
|
|
75
78
|
let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
|
|
76
79
|
const row = controls.tables.audio.row.add({
|
|
@@ -81,14 +84,14 @@ const createControls = function(config) {
|
|
|
81
84
|
}).node();
|
|
82
85
|
controls.tables.audio.draw();
|
|
83
86
|
|
|
84
|
-
$('#' + stream.id + "-button").on('click', function(){
|
|
87
|
+
$('#' + stream.id + "-button").on('click', function () {
|
|
85
88
|
//terminate stream
|
|
86
89
|
console.log("terminate audio stream " + stream.id);
|
|
87
90
|
let track = stream.getAudioTracks()[0];
|
|
88
91
|
track.stop();
|
|
89
92
|
track.dispatchEvent(new Event("ended"));
|
|
90
93
|
}).prop('disabled', true);
|
|
91
|
-
stream.getTracks()[0].onended = function() {
|
|
94
|
+
stream.getTracks()[0].onended = function () {
|
|
92
95
|
controls.tables.audio.row(row).remove().draw();
|
|
93
96
|
}
|
|
94
97
|
trackCallback({
|
|
@@ -99,7 +102,7 @@ const createControls = function(config) {
|
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
const addVideoTrackRow = async function(track) {
|
|
105
|
+
const addVideoTrackRow = async function (track) {
|
|
103
106
|
const stream = await getMedia([track]);
|
|
104
107
|
let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
|
|
105
108
|
const row = controls.tables.video.row.add({
|
|
@@ -113,14 +116,14 @@ const createControls = function(config) {
|
|
|
113
116
|
}).node();
|
|
114
117
|
controls.tables.video.draw();
|
|
115
118
|
|
|
116
|
-
$('#' + stream.id + "-button").on('click', function(){
|
|
119
|
+
$('#' + stream.id + "-button").on('click', function () {
|
|
117
120
|
//terminate stream
|
|
118
121
|
console.log("terminate video stream " + stream.id);
|
|
119
122
|
let track = stream.getVideoTracks()[0];
|
|
120
123
|
track.stop();
|
|
121
124
|
track.dispatchEvent(new Event("ended"));
|
|
122
125
|
}).prop('disabled', true);
|
|
123
|
-
stream.getTracks()[0].addEventListener("ended", function() {
|
|
126
|
+
stream.getTracks()[0].addEventListener("ended", function () {
|
|
124
127
|
controls.tables.video.row(row).remove().draw();
|
|
125
128
|
});
|
|
126
129
|
trackCallback({
|
|
@@ -130,40 +133,40 @@ const createControls = function(config) {
|
|
|
130
133
|
});
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
const format = function(d) {
|
|
136
|
+
const format = function (d) {
|
|
134
137
|
if (!d.encodings) {
|
|
135
138
|
return;
|
|
136
139
|
}
|
|
137
|
-
let details =
|
|
138
|
-
d.encodings.forEach(function(encoding){
|
|
140
|
+
let details = '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">';
|
|
141
|
+
d.encodings.forEach(function (encoding) {
|
|
139
142
|
details += '<tr>';
|
|
140
143
|
for (const [key, value] of Object.entries(encoding)) {
|
|
141
|
-
details += '<td>'+ key + '</td>'+
|
|
142
|
-
'<td>'+ value + '</td>';
|
|
144
|
+
details += '<td>' + key + '</td>' +
|
|
145
|
+
'<td>' + value + '</td>';
|
|
143
146
|
}
|
|
144
147
|
details += '</tr>';
|
|
145
148
|
});
|
|
146
|
-
details +='</table>';
|
|
149
|
+
details += '</table>';
|
|
147
150
|
return details;
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
const muteForm = function(form) {
|
|
153
|
+
const muteForm = function (form) {
|
|
151
154
|
for (const [key, value] of Object.entries(form)) {
|
|
152
155
|
value.disabled = true;
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
const unmuteForm = function(form) {
|
|
159
|
+
const unmuteForm = function (form) {
|
|
157
160
|
for (const [key, value] of Object.entries(form)) {
|
|
158
161
|
value.disabled = false;
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
const muteInput = function() {
|
|
165
|
+
const muteInput = function () {
|
|
163
166
|
muteForm(controls.entrance);
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
const roomConfig = function() {
|
|
169
|
+
const roomConfig = function () {
|
|
167
170
|
let roomConfig = {
|
|
168
171
|
url: controls.entrance.url.value,
|
|
169
172
|
roomName: controls.entrance.roomName.value,
|
|
@@ -179,9 +182,9 @@ const createControls = function(config) {
|
|
|
179
182
|
return roomConfig;
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
const getVideoStreams = function() {
|
|
185
|
+
const getVideoStreams = function () {
|
|
183
186
|
let streams = [];
|
|
184
|
-
controls.tables.video.rows().every(function(rowIdx, tableLoop, rowLoop) {
|
|
187
|
+
controls.tables.video.rows().every(function (rowIdx, tableLoop, rowLoop) {
|
|
185
188
|
let data = this.data();
|
|
186
189
|
streams.push({
|
|
187
190
|
stream: data.stream,
|
|
@@ -192,9 +195,9 @@ const createControls = function(config) {
|
|
|
192
195
|
});
|
|
193
196
|
return streams;
|
|
194
197
|
}
|
|
195
|
-
const getAudioStreams = function() {
|
|
198
|
+
const getAudioStreams = function () {
|
|
196
199
|
let streams = [];
|
|
197
|
-
controls.tables.audio.rows().every(function(rowIdx, tableLoop, rowLoop) {
|
|
200
|
+
controls.tables.audio.rows().every(function (rowIdx, tableLoop, rowLoop) {
|
|
198
201
|
let data = this.data();
|
|
199
202
|
streams.push({
|
|
200
203
|
stream: data.stream,
|
|
@@ -205,11 +208,11 @@ const createControls = function(config) {
|
|
|
205
208
|
return streams;
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
const onTrack = function(callback) {
|
|
211
|
+
const onTrack = function (callback) {
|
|
209
212
|
trackCallback = callback;
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
const displayTables = async function() {
|
|
215
|
+
const displayTables = async function () {
|
|
213
216
|
// Add event listener for opening and closing details
|
|
214
217
|
$('#videoTracksTableBody').on('click', 'td.details-control', function () {
|
|
215
218
|
let tr = $(this).closest('tr');
|
|
@@ -234,15 +237,16 @@ const createControls = function(config) {
|
|
|
234
237
|
}
|
|
235
238
|
|
|
236
239
|
// Click event listener to add a new video track
|
|
237
|
-
document.getElementById("addVideoTrack").addEventListener("click", function(e){
|
|
240
|
+
document.getElementById("addVideoTrack").addEventListener("click", function (e) {
|
|
238
241
|
let encodings = [];
|
|
239
|
-
controls.tables.encodings.rows().every(function() {
|
|
242
|
+
controls.tables.encodings.rows().every(function () {
|
|
240
243
|
let encoding = this.data();
|
|
241
244
|
encodings.push({
|
|
242
245
|
rid: encoding.rid,
|
|
243
246
|
active: encoding.active,
|
|
244
247
|
maxBitrate: encoding.maxBitrate,
|
|
245
|
-
scaleResolutionDownBy: encoding.resolutionScale
|
|
248
|
+
scaleResolutionDownBy: encoding.resolutionScale,
|
|
249
|
+
scalabilityMode: encoding.scalabilityMode
|
|
246
250
|
})
|
|
247
251
|
});
|
|
248
252
|
let track = {
|
|
@@ -254,26 +258,27 @@ const createControls = function(config) {
|
|
|
254
258
|
}
|
|
255
259
|
addVideoTrackRow(track);
|
|
256
260
|
});
|
|
257
|
-
|
|
261
|
+
|
|
258
262
|
// Click event listener to remove video quality
|
|
259
|
-
$("#videoTrackEncodingsTable").on("click", ".remove", function(){
|
|
263
|
+
$("#videoTrackEncodingsTable").on("click", ".remove", function () {
|
|
260
264
|
controls.tables.encodings.row($(this).parents('tr')).remove().draw();
|
|
261
265
|
});
|
|
262
|
-
|
|
266
|
+
|
|
263
267
|
// Click event listener to add video quality
|
|
264
|
-
document.getElementById("addVideoTrackEncoding").addEventListener("click", function(){
|
|
268
|
+
document.getElementById("addVideoTrackEncoding").addEventListener("click", function () {
|
|
265
269
|
let button = '<button class="btn btn-primary remove">Delete</button>';
|
|
266
270
|
controls.tables.encodings.row.add({
|
|
267
271
|
rid: controls.addVideoEncoding.rid.value,
|
|
268
272
|
active: controls.addVideoEncoding.active.value,
|
|
269
273
|
maxBitrate: controls.addVideoEncoding.maxBitrate.value,
|
|
270
274
|
resolutionScale: controls.addVideoEncoding.resolutionScale.value,
|
|
275
|
+
scalabilityMode: controls.addVideoEncoding.scalabilityMode.value,
|
|
271
276
|
action: button
|
|
272
277
|
}).draw();
|
|
273
278
|
});
|
|
274
279
|
|
|
275
280
|
// Click event listener to add a new audio track
|
|
276
|
-
document.getElementById("addAudioTrack").addEventListener("click", function(e){
|
|
281
|
+
document.getElementById("addAudioTrack").addEventListener("click", function (e) {
|
|
277
282
|
let encodings = [];
|
|
278
283
|
let track = {
|
|
279
284
|
source: controls.addAudioTrack.source.value,
|
|
@@ -282,10 +287,10 @@ const createControls = function(config) {
|
|
|
282
287
|
}
|
|
283
288
|
addAudioTrackRow(track);
|
|
284
289
|
});
|
|
285
|
-
|
|
290
|
+
|
|
286
291
|
}
|
|
287
292
|
|
|
288
|
-
const cleanTables = function() {
|
|
293
|
+
const cleanTables = function () {
|
|
289
294
|
controls.tables.video.rows().remove().draw();
|
|
290
295
|
controls.tables.audio.rows().remove().draw();
|
|
291
296
|
controls.tables.encodings.rows().remove().draw();
|
|
@@ -298,15 +303,16 @@ const createControls = function(config) {
|
|
|
298
303
|
getAudioStreams: getAudioStreams,
|
|
299
304
|
getVideoStreams: getVideoStreams,
|
|
300
305
|
onTrack: onTrack,
|
|
301
|
-
cleanTables: cleanTables
|
|
306
|
+
cleanTables: cleanTables,
|
|
307
|
+
controls: controls
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
305
|
-
const getMedia = async function(tracks) {
|
|
311
|
+
const getMedia = async function (tracks) {
|
|
306
312
|
//convert to constraints
|
|
307
313
|
let screen = false;
|
|
308
|
-
const constraints= {};
|
|
309
|
-
tracks.forEach(function(track){
|
|
314
|
+
const constraints = {};
|
|
315
|
+
tracks.forEach(function (track) {
|
|
310
316
|
if (track.source === "mic") {
|
|
311
317
|
//audio
|
|
312
318
|
constraints.audio = {};
|
package/src/client/main.html
CHANGED
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
<select class="form-select-sm" id="addVideoTrackCodec">
|
|
72
72
|
<option value="H264" selected>H264</option>
|
|
73
73
|
<option value="VP8">VP8</option>
|
|
74
|
+
<option value="VP9">VP9</option>
|
|
74
75
|
</select>
|
|
75
76
|
</th>
|
|
76
77
|
<th>
|
|
@@ -90,6 +91,7 @@
|
|
|
90
91
|
<th>Active</th>
|
|
91
92
|
<th>MaxBitrate</th>
|
|
92
93
|
<th>ResolutionScale</th>
|
|
94
|
+
<th>Scalability Mode</th>
|
|
93
95
|
<th>Action</th>
|
|
94
96
|
</tr>
|
|
95
97
|
</thead>
|
|
@@ -119,6 +121,11 @@
|
|
|
119
121
|
<option value="6">6</option>
|
|
120
122
|
</select>
|
|
121
123
|
</th>
|
|
124
|
+
<th>
|
|
125
|
+
<select class="form-select-sm" id="addVideoTrackScalabilityMode">
|
|
126
|
+
<option selected value="">NONE</option>
|
|
127
|
+
</select>
|
|
128
|
+
</th>
|
|
122
129
|
<th>
|
|
123
130
|
<button class="btn btn-primary" id="addVideoTrackEncoding">Add</button>
|
|
124
131
|
</th>
|
package/src/client/main.js
CHANGED
|
@@ -36,6 +36,37 @@ const defaultConfig = {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const scalabilityModes = [
|
|
40
|
+
'L1T1',
|
|
41
|
+
'L1T2',
|
|
42
|
+
'L1T3',
|
|
43
|
+
'L2T1',
|
|
44
|
+
'L2T2',
|
|
45
|
+
'L2T3',
|
|
46
|
+
'L3T1',
|
|
47
|
+
'L3T2',
|
|
48
|
+
'L3T3',
|
|
49
|
+
'L2T1h',
|
|
50
|
+
'L2T2h',
|
|
51
|
+
'L2T3h',
|
|
52
|
+
'S2T1',
|
|
53
|
+
'S2T2',
|
|
54
|
+
'S2T3',
|
|
55
|
+
'S2T1h',
|
|
56
|
+
'S2T2h',
|
|
57
|
+
'S2T3h',
|
|
58
|
+
'S3T1',
|
|
59
|
+
'S3T2',
|
|
60
|
+
'S3T3',
|
|
61
|
+
'S3T1h',
|
|
62
|
+
'S3T2h',
|
|
63
|
+
'S3T3h',
|
|
64
|
+
'L2T2_KEY',
|
|
65
|
+
'L2T3_KEY',
|
|
66
|
+
'L3T2_KEY',
|
|
67
|
+
'L3T3_KEY'
|
|
68
|
+
];
|
|
69
|
+
|
|
39
70
|
/**
|
|
40
71
|
* Load track configuration and show entrance modal
|
|
41
72
|
*/
|
|
@@ -171,11 +202,51 @@ const publishPreconfiguredStreams = async function (room, pc, streams) {
|
|
|
171
202
|
localDisplay.add(s.stream.id, "local", s.stream, contentType);
|
|
172
203
|
});
|
|
173
204
|
//join room
|
|
174
|
-
await room.join(pc, null, config,
|
|
205
|
+
await room.join(pc, null, config, 1);
|
|
175
206
|
// Enable Delete button for each preconfigured stream #WCS-3689
|
|
176
207
|
streams.forEach(function (s) {
|
|
177
208
|
$('#' + s.stream.id + "-button").prop('disabled', false);
|
|
178
209
|
});
|
|
210
|
+
cControls.controls.addVideoTrack.codec.addEventListener('change', async (event) => {
|
|
211
|
+
const mimeType = "video/" + event.target.value;
|
|
212
|
+
while (cControls.controls.addVideoEncoding.scalabilityMode.firstChild) {
|
|
213
|
+
cControls.controls.addVideoEncoding.scalabilityMode.firstChild.remove();
|
|
214
|
+
}
|
|
215
|
+
const option = document.createElement('option');
|
|
216
|
+
option.value = '';
|
|
217
|
+
option.innerText = 'NONE';
|
|
218
|
+
cControls.controls.addVideoEncoding.scalabilityMode.appendChild(option);
|
|
219
|
+
|
|
220
|
+
const capabilityPromises = [];
|
|
221
|
+
for (const mode of scalabilityModes) {
|
|
222
|
+
capabilityPromises.push(navigator.mediaCapabilities.encodingInfo({
|
|
223
|
+
type: 'webrtc',
|
|
224
|
+
video: {
|
|
225
|
+
contentType: mimeType,
|
|
226
|
+
width: 640,
|
|
227
|
+
height: 480,
|
|
228
|
+
bitrate: 10000,
|
|
229
|
+
framerate: 29.97,
|
|
230
|
+
scalabilityMode: mode
|
|
231
|
+
}
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
const capabilityResults = await Promise.all(capabilityPromises);
|
|
235
|
+
for (let i = 0; i < scalabilityModes.length; ++i) {
|
|
236
|
+
if (capabilityResults[i].supported) {
|
|
237
|
+
const option = document.createElement('option');
|
|
238
|
+
option.value = scalabilityModes[i];
|
|
239
|
+
option.innerText = scalabilityModes[i];
|
|
240
|
+
cControls.controls.addVideoEncoding.scalabilityMode.appendChild(option);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (cControls.controls.addVideoEncoding.scalabilityMode.childElementCount > 1) {
|
|
245
|
+
cControls.controls.addVideoEncoding.scalabilityMode.disabled = false;
|
|
246
|
+
} else {
|
|
247
|
+
cControls.controls.addVideoEncoding.scalabilityMode.disabled = true;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
179
250
|
} catch (e) {
|
|
180
251
|
onOperationFailed("Failed to publish a preconfigured streams", e);
|
|
181
252
|
// Enable Delete button for each preconfigured stream #WCS-3689
|
|
@@ -259,6 +330,13 @@ const subscribeTrackToEndedEvent = function (room, track, pc) {
|
|
|
259
330
|
* @param {*} encodings
|
|
260
331
|
*/
|
|
261
332
|
const addTrackToPeerConnection = function (pc, stream, track, encodings) {
|
|
333
|
+
if (encodings) {
|
|
334
|
+
for (const encoding of encodings) {
|
|
335
|
+
if (encoding.scalabilityMode === "") {
|
|
336
|
+
delete encoding.scalabilityMode;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
262
340
|
pc.addTransceiver(track, {
|
|
263
341
|
direction: "sendonly",
|
|
264
342
|
streams: [stream],
|
|
@@ -534,9 +534,9 @@ const createOneToManyParticipantView = function () {
|
|
|
534
534
|
removeVideoTrack: function (track) {
|
|
535
535
|
player.removeVideoTrack(track);
|
|
536
536
|
},
|
|
537
|
-
addVideoSource: function (remoteVideoTrack, track, onResize, muteHandler) {
|
|
537
|
+
addVideoSource: function (remoteVideoTrack, track, onResize, muteHandler, onSidClick, onTidClick) {
|
|
538
538
|
this.currentTrack = track;
|
|
539
|
-
player.setVideoSource(remoteVideoTrack, onResize, muteHandler);
|
|
539
|
+
player.setVideoSource(remoteVideoTrack, onResize, muteHandler, onSidClick, onTidClick);
|
|
540
540
|
},
|
|
541
541
|
removeVideoSource: function (track) {
|
|
542
542
|
if (this.currentTrack && this.currentTrack.mid === track.mid) {
|
|
@@ -561,11 +561,11 @@ const createOneToManyParticipantView = function () {
|
|
|
561
561
|
const additionalUserId = userId ? "#" + getShortUserId(userId) : "";
|
|
562
562
|
participantNicknameDisplay.innerText = "Name: " + nickname + additionalUserId;
|
|
563
563
|
},
|
|
564
|
-
updateQuality: function (track,
|
|
565
|
-
player.updateQuality(
|
|
564
|
+
updateQuality: function (track, quality) {
|
|
565
|
+
player.updateQuality(quality);
|
|
566
566
|
},
|
|
567
|
-
addQuality: function (track,
|
|
568
|
-
player.addQuality(
|
|
567
|
+
addQuality: function (track, quality, onQualityPick) {
|
|
568
|
+
player.addQuality(quality, onQualityPick);
|
|
569
569
|
},
|
|
570
570
|
clearQualityState: function (track) {
|
|
571
571
|
player.clearQualityState();
|
|
@@ -593,25 +593,54 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
593
593
|
const trackDisplay = createContainer(streamDisplay);
|
|
594
594
|
|
|
595
595
|
let videoElement;
|
|
596
|
-
|
|
597
|
-
|
|
596
|
+
// traciId/
|
|
597
|
+
// {btn: button,
|
|
598
|
+
// qualities:Map{quality
|
|
599
|
+
// {name:string,
|
|
600
|
+
// available:boolean,
|
|
601
|
+
// btn: button,
|
|
602
|
+
// spatialLayersInfo:Map{
|
|
603
|
+
// available:boolean,
|
|
604
|
+
// resolution{width:number,height:number},
|
|
605
|
+
// sid:number,
|
|
606
|
+
// btn: button},
|
|
607
|
+
// temporalLayersInfo:Map{
|
|
608
|
+
// available:boolean,
|
|
609
|
+
// tid:number,
|
|
610
|
+
// btn: button}
|
|
611
|
+
// }
|
|
612
|
+
// }
|
|
613
|
+
|
|
614
|
+
const tracksInfo = new Map();
|
|
598
615
|
const qualityButtons = new Map();
|
|
599
616
|
|
|
600
617
|
const lock = function () {
|
|
601
|
-
for (const btn of
|
|
618
|
+
for (const btn of tracksInfo.values()) {
|
|
602
619
|
btn.disabled = true;
|
|
603
620
|
}
|
|
604
621
|
for (const state of qualityButtons.values()) {
|
|
605
622
|
state.btn.disabled = true;
|
|
623
|
+
for (const [sid, spatialLayerButton] of state.layerButtons.spatialLayerButtons) {
|
|
624
|
+
spatialLayerButton.btn.disabled = true;
|
|
625
|
+
}
|
|
626
|
+
for (const [sid, temporalLayerButton] of state.layerButtons.temporalLayerButtons) {
|
|
627
|
+
temporalLayerButton.btn.disabled = true;
|
|
628
|
+
}
|
|
606
629
|
}
|
|
607
630
|
}
|
|
608
631
|
|
|
609
632
|
const unlock = function () {
|
|
610
|
-
for (const btn of
|
|
633
|
+
for (const btn of tracksInfo.values()) {
|
|
611
634
|
btn.disabled = false;
|
|
612
635
|
}
|
|
613
636
|
for (const state of qualityButtons.values()) {
|
|
614
637
|
state.btn.disabled = false;
|
|
638
|
+
for (const [sid, spatialLayerButton] of state.layerButtons.spatialLayerButtons) {
|
|
639
|
+
spatialLayerButton.btn.disabled = false;
|
|
640
|
+
}
|
|
641
|
+
for (const [sid, temporalLayerButton] of state.layerButtons.temporalLayerButtons) {
|
|
642
|
+
temporalLayerButton.btn.disabled = false;
|
|
643
|
+
}
|
|
615
644
|
}
|
|
616
645
|
}
|
|
617
646
|
|
|
@@ -648,9 +677,24 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
648
677
|
|
|
649
678
|
const repickQuality = function (qualityName) {
|
|
650
679
|
for (const [quality, state] of qualityButtons.entries()) {
|
|
680
|
+
state.layerButtons.temporalLayerButtons.forEach((lState, __) => {
|
|
681
|
+
if(lState.btn.style.color === QUALITY_COLORS.SELECTED) {
|
|
682
|
+
lState.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
state.layerButtons.spatialLayerButtons.forEach((lState, __) => {
|
|
686
|
+
if(lState.btn.style.color === QUALITY_COLORS.SELECTED) {
|
|
687
|
+
lState.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
688
|
+
}
|
|
689
|
+
});
|
|
651
690
|
if (quality === qualityName) {
|
|
691
|
+
state.layerButtons.temporalLayerButtons.forEach((lState, __) => showItem(lState.btn));
|
|
692
|
+
state.layerButtons.spatialLayerButtons.forEach((lState, __) => showItem(lState.btn));
|
|
693
|
+
|
|
652
694
|
state.btn.style.color = QUALITY_COLORS.SELECTED;
|
|
653
695
|
} else if (state.btn.style.color === QUALITY_COLORS.SELECTED) {
|
|
696
|
+
state.layerButtons.temporalLayerButtons.forEach((lState, __) => hideItem(lState.btn));
|
|
697
|
+
state.layerButtons.spatialLayerButtons.forEach((lState, __) => hideItem(lState.btn));
|
|
654
698
|
if (state.available) {
|
|
655
699
|
state.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
656
700
|
} else {
|
|
@@ -660,22 +704,52 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
660
704
|
}
|
|
661
705
|
}
|
|
662
706
|
|
|
707
|
+
const repickSid = function (qualityName, sid) {
|
|
708
|
+
const qualityState = qualityButtons.get(qualityName);
|
|
709
|
+
for (const [__, state] of qualityState.layerButtons.spatialLayerButtons.entries()) {
|
|
710
|
+
if (state.layerInfo.sid === sid) {
|
|
711
|
+
state.btn.style.color = QUALITY_COLORS.SELECTED;
|
|
712
|
+
} else if (state.layerInfo.available) {
|
|
713
|
+
state.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
714
|
+
} else {
|
|
715
|
+
state.btn.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const repickTid = function (qualityName, tid) {
|
|
721
|
+
const qualityState = qualityButtons.get(qualityName);
|
|
722
|
+
for (const [__, state] of qualityState.layerButtons.temporalLayerButtons.entries()) {
|
|
723
|
+
if (state.layerInfo.tid === tid) {
|
|
724
|
+
state.btn.style.color = QUALITY_COLORS.SELECTED;
|
|
725
|
+
} else if (state.layerInfo.available) {
|
|
726
|
+
state.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
727
|
+
} else {
|
|
728
|
+
state.btn.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
663
733
|
return {
|
|
664
734
|
rootDiv: streamDisplay,
|
|
665
735
|
muteButton: null,
|
|
666
736
|
autoButton: null,
|
|
737
|
+
tidListener: null,
|
|
738
|
+
sidListener: null,
|
|
667
739
|
dispose: function () {
|
|
668
740
|
streamDisplay.remove();
|
|
669
741
|
},
|
|
670
742
|
clearQualityState: function () {
|
|
671
743
|
qualityButtons.forEach((state, qName) => {
|
|
672
744
|
state.btn.remove();
|
|
745
|
+
state.layerButtons.temporalLayerButtons.forEach((lState, __) => lState.btn.remove());
|
|
746
|
+
state.layerButtons.spatialLayerButtons.forEach((lState, __) => lState.btn.remove());
|
|
673
747
|
});
|
|
674
748
|
qualityButtons.clear();
|
|
675
749
|
},
|
|
676
750
|
addVideoTrack: function (track, asyncCallback) {
|
|
677
751
|
const trackButton = document.createElement("button");
|
|
678
|
-
|
|
752
|
+
tracksInfo.set(track.mid, trackButton);
|
|
679
753
|
trackButton.innerText = "Track №" + track.mid + ": " + track.contentType;
|
|
680
754
|
trackButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
681
755
|
trackButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
@@ -696,13 +770,13 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
696
770
|
trackDisplay.appendChild(trackButton);
|
|
697
771
|
},
|
|
698
772
|
removeVideoTrack: function (track) {
|
|
699
|
-
const trackButton =
|
|
773
|
+
const trackButton = tracksInfo.get(track.mid);
|
|
700
774
|
if (trackButton) {
|
|
701
775
|
trackButton.remove();
|
|
702
|
-
|
|
776
|
+
tracksInfo.delete(track.mid);
|
|
703
777
|
}
|
|
704
778
|
},
|
|
705
|
-
setVideoSource: function (remoteVideoTrack, onResize, onMute) {
|
|
779
|
+
setVideoSource: function (remoteVideoTrack, onResize, onMute, onSidClick, onTidClick) {
|
|
706
780
|
if (!this.muteButton) {
|
|
707
781
|
const newVideoMuteBtn = document.createElement("button");
|
|
708
782
|
this.muteButton = newVideoMuteBtn;
|
|
@@ -724,6 +798,8 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
724
798
|
});
|
|
725
799
|
videoMuteDisplay.appendChild(newVideoMuteBtn);
|
|
726
800
|
}
|
|
801
|
+
this.sidListener = onSidClick;
|
|
802
|
+
this.tidListener = onTidClick;
|
|
727
803
|
|
|
728
804
|
if (videoElement) {
|
|
729
805
|
videoElement.remove();
|
|
@@ -778,7 +854,7 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
778
854
|
if (videoElement) {
|
|
779
855
|
showItem(videoElement);
|
|
780
856
|
}
|
|
781
|
-
for (const [mid, btn] of
|
|
857
|
+
for (const [mid, btn] of tracksInfo.entries()) {
|
|
782
858
|
if (mid === track.mid) {
|
|
783
859
|
btn.style.color = QUALITY_COLORS.SELECTED;
|
|
784
860
|
} else if (btn.style.color === QUALITY_COLORS.SELECTED) {
|
|
@@ -788,39 +864,191 @@ const createVideoPlayer = function (participantDiv) {
|
|
|
788
864
|
trackNameDisplay.innerText = "Current video track: " + track.mid;
|
|
789
865
|
showItem(trackNameDisplay);
|
|
790
866
|
},
|
|
791
|
-
updateQuality: function (
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (available) {
|
|
800
|
-
|
|
867
|
+
updateQuality: function (quality) {
|
|
868
|
+
console.log("updateQuality" + quality.available);
|
|
869
|
+
const qualityInfo = qualityButtons.get(quality.quality);
|
|
870
|
+
if (qualityInfo) {
|
|
871
|
+
const qualityButton = qualityInfo.btn;
|
|
872
|
+
qualityInfo.available = quality.available;
|
|
873
|
+
const isSelectedQuality = qualityButton.style.color === QUALITY_COLORS.SELECTED;
|
|
874
|
+
|
|
875
|
+
if (quality.available) {
|
|
876
|
+
if(!isSelectedQuality) {
|
|
877
|
+
qualityButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
878
|
+
}
|
|
801
879
|
} else {
|
|
802
880
|
qualityButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
803
881
|
}
|
|
882
|
+
const self = this;
|
|
883
|
+
for (const spatialLayer of quality.layersInfo.spatialLayers) {
|
|
884
|
+
const localLayerInfo = qualityInfo.layerButtons.spatialLayerButtons.get(spatialLayer.sid);
|
|
885
|
+
if (localLayerInfo) {
|
|
886
|
+
if (spatialLayer.available) {
|
|
887
|
+
localLayerInfo.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
888
|
+
} else {
|
|
889
|
+
localLayerInfo.btn.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
890
|
+
}
|
|
891
|
+
localLayerInfo.btn.innerText = "sid-" + spatialLayer.sid + " | " + spatialLayer.resolution.width + "x" + spatialLayer.resolution.height;
|
|
892
|
+
} else {
|
|
893
|
+
const layerButton = document.createElement("button");
|
|
894
|
+
layerButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
895
|
+
if (!isSelectedQuality) {
|
|
896
|
+
hideItem(layerButton);
|
|
897
|
+
}
|
|
898
|
+
layerButton.innerText = "sid-" + spatialLayer.sid + " | " + spatialLayer.resolution.width + "x" + spatialLayer.resolution.height;
|
|
899
|
+
layerButton.addEventListener('click', async function () {
|
|
900
|
+
console.log("Clicked on sid button " + spatialLayer.sid);
|
|
901
|
+
if (layerButton.style.color === QUALITY_COLORS.SELECTED || layerButton.style.color === QUALITY_COLORS.UNAVAILABLE || !videoElement) {
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (self.sidListener) {
|
|
905
|
+
lock();
|
|
906
|
+
self.sidListener(spatialLayer.sid).finally(() => {
|
|
907
|
+
unlock();
|
|
908
|
+
repickSid(quality.quality, spatialLayer.sid);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
if (spatialLayer.available) {
|
|
913
|
+
layerButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
914
|
+
} else {
|
|
915
|
+
layerButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
916
|
+
}
|
|
917
|
+
qualityInfo.layerButtons.spatialLayerButtons.set(spatialLayer.sid, {
|
|
918
|
+
btn: layerButton,
|
|
919
|
+
layerInfo: spatialLayer
|
|
920
|
+
});
|
|
921
|
+
qualityDisplay.appendChild(layerButton);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
for (const temporalLayer of quality.layersInfo.temporalLayers) {
|
|
926
|
+
const localLayerInfo = qualityInfo.layerButtons.temporalLayerButtons.get(temporalLayer.tid);
|
|
927
|
+
if (localLayerInfo) {
|
|
928
|
+
if (temporalLayer.available) {
|
|
929
|
+
localLayerInfo.btn.style.color = QUALITY_COLORS.AVAILABLE;
|
|
930
|
+
} else {
|
|
931
|
+
localLayerInfo.btn.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
932
|
+
}
|
|
933
|
+
localLayerInfo.btn.innerText = "tid-" + temporalLayer.tid;
|
|
934
|
+
} else {
|
|
935
|
+
const layerButton = document.createElement("button");
|
|
936
|
+
layerButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
937
|
+
if (!isSelectedQuality) {
|
|
938
|
+
hideItem(layerButton);
|
|
939
|
+
}
|
|
940
|
+
layerButton.innerText = "tid-" + temporalLayer.tid;
|
|
941
|
+
layerButton.addEventListener('click', async function () {
|
|
942
|
+
console.log("Clicked on tid button " + temporalLayer.tid);
|
|
943
|
+
if (layerButton.style.color === QUALITY_COLORS.SELECTED || layerButton.style.color === QUALITY_COLORS.UNAVAILABLE || !videoElement) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (self.tidListener) {
|
|
947
|
+
lock();
|
|
948
|
+
self.tidListener(temporalLayer.tid).finally(() => {
|
|
949
|
+
unlock();
|
|
950
|
+
repickTid(quality.quality, temporalLayer.tid);
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
if (temporalLayer.available) {
|
|
955
|
+
layerButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
956
|
+
} else {
|
|
957
|
+
layerButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
958
|
+
}
|
|
959
|
+
qualityInfo.layerButtons.temporalLayerButtons.set(temporalLayer.tid, {
|
|
960
|
+
btn: layerButton,
|
|
961
|
+
layerInfo: temporalLayer
|
|
962
|
+
})
|
|
963
|
+
qualityDisplay.appendChild(layerButton);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
804
966
|
}
|
|
805
967
|
},
|
|
806
|
-
addQuality: function (
|
|
968
|
+
addQuality: function (quality, onQualityClick) {
|
|
969
|
+
console.log("addQuality" + quality.available);
|
|
970
|
+
|
|
807
971
|
const qualityButton = document.createElement("button");
|
|
808
|
-
|
|
809
|
-
qualityButton.innerText = qualityName;
|
|
972
|
+
qualityButton.innerText = quality.quality;
|
|
810
973
|
qualityButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
811
|
-
if (available) {
|
|
974
|
+
if (quality.available) {
|
|
812
975
|
qualityButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
813
976
|
} else {
|
|
814
977
|
qualityButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
815
978
|
}
|
|
816
979
|
qualityDisplay.appendChild(qualityButton);
|
|
980
|
+
const self = this;
|
|
817
981
|
qualityButton.addEventListener('click', async function () {
|
|
818
|
-
console.log("Clicked on quality button " +
|
|
982
|
+
console.log("Clicked on quality button " + quality.quality);
|
|
819
983
|
if (qualityButton.style.color === QUALITY_COLORS.SELECTED || qualityButton.style.color === QUALITY_COLORS.UNAVAILABLE || !videoElement) {
|
|
820
984
|
return;
|
|
821
985
|
}
|
|
822
986
|
lock();
|
|
823
|
-
|
|
987
|
+
onQualityClick().finally(() => {
|
|
988
|
+
unlock();
|
|
989
|
+
repickQuality(quality.quality);
|
|
990
|
+
});
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
const spatialLayers = new Map();
|
|
994
|
+
for (const spatialLayer of quality.layersInfo.spatialLayers) {
|
|
995
|
+
const layerButton = document.createElement("button");
|
|
996
|
+
layerButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
997
|
+
hideItem(layerButton);
|
|
998
|
+
layerButton.innerText = "sid-" + spatialLayer.sid + " | " + spatialLayer.resolution.width + "x" + spatialLayer.resolution.height;
|
|
999
|
+
layerButton.addEventListener('click', async function () {
|
|
1000
|
+
console.log("Clicked on sid button " + spatialLayer.sid);
|
|
1001
|
+
if (layerButton.style.color === QUALITY_COLORS.SELECTED || layerButton.style.color === QUALITY_COLORS.UNAVAILABLE || !videoElement) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
if (self.sidListener) {
|
|
1005
|
+
lock();
|
|
1006
|
+
self.sidListener(spatialLayer.sid).finally(() => {
|
|
1007
|
+
unlock();
|
|
1008
|
+
repickSid(quality.quality, spatialLayer.sid);
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
if (spatialLayer.available) {
|
|
1013
|
+
layerButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
1014
|
+
} else {
|
|
1015
|
+
layerButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
1016
|
+
}
|
|
1017
|
+
spatialLayers.set(spatialLayer.sid, {btn: layerButton, layerInfo: spatialLayer});
|
|
1018
|
+
qualityDisplay.appendChild(layerButton);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const temporalLayers = new Map();
|
|
1022
|
+
for (const temporalLayer of quality.layersInfo.temporalLayers) {
|
|
1023
|
+
const layerButton = document.createElement("button");
|
|
1024
|
+
layerButton.setAttribute("style", "display:inline-block; border: solid; border-width: 1px");
|
|
1025
|
+
hideItem(layerButton);
|
|
1026
|
+
layerButton.innerText = "tid-" + temporalLayer.tid;
|
|
1027
|
+
layerButton.addEventListener('click', async function () {
|
|
1028
|
+
console.log("Clicked on tid button " + temporalLayer.tid);
|
|
1029
|
+
if (layerButton.style.color === QUALITY_COLORS.SELECTED || layerButton.style.color === QUALITY_COLORS.UNAVAILABLE || !videoElement) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (self.tidListener) {
|
|
1033
|
+
lock();
|
|
1034
|
+
self.tidListener(temporalLayer.tid).finally(() => {
|
|
1035
|
+
unlock();
|
|
1036
|
+
repickTid(quality.quality, temporalLayer.tid);
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
if (temporalLayer.available) {
|
|
1041
|
+
layerButton.style.color = QUALITY_COLORS.AVAILABLE;
|
|
1042
|
+
} else {
|
|
1043
|
+
layerButton.style.color = QUALITY_COLORS.UNAVAILABLE;
|
|
1044
|
+
}
|
|
1045
|
+
temporalLayers.set(temporalLayer.tid, {btn: layerButton, layerInfo: temporalLayer})
|
|
1046
|
+
qualityDisplay.appendChild(layerButton);
|
|
1047
|
+
}
|
|
1048
|
+
qualityButtons.set(quality.quality, {
|
|
1049
|
+
btn: qualityButton,
|
|
1050
|
+
available: quality.available,
|
|
1051
|
+
layerButtons: {spatialLayerButtons: spatialLayers, temporalLayerButtons: temporalLayers}
|
|
824
1052
|
});
|
|
825
1053
|
},
|
|
826
1054
|
pickQuality: function (qualityName) {
|
|
@@ -930,10 +1158,10 @@ const createOneToOneParticipantView = function () {
|
|
|
930
1158
|
player.dispose();
|
|
931
1159
|
}
|
|
932
1160
|
},
|
|
933
|
-
addVideoSource: function (remoteVideoTrack, track, onResize, muteHandler) {
|
|
1161
|
+
addVideoSource: function (remoteVideoTrack, track, onResize, muteHandler, onSidClick, onTidClick) {
|
|
934
1162
|
const player = videoPlayers.get(track.mid);
|
|
935
1163
|
if (player) {
|
|
936
|
-
player.setVideoSource(remoteVideoTrack, onResize, muteHandler);
|
|
1164
|
+
player.setVideoSource(remoteVideoTrack, onResize, muteHandler, onSidClick, onTidClick);
|
|
937
1165
|
}
|
|
938
1166
|
},
|
|
939
1167
|
removeVideoSource: function (track) {
|
|
@@ -969,10 +1197,10 @@ const createOneToOneParticipantView = function () {
|
|
|
969
1197
|
player.updateQuality(qualityName, available);
|
|
970
1198
|
}
|
|
971
1199
|
},
|
|
972
|
-
addQuality: function (track,
|
|
1200
|
+
addQuality: function (track, quality, onQualityPick) {
|
|
973
1201
|
const player = videoPlayers.get(track.mid);
|
|
974
1202
|
if (player) {
|
|
975
|
-
player.addQuality(
|
|
1203
|
+
player.addQuality(quality, onQualityPick);
|
|
976
1204
|
}
|
|
977
1205
|
},
|
|
978
1206
|
pickQuality: function (track, qualityName) {
|
|
@@ -1040,6 +1268,10 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1040
1268
|
} else {
|
|
1041
1269
|
return self.unmuteVideo(track);
|
|
1042
1270
|
}
|
|
1271
|
+
}, (sid) => {
|
|
1272
|
+
return remoteTrack.setSid(sid)
|
|
1273
|
+
}, (tid) => {
|
|
1274
|
+
return remoteTrack.setTid(tid)
|
|
1043
1275
|
});
|
|
1044
1276
|
self.requestVideoTrack(track, remoteTrack).then(() => {
|
|
1045
1277
|
participantView.showVideoTrack(track);
|
|
@@ -1127,6 +1359,23 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1127
1359
|
const quality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
|
|
1128
1360
|
if (quality) {
|
|
1129
1361
|
quality.available = remoteQualityInfo.available;
|
|
1362
|
+
for(const info of remoteQualityInfo.layersInfo.temporalLayers) {
|
|
1363
|
+
const localTidInfo = quality.layersInfo.temporalLayers.find((t) => t.tid === info.tid);
|
|
1364
|
+
if(localTidInfo) {
|
|
1365
|
+
localTidInfo.available = info.available;
|
|
1366
|
+
} else {
|
|
1367
|
+
quality.layersInfo.temporalLayers.push(info);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
for(const info of remoteQualityInfo.layersInfo.spatialLayers) {
|
|
1371
|
+
const localSidInfo = quality.layersInfo.spatialLayers.find((s) => s.sid === info.sid);
|
|
1372
|
+
if(localSidInfo) {
|
|
1373
|
+
localSidInfo.available = info.available;
|
|
1374
|
+
localSidInfo.resolution = info.resolution;
|
|
1375
|
+
} else {
|
|
1376
|
+
quality.layersInfo.spatialLayers.push(info);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1130
1379
|
} else {
|
|
1131
1380
|
track.quality.push(remoteQualityInfo);
|
|
1132
1381
|
}
|
|
@@ -1136,7 +1385,7 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1136
1385
|
let abrManager = this.abrManagers.get(track.id);
|
|
1137
1386
|
if (abrManager && track.quality.length === 0 && remoteTrackQuality.quality.length > 0) {
|
|
1138
1387
|
const self = this;
|
|
1139
|
-
participantView.addQuality(track, "Auto",
|
|
1388
|
+
participantView.addQuality(track, {quality:"Auto",available:true,layersInfo:{spatialLayers:[],temporalLayers:[]}}, async () => {
|
|
1140
1389
|
const manager = self.abrManagers.get(track.id);
|
|
1141
1390
|
if (!manager) {
|
|
1142
1391
|
return;
|
|
@@ -1155,6 +1404,23 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1155
1404
|
const localQuality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
|
|
1156
1405
|
if (localQuality) {
|
|
1157
1406
|
localQuality.available = remoteQualityInfo.available;
|
|
1407
|
+
for(const info of remoteQualityInfo.layersInfo.temporalLayers) {
|
|
1408
|
+
const localTidInfo = localQuality.layersInfo.temporalLayers.find((t) => t.tid === info.tid);
|
|
1409
|
+
if(localTidInfo) {
|
|
1410
|
+
localTidInfo.available = info.available;
|
|
1411
|
+
} else {
|
|
1412
|
+
localQuality.layersInfo.temporalLayers.push(info);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
for(const info of remoteQualityInfo.layersInfo.spatialLayers) {
|
|
1416
|
+
const localSidInfo = localQuality.layersInfo.spatialLayers.find((s) => s.sid === info.sid);
|
|
1417
|
+
if(localSidInfo) {
|
|
1418
|
+
localSidInfo.available = info.available;
|
|
1419
|
+
localSidInfo.resolution = info.resolution;
|
|
1420
|
+
} else {
|
|
1421
|
+
localQuality.layersInfo.spatialLayers.push(info);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1158
1424
|
if (abrManager) {
|
|
1159
1425
|
abrManager.setQualityAvailable(remoteQualityInfo.quality, remoteQualityInfo.available);
|
|
1160
1426
|
}
|
|
@@ -1169,13 +1435,13 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1169
1435
|
}
|
|
1170
1436
|
if (displayOptions.quality) {
|
|
1171
1437
|
const self = this;
|
|
1172
|
-
participantView.addQuality(track, remoteQualityInfo
|
|
1438
|
+
participantView.addQuality(track, remoteQualityInfo, async (sid, tid) => {
|
|
1173
1439
|
const manager = self.abrManagers.get(track.id);
|
|
1174
1440
|
if (manager) {
|
|
1175
1441
|
manager.setManual();
|
|
1176
1442
|
manager.setQuality(remoteQualityInfo.quality);
|
|
1177
1443
|
}
|
|
1178
|
-
return self.pickQuality(track, remoteQualityInfo.quality);
|
|
1444
|
+
return self.pickQuality(track, remoteQualityInfo.quality, sid, tid);
|
|
1179
1445
|
});
|
|
1180
1446
|
}
|
|
1181
1447
|
}
|
|
@@ -1208,7 +1474,7 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1208
1474
|
abrManager.setTrack(remoteTrack);
|
|
1209
1475
|
abrManager.stop();
|
|
1210
1476
|
if (track.quality.length > 0) {
|
|
1211
|
-
participantView.addQuality(track, "Auto",
|
|
1477
|
+
participantView.addQuality(track, {quality:"Auto",available:true, layersInfo:{spatialLayers:[],temporalLayers:[]}}, async () => {
|
|
1212
1478
|
const manager = self.abrManagers.get(track.id);
|
|
1213
1479
|
if (!manager) {
|
|
1214
1480
|
return;
|
|
@@ -1230,7 +1496,7 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1230
1496
|
abrManager.setQualityAvailable(qualityDescriptor.quality, qualityDescriptor.available);
|
|
1231
1497
|
}
|
|
1232
1498
|
if (displayOptions.quality) {
|
|
1233
|
-
participantView.addQuality(track, qualityDescriptor
|
|
1499
|
+
participantView.addQuality(track, qualityDescriptor, async () => {
|
|
1234
1500
|
const manager = self.abrManagers.get(track.id);
|
|
1235
1501
|
if (manager) {
|
|
1236
1502
|
manager.setManual();
|
|
@@ -1248,11 +1514,11 @@ const createOneToOneParticipantModel = function (userId, nickname, participantVi
|
|
|
1248
1514
|
});
|
|
1249
1515
|
});
|
|
1250
1516
|
},
|
|
1251
|
-
pickQuality: async function (track, qualityName) {
|
|
1517
|
+
pickQuality: async function (track, qualityName, tid, sid) {
|
|
1252
1518
|
let remoteVideoTrack = this.remoteVideoTracks.get(track.mid);
|
|
1253
1519
|
if (remoteVideoTrack) {
|
|
1254
|
-
return remoteVideoTrack.setPreferredQuality(qualityName).then(() => {
|
|
1255
|
-
participantView.pickQuality(track, qualityName);
|
|
1520
|
+
return remoteVideoTrack.setPreferredQuality(qualityName, sid, tid).then(() => {
|
|
1521
|
+
participantView.pickQuality(track, qualityName, sid, tid);
|
|
1256
1522
|
});
|
|
1257
1523
|
}
|
|
1258
1524
|
},
|
|
@@ -1311,6 +1577,10 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1311
1577
|
} else {
|
|
1312
1578
|
return model.unmuteVideo(anotherTrack);
|
|
1313
1579
|
}
|
|
1580
|
+
}, (sid) => {
|
|
1581
|
+
return model.remoteVideoTrack.setSid(sid)
|
|
1582
|
+
}, (tid) => {
|
|
1583
|
+
return model.remoteVideoTrack.setTid(tid)
|
|
1314
1584
|
});
|
|
1315
1585
|
model.requestVideoTrack(anotherTrack, model.remoteVideoTrack).then(() => {
|
|
1316
1586
|
participantView.showVideoTrack(anotherTrack)
|
|
@@ -1472,7 +1742,7 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1472
1742
|
|
|
1473
1743
|
},
|
|
1474
1744
|
setUserId: function (userId) {
|
|
1475
|
-
|
|
1745
|
+
this.userId = userId;
|
|
1476
1746
|
},
|
|
1477
1747
|
setNickname: function (nickname) {
|
|
1478
1748
|
this.nickname = nickname;
|
|
@@ -1490,15 +1760,32 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1490
1760
|
const quality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
|
|
1491
1761
|
if (quality) {
|
|
1492
1762
|
quality.available = remoteQualityInfo.available;
|
|
1763
|
+
for(const info of remoteQualityInfo.layersInfo.temporalLayers) {
|
|
1764
|
+
const localTidInfo = quality.layersInfo.temporalLayers.find((t) => t.tid === info.tid);
|
|
1765
|
+
if(localTidInfo) {
|
|
1766
|
+
localTidInfo.available = info.available;
|
|
1767
|
+
} else {
|
|
1768
|
+
quality.layersInfo.temporalLayers.push(info);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
for(const info of remoteQualityInfo.layersInfo.spatialLayers) {
|
|
1772
|
+
const localSidInfo = quality.layersInfo.spatialLayers.find((s) => s.sid === info.sid);
|
|
1773
|
+
if(localSidInfo) {
|
|
1774
|
+
localSidInfo.available = info.available;
|
|
1775
|
+
localSidInfo.resolution = info.resolution;
|
|
1776
|
+
} else {
|
|
1777
|
+
quality.layersInfo.spatialLayers.push(info);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1493
1780
|
} else {
|
|
1494
1781
|
track.quality.push(remoteQualityInfo);
|
|
1495
1782
|
}
|
|
1496
1783
|
}
|
|
1497
|
-
|
|
1784
|
+
continue;
|
|
1498
1785
|
}
|
|
1499
1786
|
if (this.abr && track.quality.length === 0 && remoteTrackQuality.quality.length > 0) {
|
|
1500
1787
|
const self = this;
|
|
1501
|
-
participantView.addQuality(track, "Auto",
|
|
1788
|
+
participantView.addQuality(track, {quality:"Auto",available:true, layersInfo:{spatialLayers:[],temporalLayers:[]}}, async () => {
|
|
1502
1789
|
if (!self.abr) {
|
|
1503
1790
|
return;
|
|
1504
1791
|
}
|
|
@@ -1516,11 +1803,28 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1516
1803
|
const localQuality = track.quality.find((q) => q.quality === remoteQualityInfo.quality);
|
|
1517
1804
|
if (localQuality) {
|
|
1518
1805
|
localQuality.available = remoteQualityInfo.available;
|
|
1806
|
+
for(const info of remoteQualityInfo.layersInfo.temporalLayers) {
|
|
1807
|
+
const localTidInfo = localQuality.layersInfo.temporalLayers.find((t) => t.tid === info.tid);
|
|
1808
|
+
if(localTidInfo) {
|
|
1809
|
+
localTidInfo.available = info.available;
|
|
1810
|
+
} else {
|
|
1811
|
+
localQuality.layersInfo.temporalLayers.push(info);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
for(const info of remoteQualityInfo.layersInfo.spatialLayers) {
|
|
1815
|
+
const localSidInfo = localQuality.layersInfo.spatialLayers.find((s) => s.sid === info.sid);
|
|
1816
|
+
if(localSidInfo) {
|
|
1817
|
+
localSidInfo.available = info.available;
|
|
1818
|
+
localSidInfo.resolution = info.resolution;
|
|
1819
|
+
} else {
|
|
1820
|
+
localQuality.layersInfo.spatialLayers.push(info);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1519
1823
|
if (this.abr) {
|
|
1520
1824
|
this.abr.setQualityAvailable(remoteQualityInfo.quality, remoteQualityInfo.available)
|
|
1521
1825
|
}
|
|
1522
1826
|
if (displayOptions.quality) {
|
|
1523
|
-
participantView.updateQuality(track,
|
|
1827
|
+
participantView.updateQuality(track, remoteQualityInfo);
|
|
1524
1828
|
}
|
|
1525
1829
|
} else {
|
|
1526
1830
|
track.quality.push(remoteQualityInfo);
|
|
@@ -1530,7 +1834,7 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1530
1834
|
}
|
|
1531
1835
|
if (displayOptions.quality) {
|
|
1532
1836
|
const self = this;
|
|
1533
|
-
participantView.addQuality(track, remoteQualityInfo
|
|
1837
|
+
participantView.addQuality(track, remoteQualityInfo, async () => {
|
|
1534
1838
|
if (self.abr) {
|
|
1535
1839
|
self.abr.setManual();
|
|
1536
1840
|
self.abr.setQuality(remoteQualityInfo.quality);
|
|
@@ -1563,7 +1867,7 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1563
1867
|
self.abr.setTrack(remoteTrack);
|
|
1564
1868
|
|
|
1565
1869
|
if (track.quality.length > 0) {
|
|
1566
|
-
participantView.addQuality(track, "Auto",
|
|
1870
|
+
participantView.addQuality(track, {quality:"Auto",available:true,layersInfo:{spatialLayers:[],temporalLayers:[]}}, async () => {
|
|
1567
1871
|
if (!self.abr) {
|
|
1568
1872
|
return;
|
|
1569
1873
|
}
|
|
@@ -1584,7 +1888,7 @@ const createOneToManyParticipantModel = function (userId, nickname, participantV
|
|
|
1584
1888
|
self.abr.setQualityAvailable(qualityDescriptor.quality, qualityDescriptor.available);
|
|
1585
1889
|
}
|
|
1586
1890
|
if (displayOptions.quality) {
|
|
1587
|
-
participantView.addQuality(track, qualityDescriptor
|
|
1891
|
+
participantView.addQuality(track, qualityDescriptor, async () => {
|
|
1588
1892
|
if (self.abr) {
|
|
1589
1893
|
self.abr.setManual();
|
|
1590
1894
|
self.abr.setQuality(qualityDescriptor.quality);
|
package/src/controller/parser.js
CHANGED
|
@@ -189,9 +189,11 @@ const statsToTable = function(stats) {
|
|
|
189
189
|
pStats.push(trackToTable(track, participant.nickName, "NA"));
|
|
190
190
|
tracksOut++;
|
|
191
191
|
bitrateOut += track.bitrate;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
if (track.type === 'video' && track.feedbackStats) {
|
|
193
|
+
firOut += track.feedbackStats.receivedFIR;
|
|
194
|
+
pliOut += track.feedbackStats.receivedPLI;
|
|
195
|
+
nackOut += track.feedbackStats.receivedNACK;
|
|
196
|
+
}
|
|
195
197
|
}
|
|
196
198
|
});
|
|
197
199
|
participant.incomingTracks.forEach(function(track) {
|