Search Results for

    Show / Hide Table of Contents

    Quickstart: JavaScript

    Streaming video with the LiveSwitch Web SDK is quick to implement and easy to manage. The easiest way to learn how to use the SDK is to follow these Quickstart instructions.

    Add an Application

    Before you can work on your code, you must add an application in the LiveSwitch Cloud.

    1. Log in to the LiveSwitch Cloud. If you don't have an account, sign up for a LiveSwitch Cloud account.
    2. Click Applications > New.
    3. Enter a name for your application and click OK.
    4. Click the application you just created to open the Application Settings page. You can find your Application ID and Shared Secret on this page. This information is required for generating your authorization token.

    Try Out a Hello World Example on CodePen

    To run the CodePen example, you need to put your Application ID and Shared Secret in the JavaScript code. You can enter them in the boxes below and click Submit. This will populate your Application ID and Shared Secret to all the CodePen examples on this page, so that you don't have to enter them each time when you run a pen.

    This Hello World example demonstrates the basics for streaming media using the LiveSwitch Web SDK.

    Create HTML With Embedded JavaScript

    If you're interested in the CodePen example above and want to get hands-on experience with using the LiveSwitch Web SDK, you can follow the instructions below to create a web page to stream video on it.

    First, create a project and install the LiveSwitch Web SDK. Then, create a JavaScript file and an HTML file to stream video on.

    1. Install node.js and npm if you haven't done so.

    2. In a location where you want to store your project files, create a directory named my-app.

    3. Navigate to your project directory, initialize a project, and install the LiveSwitch Web SDK.

      cd my-app
      npm init -y
      npm install fm.liveswitch
      
    4. In your project directory, create a file named MediaStreamingLogic.js.

    5. Add the following content and replace with your own Application ID and Shared Secret. If you use your own LiveSwitch Server, replace the Gateway URL. This information is used for generating an authorization token.

      var HelloWorld;
      (function (HelloWorld) {
          var MediaStreamingLogic = /** @class */ (function () {
              function MediaStreamingLogic() {
      
                  //Replace the following with the values from the LiveSwitch Console
                  this.applicationId = 'replace-with-your-app-id';
                  this.sharedSecret = 'replace-with-your-shared-secret';
      
                  this.channelId = 'hello-world-channel';
                  this.gatewayUrl = 'https://cloud.liveswitch.io/';
                  this.reRegisterBackoff = 200;
                  this.maxRegisterBackoff = 60000;
                  this.unregistering = false;
                  this.layoutManager = new fm.liveswitch.DomLayoutManager(document.getElementById("my-container"));
                  this.downstreamConnections = {};
                  // Create a new local media for screen capturing.
                  this.localScreenMedia = new fm.liveswitch.LocalMedia(false, true, true);
                  // Log to console.
                  fm.liveswitch.Log.registerProvider(new fm.liveswitch.ConsoleLogProvider(fm.liveswitch.LogLevel.Debug));
              }
              MediaStreamingLogic.getInstance = function () {
                  if (MediaStreamingLogic.instance == null) {
                      MediaStreamingLogic.instance = new MediaStreamingLogic();
                  }
                  return MediaStreamingLogic.instance;
              };
              MediaStreamingLogic.prototype.checkAppCredentialsSet = function () {
                  if (
                      app.applicationId == "replace-with-your-app-id" ||
                      app.applicationId == undefined
                  ) {
                      alert(
                          `ERROR: Your applicationId is not set. Find your Application ID by selecting your chosen Application at your LiveSwitch Admin Console and add it to config.js.`
                      );
                  }
                  if (
                      app.sharedSecret == "replace-with-your-shared-secret" ||
                      app.sharedSecret == undefined
                  ) {
                      alert(
                          `ERROR: Your sharedSecret is not set. Find your Shared Secret by selecting your chosen Application at your LiveSwitch Admin Console and add it to config.js.`
                      );
                  }
              };
              MediaStreamingLogic.prototype.joinAsync = function () {
                  const promise = new fm.liveswitch.Promise();
      
                  // Create a client.
                  this.client = new fm.liveswitch.Client(this.gatewayUrl, this.applicationId);
      
                  // Generate a token (do this on the server to avoid exposing your shared secret).
                  const token = fm.liveswitch.Token.generateClientRegisterToken(
                      this.applicationId,
                      this.client.getUserId(),
                      this.client.getDeviceId(),
                      this.client.getId(),
                      null,
                      [new fm.liveswitch.ChannelClaim(this.channelId)],
                      this.sharedSecret
                  );
      
                  // Allow re-register.
                  this.unregistering = false;
      
                  this.client.addOnStateChange(() => {
                      // Write registration state to log.
                      fm.liveswitch.Log.debug(
                          `Client is ${new fm.liveswitch.ClientStateWrapper(
                              this.client.getState()
                          )}.`
                      );
      
                      if (
                          this.client.getState() === fm.liveswitch.ClientState.Unregistered &&
                          !this.unregistering
                      ) {
                          fm.liveswitch.Log.debug(
                              `Registering with backoff = ${this.reRegisterBackoff}.`
                          );
                          this.displayMessage(
                              "Client unregistered unexpectedly, trying to re-register."
                          );
      
                          // Re-register after a backoff.
                          setTimeout(() => {
                              // Incrementally increase register backoff to prevent runaway process.
                              if (this.reRegisterBackoff <= this.maxRegisterBackoff) {
                                  this.reRegisterBackoff += this.reRegisterBackoff;
                              }
      
                              // Register client with token.
                              this.client
                                  .register(token)
                                  .then((channels) => {
                                      // Reset re-register backoff after successful registration.
                                      this.reRegisterBackoff = 200;
                                      this.onClientRegistered(channels);
                                      promise.resolve(null);
                                  })
                                  .fail((ex) => {
                                      fm.liveswitch.Log.error("Failed to register with Gateway.");
                                      promise.reject(ex);
                                  });
                          }, this.reRegisterBackoff);
                      }
                  });
      
                  // Register client with token.
                  this.client
                      .register(token)
                      .then((channels) => {
                          this.onClientRegistered(channels);
                          promise.resolve(null);
                      })
                      .fail((ex) => {
                          fm.liveswitch.Log.error("Failed to register with Gateway.");
                          promise.reject(ex);
                      });
      
                  return promise;
              };
      
              MediaStreamingLogic.prototype.leaveAsync = function () {
                  // Disable re-register.
                  this.unregistering = true;
                  return this.client
                      .unregister()
                      .then(() =>
                          this.displayMessage(
                              `Client ${this.client.getId()} has successfully unregistered from channel ${this.channel.getId()}.`
                          )
                      )
                      .fail(() => fm.liveswitch.Log.error("Unregistration failed."));
              };
      
              MediaStreamingLogic.prototype.onClientRegistered = function (channels) {
                  // Store our channel reference.
                  this.channel = channels[0];
                  this.displayMessage(
                      `Client ${this.client.getId()} has successfully connected to channel ${this.channel.getId()}, Hello World!`
                  );
      
                  // Open a new SFU upstream connection.
                  this.upstreamConnection = this.openSfuUpstreamConnection(this.localMedia);
      
                  // Open a new SFU downstream connection when a remote upstream connection is opened.
                  this.channel.addOnRemoteUpstreamConnectionOpen((remoteConnectionInfo) =>
                      this.openSfuDownstreamConnection(remoteConnectionInfo)
                  );
              };
      
              MediaStreamingLogic.localMedia = undefined;
              MediaStreamingLogic.prototype.layoutManager = new fm.liveswitch.DomLayoutManager(
                  document.getElementById("my-container")
              );
      
              MediaStreamingLogic.prototype.startLocalMedia = function () {
                  const promise = new fm.liveswitch.Promise();
      
                  if (this.localMedia == null) {
                      // Create local media with audio and video enabled.
                      const audioEnabled = true;
                      const videoEnabled = true;
                      this.localMedia = new fm.liveswitch.LocalMedia(
                          audioEnabled,
                          videoEnabled
                      );
      
                      // Set local media in the layout.
                      this.layoutManager.setLocalMedia(this.localMedia);
                  }
      
                  // Start capturing local media.
                  this.localMedia
                      .start()
                      .then(() => {
                          fm.liveswitch.Log.debug("Media capture started.");
                          promise.resolve(null);
                      })
                      .fail((ex) => {
                          fm.liveswitch.Log.error(ex.message);
                          promise.reject(ex);
                      });
      
                  return promise;
              };
      
              MediaStreamingLogic.prototype.stopLocalMedia = function () {
                  const promise = new fm.liveswitch.Promise();
      
                  // Stop capturing local media.
                  this.localMedia
                      .stop()
                      .then(() => {
                          fm.liveswitch.Log.debug("Media capture stopped.");
                          promise.resolve(null);
                      })
                      .fail((ex) => {
                          fm.liveswitch.Log.error(ex.message);
                          promise.reject(ex);
                      });
      
                  return promise;
              };
      
              MediaStreamingLogic.upstreamConnection = undefined;
      
              MediaStreamingLogic.prototype.openSfuUpstreamConnection = function (localMedia) {
                  // Create audio and video streams from local media.
                  const audioStream = new fm.liveswitch.AudioStream(localMedia);
                  const videoStream = new fm.liveswitch.VideoStream(localMedia);
      
                  // Create a SFU upstream connection with local audio and video.
                  const connection = this.channel.createSfuUpstreamConnection(
                      audioStream,
                      videoStream
                  );
      
                  connection.addOnStateChange((conn) => {
                      fm.liveswitch.Log.debug(
                          `Upstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                              conn.getState()
                          ).toString()}.`
                      );
                  });
      
                  connection.open();
      
                  return connection;
              };
      
              MediaStreamingLogic.prototype.openSfuDownstreamConnection = function (remoteConnectionInfo) {
                  // Create remote media.
                  const remoteMedia = new fm.liveswitch.RemoteMedia();
                  const audioStream = new fm.liveswitch.AudioStream(remoteMedia);
                  const videoStream = new fm.liveswitch.VideoStream(remoteMedia);
      
                  // Add remote media to the layout.
                  this.layoutManager.addRemoteMedia(remoteMedia);
      
                  // Create a SFU downstream connection with remote audio and video.
                  const connection = this.channel.createSfuDownstreamConnection(
                      remoteConnectionInfo,
                      audioStream,
                      videoStream
                  );
      
                  // Store the downstream connection.
                  this.downstreamConnections[connection.getId()] = connection;
      
                  connection.addOnStateChange((conn) => {
                      fm.liveswitch.Log.debug(
                          `Downstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                              conn.getState()
                          ).toString()}.`
                      );
      
                      // Remove the remote media from the layout and destroy it if the remote is closed.
                      if (conn.getRemoteClosed()) {
                          delete this.downstreamConnections[connection.getId()];
                          this.layoutManager.removeRemoteMedia(remoteMedia);
                          remoteMedia.destroy();
                      }
                  });
      
                  connection.open();
                  return connection;
              };
              MediaStreamingLogic.downstreamConnections = { };
      
              MediaStreamingLogic.prototype.displayMessage = function (msg) {
                  const textContainer = document.getElementById("text-container");
                  textContainer.appendChild(document.createTextNode(msg));
                  textContainer.appendChild(document.createElement("br"));
              };
              return MediaStreamingLogic;
          }());
          HelloWorld.MediaStreamingLogic = MediaStreamingLogic;
      })(HelloWorld || (HelloWorld = {}));
      
    6. In your project directory, create a directory named html.

    7. In the html directory, create an HTML file named hello-world.html. In this file, import the LiveSwitch Web SDK to the project and create logic to stream video. For information about how to generate an authorization token, register a client to a channel and stream media, see Media Streaming Basics. The completed file should look like the following:

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
        <meta charset="UTF-8">
        <title>Hello World - JavaScript</title>
      </head>
      
      <body>
        <div style="width: 1210px">
          <div style="float: left;">
            <div id="my-container" style="width:800px; height:600px;"></div>
            <div style="float: left; margin-top: 5px;">
              <div style="margin-left: 0">
                <br />
              </div>
              <br />
            </div>
          </div>
          <div style="margin-left: 810px;">
            <div id="text-container"
              style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
          </div>
        </div>
        <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
        <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
        <script>
          var app = HelloWorld.MediaStreamingLogic.getInstance();
          window.onload = () => {
            app.checkAppCredentialsSet();
            app.startLocalMedia().then(() => {
              // Create and register the client.
              app.joinAsync();
            });
          };
        </script>
      </body>
      </html>
      
    8. Install a light-weight HTTP server:

      npm install -g http-server
      
    9. Start the server:

      http-server
      
    10. Open a browser and navigate to this URL: http://127.0.0.1:8080/html/hello-world.html

    In your browser, you should see video streaming on the web page. You can open the page in multiple browser windows or tabs to emulate a video conference with multiple participants.

    Media Streaming Basics

    Now, let's go over the important steps you need for streaming media.

    Step 1: Register clients to a channel with a token

    In a video conference, you need some way for the participants to communicate with each other. In LiveSwitch, the Gateway fulfills this role. The Gateway connects everyone who wants to participate in a media session. It controls which individuals can communicate with each other and restricts unauthorized access.

    A channel is a unique identifier that describes an audio or video conference. Generally, for each video conference that the participants join in, you will want to have a unique channel associated with it.

    Before you can start a media session, you must create an authorization token and register the client to a channel with the token.

    Step 2: Handle local and remote video and audio

    To stream media in a video conference, you need to produce audio and video. Local media is the audio and video produced and sent by the current user, while remote media is the audio and video sent by other participants.

    LiveSwitch provides local and remote media implementations that you can use directly with all the browsers. To stream media, you need to create a type of connection, such as an SFU connection, to upload and download the local and remote media, as well as create logic to start and stop the local media.

    More Examples

    With the LiveSwitch Web SDK, you can do so much more. Here are some examples that demonstrate how to reregister and unregister, share screen, mute local and remote media, send and receive text messages and files, change video and audio input and output devices, and broadcast.

    Reregister and Unregister

    This example shows how to reregister clients who disconnected and clean up resources by unregistering clients who left a channel. For more information, refer to the TypeScript tutorial on how to reregister and unregister.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Re-register and Unregister</title>
    </head>
    
    <body>
    
      <div>
        <div style="float: left;">
    
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">Join</button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">Leave</button>
              <br />
            </div>
            <br />
            <div id="my-container" style="width:800px; height:600px;"></div>
          </div>
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
        </div>
      </div>
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        const joinBtn = document.getElementById("join-btn");
        const leaveBtn = document.getElementById("leave-btn");
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
    
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        };
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        };
      </script>
    
    </body>
    
    </html>
    

    Mute Media

    This example demonstrates how to mute local media so that other participants can't see or hear you. It also demonstrates how to mute remote media so that you can't see or hear other participants. For more information, refer to the TypeScript tutorial on how to mute media.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Mute Video and Audio</title>
    </head>
    
    <body>
    
      <div>
        <div style="float: left;">
    
          <div style="float: left; margin-top: 5px; margin-bottom: 20px">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">Join</button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">Leave</button>
            </div>
            <br />
            <button type="button" id="mute-audio-btn" style="margin: 2px; margin-top: 10px;">Mute Audio</button>
            <button type="button" id="disable-remote-audio-btn" style="margin: 2px;">Disable Remote Audio</button>
            <br />
            <button type="button" id="mute-video-btn" style="margin: 2px;">Mute Video</button>
            <button type="button" id="disable-remote-video-btn" style="margin: 2px;">Disable Remote Video</button>
            <div id="my-container" style="width:800px; height:600px;"></div>
          </div>
    
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
        </div>
      </div>
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        app.toggleMuteLocalAudio = function () {
          // Retrieve and update the config of the upstream connection.
          const config = this.upstreamConnection.getConfig();
          config.setLocalAudioMuted(!config.getLocalAudioMuted());
          return this.upstreamConnection.update(config);
        };
    
        app.toggleMuteLocalVideo = function () {
          // Retrieve and update the config of the upstream connection.
          const config = this.upstreamConnection.getConfig();
          config.setLocalVideoMuted(!config.getLocalVideoMuted());
          return this.upstreamConnection.update(config);
        };
    
        app.toggleDisableRemoteAudio = function () {
          // Retrieve and update the config of each of the downstream connections.
          for (const id in this.downstreamConnections) {
            const connection = this.downstreamConnections[id];
            const config = connection.getConfig();
            config.setRemoteAudioDisabled(!config.getRemoteAudioDisabled());
            connection.update(config);
          }
        };
    
        app.toggleDisableRemoteVideo = function () {
          // Retrieve and update the config of each of the downstream connections.
          for (const id in this.downstreamConnections) {
            const connection = this.downstreamConnections[id];
            const config = connection.getConfig();
            config.setRemoteVideoDisabled(!config.getRemoteVideoDisabled());
            connection.update(config);
          }
        };
    
        const joinBtn = document.getElementById("join-btn");
        const leaveBtn = document.getElementById("leave-btn");
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        };
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        };
    
    
    
        const muteAudioBtn = document.getElementById("mute-audio-btn");
        const muteVideoBtn = document.getElementById("mute-video-btn");
    
        muteAudioBtn.onclick = () => {
          app.toggleMuteLocalAudio().then(() => {
            muteAudioBtn.innerText = app.localMedia.getAudioMuted() ?
              "Unmute Audio" :
              "Mute Audio";
          });
        };
    
        muteVideoBtn.onclick = () => {
          app.toggleMuteLocalVideo().then(() => {
            muteVideoBtn.innerText = app.localMedia.getVideoMuted() ?
              "Unmute Video" :
              "Mute Video";
          });
        };
    
        const disableRemoteAudioBtn = document.getElementById(
          "disable-remote-audio-btn"
        );
        const disableRemoteVideoBtn = document.getElementById(
          "disable-remote-video-btn"
        );
    
        disableRemoteAudioBtn.onclick = () => {
          app.toggleDisableRemoteAudio();
          disableRemoteAudioBtn.innerText =
            disableRemoteAudioBtn.innerText.indexOf("Disable") !== -1 ?
              "Enable Remote Audio" :
              "Disable Remote Audio";
        };
    
        disableRemoteVideoBtn.onclick = () => {
          app.toggleDisableRemoteVideo();
          disableRemoteVideoBtn.innerText =
            disableRemoteVideoBtn.innerText.indexOf("Disable") !== -1 ?
              "Enable Remote Video" :
              "Disable Remote Video";
        };
      </script>
    
    </body>
    
    </html>
    

    Share Screens

    This example demonstrates how to share screens at a conference. To share a user's screen, create an additional "screen share" local media object to capture the local user's screen. Then, create an additional upstream connection to upload the "screen share" local media. Other participants get the "screen share" downstream connection the same way as the other media downstream. For more information, refer to the TypeScript tutorial on how to share screens.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Share Screen</title>
    </head>
    
    <body>
      <div>
        <div style="float: left;">
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">Join</button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">Leave</button>
              <br />
              <button type="button" id="screenshare-toggle-btn" style="margin: 2px;">Toggle ScreenShare</button>
            </div>
            <br />
            <div id="my-container" style="width:800px; height:600px;"></div>
          </div>
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
        </div>
      </div>
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        // Create a new local media for screen capturing.
        app.localScreenMedia = new fm.liveswitch.LocalMedia(false, true, true);
    
        app.screenSharingUpstreamConnection = undefined;
    
        app.toggleScreenSharing = function () {
          if (
            this.localScreenMedia.getState() === fm.liveswitch.LocalMediaState.New ||
            this.localScreenMedia.getState() === fm.liveswitch.LocalMediaState.Stopped
          ) {
            this.startScreenSharing();
          } else {
            this.stopScreenSharing();
          }
        }
    
        app.startScreenSharing = function () {
          // Start screen capturing.
          this.localScreenMedia
            .start()
            .then(() => {
              fm.liveswitch.Log.debug("Screen capture started.");
    
              // Open a SFU upstream connection for screen sharing.
              this.screenSharingUpstreamConnection = this.openSfuUpstreamConnection(
                this.localScreenMedia
              );
            })
            .fail(() => {
              fm.liveswitch.Log.error("Screen capture could not be started.");
            });
    
          // Add the screen sharing media to the layout.
          this.layoutManager.addRemoteMedia(this.localScreenMedia);
        }
    
        app.stopScreenSharing = function () {
          // Close the screen sharing upstream connection.
          this.screenSharingUpstreamConnection.close().then(() => {
            // Stop the screen capturing and remove it from the layout.
            this.localScreenMedia.stop();
            this.layoutManager.removeRemoteMedia(this.localScreenMedia);
          });
        }
    
    
    
        const joinBtn = document.getElementById("join-btn");
        const leaveBtn = document.getElementById("leave-btn");
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
    
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        };
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        };
    
        const screenShareToggleBtn = document.getElementById("screenshare-toggle-btn");
        screenShareToggleBtn.onclick = () => app.toggleScreenSharing();
        app.localScreenMedia.addOnVideoStarted(() => {
          app.localScreenMedia.getVideoTrack().addOnStopped(() => {
            app.toggleScreenSharing();
          });
        });
      </script>
    </body>
    
    </html>
    

    Change Media Devices

    This example demonstrates how to implement the ability for your users to change their media devices in a video conference. For example, you can have users switch to a different camera or microphone. For more information, refer to the Change Media Devices topic.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8" />
      <title>Change Devices</title>
    </head>
    
    <body>
      <div>
        <div style="float: left;">
    
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">
                Join
              </button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">
                Leave
              </button>
              <br />
            </div>
            <br />
            <label for="audioInputs">Audio Inputs:</label>
            <select id="audioInputs" style="margin: 2px;"> </select>
            <br />
            <label for="videoInputs">Video Inputs:</label>
            <select id="videoInputs" style="margin: 2px;"> </select>
            <br />
            <label for="audioOutputs">Audio Outputs:</label>
            <select id="audioOutputs" style="margin: 2px;"> </select>
            <br />
            <div id="my-container" style="width:800px; height:600px;"></div>
          </div>
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
          <div style="margin-top: 5px; width: 100%;"></div>
        </div>
      </div>
      <script src="./../node_modules/fm.liveswitch/fm.liveswitch.js"></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        // Switch audio input device.
        app.getAudioInputs = function () {
          return this.localMedia.getAudioInputs();
        };
    
        app.setAudioInput = function (input) {
          this.localMedia.changeAudioSourceInput(input);
        };
    
        // Get a list of video input devices.
        app.getVideoInputs = function () {
          return this.localMedia.getVideoInputs();
        };
    
        // Switch video input device.
        app.setVideoInput = function (input) {
          this.localMedia.changeVideoSourceInput(input);
        };
    
        // Switch audio output device.
        app.getAudioOutputs = function () {
          const remoteMedia = new fm.liveswitch.RemoteMedia(true, true);
          return remoteMedia.getAudioSinkOutputs();
        }
    
        app.setAudioOutput = function (output) {
          // Set the audio output device for each downstream connection.
          for (const connectionID in this.downstreamConnections) {
            const connection = this.downstreamConnections[connectionID];
            const remoteMedia = connection.getAudioStream().getRemoteMedia();
            remoteMedia.changeAudioSinkOutput(output);
          }
        }
    
        const joinBtn = document.getElementById('join-btn');
        const leaveBtn = document.getElementById('leave-btn');
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
            loadInputs();
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        }
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              clearInputs();
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        }
    
    
        function loadInputs() {
          app.getAudioInputs().then(audioInputs => {
            const selectBox = document.getElementById('audioInputs')
            for (const input of audioInputs) {
              const option = document.createElement('option');
              option.text = input.getName();
              selectBox.add(option);
            };
            selectBox.onchange = () =>
              app.setAudioInput(audioInputs[selectBox.selectedIndex]);
          });
    
          app.getVideoInputs().then(videoInputs => {
            const selectBox = document.getElementById('videoInputs');
            for (const input of videoInputs) {
              const option = document.createElement('option');
              option.text = input.getName();
              selectBox.add(option);
            }
            selectBox.onchange = () =>
              app.setVideoInput(videoInputs[selectBox.selectedIndex]);
          })
    
          app.getAudioOutputs().then(audioOutputs => {
            const selectBox = document.getElementById('audioOutputs');
            for (const output of audioOutputs) {
              const option = document.createElement('option');
              option.text = output.getName();
              selectBox.add(option);
            }
            selectBox.onchange = () =>
              app.setAudioOutput(audioOutputs[selectBox.selectedIndex]);
          })
          // </Change Devices>
        }
    
        function clearInputs() {
          // Remove the lists of available devices.
          removeOptions(document.getElementById('audioInputs'))
          removeOptions(document.getElementById('videoInputs'))
          removeOptions(document.getElementById('audioOutputs'))
        }
    
        function removeOptions(selectElement) {
          const length = selectElement.options.length - 1
          for (let i = length; i >= 0; i--) {
            selectElement.remove(i)
          }
        }
      </script>
    </body>
    
    </html>
    

    Text Chat

    This example demonstrates how to text chat in a conference. DataChannel is used for sending and receiving text messages. DataChannel supports the transfer of binary or text data. For more information, refer to the TypeScript tutorial on how to send and receive text messages.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Text Chat</title>
    </head>
    
    <body>
    
      <div>
        <div style="float: left;">
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">Join</button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">Leave</button>
              <br />
            </div>
            <div id="my-container" style="width:800px; height:600px;"></div>
            <br />
          </div>
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
          <div style="position: relative; margin-top: 5px; width: 100%;">
            <div style="float: left;">
              <input type="text" id="inputbox"
                style="box-sizing: border-box; width: 300px; height: 30px; border: 1px solid black; padding: 4px; ">
                
            </div> 
            <div style="margin-left: 71%;">
              <button type="button" id="send-message-btn"
                style="position: absolute; width: 100px; height: 30px; vertical-align: top;">Send</button>
            </div>
          </div>
        </div>
      </div>
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        app.openSfuUpstreamConnection = function (localMedia) {
          // Create audio and video streams from local media.
          const audioStream = new fm.liveswitch.AudioStream(localMedia);
          const videoStream = new fm.liveswitch.VideoStream(localMedia);
    
          // Create data channel and stream.
          const dataChannelText = new fm.liveswitch.DataChannel("text-channel");
          if (this.textChannel == null) {
            this.textChannel = dataChannelText;
          }
    
          const dataStream = new fm.liveswitch.DataStream(dataChannelText);
    
          // Create a SFU upstream connection with local audio and video.
          const connection = this.channel.createSfuUpstreamConnection(
            audioStream,
            videoStream,
            dataStream
          );
    
          connection.addOnStateChange((conn) => {
            fm.liveswitch.Log.debug(
              `Upstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                conn.getState()
              ).toString()}.`
            );
          });
    
          connection.open();
    
          return connection;
        }
    
        app.openSfuDownstreamConnection = function (remoteConnectionInfo) {
          // Create remote media.
          const remoteMedia = new fm.liveswitch.RemoteMedia();
          const audioStream = new fm.liveswitch.AudioStream(remoteMedia);
          const videoStream = new fm.liveswitch.VideoStream(remoteMedia);
    
          // Add remote media to the layout.
          this.layoutManager.addRemoteMedia(remoteMedia);
    
          // Create data channel and set onReceive.
          const dataChannelText = new fm.liveswitch.DataChannel("text-channel");
          dataChannelText.setOnReceive((e) => this.onTextReceive(e));
    
          // Create data stream with the data channel.
          const dataStream = new fm.liveswitch.DataStream(dataChannelText);
    
          // Create a SFU downstream connection with remote audio and video.
          const connection = this.channel.createSfuDownstreamConnection(
            remoteConnectionInfo,
            audioStream,
            videoStream,
            dataStream
          );
    
          // Store the downstream connection.
          this.downstreamConnections[connection.getId()] = connection;
    
          connection.addOnStateChange((conn) => {
            fm.liveswitch.Log.debug(
              `Downstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                conn.getState()
              ).toString()}.`
            );
    
            // Remove the remote media from the layout and destroy it if the remote is closed.
            if (conn.getRemoteClosed()) {
              delete this.downstreamConnections[connection.getId()];
              this.layoutManager.removeRemoteMedia(remoteMedia);
              remoteMedia.destroy();
            }
          });
    
          connection.open();
          return connection;
        }
    
        app.textChannel = undefined;
    
        app.sendMessage = function (msg) {
          // Prepend user ID to the message.
          const chatMsg = `${this.client.getUserId()}: ${msg}`;
    
          // Display the message locally.
          this.displayMessage(chatMsg);
    
          // Send the message through the data channel.
          this.textChannel.sendDataString(chatMsg);
        }
    
        app.onTextReceive = function (dataChannelReceiveArgs) {
          this.displayMessage(dataChannelReceiveArgs.getDataString());
        }
    
        const joinBtn = document.getElementById("join-btn");
        const leaveBtn = document.getElementById("leave-btn");
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
    
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        };
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        };
    
        const sendMessageBtn = document.getElementById("send-message-btn");
    
        sendMessageBtn.onclick = () => {
          const inputbox = document.getElementById("inputbox");
          const msg = inputbox.value;
          if (msg != null && msg.length > 0) {
            inputbox.value = "";
            app.sendMessage(msg);
          }
        };
      </script>
    
    </body>
    
    </html>
    

    Transfer Files

    This example demonstrates how to add an additional DataChannel to transfer binary data. For more information, refer to the TypeScript tutorial on how to transfer files.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>File Transfer</title>
    </head>
    
    <body>
      <div>
        <div style="float: left;">
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="join-btn" style="margin: 2px;">Join</button>
              <button type="button" id="leave-btn" style="margin: 2px; display: none;">Leave</button>
              <br />
            </div>
            <div id="my-container" style="width:800px; height:600px;"></div>
            <br />
          </div>
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
          <div style="margin-top: 5px; width: 100%;">
            <button type="button" id="upload-file-btn" style="width: 400px; height: 30px;">📎</button>
            <input type="file" id="fileId" style="display:none">
          </div>
        </div>
      </div>
    
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        var app = HelloWorld.MediaStreamingLogic.getInstance();
    
        app.saveFile = function (fileName, data) {
          // Prompt the user to download the file.
          if (
            confirm(
              `You've received a file from this channel, do you wish to download ${fileName}?`
            ) === true
          ) {
            const file = new Blob([data]);
            if (window.navigator.msSaveOrOpenBlob) {
              // For IE.
              window.navigator.msSaveOrOpenBlob(file, fileName);
            } else {
              // For other browsers.
              const a = document.createElement("a");
              const url = URL.createObjectURL(file);
              a.href = url;
              a.download = fileName;
              document.body.appendChild(a);
              a.click();
              setTimeout(() => {
                document.body.removeChild(a);
                window.URL.revokeObjectURL(url);
              }, 0);
            }
          }
        }
    
        fileChannel: undefined,
    
          app.sendFile = function (fileName, file) {
            // Convert the file name to a byte array.
            const fileNameBytes = DataBytesConverter.toDataBytes(fileName);
    
            // Convert the file name byte array's length to a byte array.
            const fileNameBytesLength = DataBytesConverter.intToDataBytes(
              fileNameBytes.length
            );
    
            // Wrap the file into a data buffer.
            const dataBuffer = fm.liveswitch.DataBuffer.wrap(file);
    
            // Prepend the byte arrays to the data buffer.
            dataBuffer.prepend(fm.liveswitch.DataBuffer.wrap(fileNameBytes));
            dataBuffer.prepend(fm.liveswitch.DataBuffer.wrap(fileNameBytesLength));
    
            // Send the data buffer through the data channel.
            this.fileChannel.sendDataBytes(dataBuffer);
          }
    
        app.onFileReceive = function (dataChannelReceiveArgs) {
          // Retrieve the data buffer.
          const dataBuffer = dataChannelReceiveArgs.getDataBytes();
    
          // Get the offset to the actual data.
          const dataOffset = dataBuffer.getIndex();
    
          // Get the file name byte array's length and retrieve the file name.
          const fileNameBytesLength = DataBytesConverter.toInt(
            dataBuffer.getData().slice(dataOffset)
          );
          const fileName = DataBytesConverter.toString(
            dataBuffer
              .getData()
              .slice(dataOffset + 4, dataOffset + 4 + fileNameBytesLength)
          );
    
          // Get the offset to the file's data and the file size.
          const fileOffset = dataOffset + 4 + fileNameBytesLength;
          const fileSize = dataBuffer.getLength() - 4 - fileNameBytesLength;
    
          // Retrieve the file's data using the offset and the file's size.
          const dataBytes = dataBuffer
            .getData()
            .slice(fileOffset, fileOffset + fileSize);
    
          // Save the file.
          this.saveFile(fileName, dataBytes);
        }
    
        app.openSfuUpstreamConnection = function (localMedia) {
          // Create audio and video streams from local media.
          const audioStream = new fm.liveswitch.AudioStream(localMedia);
          const videoStream = new fm.liveswitch.VideoStream(localMedia);
    
          const dataChannelFile = new fm.liveswitch.DataChannel("file-channel");
          if (this.fileChannel == null) {
            this.fileChannel = dataChannelFile;
          }
    
          const dataStream = new fm.liveswitch.DataStream(dataChannelFile);
    
          // Create a SFU upstream connection with local audio and video.
          const connection = this.channel.createSfuUpstreamConnection(
            audioStream,
            videoStream,
            dataStream
          );
    
          connection.addOnStateChange((conn) => {
            fm.liveswitch.Log.debug(
              `Upstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                conn.getState()
              ).toString()}.`
            );
          });
    
          connection.open();
    
          return connection;
        }
    
        app.openSfuDownstreamConnection = function (remoteConnectionInfo) {
          // Create remote media.
          const remoteMedia = new fm.liveswitch.RemoteMedia();
          const audioStream = new fm.liveswitch.AudioStream(remoteMedia);
          const videoStream = new fm.liveswitch.VideoStream(remoteMedia);
    
          // Add remote media to the layout.
          this.layoutManager.addRemoteMedia(remoteMedia);
    
          const dataChannelFile = new fm.liveswitch.DataChannel("file-channel");
          dataChannelFile.setOnReceive((e) => this.onFileReceive(e));
    
          // Create data stream with the data channel.
          const dataStream = new fm.liveswitch.DataStream(dataChannelFile);
    
          // Create a SFU downstream connection with remote audio and video.
          const connection = this.channel.createSfuDownstreamConnection(
            remoteConnectionInfo,
            audioStream,
            videoStream,
            dataStream
          );
    
          // Store the downstream connection.
          this.downstreamConnections[connection.getId()] = connection;
    
          connection.addOnStateChange((conn) => {
            fm.liveswitch.Log.debug(
              `Downstream connection is ${new fm.liveswitch.ConnectionStateWrapper(
                conn.getState()
              ).toString()}.`
            );
    
            // Remove the remote media from the layout and destroy it if the remote is closed.
            if (conn.getRemoteClosed()) {
              delete this.downstreamConnections[connection.getId()];
              this.layoutManager.removeRemoteMedia(remoteMedia);
              remoteMedia.destroy();
            }
          });
    
          connection.open();
          return connection;
        }
    
        const DataBytesConverter = {
          /**
           * Convert a string into Uint8Array
           */
          toDataBytes: function (str) {
            return new TextEncoder().encode(str);
          },
    
          /**
           * Convert an int into Uint8Array
           */
          intToDataBytes: function (x) {
            return new Uint8Array([
              (x & 0xff000000) >> 24,
              (x & 0x00ff0000) >> 16,
              (x & 0x0000ff00) >> 8,
              x & 0x000000ff
            ]);
          },
    
          /**
           * Convert Uint8Array into int
           */
          toInt: function (dataBytes) {
            return new DataView(dataBytes.buffer).getUint32(0);
          },
    
          /**
           * Convert Uint8Array into string
           */
          toString: function (dataBytes) {
            return new TextDecoder().decode(dataBytes);
          }
        };
    
        const joinBtn = document.getElementById("join-btn");
        const leaveBtn = document.getElementById("leave-btn");
    
        joinBtn.onclick = () => {
          app.checkAppCredentialsSet();
          app.startLocalMedia().then(() => {
    
            // Create and register the client.
            app.joinAsync().then(() => {
              joinBtn.style.display = "none";
              leaveBtn.style.display = "inline-block";
            });
          });
        };
    
        leaveBtn.onclick = () => {
          app.stopLocalMedia().then(() => {
            app.leaveAsync().then(() => {
              joinBtn.style.display = "inline-block";
              leaveBtn.style.display = "none";
            });
          });
        };
    
        const uploadFileBtn = document.getElementById("upload-file-btn");
        const fileInput = document.getElementById("fileId");
    
        uploadFileBtn.onclick = () => fileInput.click();
    
        fileInput.onchange = (e) => {
          const file = e.target.files[0];
          if (!file) {
            return;
          }
    
          // Read the file to an array buffer and send it through the data channel.
          const reader = new FileReader();
          reader.onload = (e) => {
            const contents = new Uint8Array(e.target.result);
            app.sendFile(file.name, contents);
            alert(`You've sent ${file.name} to the channel!`);
          };
          reader.readAsArrayBuffer(file);
        };
      </script>
    
    </body>
    
    </html>
    

    Broadcast

    This example demonstrates how to do broadcasting. LiveSwitch supports massive-scale broadcasting of audio and video data from SFU upstream connections to SFU downstream connections. In broadcasting, there is one Broadcaster and multiple Receivers. For more information, refer to the TypeScript tutorial on how to do broadcasting.

    Important

    When generating tokens for a broadcast, set the disableSendAudio and disableSendVideo properties on the channel claim. This ensures that viewers can't override a broadcast when you use Media IDs. For more information, refer to the Set Connection Permission section.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title> Broadcaster and Receiver</title>
    </head>
    
    <body>
      <div>
        <div style="float: left;">
          <div style="float: left; margin-top: 5px;">
            <div style="margin-left: 0">
              <button type="button" id="broadcast-btn" style="margin: 2px;">Broadcast</button>
              <button type="button" id="receive-btn" style="margin: 2px;">Receive</button>
              <br />
            </div>
            <div id="my-container" style="width:800px; height:600px;"></div>
            <br />
          </div>
    
        </div>
        <div style="margin-left: 810px;">
          <div id="text-container"
            style="box-sizing: border-box; width: 400px; height: 600px; border: 1px solid black; padding: 5px;"></div>
        </div>
      </div>
      <script src='./../node_modules/fm.liveswitch/fm.liveswitch.js'></script>
      <script type="text/javascript" src='../MediaStreamingLogic.js'></script>
      <script>
        let app = HelloWorld.MediaStreamingLogic.getInstance();
    
        // <broadcast>
        const broadcastBtn = document.getElementById("broadcast-btn");
        const receiveBtn = document.getElementById("receive-btn");
    
        broadcastBtn.onclick = () => {
          app.checkAppCredentialsSet();
          startAs(new Broadcaster());
        }
        receiveBtn.onclick = () => {
          app.checkAppCredentialsSet();
          startAs(new Receiver());
        }
    
        function startAs(participant) {
    
          // Start capturing local media (broadcaster only).
          participant.start().then(() => {
            // Register and establish connection.
            participant.joinAsync().then(() => {
              broadcastBtn.disabled = true;
              receiveBtn.disabled = true;
            });
          });
        }
        // </broadcast>
    
        class Participant {
          // Media Id, which will be used by both the broadcaster and receivers.
          mediaId = "your-media-id";
    
          // Layout manager
          layoutManager = new fm.liveswitch.DomLayoutManager(
            document.getElementById("my-container")
          );
    
          // Fields
          client = undefined;
          channel = undefined;
    
          constructor() { }
    
          joinAsync() {
            const promise = new fm.liveswitch.Promise();
    
            // Create the client.
            this.client = new fm.liveswitch.Client(app.gatewayUrl, app.applicationId);
    
            // Write registration state to log.
            this.client.addOnStateChange(() =>
              fm.liveswitch.Log.debug(
                `Client is ${new fm.liveswitch.ClientStateWrapper(
                  this.client.getState()
                )}.`
              )
            );
    
            // Generate a token (do this on the server to avoid exposing your shared secret).
            const token = fm.liveswitch.Token.generateClientRegisterToken(
              app.applicationId,
              this.client.getUserId(),
              this.client.getDeviceId(),
              this.client.getId(),
              null,
              [new fm.liveswitch.ChannelClaim(app.channelId)],
              app.sharedSecret
            );
    
            // Register client with token.
            this.client
              .register(token)
              .then((channels) => {
                // Store our channel reference.
                this.channel = channels[0];
    
                fm.liveswitch.Log.info(
                  `Client ${this.client.getId()} has successfully connected to channel ${this.channel.getId()}, Hello World!`
                );
                app.displayMessage(
                  `Client ${this.client.getId()} has successfully connected to channel ${this.channel.getId()}, Hello World!`
                );
    
                this.establishConnection();
                promise.resolve(null);
              })
              .fail((ex) => {
                fm.liveswitch.Log.error("Failed to register with Gateway.");
                promise.reject(ex);
              });
    
            return promise;
          }
    
          // Abstract methods
          establishConnection() { }
          start() { }
          stop() { }
        }
    
        class Broadcaster extends Participant {
          localMedia = undefined;
    
          establishConnection() {
            // Create a SFU upstream connection with local audio and video and the presentation ID.
            const audioStream = new fm.liveswitch.AudioStream(this.localMedia);
            const videoStream = new fm.liveswitch.VideoStream(this.localMedia);
            const connection = this.channel.createSfuUpstreamConnection(
              audioStream,
              videoStream,
              this.mediaId
            );
    
            connection.open();
          }
    
          start() {
            const promise = new fm.liveswitch.Promise();
    
            // Create local media with audio and video enabled.
            const audioEnabled = true;
            const videoEnabled = true;
            this.localMedia = new fm.liveswitch.LocalMedia(audioEnabled, videoEnabled);
    
            // Set local media in the layout.
            this.layoutManager.setLocalMedia(this.localMedia);
    
            // Start local media capturing.
            this.localMedia
              .start()
              .then(() => {
                fm.liveswitch.Log.debug("Media capture started.");
                promise.resolve(null);
              })
              .fail((ex) => {
                fm.liveswitch.Log.error(ex.message);
                promise.reject(ex);
              });
    
            return promise;
          }
    
          stop() {
            const promise = new fm.liveswitch.Promise();
    
            // Stop local media capturing.
            this.localMedia
              .stop()
              .then(() => {
                fm.liveswitch.Log.debug("Media capture stopped.");
                promise.resolve(null);
              })
              .fail((ex) => {
                fm.liveswitch.Log.error(ex.message);
                promise.reject(ex);
              });
    
            return promise;
          }
        }
    
        class Receiver extends Participant {
          remoteMedia = undefined;
    
          establishConnection() {
            // Create remote media.
            this.remoteMedia = new fm.liveswitch.RemoteMedia();
            const audioStream = new fm.liveswitch.AudioStream(this.remoteMedia);
            const videoStream = new fm.liveswitch.VideoStream(this.remoteMedia);
    
            // Add remote media to the layout.
            this.layoutManager.addRemoteMedia(this.remoteMedia);
    
            // Create a SFU downstream connection with remote audio and video and the presentation ID.
            const connection = this.channel.createSfuDownstreamConnection(
              this.mediaId,
              audioStream,
              videoStream
            );
    
            connection.addOnStateChange((conn) => {
              // Remove the remote media from the layout if the remote is closed.
              if (conn.getRemoteClosed()) {
                this.layoutManager.removeRemoteMedia(this.remoteMedia);
              }
            });
    
            connection.open();
          }
    
          // Not needed because receiver only receives media from the broadcaster.
          start() {
            return fm.liveswitch.Promise.resolveNow();
          }
    
          // Not needed because receiver only receives media from the broadcaster.
          stop() {
            return fm.liveswitch.Promise.resolveNow();
          }
        }
      </script>
    </body>
    
    </html>
    

    For the complete example apps that this page describes, check out this GitHub repository.

    Learn More

    To learn how to install and use the LiveSwitch Web SDK to develop an app using Vue, Angular, or React, refer to the following links:

    • Set up a LiveSwitch Vue Project
    • Set up a LiveSwitch Angular Project
    • Set up a LiveSwitch React Project
    In This Article
    Back to top Copyright © LiveSwitch Inc. All Rights Reserved.Documentation for LiveSwitch Version 1.6.2