P2P Connection
In addition to SFU and MCU connections, LiveSwitch also supports a peer-to-peer (P2P) connections. This allows for users to be connected to each other in a mesh topology, where each user is connected to every other user directly. This type of connection consumes the least amount of server resources but is also the most bandwidth and CPU-intensive type of connection for end-users. The amount of bandwidth required to maintain the session increases by a constant factor as each additional user joins. This makes peer-to-peer connections preferable for small video conferences, as users' connections can generally not support more than five or six people in a conference.
Create a P2P Connection
Start by registering with the LiveSwitch gateway and obtaining an FM.LiveSwitch.Channel
instance. This is covered in the previous Registering a Client section. You should also review the Handling Local Media, Handling Remote Media, and Creating Streams and Connections sections, which review the media components and streams that are used in this guide.
When establishing a peer-to-peer connection, there are two roles to consider. The first role is the offerer. The offerer is a user who is already in a channel. When a new user joins this channel, it is the offerer's responsibility to send this new user a message that indicates that they wish to connect to them. This leads to the second role - the answerer. The answerer is a user who has just joined a channel. They are responsible for answering any connection messages that they receive. This section focuses first on defining the offerer's role and then the answerer's role.
Note
About the code examples on this page:
- For .NET MAUI and Unity, use the C# code.
- For macOS, use the iOS code.
Define the Offerer
To send an offer to clients who have just joined, you first need to be notified when a client joins. This is accomplished by adding an OnRemoteClientJoin
event handler to your Channel
instance. As indicated by the event name, this event is raised whenever a remote client joins the channel. This event handler must do two things:
- update the UI
- create and open a peer-to-peer connection
Begin by updating the UI. Create a new instance of your RemoteMedia
class. Next, add the RemoteMedia
instance's associated view to your layout manager by invoking its AddRemoteView
method. This method requires an id parameter and a view object, both of which can be accessed through properties on the RemoteMedia
instance. Next, you must create an FM.LiveSwitch.PeerConnection
instance. You do this by invoking the CreatePeerConnection
method of your Channel instance.
This method expects an instance of FM.LiveSwitch.ClientInfo
as its first parameter, which is provided to you as one of the event handler parameters. Create the PeerConnection
instance, assign it the required ICE servers and kick off the connection process by invoking its Open
method. This sends an offer to the user who has just connected, identified by the information in the ClientInfo
instance. Remember to inspect the promise returned from the Open
method.
Once complete, your app is able to send offers to new users who join the channel. The next step is to have these users respond to the offers and establish a connection.
channel.OnRemoteClientJoin += (FM.LiveSwitch.ClientInfo remoteClientInfo) =>
{
var remoteMedia = new RemoteMedia();
var audioStream = new FM.LiveSwitch.AudioStream(localMedia, remoteMedia);
var videoStream = new FM.LiveSwitch.VideoStream(localMedia, remoteMedia);
var connection = channel.CreatePeerConnection(remoteClientInfo, audioStream, videoStream);
layoutManager.AddRemoteView(remoteMedia.Id, remoteMedia.View);
connection.OnStateChange += (FM.LiveSwitch.ManagedConnection c) =>
{
if (c.State == FM.LiveSwitch.ConnectionState.Closing || c.State == FM.LiveSwitch.ConnectionState.Failing)
{
layoutManager.RemoveRemoteView(remoteMedia.Id);
}
};
connection.IceServers = ...
connection.Open().Then((result) =>
{
Console.WriteLine("offerer's connection established");
}).Fail((ex) =>
{
Console.WriteLine("an error occurred");
});
};
Define the Answerer
The code for responding to an offer is almost identical to the code for sending one. The key difference is that instead of adding an OnRemoteClientJoin
event handler, you add an OnPeerConnectionOffer
event handler. Similar to above, this event handler has two key responsibilities:
- update the UI
- create and open a peer-to-peer connection
Proceed as you did above when writing this event handler. Create a RemoteMedia
instance, and add the new view to the layout. Once again, invoke CreatePeerConnection
, but this time you pass it an instance of FM.LiveSwitch.PeerConnectionOffer
. You can obtain this instance from the parameters of the event handler. The CreatePeerConnection
method recognizes that because you have provided it with an offer, that it must now respond with an answer. Invoke the Open
method of the created FM.LiveSwitch.PeerConnection
instance and an answer is generated and sent back to the original peer. As usual, the Open
method returns a promise wrapping the result of the Open
invocation.
Once you have worked through the example below you will have now defined both the offerer and answerer role, and two or more users should be able to connect to each other when they join a channel. The next sections focus on what to do when users leave and how to properly end a peer-to-peer session.
channel.OnPeerConnectionOffer += (peerConnectionOffer) =>
{
var remoteMedia = new RemoteMedia();
var audioStream = new FM.LiveSwitch.AudioStream(localMedia, remoteMedia);
var videoStream = new FM.LiveSwitch.VideoStream(localMedia, remoteMedia);
var connection = channel.CreatePeerConnection(peerConnectionOffer, audioStream, videoStream);
layoutManager.AddRemoteView(remoteMedia.Id, remoteMedia.View);
connection.OnStateChange += (FM.LiveSwitch.ManagedConnection c) =>
{
if (c.State == FM.LiveSwitch.ConnectionState.Closing || c.State == FM.LiveSwitch.ConnectionState.Failing)
{
layoutManager.RemoveRemoteView(remoteMedia.Id);
}
};
connection.IceServers = ...
connection.Open().Then((object result) =>
{
Console.WriteLine("answerer's connection established");
}).Fail((Exception ex) =>
{
Console.WriteLine("an error occurred");
});
};
Close a Connection
If you need to close a connection manually, you can do so by invoking the Close
method of the PeerConnection
instance. The Close
method returns a promise, the result of which you can inspect to ensure the connection has been closed properly.
Closing a connection is straightforward - note though, that the result object in this promise is not used, which is why it is assigned the generic object
type.
connection.Close().Then((object result) =>
{
Console.WriteLine("connection closed");
}).Fail((Exception ex) =>
{
Console.WriteLine("an error occurred");
});
You now know how to properly establish and tear down a peer-to-peer session. Once again, peer-to-peer sessions work best with small amounts of participants, as the required bandwidth escalates quickly as more people join the session. You should still try to use them whenever possible, however, as they can provide substantial savings on server bandwidth.