Skip to content

The Power of Data Channels

Oliver Hargreaves Feb 23, 2024 12:49:57 PM

The LiveSwitch SDK is a powerful toolkit that allows developers to add a number of features to their applications.  We have primarily focused on how developers can modify their audio and video streams, and we have reviewed how to add chat functionality to your application. In this post, we will cover another use case for data channels besides chat.

Data Channels operate using the same WebRTC structure as media, and they are part of upstream and downstream connections. Your application can support a number of data channels, and these allow you to pass metadata between users without muddying the chat channel.

Some use cases for these data channels are: transmitting spatial data in a virtual meeting space, transmitting data from an external medical device, and custom status messages.

In this example, we will send custom status messages when a user mutes their audio, and we will change the mute icon to reflect the audio status in our layout.

To begin, let’s create a helper function to initialize our data channel. As part of this helper function, we will create our data channel object, name our data channel, define what to do when the state of the data channel changes, and handle what to do when a message is received on the data channel. 

// handler to initialize data channel

let prepareDataChannel = function () {
    // initialize the data channel
    var dataChannel = new fm.liveswitch.DataChannel("data");

    // create handler for the updates on the data channel
    var onStateChange = function (dataChannel) {
        // write data channel state to the UI
        writeStatus("DataChannel state change : " + dataChannel.getState());
        // once the channel is connected send an initial message
        if (dataChannel.getState() == fm.liveswitch.DataChannelState.Connected) {
            dataChannel.sendDataString("Hello World!");
        }
        // when the channel is closing or has closed, write a message to the UI
        if ( dataChannel.getState() == fm.liveswitch.DataChannelState.Closing || dataChannel.getState() == fm.liveswitch.DataChannelState.Failed ) {
            // Clean up if needed
            writeStatus("dataChannel cleanup");
        }
    };
    // define what to do when a message is received
    var onReceive = function (dataChannelReceiveArgs) {
        if (dataChannelReceiveArgs.getDataString() != null) {
            // as long as the message has content, write the message to the screen
            writeStatus( "System " + "Data channel connection established. Received test message from server: " + dataChannelReceiveArgs.getDataString() );
        }
    };
    // set the 2 handlers on the data channel object
    dataChannel.addOnStateChange(onStateChange);
    dataChannel.setOnReceive(onReceive);
    return dataChannel;
};

We can use this helper function to initialize our data channel when we establish our upstream and downstream connections.

For our upstream connection, here is how we add the data channel.

if (dataChannelsSupported) {

    upstreamDataChannel = prepareDataChannel();
    dataStream = new fm.liveswitch.DataStream(upstreamDataChannel);
}
senderUpstreamConnection = senderChannel.createSfuUpstreamConnection( audioStream, videoStream, dataStream );

For our downstream connections, here is how we add the data channel.

// check if data channels are enabled

if (dataChannelsSupported && remoteConnectionInfo.getHasData() != null) {
    if (remoteConnectionInfo.getHasData() != null) {
        // initialize data channels for later use                      downstreamDataChannel = prepareDataChannel();     
        dataStream = new fm.liveswitch.DataStream(downstreamDataChannel);
    }
}
// create the downstream connection with the dataChannel attached to it
let conn = receiverChannel.createSfuDownstreamConnection( remoteConnectionInfo, audioStream, videoStream, dataStream );

Now when we mute our audio, we can use the following code to send a message to other users.

downstreamDataChannel.sendDataString("Remote Audio Muted!");

Similar to our chat example, you can add more complex data structures as part of the data channel message. To do this, create an in-memory object and encode it using JSON.stringify(messageObject), and then make sure to decode it on the receiving end with JSON.parse(messageString).

To see this in action, take a look at our codepen that demonstrates this functionality.

Additionally, if you would like to try out adding data channel messaging to your application, sign up for our free 30-day trial.

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