@@ -39,15 +39,17 @@ This quickstart uses webpack to bundle the application assets. Run the following
39
39
``` console
40
40
41
41
```
42
- Create an ` index.html ` file in the root directory of your project. We'll use this file to configure a basic layout that will allow the user to place a 1:1 video call.
43
42
44
43
Here's the code:
44
+
45
+ Create an ` index.html ` file in the root directory of your project. We'll use this file to configure a basic layout that will allow the user to place a 1:1 video call.
45
46
``` html
46
47
<!-- index.html -->
47
48
<!DOCTYPE html>
48
49
<html >
49
50
<head >
50
51
<title >Azure Communication Services - Calling Web SDK</title >
52
+ <link rel =" stylesheet" type =" text/css" href =" styles.css" />
51
53
</head >
52
54
<body >
53
55
<h4 >Azure Communication Services - Calling Web SDK</h4 >
@@ -71,7 +73,7 @@ Here's the code:
71
73
<br >
72
74
<div id =" connectedLabel" style =" color : #13bb13 ;" hidden >Call is connected!</div >
73
75
<br >
74
- <div id =" remoteVideoContainer " style =" width : 40% ;" hidden >Remote participants' video streams:</div >
76
+ <div id =" remoteVideosGallery " style =" width : 40% ;" hidden >Remote participants' video streams:</div >
75
77
<br >
76
78
<div id =" localVideoContainer" style =" width : 30% ;" hidden >Local video stream:</div >
77
79
<!-- points to the bundle generated from client.js -->
@@ -80,8 +82,6 @@ Here's the code:
80
82
</html >
81
83
```
82
84
83
- ## Azure Communication Services Calling Web SDK Object model
84
-
85
85
The following classes and interfaces handle some of the major features of the Azure Communication Services Calling SDK:
86
86
87
87
| Name | Description |
@@ -125,7 +125,7 @@ let acceptCallButton = document.getElementById('accept-call-button');
125
125
let startVideoButton = document .getElementById (' start-video-button' );
126
126
let stopVideoButton = document .getElementById (' stop-video-button' );
127
127
let connectedLabel = document .getElementById (' connectedLabel' );
128
- let remoteVideoContainer = document .getElementById (' remoteVideoContainer ' );
128
+ let remoteVideosGallery = document .getElementById (' remoteVideosGallery ' );
129
129
let localVideoContainer = document .getElementById (' localVideoContainer' );
130
130
131
131
/**
@@ -197,8 +197,10 @@ acceptCallButton.onclick = async () => {
197
197
}
198
198
}
199
199
200
- // Subscribe to a call obj.
201
- // Listen for property changes and collection updates.
200
+ /**
201
+ * Subscribe to a call obj.
202
+ * Listen for property changes and collection updates.
203
+ */
202
204
subscribeToCall = (call ) => {
203
205
try {
204
206
// Inspect the initial call.id value.
@@ -220,6 +222,7 @@ subscribeToCall = (call) => {
220
222
hangUpCallButton .disabled = false ;
221
223
startVideoButton .disabled = false ;
222
224
stopVideoButton .disabled = false ;
225
+ remoteVideosGallery .hidden = false ;
223
226
} else if (call .state === ' Disconnected' ) {
224
227
connectedLabel .hidden = true ;
225
228
startCallButton .disabled = false ;
@@ -265,8 +268,10 @@ subscribeToCall = (call) => {
265
268
}
266
269
}
267
270
268
- // Subscribe to a remote participant obj.
269
- // Listen for property changes and collection udpates.
271
+ /**
272
+ * Subscribe to a remote participant obj.
273
+ * Listen for property changes and collection udpates.
274
+ */
270
275
subscribeToRemoteParticipant = (remoteParticipant ) => {
271
276
try {
272
277
// Inspect the initial remoteParticipant.state value.
@@ -304,43 +309,72 @@ subscribeToRemoteParticipant = (remoteParticipant) => {
304
309
* you can choose to destroy the whole 'Renderer', a specific 'RendererView' or keep them, but this will result in displaying blank video frame.
305
310
*/
306
311
subscribeToRemoteVideoStream = async (remoteVideoStream ) => {
307
- // Create a video stream renderer for the remote video stream.
308
- let videoStreamRenderer = new VideoStreamRenderer (remoteVideoStream);
312
+ let renderer = new VideoStreamRenderer (remoteVideoStream);
309
313
let view;
310
- const renderVideo = async () => {
314
+ let remoteVideoContainer = document .createElement (' div' );
315
+ remoteVideoContainer .className = ' remote-video-container' ;
316
+
317
+ /**
318
+ * isReceiving API is currently an @alpha feature. Do not use in production.
319
+ * To use this api please use 'alpha' release of Azure Communication Services Calling Web SDK.
320
+ * Create a CSS class to style your loading spinner. Take a look at our
321
+ * video calling quickstart, to see how to create a loading spinner.
322
+ *
323
+ let loadingSpinner = document.createElement('div');
324
+ loadingSpinner.className = 'loading-spinner';
325
+ remoteVideoStream.on('isReceivingChanged', () => {
311
326
try {
312
- // Create a renderer view for the remote video stream.
313
- view = await videoStreamRenderer .createView ();
314
- // Attach the renderer view to the UI.
315
- remoteVideoContainer .hidden = false ;
316
- remoteVideoContainer .appendChild (view .target );
327
+ if (remoteVideoStream.isAvailable) {
328
+ const isReceiving = remoteVideoStream.isReceiving;
329
+ const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
330
+ if (!isReceiving && !isLoadingSpinnerActive) {
331
+ remoteVideoContainer.appendChild(loadingSpinner);
332
+ } else if (isReceiving && isLoadingSpinnerActive) {
333
+ remoteVideoContainer.removeChild(loadingSpinner);
334
+ }
335
+ }
317
336
} catch (e) {
318
- console .warn (` Failed to createView, reason=${ e .message } , code=${ e .code } ` );
319
- }
337
+ console.error(e);
338
+ }
339
+ });
340
+ */
341
+
342
+ const createView = async () => {
343
+ // Create a renderer view for the remote video stream.
344
+ view = await renderer .createView ();
345
+ // Attach the renderer view to the UI.
346
+ remoteVideoContainer .appendChild (view .target );
347
+ remoteVideosGallery .appendChild (remoteVideoContainer);
320
348
}
321
-
322
- remoteVideoStream .on (' isAvailableChanged' , async () => {
323
- // Participant has switched video on.
324
- if (remoteVideoStream .isAvailable ) {
325
- await renderVideo ();
326
349
327
- // Participant has switched video off.
328
- } else {
329
- if (view) {
350
+ // Remote participant has switched video on/off
351
+ remoteVideoStream .on (' isAvailableChanged' , async () => {
352
+ try {
353
+ if (remoteVideoStream .isAvailable ) {
354
+ await createView ();
355
+ } else {
330
356
view .dispose ();
331
- view = undefined ;
357
+ remoteVideosGallery . removeChild (remoteVideoContainer) ;
332
358
}
359
+ } catch (e) {
360
+ console .error (e);
333
361
}
334
362
});
335
363
336
- // Participant has video on initially.
364
+ // Remote participant has video on initially.
337
365
if (remoteVideoStream .isAvailable ) {
338
- await renderVideo ();
366
+ try {
367
+ await createView ();
368
+ } catch (e) {
369
+ console .error (e);
370
+ }
339
371
}
340
372
}
341
373
342
- // Start your local video stream.
343
- // This will send your local video stream to remote participants so they can view it.
374
+ /**
375
+ * Start your local video stream.
376
+ * This will send your local video stream to remote participants so they can view it.
377
+ */
344
378
startVideoButton .onclick = async () => {
345
379
try {
346
380
const localVideoStream = await createLocalVideoStream ();
@@ -350,8 +384,10 @@ startVideoButton.onclick = async () => {
350
384
}
351
385
}
352
386
353
- // Stop your local video stream.
354
- // This will stop your local video stream from being sent to remote participants.
387
+ /**
388
+ * Stop your local video stream.
389
+ * This will stop your local video stream from being sent to remote participants.
390
+ */
355
391
stopVideoButton .onclick = async () => {
356
392
try {
357
393
await call .stopVideo (localVideoStream);
@@ -365,7 +401,6 @@ stopVideoButton.onclick = async () => {
365
401
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
366
402
* You may then attach view.target to any UI element.
367
403
*/
368
- // Create a local video stream for your camera device
369
404
createLocalVideoStream = async () => {
370
405
const camera = (await deviceManager .getCameras ())[0 ];
371
406
if (camera) {
@@ -374,7 +409,10 @@ createLocalVideoStream = async () => {
374
409
console .error (` No camera device found on the system` );
375
410
}
376
411
}
377
- // Display your local video stream preview in your UI
412
+
413
+ /**
414
+ * Display your local video stream preview in your UI
415
+ */
378
416
displayLocalVideoStream = async () => {
379
417
try {
380
418
localVideoStreamRenderer = new VideoStreamRenderer (localVideoStream);
@@ -385,7 +423,10 @@ displayLocalVideoStream = async () => {
385
423
console .error (error);
386
424
}
387
425
}
388
- // Remove your local video stream preview from your UI
426
+
427
+ /**
428
+ * Remove your local video stream preview from your UI
429
+ */
389
430
removeLocalVideoStream = async () => {
390
431
try {
391
432
localVideoStreamRenderer .dispose ();
@@ -395,13 +436,50 @@ removeLocalVideoStream = async() => {
395
436
}
396
437
}
397
438
398
- // End the current call
439
+ /**
440
+ * End current call
441
+ */
399
442
hangUpCallButton .addEventListener (" click" , async () => {
400
443
// end the current call
401
444
await call .hangUp ();
402
445
});
403
446
```
404
447
448
+ Create a file in the root directory of your project called ` styles.css ` to contain the application styling for this quickstart. Add the following code to styles.css:
449
+ ``` css
450
+ /* *
451
+ * CSS for styling the loading spinner over the remote video stream
452
+ */
453
+ .remote-video-container {
454
+ position : relative ;
455
+ }
456
+ .loading-spinner {
457
+ border : 12px solid #f3f3f3 ;
458
+ border-radius : 50% ;
459
+ border-top : 12px solid #ca5010 ;
460
+ width : 100px ;
461
+ height : 100px ;
462
+ -webkit-animation : spin 2s linear infinite ; /* Safari */
463
+ animation : spin 2s linear infinite ;
464
+ position : absolute ;
465
+ margin : auto ;
466
+ top : 0 ;
467
+ bottom : 0 ;
468
+ left : 0 ;
469
+ right : 0 ;
470
+ transform : translate (-50% , -50% );
471
+ }
472
+ @keyframes spin {
473
+ 0% { transform : rotate (0deg ); }
474
+ 100% { transform : rotate (360deg ); }
475
+ }
476
+ /* Safari */
477
+ @-webkit-keyframes spin {
478
+ 0% { -webkit-transform : rotate (0deg ); }
479
+ 100% { -webkit-transform : rotate (360deg ); }
480
+ }
481
+ ```
482
+
405
483
## Run the code
406
484
Use the ` webpack-dev-server ` to build and run your app. Run the following command to bundle the application host in a local webserver:
407
485
0 commit comments