Skip to content

Commit bdb7847

Browse files
authored
Update video-calling-javascript.md
1 parent 1d29d3c commit bdb7847

File tree

1 file changed

+119
-38
lines changed

1 file changed

+119
-38
lines changed

articles/communication-services/quickstarts/voice-video-calling/includes/video-calling/video-calling-javascript.md

Lines changed: 119 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,17 @@ This quickstart uses webpack to bundle the application assets. Run the following
3939
```console
4040
4141
```
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.
4342

4443
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.
4546
```html
4647
<!-- index.html -->
4748
<!DOCTYPE html>
4849
<html>
4950
<head>
5051
<title>Azure Communication Services - Calling Web SDK</title>
52+
<link rel="stylesheet" type="text/css" href="styles.css"/>
5153
</head>
5254
<body>
5355
<h4>Azure Communication Services - Calling Web SDK</h4>
@@ -71,7 +73,7 @@ Here's the code:
7173
<br>
7274
<div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
7375
<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>
7577
<br>
7678
<div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
7779
<!-- points to the bundle generated from client.js -->
@@ -80,8 +82,6 @@ Here's the code:
8082
</html>
8183
```
8284

83-
## Azure Communication Services Calling Web SDK Object model
84-
8585
The following classes and interfaces handle some of the major features of the Azure Communication Services Calling SDK:
8686

8787
| Name | Description |
@@ -97,6 +97,13 @@ The following classes and interfaces handle some of the major features of the Az
9797

9898
Create a file in the root directory of your project called `client.js` to contain the application logic for this quickstart. Add the following code to client.js:
9999
```JavaScript
100+
/*************************************
101+
* Example code - client.js *
102+
* Convert this script into a *
103+
* bundle.js that your html index *
104+
* page can use. *
105+
*************************************/
106+
100107
// Make sure to install the necessary dependencies
101108
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
102109
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
@@ -125,7 +132,7 @@ let acceptCallButton = document.getElementById('accept-call-button');
125132
let startVideoButton = document.getElementById('start-video-button');
126133
let stopVideoButton = document.getElementById('stop-video-button');
127134
let connectedLabel = document.getElementById('connectedLabel');
128-
let remoteVideoContainer = document.getElementById('remoteVideoContainer');
135+
let remoteVideosGallery = document.getElementById('remoteVideosGallery');
129136
let localVideoContainer = document.getElementById('localVideoContainer');
130137

131138
/**
@@ -197,8 +204,10 @@ acceptCallButton.onclick = async () => {
197204
}
198205
}
199206

200-
// Subscribe to a call obj.
201-
// Listen for property changes and collection updates.
207+
/**
208+
* Subscribe to a call obj.
209+
* Listen for property changes and collection updates.
210+
*/
202211
subscribeToCall = (call) => {
203212
try {
204213
// Inspect the initial call.id value.
@@ -220,6 +229,7 @@ subscribeToCall = (call) => {
220229
hangUpCallButton.disabled = false;
221230
startVideoButton.disabled = false;
222231
stopVideoButton.disabled = false;
232+
remoteVideosGallery.hidden = false;
223233
} else if (call.state === 'Disconnected') {
224234
connectedLabel.hidden = true;
225235
startCallButton.disabled = false;
@@ -265,8 +275,10 @@ subscribeToCall = (call) => {
265275
}
266276
}
267277

268-
// Subscribe to a remote participant obj.
269-
// Listen for property changes and collection udpates.
278+
/**
279+
* Subscribe to a remote participant obj.
280+
* Listen for property changes and collection udpates.
281+
*/
270282
subscribeToRemoteParticipant = (remoteParticipant) => {
271283
try {
272284
// Inspect the initial remoteParticipant.state value.
@@ -304,43 +316,68 @@ subscribeToRemoteParticipant = (remoteParticipant) => {
304316
* you can choose to destroy the whole 'Renderer', a specific 'RendererView' or keep them, but this will result in displaying blank video frame.
305317
*/
306318
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
307-
// Create a video stream renderer for the remote video stream.
308-
let videoStreamRenderer = new VideoStreamRenderer(remoteVideoStream);
319+
let renderer = new VideoStreamRenderer(remoteVideoStream);
309320
let view;
310-
const renderVideo = async () => {
321+
let remoteVideoContainer = document.createElement('div');
322+
remoteVideoContainer.className = 'remote-video-container';
323+
324+
/**
325+
* isReceiving API is currently an @alpha feature. Do not use in production.
326+
*/
327+
let loadingSpinner = document.createElement('div');
328+
loadingSpinner.className = 'loading-spinner';
329+
remoteVideoStream.on('isReceivingChanged', () => {
311330
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);
331+
if (remoteVideoStream.isAvailable) {
332+
const isReceiving = remoteVideoStream.isReceiving;
333+
const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
334+
if (!isReceiving && !isLoadingSpinnerActive) {
335+
remoteVideoContainer.appendChild(loadingSpinner);
336+
} else if (isReceiving && isLoadingSpinnerActive) {
337+
remoteVideoContainer.removeChild(loadingSpinner);
338+
}
339+
}
317340
} catch (e) {
318-
console.warn(`Failed to createView, reason=${e.message}, code=${e.code}`);
319-
}
341+
console.error(e);
342+
}
343+
});
344+
345+
const createView = async () => {
346+
// Create a renderer view for the remote video stream.
347+
view = await renderer.createView();
348+
// Attach the renderer view to the UI.
349+
remoteVideoContainer.appendChild(view.target);
350+
remoteVideosGallery.appendChild(remoteVideoContainer);
320351
}
321-
322-
remoteVideoStream.on('isAvailableChanged', async () => {
323-
// Participant has switched video on.
324-
if (remoteVideoStream.isAvailable) {
325-
await renderVideo();
326352

327-
// Participant has switched video off.
328-
} else {
329-
if (view) {
353+
// Remote participant has switched video on/off
354+
remoteVideoStream.on('isAvailableChanged', async () => {
355+
try {
356+
if (remoteVideoStream.isAvailable) {
357+
await createView();
358+
} else {
330359
view.dispose();
331-
view = undefined;
360+
remoteVideosGallery.removeChild(remoteVideoContainer);
332361
}
362+
} catch (e) {
363+
console.error(e);
333364
}
334365
});
335366

336-
// Participant has video on initially.
367+
// Remote participant has video on initially.
337368
if (remoteVideoStream.isAvailable) {
338-
await renderVideo();
369+
try {
370+
await createView();
371+
} catch (e) {
372+
console.error(e);
373+
}
339374
}
340375
}
341376

342-
// Start your local video stream.
343-
// This will send your local video stream to remote participants so they can view it.
377+
/**
378+
* Start your local video stream.
379+
* This will send your local video stream to remote participants so they can view it.
380+
*/
344381
startVideoButton.onclick = async () => {
345382
try {
346383
const localVideoStream = await createLocalVideoStream();
@@ -350,8 +387,10 @@ startVideoButton.onclick = async () => {
350387
}
351388
}
352389

353-
// Stop your local video stream.
354-
// This will stop your local video stream from being sent to remote participants.
390+
/**
391+
* Stop your local video stream.
392+
* This will stop your local video stream from being sent to remote participants.
393+
*/
355394
stopVideoButton.onclick = async () => {
356395
try {
357396
await call.stopVideo(localVideoStream);
@@ -365,7 +404,6 @@ stopVideoButton.onclick = async () => {
365404
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
366405
* You may then attach view.target to any UI element.
367406
*/
368-
// Create a local video stream for your camera device
369407
createLocalVideoStream = async () => {
370408
const camera = (await deviceManager.getCameras())[0];
371409
if (camera) {
@@ -374,7 +412,10 @@ createLocalVideoStream = async () => {
374412
console.error(`No camera device found on the system`);
375413
}
376414
}
377-
// Display your local video stream preview in your UI
415+
416+
/**
417+
* Display your local video stream preview in your UI
418+
*/
378419
displayLocalVideoStream = async () => {
379420
try {
380421
localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
@@ -385,7 +426,10 @@ displayLocalVideoStream = async () => {
385426
console.error(error);
386427
}
387428
}
388-
// Remove your local video stream preview from your UI
429+
430+
/**
431+
* Remove your local video stream preview from your UI
432+
*/
389433
removeLocalVideoStream = async() => {
390434
try {
391435
localVideoStreamRenderer.dispose();
@@ -395,13 +439,50 @@ removeLocalVideoStream = async() => {
395439
}
396440
}
397441

398-
// End the current call
442+
/**
443+
* End current call
444+
*/
399445
hangUpCallButton.addEventListener("click", async () => {
400446
// end the current call
401447
await call.hangUp();
402448
});
403449
```
404450

451+
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:
452+
```css
453+
/**
454+
* CSS for styling the loading spinner over the remote video stream
455+
*/
456+
.remote-video-container {
457+
position: relative;
458+
}
459+
.loading-spinner {
460+
border: 12px solid #f3f3f3;
461+
border-radius: 50%;
462+
border-top: 12px solid #ca5010;
463+
width: 100px;
464+
height: 100px;
465+
-webkit-animation: spin 2s linear infinite; /* Safari */
466+
animation: spin 2s linear infinite;
467+
position: absolute;
468+
margin: auto;
469+
top: 0;
470+
bottom: 0;
471+
left: 0;
472+
right: 0;
473+
transform: translate(-50%, -50%);
474+
}
475+
@keyframes spin {
476+
0% { transform: rotate(0deg); }
477+
100% { transform: rotate(360deg); }
478+
}
479+
/* Safari */
480+
@-webkit-keyframes spin {
481+
0% { -webkit-transform: rotate(0deg); }
482+
100% { -webkit-transform: rotate(360deg); }
483+
}
484+
```
485+
405486
## Run the code
406487
Use the `webpack-dev-server` to build and run your app. Run the following command to bundle the application host in a local webserver:
407488

0 commit comments

Comments
 (0)