Skip to content

Host Larger SFU Meetings in LiveSwitch

Jacob Steele Aug 24, 2023 4:50:13 PM

Are you experiencing issues with your conference quality when the number of participants increases? Don't worry, we have a solution for you! In this article, we'll explore a technique that can significantly enhance the performance of your SFU meetings in LiveSwitch.

Question: My conference works great when we have 3-4 people, but once we get up to 10-15 people our conference starts to fall apart, is there anything we can do?

Answer: To tackle the challenge of accommodating more participants in your conference, we'll leverage bandwidth sharing. The basic idea is to dynamically adjust the bitrate allocation based on the number of active video streams. By intelligently distributing the available bandwidth, we can maintain video quality while accommodating more participants.

 

Implementation Steps

  1. Set Bitrate Targets
    Define the bandwidth requirements or bitrate for a stream that occupies the entire view, such as 720p with a target bitrate of 2 Mbps.
    let roomBitrate = 2000; //kbps
  2. Wire Up SFU Connection Events
    Implement SFU connection open and close events to track the number of upstream connections in the conference.
    // When someone opens a SFU Upstream Connection (your view view will decrease in size for others)
    
    channel.addOnRemoteUpstreamConnectionOpen((remoteConnectionInfo) => {
      adjustBitrateBasedOnStreams(channel, sfuConnection);
    });

    // When someone closes a SFU Upstream Connection (your view view will increase in size for others)
    channel.addOnRemoteUpstreamConnectionClose((remoteConnectionInfo) => {
      adjustBitrateBasedOnStreams(channel, sfuConnection);
    });
  3. Adjust Bitrate Based on Upstreams
    Calculate the bitrate required for each participant based on the number of upstream connections. Dynamically set the maximum send bitrate for the SFU connection to distribute bandwidth effectively.
    let adjustBitrateBasedOnStreams = (channel, sfuConnection) => {
    
      let numberOfUpstreams = channel.getRemoteUpstreamConnectionInfos().length + 1;
      //adjustSfuConnectionBitrate(maxBitRate / numberOfParticipants, sfuConnection);
      let myBitrate = (roomBitrate / numberOfUpstreams);
      var videoStream = sfuConnection.getVideoStream();
      videoStream.setMaxSendBitrate(myBitrate);
    };
  4. Create the SFU Connection
    Create the SFU connection and initiate the connection with the adjusted bitrate.
    //Starts a SFU connection with a chosen bitrate.
    
    let startSfuConnection = (channel, lm) => {
      let audioStream = new fm.liveswitch.AudioStream(lm);
      let videoStream = new fm.liveswitch.VideoStream(lm);
     
      let
    connection = channel.createSfuUpstreamConnection( audioStream, videoStream );
      // Do the initial adjustment
      adjustBitrateBasedOnStreams(channel, connection);
      connection.open();
      return connection;
    };

    let sfuConnection = startSfuConnection(channel, lm);

Please note that conducting an actual SFU conference involves additional elements such as downstream connections and layout managers. However, the code snippets provided above focus solely on the essential implementation steps for adjusting the bitrate based on upstream connections. It serves as a foundational example to help you understand and incorporate the bitrate adjustment functionality into your SFU conference setup.

 

Code Example

//Output a line of text on the page.

let writeStatus = function (message) {
  let sdiv = document.getElementById("status-div");
  let p = document.createElement("p");
  p.innerHTML = message;
  sdiv.appendChild(p);
};

// Constants
let appId = "a27b4c9d-00b5-443a-8419-2bb609356191";
let gateway = "https://cloud.liveswitch.io/";
let secret = "50f6634477754acab5e5502fd0715964a55bd4ffec80457698ec1874638be0b3";
let username = "JakeTestUser1";
let claims = [new fm.liveswitch.ChannelClaim("111113")];
let roomBitrate = 2000;

// Returns a new client
let createClient = () => {
  return new fm.liveswitch.Client(gateway, appId, username, "chrome-js-mac");
};

// Returns a new token
let getToken = (client, claims) => {
  return fm.liveswitch.Token.generateClientRegisterToken(
    appId,
    client.getUserId(),
    client.getDeviceId(),
    client.getId(),
    null,
    claims,
    secret
  );
};

// Creates the client, token and localmedia we will use for the demo.
let client = createClient();
let token = getToken(client, claims);
let localMedia = new fm.liveswitch.LocalMedia({
  video: { width: { ideal: 640 }, height: { ideal: 480 } },
  audio: true
});

//Starts a SFU connection with a chosen bitrate.
let startSfuConnection = (channel, lm) => {
  writeStatus("Opening SFU connection");
  let audioStream = new fm.liveswitch.AudioStream(lm);
  let videoStream = new fm.liveswitch.VideoStream(lm);

  let connection = channel.createSfuUpstreamConnection(
    audioStream,
    videoStream
  );
  // Do the initial adjustment
  adjustBitrateBasedOnStreams(channel, connection);
  connection.open();
  writeStatus("Starting SFU Connection");
  return connection;
};

let adjustBitrateBasedOnStreams = (channel, sfuConnection) => {
  let numberOfUpstreams = channel.getRemoteUpstreamConnectionInfos().length + 1;
  let myBitrate = roomBitrate / numberOfUpstreams;
  writeStatus("Adjusting my bitrate to: " + myBitrate);
  var videoStream = sfuConnection.getVideoStream();
  videoStream.setMaxSendBitrate(myBitrate);
};

//init
localMedia
  .start()
  .then((lm) => {
    // Local Media Started.
    writeStatus("Started Local Media.");
    client
      .register(token)
      .then((channels) => {
        // Client registered.
        writeStatus("Connected to Server.");
        let channel = channels[0];
        // Creates the new SFU Upstream Connection.
        let sfuConnection = startSfuConnection(channel, lm);
        // Subscribes to channel event AddOnRemoteClientJoin.
        channel.addOnRemoteUpstreamConnectionOpen((remoteConnectionInfo) => {
          adjustBitrateBasedOnStreams(channel, sfuConnection);
        });
        channel.addOnRemoteUpstreamConnectionClose((remoteConnectionInfo) => {
          adjustBitrateBasedOnStreams(channel, sfuConnection);
        });
      })
      .fail((ex) => {
        writeStatus("ERROR: " + ex);
      });
  })
  .fail((ex) => {
    writeStatus("ERROR: " + ex);
  });

Feel free to explore our live demo on CodePen!

 

Need assistance in architecting the perfect WebRTC application? Let our team help out! Get in touch with us today!