Getting Started

Introduction

This hands-on guide will help you create a simple web application that supports making a Video Call using the Dolby.io Communications APIs. You will create a web application to host one-on-one meetings.

By inviting a remote participant you can experience Dolby Voice for yourself. This starter project provides the foundation upon which you can add additional features as you build out your own solutions for events, collaboration, education, live streaming, and much more.

What You'll Need

This project uses the Web SDK and requires a basic knowledge of HTML, CSS, and JavaScript.

Before you start:

  • Sign up for a free Dolby.io account
  • Make sure that you have a working video camera and microphone
  • Make sure that you have Git installed on your computer
  • Copy the Client Access Token from the Dolby.io dashboard

What You'll Build

If you want to skip ahead to the final step the complete starter project is available as a hosted demo for you to try out.

The completed project is contained in a single index.html file, so you can review the full final source code if you don't want to follow along step-by-step.

The final application looks like the one pictured below (Figure 1). When you open the application, it will prompt you for a Dolby.io Client Access Token. You can find a token by clicking API keys listed next to your application on the Dolby.io Dashboard.

Using the App:

  1. Click the Join button to start a conference
  2. Click the Invite button to copy a URL to your clipboard that can be shared. You can ask a colleague or friend to join or open two web browsers yourself to test.
  3. Click the Leave button to end the conference
1868

Figure 1 - Screenshot of starter application

📘

Want to build a mobile app instead?

If you want to build a mobile application, get started with our Android SDK, iOS SDK or React-Native guides.

Build the Application

Each step in this guide will focus on an important component of the basic Video Call. You can either follow along by starting from the boiler plate code in the root of the project or start with the final full source code listing and read the explanations below to understand how it works.

0. Clone the Repository

👍

Open index.html from video-calls folder of project

git clone https://github.com/dolbyio-samples/comms-sdk-web-getting-started.git

This starter project includes a folder for the video-calls application.

cd comms-sdk-web-getting-started/video-calls

We start with the scaffolding of a not yet functional basic single-page web application. This application is using Bootstrap to add basic layout and functional behaviors. This is not required, you can use other libraries that may fit your particular application needs. What's important is that we've added buttons which we can assign behavior to user-actions as we continue. We've also defined a container where the video of each participant will be placed.

View the App Locally

There are many ways you can view static HTML in a web browser.
a) You can open the file directly from your operating system with a file:// path in your browser.
b) Alternatively, you can use a development web server like those available in python3 or node if you have them installed to view http:// in your browser.

npx live-server
python3 -m http.server
open index.html
start index.html

You'll be editing this same index.html to add video conferencing to the layout so want to refresh this application as you make changes in each step that follows.

1. Initialize the Web SDK

👍

Step 1 - Initialize the Web SDK

  • Begin by editing the video-calls/index.html in your favorite text editor or IDE
  • Add the voxeet-web-sdk and dolbyio-auth-helper <script> library dependencies into the index.html file where you will find a matching comment.
  • Add the initializeToken() function and call to your application into the index.html file where you will find a matching comment.

In order to create a video call you will need to include the Dolby.io Communications Web SDK in your application. You can do this by adding a <script> element in the <head> section to include the SDK from a hosted location (CDN).

<!-- Step 1: Include Dolby.io Web SDK -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@voxeet/voxeet-web-sdk/dist/voxeet-sdk.js"></script>
<script type="text/javascript" src="https://developer.dolby.io/demos/comms-sdk-web-getting-started/util/dolbyio-auth-helper.js"></script>

The Dolby.io Communications Web SDK is available globally to your application with the VoxeetSDK namespace. Before going to production, you may want to revisit Initializing the SDK to learn how to use a specific version or use a bundler to npm install this dependency instead of pulling from a CDN.

// Step 1: Define initializeToken()
const initializeToken = () => {
    const token = dolbyio.getAccessToken();
    VoxeetSDK.initializeToken(token, () => new Promise((resolve) => resolve(token)));
    shareMessage("Step 1: Web SDK initialized.");
    return token;
}

In order to establish a connection to the server you must authenticate your application. This is done by calling the VoxeetSDK.initializeToken method which accepts a JSON Web Token (JWT) as a parameter.

The dolbyio-auth-token-helper.js library that we include opens a modal popup to prompt you to paste that client access token copied from the Dolby.io Dashboard and uses it as a parameter. Before production, you'll want to review the Authentication Overview for more detail on how to generate and refresh tokens without copying it from the dashboard. For the purposes of getting started, copying a temporary token will work for now.

You need to call this as part of the main application.

// Step 1: Call initializeToken()
const token = await initializeToken();

If you refresh your app after this step, you should see a message that the "Web SDK initialized".

2. Open a Session

👍

Step 2 - Call VoxeetSDK.session.open()

  • Add the VoxeetSDK.session.open() method to your application and call it from the main() block.

A session is a connection between the client application and the Dolby.io Communications APIs. Initializing a session happens asynchronously because of the network handshake that must occur. When opening a session, you should provide a name. Commonly, this will be the name of the participant who established the session.

// Step 2: Define openSession()
const openSession = async (sessionName) => {
    try {
      await VoxeetSDK.session.open({ name: sessionName });
      shareMessage("Step 2: Session opened.");
    } catch (error) {
      shareMessage("Error: Make sure you have a valid Dolby.io Client Token");
    }
}

You can call this to open a session any time after you've initialized the SDK. Adding it to the main() method will insure the session is already open when the user is ready to join.

// Step 2: Call openSession()
await openSession(name);

If you refresh your app after this step, you should see confirmation that the "Session opened".

3. Create and Join a Conference

👍

Step 3 - Call VoxeetSDK.conference.create() then join()

  • Add the VoxeetSDK.conference.create() method to your application.
  • Add an onclick handler for when a user clicks the Join button.

A conference is a multi-person call where participants exchange audio and/or video with one another. With our application we may be hosting multiple conferences simultaneously in order to support many users.

To distinguish between multiple conferences, you should assign an alias or name. When multiple participants join a conference of the same name using a token for the same Dolby.io application they will be able to meet in a call. This sample application is defaulting to the name web-sdk-starter but can be changed with a given querystring parameter.

// Step 3: Define createAndJoinConference()
const createAndJoinConference = async (conferenceAlias, participantName) => {
    if (!VoxeetSDK.session.isOpen()) { await openSession(participantName); };
    const conferenceOptions = {
        alias: conferenceAlias
    }
    const joinOptions = {
        constraints: { audio: true, video: true }
    };
  
    try {
      const conference = await VoxeetSDK.conference.create(conferenceOptions);
      await VoxeetSDK.conference.join(conference, joinOptions);
      shareMessage(`Step 3: Conference '${conferenceAlias}' created and joined.`);
    } catch (error) {
      console.error(error);
    }
};

The create() and join() methods support a variety of options that are demonstrated in other tutorials.

In order to access media devices from the web browser, for security reasons this should be a user initiated action. When using the application for the first time, upon clicking the Join button the browser will prompt to confirm permissions to access the microphone and video camera.

// Step 3: Call createAndJoinConference()
document.getElementById('btn-join').onclick = async () => {
    await createAndJoinConference(alias, name);
};

Refresh your app, you should be able to confirm that you have joined a conference. You won't see your video until after the next step.

4. Share Video

👍

Step 4 - Handle Conference Event Flows

  • Add the handleConferenceFlow() and call it from the main() function.
  • Add the shareVideo() function.
  • Include a CSS class for mirroring the video.

During a conference a series of events will occur. For a VideoCall, we want to pay attention to when the streamAdded and streamUpdated events occur which correspond to audio and video being started by a participant.

// Step 4: Define handleConferenceFlow()
const handleConferenceFlow = () => {

    VoxeetSDK.conference.on('streamAdded', (participant, stream) => {
        if (stream.type === 'Camera') {
            shareVideo(participant, stream);
        }
    });
    
    VoxeetSDK.conference.on('streamUpdated', (participant, stream) => {
        if (stream.type === 'Camera' && stream.getVideoTracks().length) {
            shareVideo(participant, stream);
        }
    });
  
   // Step 6: Later in step 6 we will define leave handlers here
  
}

The diagram below (Figure 2) shows the series of events that may occur during a conference. Since the same event will fire for audio and screen sharing you must check the type to respond appropriately with the behavior desired in the application.

515

Figure 2 - Sequence of conference events

We want to replace the <div> placeholder elements in the user interface with a new video node that has the media stream attached. That is what the shareVideo() function below handles.

// Step 4: Define shareVideo()
const shareVideo = async (participant, stream) => {
    let perspective = 'self-view';
    if (VoxeetSDK.session.participant.id !== participant.id) {
        perspective = 'remote-view';
    }

    let videoNode = document.getElementById(`video-${participant.id}`);
    if (!videoNode) {
        videoNode = document.createElement("video");

        videoNode.setAttribute("id", `video-${participant.id}`);
        videoNode.setAttribute("height", "100%");
        videoNode.setAttribute("width", "100%");
      
        videoNode.muted = true;
        videoNode.autoplay = true;
        videoNode.playsinline = true;
      
        const videoContainer = document.getElementById(perspective);
        videoContainer.lastElementChild.replaceWith(videoNode);
        videoContainer.firstElementChild.innerText = participant.info.name;
    }

    navigator.attachMediaStream(videoNode, stream);
    shareMessage(`Step 4: Video of participant '${participant.info.name}' started.`);
}

As conference events occur, we test if the current user's session (VoxeetSDK.session.participant.id) matches the session that initiated the media stream (participant.id) so that we can determine whether the participant is the user of the application or somebody else who has been invited into the conference. In the HTML layout for this application, we had defined separate placeholders for self-view and remote-view for the local application user and remote guests respectively.

To help with making that identification the participant.info.name is also displayed as text which will identify the name that was given when an initial session was created in the previous step. By default, the local user was labelled developer. While the name is not guaranteed to be unique, the participant.id is so we use the id when uniquely identifying the video element.

In the case of the local participant, for an improved user experience it is common to mirror the video display so that the effect is similar to when looking in a mirror which is what a user expects and feels more natural.

<!-- Step 4: Mirror Video Effect -->
<style>
  #self-view video {                      /* Flip the display of the local user in self view*/
    -webkit-transform: rotateY(180deg);   /* Safari, Chrome */
    -moz-transform: rotateY(180deg);      /* Firefox */
    transform: rotateY(180deg);           /* Microsoft, etc. */
  }
</style>

This video node should have autoplay enabled so that the video starts as soon as the stream begins. For some web browsers, there can be client-side loopback with audio so setting playsinline to true will avoid that effect.

// Step 4: Call handleConferenceFlow() 
handleConferenceFlow();

Refreshing the app, you should be able to see your own video in a mirror image. Next, we will invite another person into the conference.

5. Invite a Remote Participant

👍

Step 5 - Invite a Remote Participant

  • Add the onclick handler for the Invite button.
  • Invite a coworker or friend to help you test (or open the app twice)

During development, we've been running this application in a local web browser. Unless you configure your local computer to accept outside connections that limits how you can test that everything is working.

To make it easier to test, we've also hosted a version of the same application. By clicking the invite button, it'll include the token, alias, and participant name as query string parameters.

By using the same token and conference alias, you can connect with a remote participant by sharing a URL with those values as parameters. You can also verify your app is working by opening a second web browser.

// Step 5: Create Invite link
document.getElementById('btn-invite').onclick = () => {
    const url = `https://developer.dolby.io/demos/comms-sdk-web-getting-started/index.html?token=${token}&alias=${alias}&name=guest`;
    navigator.clipboard.writeText(url);
    shareMessage(`Share the URL copied to your browser clipboard: ${url}`);
}

📘

Free Credits

Your Dolby.io account comes with free credits to help you get you started. You'll have plenty of minutes to complete and test everything this and other tutorials.

Refresh your app and you should be able to join and invite somebody to verify you can see their video. You may also find it useful to open a second browser so that you can join the same conference as a quick verification step.

6. Leave the Conference

👍

Step 6 - Call VoxeetSDK.conference.leave()

  • Add the VoxeetSDK.conference.leave() method to your application
  • Add the streamRemoved conference event handler
  • Add the onclick handler for the Leave button.

Technically, by closing the web browser a session will eventually end and remove a participant from the conference. We can handle the situation more gracefully than that. The leave() method will cleanly remove a participant from the conference.

// Step 6: Define leaveConference()
const leaveConference = async () => {
   try {
     await VoxeetSDK.conference.leave();
     shareMessage("Getting Started: Conference has ended.");
   } catch (error) {
     console.error(error);
   }
}

Typically, this will be a user generated action when somebody is ready to exit and clicks a button to do so.

// Step 6: Call leaveConference()
document.getElementById('btn-leave').onclick = async () => {
    await leaveConference();
};

When a remote participant clicks a button, your local application is not aware. To keep the applications in sync, a streamRemoved event will occur. You can use this as a signal to remove the video element to signal that the participant is no longer in the conference.

If a participant has left, that event can signal there is no need to keep an active session open.

// Step 6: Define leave handlers
VoxeetSDK.conference.on('streamRemoved', (participant, stream) => {
    const videoNode = document.getElementById(`video-${participant.id}`);
    if (videoNode) {
        videoNode.parentNode.removeChild(videoNode);
    }
});

VoxeetSDK.conference.on("left", async () => {
    await VoxeetSDK.session.close();
});

Refresh your app again to confirm you can leave the conference.

Congratulations, you have now experienced your first Dolby.io Video Call.

Continue Learning

Continue growing your application by following another tutorial to add more features to your project or review samples from the gallery.

Troubleshooting

Some tips for common problems that you may run into when setting up your first project. Use the Developer Tools in your browser for inspecting any console log messages for errors.

Expired or invalid token

ServerError: Expired or invalid token
    at voxeet-web-sdk:1:154564

An invalid token can occur if the token was not copied correctly or has been used for a period of time longer than its original expiration date. You should fetch a new token from the dashboard and try again.

Uninitialized VoxeetSDK

Error: Uninitialized VoxeetSDK
    at _.<anonymous> (voxeet-web-sdk:190:127380)

An Uninitialized VoxeetSDK error may occurs when the token is not being given at all such as when the initializeToken() method receives an undefined value for the token. Make sure the token is being assigned and passed in to the method.

PeerConnectionDisconnectedError

voxeet-web-sdk:1 Uncaught (in promise) PeerConnectionDisconnectedError: Peer connection has been disconnected
    at Object.t.PeerDisconnectedError (voxeet-web-sdk:1:8268)
    at t.default.onPeerConnectionStatus (voxeet-web-sdk:190:43956)

A PeerConnectionDisconnectedError may occur if a session is left open for too long without any activity. The server will timeout and disconnect. To address this type of issue you should properly close a session when the application is not actively being used and then re-open a new session before creating another conference.

Create 401

voxeet-web-sdk:1          POST https://session.voxeet.com/v1/conferences/create 401

A /conferences/create 401 may occur if a session has been closed or not yet opened before trying to create a conference. The session can be expensive to establish depending on network conditions, so make sure you await for the async operation to complete.

accessTokenTimeoutHalfExpiration

Uncaught TypeError: Cannot read properties of undefined (reading 'then')
    at _.accessTokenTimeoutHalfExpiration (voxeet-web-sdk:190:129352)

A TypeError may occur during the authentication flow. The application when attempting to initialize should handle refresh token behavior to fetch a new token from the server. In the web starter project, you may just want to get a new token from the dashboard and try again.

MediaError Already started

voxeet-web-sdk:1 MediaError: Already started
    at d.<anonymous> (voxeet-web-sdk:1:118338)

A MediaError may occur when trying to create a conference of the same name. If you are trying to set a video constraint to true instead of waiting to call startVideo() then you may see this error.

vsl_impl.wasm 404 (Not Found)

https://unpkg.com/@voxeet/vsl_impl.wasm 404 (Not Found) 

To take advantage of the newest features available in SDK 3.7 or later, you will want to make sure that the web assembly modules are found. Using path aliases from CDNs uses redirects and will prevent the SDK from locating these external files. We recommend using the full path to the SDK JavaScript file. See Initializing the SDK and use this CDN instead: https://cdn.jsdelivr.net/npm/@voxeet/voxeet-web-sdk/dist/voxeet-sdk.js.

❗️

Need More Help?

Still having trouble? Getting Started is important. If these troubleshooting tips didn't get you back on track, contact customer support or join the next live developer workshop for some personalized assistance.