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.
let roomBitrate = 2000; //kbps
// 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);
});
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);
};
//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.
//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!