@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flashphoner/sfusdk-examples",
3
- "version": "2.0.190",
3
+ "version": "2.0.193",
4
4
  "description": "Official Flashphoner WebCallServer SFU SDK usage examples",
5
5
  "main": "dist/sfu.js",
6
6
  "types": "src/sfu.ts",
@@ -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
@@ -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]).then(function(stream){
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();
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
- $('#' + stream.id + "-button").on('click', function(){
85
- //terminate stream
86
- console.log("terminate stream " + stream.id);
87
- let track = stream.getAudioTracks()[0];
88
- track.stop();
89
- track.dispatchEvent(new Event("ended"));
90
- });
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
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]).then(function(stream){
104
- let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
105
- const row = controls.tables.video.row.add({
106
- source: track.source,
107
- width: track.width,
108
- height: track.height,
109
- codec: track.codec,
110
- action: button,
111
- stream: stream,
112
- encodings: track.encodings
113
- }).node();
114
- controls.tables.video.draw();
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
- $('#' + stream.id + "-button").on('click', function(){
117
- //terminate stream
118
- console.log("terminate stream " + stream.id);
119
- let track = stream.getVideoTracks()[0];
120
- track.stop();
121
- track.dispatchEvent(new Event("ended"));
122
- });
123
- stream.getTracks()[0].addEventListener("ended", function() {
124
- controls.tables.video.row(row).remove().draw();
125
- });
126
- trackCallback({
127
- stream: stream,
128
- encodings: track.encodings,
129
- source: track.source
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
- document.getElementById("addVideoTrack").addEventListener("click", function(e){
224
- let encodings = [];
225
- controls.tables.encodings.rows().every(function() {
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
- document.getElementById("addVideoTrackEncoding").addEventListener("click", function(){
249
- let button = '<button class="btn btn-primary remove">Delete</button>';
250
- controls.tables.encodings.row.add({
251
- rid: controls.addVideoEncoding.rid.value,
252
- active: controls.addVideoEncoding.active.value,
253
- maxBitrate: controls.addVideoEncoding.maxBitrate.value,
254
- resolutionScale: controls.addVideoEncoding.resolutionScale.value,
255
- action: button
256
- }).draw();
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
- document.getElementById("addAudioTrack").addEventListener("click", function(e){
260
- let encodings = [];
261
- let track = {
262
- source: controls.addAudioTrack.source.value,
263
- channels: controls.addAudioTrack.channels.value,
264
- encodings: encodings
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
- const onTrack = function(callback) {
270
- trackCallback = callback;
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
 
@@ -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
  }
@@ -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>
@@ -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
- * load config and show entrance modal
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
- * connect to server
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
- const session = sfu.createRoom(roomConfig);
71
- session.on(constants.SFU_EVENT.CONNECTED, function() {
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
- //connected to server
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
- //track ended, see if we need to cleanup
135
- let negotiate = false;
136
- for (const sender of pc.getSenders()) {
137
- if (sender.track === track) {
138
- pc.removeTrack(sender);
139
- //track found, set renegotiation flag
140
- negotiate = true;
141
- break;
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
- if (negotiate) {
145
- //kickoff renegotiation
146
- room.updateState();
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
  }
@@ -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
- const session = sfu.createRoom(roomConfig);
171
- session.on(constants.SFU_EVENT.CONNECTED, function() {
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
- }).on(constants.SFU_EVENT.DISCONNECTED, function() {
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.status + " " + e.statusText, "red");
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
- const session = sfu.createRoom(roomConfig);
211
- session.on(constants.SFU_EVENT.CONNECTED, function() {
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
- }).on(constants.SFU_EVENT.DISCONNECTED, function() {
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.status + " " + e.statusText, "red");
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.room, track, state.pc);
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(room, track, pc) {
394
- track.addEventListener("ended", function() {
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
- let pc = new RTCPeerConnection();
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
- // Connect to the server (room should already exist)
134
- const session = sfu.createRoom(roomConfig);
135
- session.on(constants.SFU_EVENT.CONNECTED, function() {
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
- }).on(constants.SFU_EVENT.DISCONNECTED, function() {
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.status + " " + e.statusText, "red");
148
- });
153
+ setStatus("playErrorInfo", e, "red");
154
+ }
149
155
  }
150
156
 
151
157
  const onConnected = async function(state) {