Looking for IceLink documentation? This product is discontinued and has been replaced by LiveSwitch. Please contact Sales to learn more and plan your migration today.
Migrate from IceLink to LiveSwitch Cloud
LiveSwitch improves the capabilities of IceLink by providing a server component for you that works out of the box and scales with your client-side needs. LiveSwitch comes with its own fully featured SFU and MCU modules, which allow you to scale up your video conferences to support more people than you could with IceLink. LiveSwitch also has built-in support for token-based authentication, application-specific roles and session management.
Cheat Sheet
IceLink |
→ LiveSwitch Cloud |
https://v4.websync.fm/websync.ashx |
https://cloud.liveswitch.io |
Client.RequestUrl |
Client.GatewayUrl |
Client.DomainName |
Client.ApplicationId |
Client.DomainKey |
Client.SharedSecret |
Client.Connect |
Client.Register |
Client.Disconnect |
Client.Unregister |
Client.Bind |
Client.Update |
Client.Unbind |
Client.Update |
Client.Subscribe |
Client.Join |
Client.Unsubscribe |
Client.Leave |
Client.Publish |
Channel.SendMessage |
SubscribeArgs.OnReceive |
Channel.OnMessage |
SubscribeArgs.OnClientSubscribe |
Channel.OnRemoteClientJoin |
SubscribeArgs.OnUserJoin |
Channel.OnRemoteClientJoin |
SubscribeArgs.OnClientUnsubscribe |
Channel.OnRemoteClientLeave |
SubscribeArgs.OnUserLeave |
Channel.OnRemoteClientLeave |
Publisher |
Web API |
Getting Started
- First, create a LiveSwitch Cloud account if you don't have one.
- Create an Application using the LiveSwitch Cloud Console.
- Download the latest SDK and add it to your app.
You're ready to migrate.
Migrating involves converting code that references IceLink into code that references the LiveSwitch equivalents.
Not sure where to start? Just remove the IceLink SDK from your app and let your IDE tell you where to look.
Key Differences
There are some key differences to be aware of when migrating:
- Registering in LiveSwitch == connecting in IceLink.
- Unregistering in LiveSwitch == disconnecting in IceLink.
- Joining a channel in LiveSwitch == subscribing to a channel in IceLink.
- Leaving a channel in LiveSwitch == unsubscribing from a channel in IceLink.
- Updating a client in LiveSwitch == binding records to or unbinding records from a client in IceLink.
- Sending a message in LiveSwitch == publishing a message in IceLink.
- LiveSwitch uses a promise-based async/await model, as opposed to WebSync's callback model.
- Client requests are authorized by JSON web tokens.
Servers
LiveSwitch uses https://cloud.liveswitch.io as its signaling endpoint, also called the gateway URL. This is equivalent to the "request URL" in WebSync, which is https://v4.websync.fm/websync.ashx for WebSync Cloud.
Tokens
Tokens are used to authorize Register and Join requests. Register tokens authorize the initial connection, while Join tokens authorize a connected client to join a new channel. Register tokens can include a list of channels to join right away as part of the initial connection.
LiveSwitch tokens are simple JSON Web Tokens, an open, industry-standard method for representing claims securely between two parties.
Your LiveSwitch Cloud shared secret is used to sign tokens. It is very important to protect this shared secret. Generating tokens in a server-side API that can provide tokens to authorized users of your system is strongly recommended.
The simplest way to generate a token is to use the LiveSwitch SDK:
LiveSwitch
let applicationId = "my-app-id";
let userId = fm.liveswitch.Guid.newGuid().toString().replace(/-/g, '');
let deviceId = fm.liveswitch.Guid.newGuid().toString().replace(/-/g, '');
let userName = "John Doe";
let channelId = "123456";
let client = new fm.liveswitch.Client("https://cloud.liveswitch.io",
this.applicationId,
this.userId,
this.deviceId);
let claims = [new fm.liveswitch.ChannelClaim(channelId)];
let token = fm.liveswitch.Token.generateClientRegisterToken(
this.applicationId,
this.client.getUserId(),
this.client.getDeviceId(),
this.client.getId(),
null,
claims,
"--sharedSecret!--"
);
string applicationId = "my-app-id";
string userName = "John Doe";
string channelId = "123456";
var userId = Guid.NewGuid().ToString().Replace("-", "");
var deviceId = Guid.NewGuid().ToString().Replace("-", "");
Client client = new Client("https://cloud.liveswitch.io",
applicationId,
userId,
deviceId);
var channelClaims = new[] {new ChannelClaim(channelId)};
var token = FM.LiveSwitch.Token.GenerateClientRegisterToken(
applicationId,
client.UserId,
client.DeviceId,
client.Id,
client.Roles,
channelClaims,
"--replaceThisWithYourOwnSharedSecret--"
);
let applicationId = "my-app-id"
let userName = "John Doe"
let channelId = "123456"
let userId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
let deviceId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
var client: FMLiveSwitchClient = FMLiveSwitchClient.init(gatewayUrl: "https://cloud.liveswitch.io",
applicationId: applicationId,
userId: userId,
deviceId: deviceId)
var channelClaim = FMLiveSwitchChannelClaim.init()
channelClaim.setId(channelId)
var channelClaims: NSMutableArray = []
channelClaims.add(channelClaim)
var token = FMLiveSwitchToken.generateClientRegister(
withApplicationId: applicationId,
userId: client.userId(),
deviceId: client.deviceId(),
clientId: client.id(),
clientRoles: nil,
channelClaims: channelClaims,
sharedSecret: "--replaceThisWithYourOwnSharedSecret--"
);
String applicationId = "my-app-id";
String userName = "John Doe";
String channelId = "123456";
String userId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
String deviceId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
fm.liveswitch.Client client = new fm.liveswitch.Client("https://cloud.liveswitch.io", applicationId, userId, deviceId);
final fm.liveswitch.ChannelClaim[] claims = new fm.liveswitch.ChannelClaim[]{new fm.liveswitch.ChannelClaim(channelId)};
String token = fm.liveswitch.Token.generateClientRegisterToken(
applicationId,
client.getUserId(),
client.getDeviceId(),
client.getId(),
null,
claims,
"--replaceThisWithYourOwnSharedSecret--"
);
Third-Party Library
You can also use a third-party library, like jsonwebtoken:
var sharedSecret = '...';
var registerToken = jwt.sign({
type: 'register',
applicationId: '...',
userId: '...',
deviceId: '...',
channels: [{ // optional, any count
id: '...'
}],
clientRoles: ['...'], // optional, any count
region: '...' // optional
}, sharedSecret, {
algorithm: 'HS256',
expiresIn: 300 // seconds
});
var joinToken = jwt.sign({
type: 'join',
applicationId: '...',
userId: '...',
deviceId: '...',
clientId: '...'
channel: [{ // exactly 1 required
id: '...'
}]
}, sharedSecret, {
algorithm: 'HS256',
expiresIn: 300 // seconds
});
Migrating
Registering (Connecting)
Registering in LiveSwitch is equivalent to connecting in IceLink. Registering with the server requires a signed token for authorization.
Your LiveSwitch shared secret is equivalent to your WebSync domain key. This information is private for your servers only.
Your LiveSwitch Application ID is equivalent to your WebSync domain name. This information is public that you can include in your client apps.
Before (IceLink)
let client = new fm.websync.client('https://v4.websync.fm/websync.ashx');
this.client.connect({
onSuccess: (args: any) => {
console.log("connection success!");
},
onFailure: (args: any) => {
console.log("Failed to connect.");
},
});
After (LiveSwitch)
let applicationId = "my-app-id";
let sharedSecret = "---shared-secret---";
let channelId = "123456";
let client = new fm.liveswitch.Client("https://cloud.liveswitch.io", applicationId);
let claims = [new fm.liveswitch.ChannelClaim(channelId)];
let token = fm.liveswitch.Token.generateClientRegisterToken(
this.applicationId,
null,
null,
null,
null,
claims,
sharedSecret
);
client.register(token).then((channels) => {
fm.liveswitch.Log.info("Registered to Gateway!");
// Run procedures after registering
promise.resolve(null);
}, (ex) => {
fm.liveswitch.Log.error("Failed to register with Gateway.");
// Run procedures to handle exceptions
promise.reject(ex);
});
Before (IceLink)
Client client = new Client("https://v4.websync.fm/websync.ashx");
client.Connect(new ConnectArgs() {
OnSuccess = (p) =>
{
Console.Writeline("Sucesseful connnection!");
},
OnFailure = (e) =>
{
Console.Writeline("Connection failed");
}
});
After (LiveSwitch)
string applicationId = "my-app-id";
string userName = "John Doe";
string channelId = "123456";
var userId = Guid.NewGuid().ToString().Replace("-", "");
var deviceId = Guid.NewGuid().ToString().Replace("-", "");
Client client = new Client("https://cloud.liveswitch.io",
applicationId,
userId,
deviceId);
var token = FM.LiveSwitch.Token.GenerateClientRegisterToken(
applicationId,
client.UserId,
client.DeviceId,
client.Id,
client.Roles,
channelClaims,
"--replaceThisWithYourOwnSharedSecret--"
);
client.Register(token)
.Then<object>(channels =>
{
Console.Writeline("Registration successful!");
})
.Fail((e) =>
{
Console.Writeline("Failed to register client");
});
Before (IceLink)
var client = FMWebSyncClient(requestUrl: "https://v4.websync.fm/websync.ashx")
var args = FMWebSyncConnectArgs()
args?.setOnSuccessBlock { (args: FMWebSyncConnectSuccessArgs?) in
print("Sucesseful connnection!")
}
args?.setOnFailureBlock { (args: FMWebSyncConnectFailureArgs?) in
print("Connection failed")
}
client.connect(with: args)
After (LiveSwitch)
let applicationId = "my-app-id"
let userName = "John Doe"
let channelId = "123456"
let userId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
let deviceId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
var client: FMLiveSwitchClient = FMLiveSwitchClient.init(gatewayUrl: "https://cloud.liveswitch.io",
applicationId: applicationId,
userId: userId,
deviceId: deviceId)
var channelClaim = FMLiveSwitchChannelClaim.init()
channelClaim.setId(channelId)
var channelClaims: NSMutableArray = []
channelClaims.add(channelClaim)
var token = FMLiveSwitchToken.generateClientRegister(
withApplicationId: applicationId,
userId: client.userId(),
deviceId: client.deviceId(),
clientId: client.id(),
clientRoles: nil,
channelClaims: channelClaims,
sharedSecret: "--replaceThisWithYourOwnSharedSecret--"
);
client?.register(withToken: theToken).then(resolveActionBlock: { (obj: Any!) in
print("Registration successful!")
}).fail(rejectActionBlock: { (obj: Any!) in
print("Failed to register client")
})
Before (IceLink)
fm.websync.Client client = new fm.websync.Client("https://v4.websync.fm/websync.ashx");
client.connect(new fm.websync.ConnectArgs()
{{
setOnSuccess(new fm.SingleAction<fm.websync.ConnectSuccessArgs>() {
public void invoke(fm.websync.ConnectSuccessArgs e) {
System.out.println("Client connected!"):
}
});
setOnFailure(new fm.SingleAction<Cfm.websync.ConnectFailureArgs>() {
public void invoke(fm.websync.ConnectFailureArgs e) {
System.out.println("Client failed to connect");
}
});
);
After (LiveSwitch)
String applicationId = "my-app-id";
String userName = "John Doe";
String channelId = "123456";
String userId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
String deviceId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
fm.liveswitch.Client client = new fm.liveswitch.Client("https://cloud.liveswitch.io", applicationId, userId, deviceId);
final fm.liveswitch.ChannelClaim[] claims = new fm.liveswitch.ChannelClaim[]{new fm.liveswitch.ChannelClaim(channelId)};
final String token = fm.liveswitch.Token.generateClientRegisterToken(
applicationId,
client.getUserId(),
client.getDeviceId(),
client.getId(),
null,
claims,
"--replaceThisWithYourOwnSharedSecret--"
);
return client.register(token).then((fm.liveswitch.Channel[] channels) -> {
System.out.println("connected to channel: " + channels[0].getId());
}).fail((Exception ex) -> {
System.out.println("registration failed");
});
Notice the null
parameters when creating the Register token. These represent additional metadata (user ID, device ID, client roles) bound to the client and verified on the server.
You might also notice that an array of channels can be provided when registering. If provided, the server joins the client to these channels on registration. A Channel
array is returned from the call to Register
.
Unregistering (Disconnecting)
Unregistering in LiveSwitch is equivalent to disconnecting in IceLink.
Before (IceLink)
client.disconnect({
onComplete: (args: any) => {
console.log("Client disconnected");
}
});
After (LiveSwitch)
client.unregister().then(() => {
console.log("Unregistration successful!");
}).fail(() => {
console.log("Failed to unregister client.");
});
Before (IceLink)
try {
client.disconnect(new DisconnectArgs() {
onComplete = (p) => {
Console.Writeline("Successfully disconnected.");
}
});
}
catch (Exception e) {
Console.WriteLine("Client failed to disconnect.");
}
After (LiveSwitch)
client.Unregister().Then(result =>
{
Console.Writeine("Successfully unregistered.");
}).Fail(exception =>
{
Console.Writeine("Client failed to unregister.");
});
Before (IceLink)
let disconnectArgs = FMWebSyncDisconnectArgs()
disconnectArgs?.setOnComplete({ (completeArgs: FMWebSyncDisconnectCompleteArgs?) in
print("Client disconnected");
})
client?.disconnect(with: disconnectArgs)
After (LiveSwitch)
client?.unregister().then(resolveActionBlock: {(obj:Any!) in
print("Successfully unregistered.");
}).fail(rejectActionBlock: { (e: NSException?) in
print("Client failed to unregister.");
})
Before (IceLink)
client.disconnect(new fm.websync.DisconnectArgs(){{
setOnComplete(new SingleAction<fm.websync.DisconnectCompleteArgs>(){
public void invoke(fm.websync.DisconnectCompleteArgs e) {
System.out.println("disconnected");
}
});
}});
After (LiveSwitch)
client.unregister().then((Object result){
System.out.println("Successfully unregistered client!");
}).fail((Exception) -> {
System.out.println("Failed to unregister client.");
});
Updating (Binding)
Updating in LiveSwitch is equivalent to binding and unbinding in IceLink.
LiveSwitch takes a simple approach to client metadata that allows it to scale better than WebSync. Each client has a UserAlias
, a DeviceAlias
, and a Tag
property. You can change these values anytime by calling Update
. Any of these three string properties can have any value, but the intention is that UserAlias
represents the user's human-readable display name, DeviceAlias
represents the device's human-readable display name, and Tag
represents any additional metadata the app needs.
Before (IceLink)
this.client.bind({
record: {
userAlias: "Jane Doe",
tag: "NewUser"
},
onFailure: (ex) => {
console.log("Unable to bind.");
},
onSuccess: () => {
console.log("Bind successful!");
}
});
After (LiveSwitch)
let config = client.getConfig();
config.setUserAlias("Jane Doe");
config.setTag("NewUser");
client.update(config).then(() => {
console.log("Update successful");
}, (exception) => {
console.log("Update unsuccessful");
});
Before (IceLink)
client.Bind(new BindArgs(
new Record("userName", Serializer.SerializeString("Jane Doe")),
new Record("tag", Serializer.SerializeString("newUser"))
){
OnSuccess = (o) =>
{
Console.Writeline("Successfully updated client");
},
OnFailure = (e) =>
{
Console.Writeline("Failed to update client");
}
);
After (LiveSwitch)
var config = _Client.Config;
config.UserAlias = "Jane Doe";
config.Tag = "newUser";
_Client.Update(config).Then((result) =>
{
Console.WriteLine("Successfully updated the client.");
}).Fail((exception) =>
{
Console.WriteLine("Failed to update the client.");
});
Before (IceLink)
let bindArgs = FMWebSyncBindArgs(records: [
FMWebSyncRecord(key: "userName", valueJson: FMIceLinkJsonSerializer.serializeString("Jane Doe")),
FMWebSyncRecord(key: "tag", valueJson: FMIceLinkJsonSerializer.serializeString("newUser"))
])
bindArgs?.setOnSuccessBlock { (args:FMWebSyncBindSuccessArgs?) in
print("Successfully updated client");
}
bindArgs?.setOnFailureBlock { (args:FMWebSyncBindFailureArgs?) in
print("Failed to update client");
}
client!.bind(with: bindArgs)
After (LiveSwitch)
let config = _client?.config()
config?.setUserAlias("Jane Doe")
config?.setTag("newUser")
_client?.update(with: config).then(resolveActionBlock: {(obj:Any!) in
print("Successfully updated the client.");
}).fail(rejectActionBlock: { (e: NSException?) in
print("Failed to update the client.");
})
Before (IceLink)
client.bind(new fm.websync.BindArgs(
new fm.websync.Record("userName:, fm.websync.Serializer.serializeString("Jane Doe"))
new fm.websync.Record("tag", fm.websync.Serializer.serializeString("newUser")
){{
setOnSuccess(new fm.SingleAction<BindSuccessArgs>() {
public void invoke(BindSuccessArgs e) {
System.out.println("Successfully updated client");
}
});
setOnFailure(new fm.SingleAction<BindFailureArgs>() {
public void invoke(BindFailureArgs e) {
System.out.println("Failed to update client");
}
});
}});
After (LiveSwitch)
fm.liveswitch.ClientConfig config = client.getConfig();
config.setDeviceAlias("Jane Doe");
config.setTag("newUser");
client.update(config).then((result) -> {
System.out.println("Successfully updated the client!");
}).fail((exception) -> {
System.out.println("Unable to update client.");
});
Joining
Joining in LiveSwitch is equivalent to joining a conference in IceLink. Joining a channel requires a signed token for authorization.
LiveSwitch introduces a new Channel
type that is returned by the call to Join
. Received messages are delivered to channel-level event handlers.
Before (IceLink)
var joinArgs = new fm.icelink.websync4.JoinConferenceArgs("/123456");
joinArgs.setOnSuccess((args) => {
fm.icelink.Log.info("joined the conference");
});
joinArgs.setOnFailure((args) => {
fm.icelink.Log.info("failed to join the conference");
});
joinArgs.setOnRemoteClient((remoteClient) => {
// set up streams ...
var connection = new fm.icelink.Connection([... stream array ...]);
connection.setIceServers(...);
return connection;
});
fm.icelink.websync4.ClientExtensions.joinConference(this.client, joinArgs);
After (LiveSwitch)
let applicationId = "my-app-id";
let sharedSecret = "---shared-secret---";
let channelId = "123456";
let client = new fm.liveswitch.Client("", applicationId);
let claims = [new fm.liveswitch.ChannelClaim(channelId)];
let token = fm.liveswitch.Token.generateClientRegisterToken(
this.applicationId,
null,
null,
null,
null,
claims,
sharedSecret
);
client.join(channelId, token).then(function(channel) {
console.log("successfully joined channel");
}).fail(function(ex) {
console.log("failed to join channel");
});
Before (IceLink)
client.JoinConference(new JoinConferenceArgs("/123456")
{
OnSuccess = (JoinConferenceSuccessArgs e) =>
{
Console.WriteLine("Joined the conference");
},
OnFailure = (JoinConferenceFailureArgs e) =>
{
Console.WriteLine("Failed to join the conference");
},
OnRemoteClient = (PeerClient remoteClient) =>
{
// set up streams ...
var streams = ...
var connection = new FM.IceLink.Connection(streams);
connection.IceServers = ...
return connection;
}
}
After (LiveSwitch)
string applicationId = "my-app-id";
string userName = "John Doe";
string channelId = "123456";
var userId = Guid.NewGuid().ToString().Replace("-", "");
var deviceId = Guid.NewGuid().ToString().Replace("-", "");
Client client = new Client("https://cloud.liveswitch.io",
applicationId,
userId,
deviceId);
var channelClaims = new[] {new ChannelClaim(channelId)};
var token = FM.LiveSwitch.Token.GenerateClientRegisterToken(
applicationId,
client.UserId,
client.DeviceId,
client.Id,
client.Roles,
channelClaims,
"--replaceThisWithYourOwnSharedSecret--"
);
client.Join(channelId, token).Then((result) =>
{
Console.Writeline("Successfully joined channel.");
}).Fail((exception) =>
{
Console.Writeline("Failed to join channel);
});
Before (IceLink)
let conferenceArgs = FMIceLinkWebSync4JoinConferenceArgs(conferenceChannel: "/123456")
conferenceArgs!.setOnSuccessBlock { (args: FMIceLinkWebSync4JoinConferenceSuccessArgs?) in
print("Joined the conference");
}
conferenceArgs!.setOnFailureBlock { (args: FMIceLinkWebSync4JoinConferenceFailureArgs?) in
print("Failed to join the conference");
}
conferenceArgs!.setOnRemoteClientBlock { [weak self] (peer: FMIceLinkWebSync4PeerClient?) -> FMIceLinkConnection? in
let connection = FMIceLinkConnection(...)
...
return connection
}
client!.joinConference(with: conferenceArgs)
After (LiveSwitch)
let applicationId = "my-app-id"
let userName = "John Doe"
let channelId = "123456"
let userId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
let deviceId = FMLiveSwitchGuid.generate().description().replacingOccurrences(of: "-", with: "")
var client: FMLiveSwitchClient = FMLiveSwitchClient.init(gatewayUrl: "https://cloud.liveswitch.io",
applicationId: applicationId,
userId: userId,
deviceId: deviceId)
var channelClaim = FMLiveSwitchChannelClaim.init()
channelClaim.setId(channelId)
var channelClaims: NSMutableArray = []
channelClaims.add(channelClaim)
var token = FMLiveSwitchToken.generateClientRegister(
withApplicationId: applicationId,
userId: client.userId(),
deviceId: client.deviceId(),
clientId: client.id(),
clientRoles: nil,
channelClaims: channelClaims,
sharedSecret: "--replaceThisWithYourOwnSharedSecret--"
);
client?.join(withChannelId: channelId, token: token).then(resolveActionBlock: {(obj:Any!) in
print("Successfully joined channel.");
}).fail(rejectActionBlock: { (e: NSException?) in
print("Failed to join channel.");
})
Before (IceLink)
ClientExtensions.joinConference(client, new JoinConferenceArgs("/123456") {{
setOnSuccess(new IAction1<JoinConferenceSuccessArgs>() {
public void invoke(JoinConferenceSuccessArgs successArgs) {
System.out.println("Successfully joined channel!);
}
});
setOnFailure(new IAction1<JoinConferenceFailureArgs>() {
public void invoke(JoinConferenceFailureArgs failureArgs) {
System.out.println("Failed to Join channel.");
}
});
setOnRemoteClient(new IFunction1<PeerClient, Connection>() {
@Override
public Connection invoke(PeerClient peerClient) {
Connection connection = createConnection.invoke(peerClient);
connections.add(connection);
return connection;
}
});
}});
After (LiveSwitch)
String applicationId = "my-app-id";
String userName = "John Doe";
String channelId = "123456";
String userId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
String deviceId = fm.liveswitch.Guid.newGuid().toString().replaceAll("-", "");
fm.liveswitch.Client client = new fm.liveswitch.Client("https://cloud.liveswitch.io", applicationId, userId, deviceId);
final fm.liveswitch.ChannelClaim[] claims = new fm.liveswitch.ChannelClaim[]{new fm.liveswitch.ChannelClaim(channelId)};
String token = fm.liveswitch.Token.generateClientRegisterToken(
applicationId,
client.getUserId(),
client.getDeviceId(),
client.getId(),
null,
claims,
"--replaceThisWithYourOwnSharedSecret--"
);
client.join(channelId, token).then((result) -> {
System.out.println("Client successfully joined channel!");
}).fail((exception) -> {
System.out.println("Client unable to join channel.");
});
Leaving
Leaving in LiveSwitch is equivalent to leaving a conference in IceLink.
Before (IceLink)
let channelId = "123456"
client.leaveConference({
conferenceChannel: channelId,
onSuccess: function(e) {
console.log("left the conference");
},
onFailure: function(e) {
console.log("failed to leave the conference");
}
});
After (LiveSwitch)
let channelId = "123456"
client.leave(channelId).then(function(channel) {
console.log("left the channel");
}).fail(function(ex) {
console.log("failed to leave the channel");
});
Before (IceLink)
var channelId = "123456;
client.LeaveConference(new LeaveConferenceArgs(channelId)
{
OnSuccess = (LeaveConferenceSuccessArgs e) =>
{
Console.WriteLine("Left the conference");
},
OnFailure = (LeaveConferenceFailureArgs e) =>
{
Console.WriteLine("Failed to leave the conference");
}
}
After (LiveSwitch)
var channelId = "123456";
try
{
await client.Leave(channelId);
Console.WriteLine("Success!");
}
catch (Exception ex)
{
Console.WriteLine($"Failure: {ex}");
}
Before (IceLink)
let channelId = "123456"
let conferenceArgs = FMIceLinkWebSync4LeaveConferenceArgs(conferenceChannel: channelId)
conferenceArgs?.setOnSuccessBlock({ (FMIceLinkWebSync4LeaveConferenceSuccessArgs) in
print("left the conference")
})
conferenceArgs?.setOnFailureBlock({ (FMIceLinkWebSync4LeaveConferenceFailureArgs) in
print("failed to leave the conference")
})
After (LiveSwitch)
let channelId = "123456"
client?.leave(withChannelId: _channelId).then(resolveActionBlock: {(obj:Any!) in
print("Successfully joined channel.");
}).fail(rejectActionBlock: { (e: NSException?) in
print("Failed to join channel.");
})
Before (IceLink)
ClientExtensions.leaveConference(client, new LeaveConferenceArgs(channel){{
setOnSuccess(new IAction1<LeaveConferenceSuccessArgs>() {
@Override
public void invoke(LeaveConferenceSuccessArgs leaveConferenceSuccessArgs) {
System.out.println("Client successfully left the conference");
}
});
setOnFailure(new IAction1<LeaveConferenceFailureArgs>() {
@Override
public void invoke(LeaveConferenceFailureArgs leaveConferenceFailureArgs) {
System.out.println("Unable to leave the conference");
}
});
}});
After (LiveSwitch)
String channelId = "123456";
client.leave(channelId).then((result) -> {
System.out.println("Client successfully left the channel!");
}).fail((exception) -> {
System.out.println("Client unable to leave the channel.");
});
Sending Messages (Publishing)
Sending messages in LiveSwitch is equivalent to publishing in IceLink.
LiveSwitch uses Channel
objects for contextual sending and receiving of messages, as opposed to global Client
methods in WebSync.
Before (IceLink)
let json = {
userName: "John Doe",
textMessage: "Hello!"
}
client.publish(new fm.websync.PublishArgs(channelId, JSON.stringify(json)));
After (LiveSwitch)
try {
channel.sendMessage("Hello!");
} catch (exception) {
console.log("Failed to send message");
}
Before (IceLink)
var channelId = "123456";
Message m = new Message()
{
userName = "John Doe",
textMsg = "Hello!"
};
Client.Publish(new PublishArgs(channelId, JsonConvert.SerializeObject(m)));
After (LiveSwitch)
try
{
await channel.SendMessage("message");
Console.WriteLine("Success!");
}
catch (Exception ex)
{
Console.WriteLine($"Failure: {ex}");
}
Before (IceLink)
let m = ["userName": "John Doe", "textMsg": "Hello!"]
let jsonData = try! JSONSerialization.data(withJSONObject: m, options: [])
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
let publishArgs = FMWebSyncPublishArgs()
publishArgs!.setChannel(self.metadataChannel)
publishArgs!.setDataJson(jsonString)
client!.publish(with: publishArgs)
After (LiveSwitch)
channel?.sendMessage("Hello!")
Before (IceLink)
try {
JSONObject json = new JSONObject();
json.put(userNameKey, "John Doe");
json.put(textMessageKey, "Hello!");
client.publish(new PublishArgs("123456", json.toString()));
}
catch (Exception e) {
System.out.println("Unable to send message");
}
After (LiveSwitch)
try {
channel.sendMessage("Hello!");
} catch (Exception e){
System.out.println("Unable to send message");
}
Channel-level methods to send a message to a specific user, device, or client are also available in LiveSwitch.
Selective Forwarding Unit Connections (SFU)
LiveSwitch also introduces SFU connection units, which offer greater scalability than traditional P2P connections used in IceLink. Upstream and downstream connections are needed to create an SFU connection.
Upstream Connection
let localMedia = ...;
let channel = ...;
function openSfuUpstreamConnection(tag){
let connection;
let audioStream;
let videoStream;
if (this.localMedia.getAudioTrack() != null) {
audioStream = new fm.liveswitch.AudioStream(this.localMedia);
}
if (this.localMedia.getVideoTrack() != null) {
videoStream = new fm.liveswitch.VideoStream(this.localMedia);
}
connection = this.channel.createSfuUpstreamConnection(audioStream, videoStream);
connection.addOnStateChange((connection) => {
// --- Insert code here to track connection states --- //
});
connection.open();
return connection;
}
Downstream connection
let channel = ...;
function openSfuDownstreamConnection(remoteConnectionInfo, tag){
let remoteMedia = new fm.liveswitch.RemoteMedia();
let connection;
let audioStream;
let videoStream;
if (remoteConnectionInfo.getHasAudio()) {
audioStream = new fm.liveswitch.AudioStream(remoteMedia);
}
if (remoteConnectionInfo.getHasVideo()) {
videoStream = new fm.liveswitch.VideoStream(remoteMedia);
}
connection = this.channel.createSfuDownstreamConnection(remoteConnectionInfo, audioStream, videoStream);
connection.addOnStateChange((connection) => {
// --- Insert code here to track connection state --- //
});
connection.open();
return connection;
}
Upstream Connection
var _LocalMedia = ...;
var _Channel = ...;
private SfuUpstreamConnection OpenSfuUpstreamConnection(string tag = null)
{
SfuUpstreamConnection connection;
AudioStream audioStream = null;
VideoStream videoStream = null;
if (_LocalMedia.AudioTrack != null)
{
audioStream = new AudioStream(_LocalMedia);
}
if (_LocalMedia.VideoTrack != null)
{
videoStream = new VideoStream(_LocalMedia);
}
connection = _Channel.CreateSfuUpstreamConnection(audioStream, videoStream);
connection.OnStateChange += (conn) =>
{
// --- Insert code here to track connection state --- //
};
connection.Open();
return connection;
}
Downstream connection
var _Channel = ...;
private SfuDownstreamConnection OpenSfuDownstreamConnection(ConnectionInfo remoteConnectionInfo, string tag = null)
{
Remote remoteMedia = ...;
SfuDownstreamConnection connection;
AudioStream audioStream = null;
VideoStream videoStream = null;
if (remoteConnectionInfo.HasAudio)
{
audioStream = new AudioStream(remoteMedia);
}
if (remoteConnectionInfo.HasVideo)
{
videoStream = new VideoStream(remoteMedia);
}
connection = _Channel.CreateSfuDownstreamConnection(remoteConnectionInfo, audioStream, videoStream);
connection.OnStateChange += (conn) =>
{
// --- Insert code here to track connection state --- //
};
connection.Open();
return connection;
}
Upstream Connection
func OpenSfuUpstreamConnection() -> FMLiveSwitchSfuUpstreamConnection {
var connection: FMLiveSwitchSfuUpstreamConnection?
var audioStream: FMLiveSwitchAudioStream? = nil;
var videoStream: FMLiveSwitchVideoStream? = nil;
if (_localMedia?.audioTrack() != nil) {
audioStream = FMLiveSwitchAudioStream.init(localMedia: _localMedia)
}
if (_localMedia?.videoTrack() != nil) {
videoStream = FMLiveSwitchVideoStream.init(localMedia: _localMedia)
}
connection = _channel?.createSfuUpstreamConnection(with: audioStream, videoStream: videoStream)
connection?.addOnStateChange({
// --- Insert code here to track connection state --- //
})
connection?.open()
return connection!
}
Downstream connection
func OpenSfuDownstreamConnection(remoteConnectionInfo: FMLiveSwitchConnectionInfo) -> FMLiveSwitchSfuDownstreamConnection {
let remoteMedia: RemoteMedia = ...
var connection: FMLiveSwitchSfuDownstreamConnection?
var audioStream: FMLiveSwitchAudioStream? = nil
var videoStream: FMLiveSwitchVideoStream? = nil
if (remoteConnectionInfo.hasAudio()) {
audioStream = FMLiveSwitchAudioStream.init(localMedia: _localMedia, remoteMedia: remoteMedia)
}
if (remoteConnectionInfo.hasVideo() && !_audioOnly) {
videoStream = FMLiveSwitchVideoStream.init(localMedia: nil, remoteMedia: remoteMedia)
}
connection = _channel?.createSfuDownstreamConnection(withRemoteConnectionInfo: remoteConnectionInfo, audioStream: audioStream, videoStream: videoStream)
connection?.addOnStateChange({
// --- Insert code here to track connection state --- //
})
connection?.open()
return connection!
}
Upstream Connection
fm.liveswitch.LocalMedia localMedia = ...;
fm.liveswitch.Channel channel = ...;
private fm.liveswitch.SfuUpstreamConnection openSfuUpstreamConnection(final String tag){
fm.liveswitch.SfuUpstreamConnection connection;
VideoStream videoStream = null;
AudioStream audioStream = null;
audioStream = new AudioStream(localMedia);
videoStream = new VideoStream((localMedia));
connection = channel.createSfuUpstreamConnection(audioStream, videoStream);
connection.addOnStateChange(new IAction1<ManagedConnection>()
{
public void invoke(ManagedConnection connection)
{
// --- Insert code here to track connection state --- //
}
});
connection.open();
return connection;
}
Downstream connection
fm.liveswitch.Channel channel = ...;
private fm.liveswitch.SfuDownstreamConnection openSfuDownstreamConnection(final ConnectionInfo remoteConnectionInfo, final String tag){
final fm.liveswitch.RemoteMedia remoteMedia = ...;
SfuDownstreamConnection connection;
VideoStream videoStream = null;
AudioStream audioStream = null;
audioStream = new AudioStream(null, remoteMedia);
videoStream = new VideoStream(null, remoteMedia);
connection = channel.createSfuDownstreamConnection(remoteConnectionInfo, audioStream, videoStream);
connection.addOnStateChange(new IAction1<ManagedConnection>()
{
public void invoke(ManagedConnection connection)
{
// --- Insert code here to track connection state --- //
}
});
connection.open();
return connection;
}