Search Results for

    Show / Hide Table of Contents

    Data Channels

    LiveSwitch supports data channels for MCU and SFU connections. Data channels support the transfer of binary or text data. You can use data channels to transfer any sort of data outside of the audio or video use case typically associated with RTC. Data channels are not supported for the Media-over-WebSockets protocol.

    Note

    About the code examples on this page:

    • For .NET MAUI and Unity, use the C# code.
    • For macOS, use the iOS code.

    Platform Support and Interoperability

    LiveSwitch supports data channels across all platforms except in the older version of Microsoft Edge which ran on the Trident engine.

    Note

    Edge Chromium offers full data channel support.

    If you are targeting the Trident version of Microsoft Edge, you must detect unsupported browsers for data channels in your Web app code.

    Detect Unsupported Browsers

    if (fm.liveswitch.Util.isEdge())
    {
        // DataChannels are not supported as this is the older version of Edge ...
    }
    

    If you have a peer offering a data stream as part of the its SDP and a peer who does not, LiveSwitch’s MCU and SFU connections can establish a connection. The Media Server acts as a intermediary for MCU and SFU connections. For data channel interoperability, the Media Server negotiates a data stream for the peer that supports it. In addition, the Media Server negotiates no data stream for the peer that doesn't. Data doesn't flow between peers in this case, but the connection is established, and audio and video flows between the peers normally.

    The following table lists connection types that can succeed between peers offering a data stream with the Trident version of Microsoft Edge.

    Android

    (including .NET MAUI)
    iOS

    (including .NET MAUI)
    .NET mac OS Chrome Firefox Safari IE Edge Chromium Edge Trident
    Edge Trident MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1 MCU/SFU1
    Edge Chromium MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    IE MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    Safari MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    Firefox MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    Chrome MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    mac OS MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    .NET MCU/SFU/P2P MCU/SFU/P2P MCU/SFU/P2P
    iOS
    (including .NET MAUI)
    MCU/SFU/P2P MCU/SFU/P2P
    Android
    (including .NET MAUI)
    MCU/SFU/P2P

    1: MCU/SFU: Connections are successful regardless of platform, but peers on the Trident version of Microsoft Edge can't send or receive data sent by others because of the lack of DataChannel support. Audio or video behaves normally.

    P2P Best Practice

    P2P connection attempts to a peer on the Trident version of Edge that includes a data stream fail. The best practice is to separate your data stream into a separate P2P connection. This allows you to depend on your P2P audio/video connection when the data stream connection isn't possible.

    Create Data Channels

    Adding a data channel is straightforward. Create the data channel, wrap it in a data stream, and then provide that stream to your connection object like you would for an audio or video stream. You can have up to 65535 data channels in your data stream. For example, you can have one data stream for chat messages and another for sending spatial data.

    If you use multiple channels on the same stream, LiveSwitch matches these channels according to their labels by the peer. You don't need to supply data channels in the same order for all peers.

    Note

    Data streams are also added to the connection but they aren't used for signalling. For more information, see Signaling Over Data Streams.

    Important

    Don't create a data channel with a label that starts with fm/.

    For dynamically created channels, if one peer hasn't declared a channel on which another peer attempts to communicate, you can still receive data on that channel by subscribing to the DataStream.OnChannel and the OnReceive events of that channel.

    • CSharp
    • Android
    • iOS
    • JavaScript
    DataChannel dataChannel = new DataChannel("my-data-channel");
    DataStream dataStream = new DataStream(dataChannel);
    McuConnection connection = _Channel.CreateMcuConnection(..., dataStream); // or SFU
    
    DataChannel dataChannel = new DataChannel("my-data-channel");
    DataStream dataStream = new DataStream(dataChannel);
    McuConnection connection = channel.createMcuConnection(..., dataStream); // or SFU
    
    var dataChannel = FMLiveSwitchDataChannel(label: "my-data-channel")
    var dataStream = FMLiveSwitchDataStream(channel: dataChannel)
    var connection: FMLiveSwitchMcuConnection?
    connection = _channel?.createMcuConnection(with: ..., dataStream: dataStream)
    
    var dataChannel = new fm.liveswitch.DataChannel("my-data-channel");
    var dataStream = new fm.liveswitch.DataStream(dataChannel);
    let connection: fm.liveswitch.McuConnection;
    connection = this.channel.createMcuConnection(..., dataStream);
    

    Send and Receive Data

    A data channel has the following associated states:

    • New
    • Connecting
    • Connected
    • Closing
    • Closed
    • Failed

    You can only send or receive data on a data channel that's in the DataChannelState.Connected state. The other states are can be used to manage your data channel resources, but the Connected state tells you whether you can use the data channel.

    This code shows how to hook into the OnStateChange event:

    • CSharp
    • Android
    • iOS
    • JavaScript
    DataChannel dataChannel = new DataChannel("my-data-channel");
    dataChannel.OnStateChange += (e) =>
    {
        // States are New, Connecting, Connected, Closing, Closed, Failed
        if (e.State == DataChannelState.Connected)
        {
            ...
        }
        ...
    };
    
    DataChannel dataChannel = new DataChannel("my-data-channel");
    dataChannel.addOnStateChange(new IAction1<DataChannel>()
    {
        @Override
        public void invoke(DataChannel dataChannel)
        {
            // States are New, Connecting, Connected, Closing, Closed, Failed
            if (dataChannel.getState() == DataChannelState.Connected)
            {
                ...
            }
            ....
        }
    });
    
    var dataChannel = FMLiveSwitchDataChannel(label: "my-data-channel")
    dataChannel?.add(onStateChange: FMLiveSwitchAction1.init { (dataChannel) -> Void in
        var datachannelvariable = dataChannel as! FMLiveSwitchDataChannel
      
        // States are .new, .connecting, .connected, .closing, .closed, .failed
        if (datachannelvariable.state() == FMLiveSwitchDataChannelState.connected)
        {
            ...
        }
        ...
    })
    
    var dataChannel = new fm.liveswitch.DataChannel("my-data-channel");
    let onStateChange = (dc: fm.liveswitch.DataChannel) => {
        // States are New, Connecting, Connected, Closing, Closed, Failed
        if (dc.getState() == fm.liveswitch.DataChannelState.Connected) {
            ...
        }
        ...
    };
    dataChannel.addOnStateChange(onStateChange);
    

    To receive data on your channel, use the OnReceive event:

    • CSharp
    • Android
    • iOS
    • JavaScript
    DataChannel dataChannel = new DataChannel("my-data-channel")
    {
        OnReceive = (dataChannelReceiveArgs) => {
            if (dataChannelReceiveArgs.DataString != null)
            {
                 ...
            }
        }
    };
    
    DataChannel dataChannel = new DataChannel("my-data-channel");
    dataChannel.setOnReceive(new IAction1<DataChannelReceiveArgs>()
    {
        @Override
        public void invoke(DataChannelReceiveArgs dataChannelReceiveArgs)
        {
            if (dataChannelReceiveArgs.getDataString() != null)
            {
                ...
            }
        }
    });
    
    var dataChannel = FMLiveSwitchDataChannel(label: "my-data-channel")
    var setOnReceive = FMLiveSwitchAction1{dataChannelReceiveArgs -> Void in
        var dataChannelReceiveArgsVariable = dataChannelReceiveArgs as! FMLiveSwitchDataChannelReceiveArgs
        if (dataChannelReceiveArgsVariable.dataString() != nil)
        {
            ...
        }
    }
    dataChannel?.setOnReceive(setOnReceive);
    
    var dataChannel = new fm.liveswitch.DataChannel("my-data-channel");
    let onReceive = (dataChannelReceiveArgs: fm.liveswitch.DataChannelReceiveArgs) => {
        if (dataChannelReceiveArgs.getDataString() != null)
        {
            ...
        }
    }
    

    To send data on your channel, use the SendDataString method:

    • CSharp
    • Android
    • iOS
    • JavaScript
    if (dataChannel.State == DataChannelState.Connected)
    {
        dataChannel.SendDataString("Hello world!");
    }
    
    if (dataChannel.getState() == DataChannelState.Connected) {
        dataChannel.sendDataString("Hello world!");
    }
    
    if (dataChannel.state() == FMLiveSwitchDataChannelState.connected)
    {
        dataChannel.sendDataString("Hello world!");
    }
    
    if (dataChannel.getState() == fm.liveswitch.DataChannelState.Connected)
    {
        dataChannel.sendDataString("Hello world!");
    }
    

    Convert a file into data bytes to send it. To send data bytes, do the following to use the SendDataBytes method:

    1. Use DataBuffer.Wrap to convert a byte array to a DataBuffer.
    2. Use SendDataBytes to send the DataBuffer.
    • CSharp
    • Android
    • iOS
    • JavaScript
    if (dataChannel.State == DataChannelState.Connected)
    {
        byte[] byteArray = { 1, 2, 3 };
        dataChannel.SendDataBytes(DataBuffer.Wrap(byteArray));
    }
    
    if (dataChannel.getState() == DataChannelState.Connected) {
        byte[] byteArray = {1, 2, 3};
        dataChannel.sendDataBytes(DataBuffer.wrap(byteArray));
    }
    
    if (dataChannel.state() == FMLiveSwitchDataChannelState.connected) {
        let data: [UInt8] = [1, 2, 3];
        let byteArray = NSMutableData(bytes: data, length: data.count);
        dataChannel.sendDataBytes(FMLiveSwitchDataBuffer.wrap(with: byteArray));
    }
    
    if (dataChannel.getState() == fm.liveswitch.DataChannelState.Connected) {
        let byteArray = new Uint8Array([1, 2, 3]);
        dataChannel.sendDataBytes(fm.liveswitch.DataBuffer.wrap(byteArray));
    }
    

    Do the following after receiving files from a DataChannel:

    1. Access the Data property of the DataBuffer to get the byte array.
    2. Use these attributes in the DataBuffer to get the content relevant to you:
      • The Index attribute, which is the offset to get the actual content.
      • The Length attribute, which is the length of the original byte array.
    • CSharp
    • Android
    • iOS
    • JavaScript
    channel.OnReceive += (messageArgs) => 
    {
        var dataBytes = messageArgs.DataBytes;
        if (dataBytes != null)
        {
            var bytes = dataBytes.Data; // The payload byte[] might contain extra bytes that are not part of the payload.
            var index = dataBytes.Index; // Starting index of the payload’s bytes you want.
            var length = dataBytes.Length; // Length of the payload’s bytes you want.
            var firstByte = dataBytes.Data[index]; // An example of acccessing the first byte.
        }
    };
    
    channel.setOnReceive(messageArgs -> {
        DataBuffer dataBytes = messageArgs.getDataBytes();
        if (dataBytes != null) {
            byte[] bytes = dataBytes.getData(); // The payload byte[] might contain extra bytes that are not part of the payload.
            int index = dataBytes.getIndex(); // Starting index of the payload’s bytes you want.
            int length = dataBytes.getLength(); // Length of the payload’s bytes you want.
            byte firstByte = dataBytes.getData()[index]; // An example of acccessing the first byte.
        }
    });
    
    channel.setOnReceive { [weak self] (messageArgs: Any!) in
        let dataBytes = (messageArgs as! FMLiveSwitchDataChannelReceiveArgs).dataBytes()
        if (dataBytes != nil) {
            let bytes = dataBytes!.data() // The payload byte[] might contain extra bytes that are not part of the payload.
            let index = dataBytes!.index() // Starting index of the payload’s bytes you want.
            let length = dataBytes!.length() // Length of the payload’s bytes you want.
            let firstByte = dataBytes!.data()[Int(index)] // An example of acccessing the first byte.
        }
    }
    
    channel.setOnReceive((messageArgs) => {
        let dataBytes = messageArgs.getDataBytes();
        if (dataBytes != null) {
            var bytes = dataBytes.getData(); // The payload byte[] might contain extra bytes that are not part of the payload.
            var index = dataBytes.getIndex(); // Starting index of the payload’s bytes you want.
            var length = dataBytes.getLength(); // Length of the payload’s bytes you want.
            var firstByte = dataBytes.getData()[index]; // An example of acccessing the first byte.
        }
    });
    
    In This Article
    Back to top Copyright © LiveSwitch Inc. All Rights Reserved.Documentation for LiveSwitch Version 1.24.6