Get Started With a Simple Video Demo

Add high-quality, high-performance video to your application or website

SignalWire's Video API allows you to host real-time video calls and conferences on your website or app. In this guide, we will use SignalWire APIs to create our own minimal video calling website.

To get started, we will build an extremely simple video application. It will look more or less like this:

First, we will show you how to register to obtain your API key and Project ID. Then, we will see how to write a minimal backend server and, correspondingly, the associated frontend web app.

Obtaining your API key and Project ID

To get access to the SignalWire APIs, you'll have to sign in to the SignalWire website. You can sign up in trial mode, which comes with a $5 credit. This will be plenty to follow along to this guide.

Once you've signed up and verified your email, create a new project. You can give it any name, like "hello world".

After the project has been created, you'll be taken to that project's page.

Two important pieces of information about our project are displayed there:

  • Space URL: You'll use this URL to access SignalWire APIs

  • Project ID: You'll use this UUID to specify your project to the API

There is one more piece of information that we want to note down from our new project. We need to generate an API token to access SignalWire's APIs from our own code. We can do that by navigating to the "API" page from the sidebar:

Once you have navigated to the API page, click on "Create Token". Give it a name so that you can identify it later, and make sure that the "Video" scope is enabled. Then hit "Save". After the token is created, you will see it listed in the table.

🚧

API Tokens are confidential

It is important that the API tokens are kept confidential. They can be used to make API requests on your behalf. Take extreme care to make sure that the tokens don't get pushed to GitHub. Make sure that the tokens aren't publicly accessible, for example they must not be exposed in frontend code. For Node.js backends, you can use dotenv files or similar mechanisms to safely store confidential constants.

We will use this API token to authenticate with SignalWire APIs.

With this, we have collected all the pieces of information that we need to access SignalWire Video API for our getting started app. In particular, we have collected:

  1. The Project ID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
  2. The space URL (.signalwire.com)
  3. The API token (PTxxxxxxxxxxx...)

We can now start writing our application.

Writing a video calling web-app

To help you get started with using SignalWire Video APIs in your own software, we'll code a very minimal video-calling web app.

Before we begin, try using the demo app below. This is what we will build, step by step. Feel free to explore the codebase. You don't necessarily have to understand everything right now, but try tinkering with it.

Demo: https://codesandbox.io/s/zen-hill-0e218

Our application is composed by two parts:

  1. A backend written in Node.js (this is a simple proxy server, so if you prefer you can use really any kind of platform such as PHP or Python instead of Node.js)

  2. A frontend written in JavaScript (SignalWire JS SDK will do most of the work for us)

📘

Why do I need my own backend?

The API token that we have obtained above gives full admin access to your SignalWire APIs. When you are building a web application, or any application that runs on a user's device, the code running on the device should be considered untrusted (secrets can be stolen). Since you must use the API token from a trusted environment, you do so in your own server. This also allows you to build your own authorization policies, instead of giving each client a potential admin access.

A typical network interaction is illustrated in the following figure. Your server can independently get access to the SignalWire servers (for example to get a list of active rooms). Instead, for a client to interact with SignalWire servers, it must first ask your own server to provide it with a limited-scope token (we call this the Video Room Token).

Sequence diagram of a typical network interaction between your client application, your server, and SignalWire servers.Sequence diagram of a typical network interaction between your client application, your server, and SignalWire servers.

Sequence diagram of a typical network interaction between your client application, your server, and SignalWire servers.

📘

What is the difference between the API token and the Video Room Token?

The API token gives full access to SignalWire APIs. Whoever owns the API token can for example delete any room, mute or unmute any participant, and so on without limitations. You must only use the API token in your server to communicate with SignalWire.

The Video Room Token is a limited-scope token that can be used by clients to access SignalWire APIs without knowing the API token. Clients must ask for a Video Room Token to your own server, which in turn will obtain it from SignalWire servers and pass it back to the client. Video Room Tokens are associated to a given <user, room> pair, so you can think of them as a personal key to access a given room, by a given user. Your server decides the permissions for each individual Video Room Token, for example whether they are allowed to mute other users.

Backend

In the SignalWire Video API, the room is the basic unit where video calls and conferences are hosted. When a participant first joins a room, a room session is started. When the last participant leaves a room, the room session ends.

To get access to a room, a client must obtain a Video Room Token. Your server can obtain a Video Room Token through the REST APIs. For example, to get a token for a user with name "john" and a room "office", with only video muting permissions, we can do:

curl --request POST \
     --url 'https://your_space_id.signalwire.com/api/video/room_tokens' \
     --user 'project_id:api_token' \
     --header 'Content-Type: application/json' \
     --data '{"user_name": "john", "room_name": "office", "permissions": ["room.self.video_mute"]}'

The response will look like this, and can be safely sent to the client:

{
  "token": "eyJ0eXAiOiJWUlQiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2MzI0OTE3ODAsImp0aSI6ImM2NmU3ODlkLTJmMjItMTIzNC1hNzMzLThlZjA2MzdmNWI2YiIsInN1YiI6ImExNmQ4ZjllLTIxNjYtNGU4Mi01Njc4LWE0ODQwZjIxN2JjMyIsInUiOiJqb2huIiwiciI6Im9mZmljZSIsInMiOlsicm9vbS5yZWNvcmRpbmciXSwiYWNyIjp0cnVlfQ.qYQwQ1PEnzGbAIb1RoVuYLf0mlqApi15wSC2n7QMCFP4M7jOjOIb_Ia_BhKnbnTHb7sI78d2jS7f_qsFV2OHLw"
}

📘

Since the REST APIs require that you provide both the Project ID and the API token for basic authentication, you should never use this API via the browser. For your application, you should set up a proxy server only accessible to users that you have authorized. Below, we'll see an example of a proxy server written in Node.js.

Instead of using curl, let's then build a Node.js server that accepts an incoming request for a Video Room Token, obtains the Video Room Token, and sends it back to the client. Our server only needs to expose a single endpoint, which we decided to call /get_token (but it can be anything you want).

Assuming we are using Express:

const auth = {
    username: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", // Project ID
    password: "PTxxx...xxx", // API token
};
const apiurl = "https://<signalwire_username>.signalwire.com/api/video"

// Endpoint to request Video Room Token for video call
app.post("/get_token", async (req, res) => {
  let { user_name, room_name } = req.body;
  console.log("Received name", user_name);
  try {
    // get the Video Room Token from SignalWire
    let token = await axios.post(
      apiurl + "/room_tokens",
      {
        user_name: user_name,
        room_name: room_name,
        permissions: [
          "room.list_available_layouts",
          "room.set_layout",
          "room.self.audio_mute",
          "room.self.audio_unmute",
          "room.self.video_mute",
          "room.self.video_unmute"
        ]
      },
      { auth }
    );
    console.log(token.data.token);

    // send the Video Room Token back to the client
    return res.json({ token: token.data.token });
  } catch (e) {
    console.log(e);
    return res.sendStatus(500);
  }
});

Let's break this piece of code down.

  1. The user's user_name will be used to identify them in the video call, so the frontend will send this information when making a request to our /get_token endpoint. Likewise, the room_name determines the name of the room to join. If the room doesn't exist, it will get created automatically.

  2. We send a post request to the room_tokens endpoint of SignalWire REST APIs. The room_tokens endpoint sends back a Video Room Token that we can forward to our client.

Now we have everything we need to start building the frontend.

Frontend

The SignalWire JavaScript SDK makes it surprisingly easy to integrate video calling into any web application. It only takes a few minutes to set up a basic example. First, we need to include the SDK in our HTML.

<!-- Import SignalWire library -->
<script src="https://unpkg.com/@signalwire/[email protected]"></script>

Then you can interact with the SDK using the global variable SignalWire. We'll mainly be interested in the SignalWire.Video.RoomSession class for this guide, but if you'd like to explore this API, feel free to browse the SDK documentation.

To start the video session, we instantiate a new RoomSession object and then we join it:

roomSession = new SignalWire.Video.RoomSession({
  token,
  rootElement: document.getElementById('root'), // the html element in which to display the video
})

try {
  await roomSession.join()
} catch (error) {
  console.error('Error', error)
}

The RoomSession constructor takes at least two parameters: the Video Room Token that will be used to authenticate with SignalWire servers, and the rootElement which is an empty HTML element in your DOM (for example a <div>). This empty HTML element will serve as the container for the video stream. When RoomSession.join is called, the SDK will join the room and the video will appear in rootElement.

To obtain the Video Room Token we perform a POST request to the /get_token endpoint that we wrote in the previous section:

const backendurl = "http://localhost:4000"
let token = await axios.post(backendurl + "/get_token", {
  user_name: username,
  room_name: roomname
});
console.log(token.data)
token = token.data.token

This is all you need to get the video up and running.

Events

The room session object returned by joinRoom supports the standard .on() method to attach event listeners, and the corresponding .off() method to detach them. Some of the events that can be listened to are (find the complete list in our API reference):

  • room.joined: you have joined the room
  • room.updated: a room property has been updated
  • room.ended: the room has ended
  • member.updated: a member property has changed (e.g., video muted)
  • member.updated.audio_muted: the audio_muted state has changed for a member
  • member.updated.video_muted: the video_muted state has changed for a member
  • member.updated.visible: the member is now visible in the video layout
  • member.left: a member left the room
  • layout.changed: the layout of the room has changed

Here's how these events are used in our example program:

roomSession.on("room.joined", e => logevent("You joined the room"))
roomSession.on("member.joined", e => logevent(e.member.name + " has joined the room"))
roomSession.on("member.left", e => logevent(e.member.id + " has left the room"))

Wrap up

All the above ideas can be combined to create the following function, which we'll use (with minor variations) in the frontend:

async function join() {
  const username = $("usernameinput").value.trim();
  const roomname = $("roomnameinput").value.trim();
  gotopage("loading")

  try {
    token = await axios.post(backendurl + "/get_token", {
      user_name: username,
      room_name: roomname
    });
    console.log(token.data);
    token = token.data.token;

    try {
      console.log("Setting up RTC session");
      try {
        roomSession = await SignalWire.Video.joinRoom({
          token,
          rootElementId: "root",
          video: true
        });
      } catch (e) {
        console.log(e);
      }

      roomSession.on("room.joined", e => logevent("You joined the room"))
      roomSession.on("member.joined", e => logevent(e.member.name + " has joined the room"))
      roomSession.on("member.left", e => logevent(e.member.id + " has left the room"))

    } catch (error) {
      console.error("Something went wrong", error);
    }

    gotopage("videoroom");
  } catch (e) {
    console.log(e);
    alert("Error encountered. Please try again.");
    gotopage("getusername");
  }
}

The full code is available here.

Demo

If you want to tinker with an online demo, here is a version running on CodeSandbox: https://codesandbox.io/s/zen-hill-0e218.

Try to take a look at the code and perform changes to see the results in real-time. To give you some inspiration, the demo on CodeSandbox also includes functionalities for changing room layout and for sharing the screen. Here is what you will find:

If, instead, you are looking for a more complete demo, take a look at our article Making a Zoom Clone with SignalWire Video API.

Conclusion

The really neat thing about SignalWire Video technology is that it only streams a single video stream no matter how many participants there are. The video is composited on powerful SignalWire servers by stitching all individual video streams together. So you can go ahead and invite as many people as you like to your virtual video-party. Your app will run without a hitch.


Did this page help you?