Legacy (1.2 and older)
Security
WebRTC in general is very secure and LiveSwitch follows the WebRTC spec. Here we discuss implementation specifics of LiveSwitch's security measures, as well as covering basic network-related configuration.
Network Configuration and Required Ports
- STUN servers provide a WAN address discovery service and listen on port 3478 by default, but any port can be used. Firewall rules must be added to allow inbound UDP traffic on the configured port.
- TURN servers extend STUN to support relaying around restrictive firewalls. Like STUN, they listen on port 3478 by default, but any port can be used. Firewall rules must be added to allow inbound UDP and/or TCP traffic on the configured port.
- TURNS servers extend TURN by using TLS (SSL) to secure the underlying TCP connection. Since application data is already encrypted, this simply adds a layer of security on the TURN headers, but is useful to traverse firewalls that only allow TLS/SSL traffic. TURNS servers listen on port 5349 by default, but any port can be used. Port 443 is recommended since it is generally allowed by client networks. Firewall rules must be added to allow inbound TCP traffic on the configured port.
- Clients are the originator of requests and as such, they require no special firewall rules, provided outbound traffic is allowed.
- Media Server clustering requires TCP traffic on port 8445. If 8445 is unavailable the next port will be tried incrementally until one is available (i.e. 8446, 8447, 8448, 8449, etc).
- Media Server client connections require inbound UDP traffic on ports 49152-65535 on public interfaces.
- The SIP Connector requires TCP/UDP traffic on port 5060 on public interfaces.
Even the most restrictive corporate networks generally allow HTTP traffic on port 80 and TLS/SSL traffic on port 443 for HTTPS. For this reason, we recommend running a TURN server that listens on port 443 for TURNS.
Encryption
All connections are secured using DTLS. Secure X.509 certificates are automatically generated for each connection using ECDSA (default, P-256 curve) or RSA (2048-bit) signing, but custom certificates and key-pairs can be provided. Certificate fingerprints (used to verify the peer during key exchange) are exchanged via signaling in the SDP blobs, so it is imperative to ensure security at the signaling layer.
Connection Security
- DTLS 1.2 with support for cipher suites that support perfect forward secrecy (PFS). See below for the specific cipher suites allowed by the key exchange.
- Certificates use ECDSA signing with keys generated for the P-256 curve.
- Certificates can use RSA signing with variable key length (default is 2048 bits).
- Data is encrypted using AES 128-bit encryption with 80-bit message authentication per SRTP standards.
Cipher Suites Used for DTLS
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
- TLS_RSA_WITH_AES_128_CBC_SHA,
- TLS_RSA_WITH_AES_128_GCM_SHA256,
- TLS_RSA_WITH_AES_128_CBC_SHA256
signaling
LiveSwitch connections are extremely secure once established, provided the signaling layer is also secure. Since the certificate fingerprints must be signalled in the SDP offer/answer blobs, a weakness in the signaling layer allows the possibility of a man-in-the-middle attack. For LiveSwitch, it is the Gateway that provides the signaling layer, and when installed on a server with a trusted SSL certificate, LiveSwitch's Gateway uses TLS encryption (HTTPS/WSS). The LiveSwitch Gateway also allows you to provide rate limiting, black listing, and white listing of client connections through a token-based registration model. Managing these registration tokens is an application-level security concern. We recommend an authenticated API endpoint on your own authentication server to generate and deliver registration tokens to the client securely at runtime as requested. For DDoS protection, we recommend using a third-party service like CloudFlare.
Again, all signaling connections must be secured via TLS to make security guarantees, so your LiveSwitch Gateway must be running on a server with a trusted SSL certificate, and all traffic to the Gateway must be configured to use secure connections.
TURN Traffic Concerns and Secure TURN (TURNS)
The percentage of time that WebRTC traffic is established over relay (TURN) varies greatly depending on the network environment. For example, TURN may not be required in many home networks, but in more restrictive corporate networks, where symmetric NAT is normal, requiring TURN is far more common. In general, we estimate 20%-30% of connections use TURN.
When connecting to a TURN server, clients are authenticated by username and password. Managing these credentials is an application-level security concern. We recommend an authenticated API endpoint on your own authentication server to deliver these credentials to the client securely at runtime as requested.
For added security, you can force all traffic through TURN, which guarantees that clients cannot learn information about each others' private networks (optionally including their WAN IP).
When TURN is in use, all data flows through the TURN server. Before sending data to the TURN server, clients first encrypt it using the keys exchanged via DTLS and then prepend a TURN header with destination address information. The TURN server can only read the header, as it does not have access to the keys necessary to decrypt the payload. TURNS provides an additional level of layer of security on top of this (for TCP only) by securing the TCP stream with TLS. By doing this, the entire stream is encrypted, which includes the TURN headers (and re-encrypts the payload). The TURN server is still not able to decrypt the payload, but by encrypting the entire stream, the headers are now safe from packet sniffers. This allows the traffic to pass through stateful packet inspection in routers that would otherwise block non-HTTPS traffic.
Server Requirements
LiveSwitch has several server components that have particular hardware requirements. The following specifications describe the requirements of each component, which will help you when you are setting up your application's infrastructure.
Gateway
Firewall
- Allow inbound TCP traffic on port 8080 (default, configurable) for HTTP
- Allow inbound TCP traffic on port 8443 (default, configurable) for HTTPS
Minimum Hardware
- 2x CPU
- 2GB RAM
- Moderate network performance
Recommended Hardware
- 4x CPU
- 4GB RAM
- High network performance
High Availability
- 2+ gateways
- Load balance the with high-availability guarantees
Media Server
Firewall
- For clustering, allow TCP traffic on port 8445. If 8445 is unavailable, media servers will try the next port, incrementally, until one is available (i.e. 8446, 8447, 8448, 8449, etc).
- For client connections, allow inbound UDP traffic on ports 49152-65535 on public interfaces.
Minimum
- 4x CPU
- 4GB RAM
- High network performance
Recommended Hardware
- 8x CPU
- 8GB RAM
- Highest network performance
High Availability
- 2+ media servers
SIP Connector
Firewall
- For SIP requests allow UDP and or TCP traffic on port 5060 on public interfaces.
Installing LiveSwitch on Windows
This section describes how to setup LiveSwitch on a Windows host. For information on Linux host setup, skip ahead to the next section on Installing LiveSwitch on Linux.
LiveSwitch has several server components that are necessary for the application to run. To make the installation and configuration of these components easier, an installer is available that streamlines the setup process. You can download this installer from our Downloads page.
Once you have downloaded the installer, locate, extract, and run the FM.LiveSwitch.Installer.msi file. When you start the installer, you should be greeted with a "Welcome to LiveSwitch" splash screen, that will look like the following.
You should run the installer with elevated permissions, as it will need to create several services on your machine. To do so, right click on the .msi and select "Run as Administrator".
Click "Next" to begin the installation process.
Accepting the License Agreement
The next screen in the installer will prompt you to accept the LiveSwitch licensing agreement. If you do not accept, the installer will exit. To accept, read through the terms, and then click the checkbox indicating your acceptance.
Selecting Features to Install
The LiveSwitch installer is capable of installing several product features. On this screen, you will be prompted to select which features you would like installed:
- LiveSwitch Gateway: Connects the various parts of LiveSwitch together, required.
- LiveSwitch Media Server: Manages SFU and MCU sessions, required.
- LiveSwitch SIP Connector: Allows LiveSwitch to connect to SIP endpoints, optional.
- Chat Example: Demo of LiveSwitch, optional.
Select which features you would like installed and click the Install button.
At this point, if you did not start the installer with elevated permissions, a UAC prompt will appear asking for them. The prompt will have two options, Yes and No. Click Yes to continue.
Note that although the application will allow you to skip installation of the LiveSwitch Gateway and Media Server components, these components are still required somewhere if you want a full installation of LiveSwitch. The installer allows you to skip these components on a given installation so that you can build up a server cluster by installing individual components on separate servers. In this case, you would run the installer multiple times, once on each server, and each installation would select a single component. See Scaling LiveSwitch for details.
Setting the License Key
Once you have started the installation process, a dialog will appear that prompts you to enter your license key. You can obtain a license key by logging in to your account and going to the downloads page: https://www.frozenmountain.com/downloads
If you enter an invalid license key, the installer will not let you proceed to the next step, so don't worry about entering it incorrectly.
Setting Your Redis Configuration
Redis is a high-performance, in-memory data store that is used as a cache and a message broker. It is commonly used as a backing store for applications that need to propagate data throughout a set of servers. LiveSwitch requires a Redis 3.2+ backend. To configure this, specify your Redis configuration (note you can choose to simply run Redis Locally for development purposes).
Redis Supported Version
You will have to set up Redis 3.2+ to have your Gateway run properly.
Setting the Initial Gateway Configuration
The next dialog will appear if you have selected to install the LiveSwitch Gateway. You will see two sets of options, labelled Gateway and Admin. These options allow you to configure the HTTP and HTTPS bindings for the LiveSwitch Gateway and the LiveSwitch Gateway's Administration Console. By default, no bindings are selected.
You must, at minimum, select an HTTP port for the Gateway, or other LiveSwitch components will not be able to connect to the LiveSwitch Gateway. If you specify an HTTPS port, you must also specify an X509 certificate at this time.
There are other options that you can specify through the LiveSwitch Gateway's configuration file. Refer to the Configuring the Gateway section for more information.
Setting the Initial Media Server Configuration
The next dialog appears if you have selected to install the LiveSwitch Media Server. There are three main options here.
The Gateway URL is the address that the media server will target. Generally, this URL should match the settings you entered in the previous dialog, or else the URL should correspond to a LiveSwitch Gateway that you have installed on another machine.
The STUN address or public IP is intended to be used if you are installing on a server. If you specify a STUN address, the server will auto-discover its public IP address at runtime. If you don't want to rely on an external STUN server, you can opt to explicitly set the public IP address of the server.
As with the LiveSwitch Gateway, there are other options that you can specify through the LiveSwitch Media Server's configuration file. Refer to the Configuring the Media Server section for more information.
Setting the Initial SIP Connector Configuration
The following configuration dialog allows you to configure the LiveSwitch SIP Connector if you selected it earlier in the install process. Like the previous dialog, it allows you to set the URL of the LiveSwitch Gateway that you want to use. Again, this URL must match the settings of either a LiveSwitch Gateway that you have installed locally, or a LiveSwitch Gateway that is installed on another machine. You must also specify your SIP registration credentials at this time.
Refer to the the Configuring the SIP Connector section for more configuration options.
Starting the LiveSwitch Web Example
If you have selected to install the Web Example, the installer will copy it to your hard drive and run it now. You will first be prompted to enter your LiveSwitch Gateway URL.
Once that is done, three windows will open. The first is a command prompt window that will display log messages related to the operation of the LiveSwitch services. The second is a browser window of your default browser. This window will open to the Web Example, so that you can try it out. This example is the same as our public demo at https://demo.liveswitch.fm. The final window is the administration console, which will show you who has connected to your LiveSwitch Gateway.
Installation Results
The installation components will be installed to C:\Program Files\Frozen Mountain Software\LiveSwitch
. This directory will contain four sub-directories, one for each product feature that you have installed. If you have installed the Gateway, SIP Connector or Media Server product features, a corresponding service for each of these will also be installed. These services are named "LiveSwitch Gateway", "LiveSwitch SipConnector" and "LiveSwitch MediaServer", respectively. They will be started automatically when your machine starts.
Application | Default HTTP URL | Default HTTPS URL |
---|---|---|
LiveSwitch Gateway | http://localhost:8080/sync | |
LiveSwitch Media Server | n/a (media servers have no http listeners) | n/a (media servers have no http listeners) |
LiveSwitch Admin | http://localhost:9090/admin | |
LiveSwitch Demo App | http://localhost:52008/index.htm | n/a (the demo app runs over http only) |
To re-run the LiveSwitch demo application if you have closed it, browse to C:\Program Files\Frozen Mountain Software\LiveSwitch\Chat Example
and run the command "node server.js". This will launch the demo application server, which can be accessed via the URL noted above.
If you wish to uninstall LiveSwitch, you can do so as you would any other application, from the Programs and Features section of Windows.
Installing LiveSwitch on Linux
This section describes how to set up LiveSwitch on a Linux host. For information on Windows host setup, refer to the previous section, Installing LiveSwitch on Windows.
A typical LiveSwitch Linux install is composed of three services - the "gateway" service, the "media server" service and the "sip connector" service. These services are made available in a tarball, which you can access by downloading the official LiveSwitch distribution from our Downloads page. The compiled Linux services are available in the root of the distribution, in the liveswitch.tar.gz
archive.
There is currently no Linux installer for LiveSwitch. You can install it by following the steps described below.
Install .NET Core
LiveSwitch for Linux is built on the .NET Core SDK. To run LiveSwitch on Linux, you must install .NET Core. Instructions for this step vary based on the distribution of your Linux host. Fortunately, Microsoft provides step-by-step instructions for common Linux distributions. To get instructions for your host, visit the following site: https://www.microsoft.com/net/learn/get-started/linux. Once there, select your distribution from the drop down box and follow the steps. Once .NET Core is installed, proceed to the next section.
Install Dependencies
LiveSwitch for linux requires libfontconfig1. To install libfontconfig1, use the following command:
apt-get update apt-get install libfontconfig1
Install the LiveSwitch Services
As mentioned above, LiveSwitch is composed of three core services. The default LiveSwitch installation assumes that these services will exist in the /opt/liveswitch
directory and that the services will run on systemd. You can, of course, run the services in any way you choose, but this guide will focus on installing the three core services as systemd service units.
First, unzip the tarball to the /opt
directory.
tar -xvzf liveswitch.tar.gz -C /opt
Afterwards, you should see three sub-folders, /opt/liveswitch/gateway
, /opt/liveswitch/media-server
, and /opt/liveswitch/sip-connector
. Inside each of these directories is a systemd service unit file specific to the each service. Respectively, they are gateway.service
, media-server.service
and sip-connector.service
.
You can use these service units as-is, by symlinking them into the systemd service unit directory.
ln /opt/liveswitch/gateway/gateway.service /etc/systemd/system/gateway.service ln /opt/liveswitch/media-server/media-server.service /etc/systemd/system/media-server.service ln /opt/liveswitch/sip-connector/sip-connector.service /etc/systemd/system/sip-connector.service
Once the services have been symlinked, use systemctl
to enable them and then reload the systemd daemon.
systemctl enable gateway systemctl enable media-server systemctl enable sip-connector systemctl daemon-reload
If the services have not been started already, you can start them with systemctl. Make sure that you set your license key in the configuration file for each service before you start them.
systemctl start gateway systemctl start media-server systemctl start sip-connector
Configuring the Gateway
The LiveSwitch Gateway is a service that allows the various LiveSwitch components to communicate with each other and with your client applications.
Finding the Configuration File
To configure the LiveSwitch Gateway, navigate to the directory with the service executable file. By default, this is C:\Program Files\Frozen Mountain Software\LiveSwitch\Gateway
. In this directory, look for a file named FM.LiveSwitch.Gateway.Service.config
. You can edit this file to change the LiveSwitch Gateway settings.
The LiveSwitch Gateway configuration file has one top-level configuration section, gateway
. This should be specified in the configuration file, as shown below:
<configuration> <configSections> <section name="gateway" type="FM.LiveSwitch.Gateway.Configurations.GatewayConfigurationSection, FM.LiveSwitch.Gateway" /> </configSections> </configuration>
The next sections of this document will describe the configuration options for this element.
Editing the Gateway Section
The gateway
section provides configuration for the LiveSwitch Gateway that determines which components and clients are allowed to connect to it and which URIs that these components and clients can use to connect to it. There are several main configuration sections:
license
sets the license key for the serviceauth
sets the authorization settingsredis
configures a Redis backendweb
sets up HTTP and HTTPS bindingsadmin
sets the web bindings for the administrator UI
Each of these is explored in detail in the following sections.
Setting the License Key
You must specify a single license
element. This element has a single key
attribute. Its value should contain your LiveSwitch license key.
<gateway> <license key="fmeyJpZCI6IjZm...DAwMDAwMH0=" /> </gateway>
Modifying Authorization Settings
One of the main roles of the LiveSwitch Gateway is to authenticate users and other components of the LiveSwitch stack. The auth
element specifies how this authorization is performed. At minimum, you must provide a shared secret for each of the three components that can connect to the gateway:
- Clients: These are end-users that connect to the gateway.
- Media Servers: These are server side components that manage SFU and MCU sessions.
- SIP Connectors: These are server side components that handle SIP trunking.
Each of these has an XML element associated with it - clients
, connectors
and mediaServers
, respectively. To set a shared secret for one of these elements, specify the sharedSecret
attribute on the element. The shared secret can be any arbitrary string, but a GUID is common, as shown below.
<gateway> <auth> <clients sharedSecret="00000000-0000-0000-0000-000000000000" /> <connectors sharedSecret="11111111-1111-1111-1111-111111111111" /> <mediaServers sharedSecret="22222222-2222-2222-2222-2222222222222" /> </auth> </gateway>
In the above configuration, you specified one shared secret for all clients. If your gateway only runs a single application, this is perfectly fine. If, however, you wish to have multiple applications running on a single LiveSwitch Gateway, you can create separate shared secrets for each application. To do this, instead of adding the sharedSecret
attribute to the clients
element, specify each application using the add
element:
<gateway> <auth> <clients> <add applicationId="first-application" sharedSecret="00000000-0000-0000-0000-00000000000" /> <add applicationId="second-application" sharedSecret="00000000-1111-1111-1111-00000000000" /> </clients> <connectors sharedSecret="11111111-1111-1111-1111-111111111111" /> <mediaServers sharedSecret="22222222-2222-2222-2222-2222222222222" /> </auth> </gateway>
Keep track of these shared secrets, you will need to specify them in the configuration files for your LiveSwitch Media Server and LiveSwitch SIP Connector. To see how to set these, make sure you read the Configuring the Media Server and Configuring the SIP Connector sections.
Setting Web Bindings
The web
element defines the HTTP and HTTPS bindings for the LiveSwitch Gateway. The web
element itself has a single required attribute, path
. This attribute defines the path that LiveSwitch components and clients will use to connect to the gateway. The default path is "sync". If, for example, you had a LiveSwitch Gateway listening on localhost:8080, you would connect to it with the URL: http://localhost:8080/sync
. The following snippet shows what this configuration looks like:
<gateway> <web path="sync"> </web> </gateway>
The previous configuration block alone is not enough to configure the LiveSwitch Gateway bindings. You must also specify which http and https endpoints that the LiveSwitch Gateway should bind to. This is done with the http
and https
elements. You can specify multiple bindings for each collection. The following configuration sets up a wildcard binding that listens for HTTP requests on port 8080.
<gateway> <web path="sync"> <http> <binding host="*" port="8080" /> </http> </web> </gateway>
If you want the server to only accept requests for a specific host, you can specify the host in the host
attribute. The following example only accepts requests made to http://liveswitch.frozenmountain.com:
<gateway> <web path="sync"> <http> <binding host="liveswitch.frozenmountain.com" port="8080" /> </http> </web> </gateway>
Windows HTTPS Bindings
For HTTPS, you specify a binding
element the same way you did for HTTP, except you nest it under an https
element instead of an http
element. On Windows, you must first install the certificate to the Personal certificate store and then specify the certificate thumbprint in your HTTPS binding
element. Use the certifcateHash
attribute to specify the thumbprint, which should be an SHA-1 hash of the entire certificate. The following example shows how to add an HTTPS binding on Windows.
<gateway> <web path="sync"> <https> <binding host="*" port="8443" certificateHash="cb4442bca824b940dd9a17da3cab7250bb1b8ddd" /> </https> </web> </gateway>
You can combine both of these attributes and provide multiple HTTP and HTTPS bindings for your LiveSwitch Gateway, but make sure you specify a different host and port combination for each binding.
Certificates need to be installed to Personal -> Certificates on Local Computer. The installed certificate must be in .pfx format to contain both the public and private keys.
We have seen an issue where when you copy the thumbprint from the MMC, it may actually include an invisible zero-width space at the beginning and end. If you are getting certficate errors:
- Highlight the opening quote and first character of your hash and replace them both.
- Highlight the final character of your hash and the closing quote and replace them both.
- Restart the Gateway service.
Linux HTTPS Bindings
For HTTPS on Linux, instead of installing to a certificate store, you must instead provide the path to a .pfx file containing both the certificate and private key for the certificate. You can make a .pfx file from a certificate and a private key using OpenSSL, like so:
openssl pkcs12 -export -out my.domain.pfx -inkey my.domain_certificate.pem -in my.domain_certificate.crt
Once you have a pfx file, add a binding
element underneath your HTTPS element, and specify the certificatePath
attribute. Provide the absolute path to your pfx file using this attribute, as shown below:
<gateway> <web path="sync"> <https> <binding host="*" port="8443" certificatePath="/etc/ssl/my.domain.pfx" /> </https> </web> </gateway>
As with Windows, you can specify multiple HTTPS and HTTP bindings, so long as each one binds to a different port.
Configuring the Redis Backend
Redis is a high-performance, in-memory data store that is used as a cache and a message broker. It is commonly used as a backing store for applications that need to propagate data throughout a set of servers. LiveSwitch has full support for using a Redis backend. To configure this, specify a redis
element that has one connectionString
attribute. The simplest possible connection string that you can have is simply "localhost". The following snippet configures the Redis backend provider to use the Redis server running on localhost.
<gateway> <redis connectionString="localhost" /> </gateway>
Note that this is not required. By default, the LiveSwitch Gateway runs in-memory. If you do not have a Redis database up and running, it's perfectly okay to omit this for now. You can always switch over to it painlessly in the future.
More information on connection strings is available at: https://stackexchange.github.io/StackExchange.Redis/Configuration.
Redis for multiple environments
If you are running multiple environments (e.g. production and staging), you will need to run separate Redis backends for each.
Setting Administrator Bindings
The LiveSwitch Gateway also provides a UI for administrators. The UI lists the status of all components that are connected to the LiveSwitch Gateway and allows administrators to deactivate components that might be misbehaving. You configure this administration interface the same way that you configure regular web bindings. The following sample shows you how to configure the administrator UI to be accessible via HTTP on port 9090 or via HTTPS on port 9443.
<gateway> <admin path="admin"> <http> <binding host="*" port="9090" /> </http> <https> <binding host="*" port="9443" certificateHash="cb4442bca824b940dd9a17da3cab7250bb1b8ddd" /> </https> </admin> </gateway>
As with regular web bindings, you can specify as many administrator bindings as you like.
Ensure your admin UI is secure
Access to your administrative route must be blocked by your firewall. Prevent unauthorized access to administrative functionality.
Restarting the Service
Whenever you modify the configuration for the LiveSwitch Gateway service, you must restart the service for the changes to be applied. Run the following commands in either the Windows Command Prompt or a PowerShell window with administrator access:
net stop "LiveSwitch Gateway" net start "LiveSwitch Gateway"
Configuring the Media Server
The LiveSwitch Media Server is a service that manages media data for LiveSwitch media sessions. In a multipoint conference, the LiveSwitch Media Server performs any forwarding or multiplexing required by the session.
Finding the Configuration File
To configure the LiveSwitch Media Server, navigate to the directory with the service executable file. By default, this is C:\Program Files\Frozen Mountain Software\LiveSwitch\Media Server
. In this directory, look for a file named FM.LiveSwitch.MediaServer.Service.config
. You can edit this file to change the LiveSwitch Media Server settings.
The LiveSwitch Media Server configuration file has one top-level configuration section, mediaServer
. This should be specified in the configuration file, as shown below:
<configuration> <configSections> <section name="mediaServer" type="FM.LiveSwitch.MediaServer.Configurations.MediaServerConfigurationSection, FM.LiveSwitch.MediaServer" /> </configSections> </configuration>
The next sections of this document will describe the configuration options for this element.
Editing the Media Server Section
The mediaServer
section provides configuration for the LiveSwitch Media Server that determines how the relay server is configured and which codecs are available to clients. There are several main configuration sections:
log specifies the logging level and configuration
gateway
specifies which LiveSwitch Gateway to connect toconnection
sets the local network settings for connections from the media servercodecs
specifies which codecs that the media server should usemcu
configures settings specific to MCU modesfu
configures settings specific to SFU moderecording
enables or disables session recording
Each of these is explored in detail in the following sections.
Specifying the Gateway
You must also specify a single gateway
element. This element defines how the LiveSwitch Media Server will connect to your LiveSwitch Gateway. The gateway element has two attributes that you must specify, gatewayUrl
and sharedSecret
. The gatewayUrl
attribute should contain the scheme, domain and path to your LiveSwitch Gateway route. Refer to the section on Configuring the Gateway for more information on how to modify this value. The sharedSecret
attribute should contain the shared secret that your gateway has specified for LiveSwitch Media Servers. This should be the same value that you have specified in your LiveSwitch Gateway configuration file. Again, refer to the section on Configuring the Gateway for information on configuring this.
<mediaServer> <gateway gatewayUrl="http://localhost:8080/sync" sharedSecret="22222222-2222-2222-2222-222222222222" /> </mediaServer>
Again, make sure the shared secret that you specify here matches what is specified in the mediaServers
element in your LiveSwitch Gateway configuration file. If it does not, then the LiveSwitch Media Server will not be able to connect to the gateway.
Specifying Local Network Settings
The connection
element lets you configure the public IP address of the media server as it will be visible to clients. This element has two possible attributes, stunAddress
and publicIPAddress
. If you know the public IP address of your LiveSwitch Media Server, you should specify it here using the publicIPAddress
attribute, like so:
<mediaServer> <connection publicIPAddress="24.0.0.92" /> </mediaServer>
If you do not know the public IP address of your LiveSwitch Media Server, you can instead specify the address of a STUN server. When the LiveSwitch Media Server starts up, it will use this server to discover its own public IP address. When specifying the address of the STUN server, specify the domain and optionally the port (default is 3478). The following example shows the use of the stunAddress
attribute.
<mediaServer> <connection stunAddress="stun.liveswitch.fm" /> </mediaServer>
Generally, using the stunAddress
attribute is more robust, as it automatically handles changes to the public IP address. You should specify only one of these configuration options.
Note
Only specify a stunAddress or a publicIPAddress. If both are specified, connectivity may not be established.
Enabling Bandwidth Adaptation
In your media server configuration, set the bandwidthAdaptationPolicy
attribute to enabled on the connection
element:
<connection bandwidthAdaptationPolicy="enabled" ... />
Then, in your client code, set BandwidthAdaptationPolicy
to Enabled
on your VideoStream
.
Bandwidth adaptation in browsers
Bandwidth adaptation is enabled by default in WebRTC-capable browser clients (Chrome, Firefox, Edge, and Safari).
Native bandwidth adaptation is experimental
Note that in the native platforms bandwidth adaptation is still experimental and is not recommended for AudioStreams at this time.
Modifying Codec Settings
The codecs
element is a collection that contains information on which codecs are supported by the LiveSwitch Media Server. Each codec is represented by its own distinct element - ie: the h264 codec has an h264 element. If a codec element does not appear under this element, then the LiveSwitch Media Server will ignore requests to use it. The available codecs are:
- VP8: Specified with the
vp8
element. You can additionally set the bitrate with thebitrate
attribute. This value is in kbps. - H264: Enabled with the
h264
element. You can additionally set the bitrate with thebitrate
attribute. This value is in kbps. - Opus: Enabled with the
opus
element. You can additionally set the bitrate with thebitrate
attribute. This value is in kbps. - PCMU: Enabled with the
pcmu
element. - PCMA: Enabled with the
pcma
element.
Please note that bitrate here (in Kbps) is a downstream rate, which means that if you specify 1024, then this is the maximum rate that will be sent in the downstream. In the example below, h264 will sent with a max of 512 Kbps while vp8 will max out at 1024 Kbps.
The sample configuration below enables all available codecs:
<mediaServer> <codecs> <vp8 bitrate="1024" /> <h264 bitrate="512" /> <opus bitrate="32" /> <pcmu /> <pcma /> </codecs> </mediaServer>
Note that if you disable too many codecs, the LiveSwitch Media Server will be unable to accept connections.
Configuring MCU Sessions
The mcu
element allows you to set configuration options for the LiveSwitch Media Server when it is running in MCU mode. Among other things, this allows you to define how you want the output video to be formatted by the multiplexer. To configure this, add a videoOutput
element to your mcu
element. With this element, you can set both the dimensions and the frame rate of the video that the MCU outputs. The XML snippet below shows how you can configure the MCU to output video at 1280x720 and at 30 frames per second.
<mcu> <videoOutput width="1280" height="720" frameRate="30" /> </mcu>
For video resolutions, you should try to stick to common resolutions, as not all combinations are valid for all codecs. For example, don't enter 147x933. You should also keep your frame rate to a reasonable value. Anything beyond 30 frames per second will require a powerful server to keep up in larger video conferences.
Another aspect that you can configure is the maximum bitrate for participant video while in an MCU session. This is useful to limit the amount of bandwidth that the MCU consumes. To do this, add a videoInput
element or an audioInput
element and set the maxParticipantBitrate
attribute. The value of this attribute is in kbps. This is demonstrated below, where a maximum video bitrate of 1024kbps and a maximum bitrate of 32kbps is set.
Please note that bitrate here (in Kbps) is an upstream rate, which means that if you specify 1024, then this is what the Media Server will attempt to negotiate with participants.
<mcu> <videoInput maxParticipantBitrate="1024" /> <audioInput maxParticipantBitrate="32" /> </mcu>
Configuring SFU Sessions
Similar to the mcu
element described above, the sfu
element allows you to set configuration options for the LiveSwitch Media Server when it is running in SFU mode. You are not able to set the video or audio output, as the SFU does not actually perform any processing of audio and video streams - it merely forwards them. You can however, still set the maximum bitrate of the participants' input streams. To configure this, again, you use two elements, videoInput
and audioInput
. For both, set the maxParticipantBitrate
attribute. The sample below demonstrates how to set a maximum video bitrate of 1024kbps and a maximum audio bitrate of 32kbps.
<sfu> <videoInput maxParticipantBitrate="1024" /> <audioInput maxParticipantBitrate="32" /> </sfu>
Enabling Recording
The recording
element controls whether or not the LiveSwitch Media Server records any sessions. The recording
element has two attributes, which must both be specified to enable recording. To enable recording, the enabled
attribute must be set to "true"
. You must then specify the path
attribute, which must be a path to an existing directory. This is demonstrated below:
<mediaServer> <recording enabled="true" path="C:\Temp" /> </mediaServer>
Starting with your specified path, recordings are written to path
> appId > channelId > userId > deviceId > clientId > connectionId > audio.mkv
or path > appId > channelId > userId > deviceId > clientId > connectionId > video.mkv
.
Note that in a peer-to-peer session, no recording is performed. This is because in this type of session, no users are connected to the LiveSwitch Media Server itself. If you wish to record a session, you must ensure that it is an SFU or MCU session.
Configuring per-Application and per-Channel Settings
The above configuration options modify the settings for the entire LiveSwitch Media Server. This is useful as a default but you may wish to have settings that differ for each application. For example, if you are running a service with a free tier, you may wish to reserve larger amounts of bandwidth for your paying customers. LiveSwitch accommodates this by allowing you to modify individual configuration options at either the application level or the channel level.
The following configuration options demonstrate how to specify a low-resolution MCU output stream for one application and a high-resolution MCU stream for another.
<mediaServer> <application id="my-free-application"> <mcu> <videoOutput width="640" height="360" frameRate="15" /> </mcu> </application> <application id="my-paid-application"> <mcu> <videoOutput width="1280" height="720" frameRate="30" /> </mcu> </application> </mediaServer>
You can also modify the settings on a per-channel level. The following XML sample disables recording by default but enables it for a single channel.
<mediaServer> <application id="my-application"> <recording enabled="false" /> <channel id="recording-enabled-channel"> <recording enabled="true" /> </channel> </application> </mediaServer>
You can modify any setting under the connection
, codecs
, mcu
, sfu
and recording
configuration elements. You cannot specify application-specific settings for the gateway
configuration element.
Restarting the Service
Whenever you modify the configuration for the LiveSwitch Media Server service, you must restart the service for the changes to be applied. Run the following commands in either the Windows Command Prompt or a PowerShell window with administrator access:
net stop "LiveSwitch Media Server" net start "LiveSwitch Media Server"
Configuring the SIP Connector
The LiveSwitch SIP connector is a service that provides SIP trunking, which allows your application to add SIP endpoints to your media sessions.
Finding the Configuration File
To configure the LiveSwitch SIP Connector, navigate to the directory with the service executable file. By default, this is C:\Program Files\Frozen Mountain Software\LiveSwitch\SIP Connector
. In this directory, look for a file named FM.LiveSwitch.Connector.Service.exe.config
. You can edit this file to change the Connector settings.
The LiveSwitch SIP Connector configuration file has two configuration sections, sip
and connector
. These should be specified in the configuration file, as shown below:
<configuration> <configSections> <section name="sip" type="FM.LiveSwitch.SipConnector.Configurations.SipConfigurationSection, FM.LiveSwitch.SipConnector" /> <section name="connector" type="FM.LiveSwitch.SipConnector.Configurations.ConnectorConfigurationSection, FM.LiveSwitch.SipConnector" /> </configSections> </configuration>
The next sections of this document will describe the configuration options for these elements.
Editing the Connector Section
The connector
section provides configuration for the LiveSwitch SIP Connector that allows it to communicate with the LiveSwitch Gateway. A typical connector
section looks like the following sample:
<connector gatewayUrl="http://localhost/sync" sharedSecret="11111111-1111-1111-1111-111111111111"> </connector>
On the connector
element, there are two attributes that you must specify, gatewayUrl
and sharedSecret
. The gatewayUrl
attribute should contain the scheme, domain and path to your LiveSwitch Gateway route. Refer to the section on Configuring the Gateway for more information on how to modify this value. The sharedSecret
attribute should contain the shared secret that your gateway has specified for SIP Connectors. This should be the same value that you have specified in your LiveSwitch Gateway configuration file. Again, refer to the section on Configuring the Gateway for information on configuring this.
Editing the SIP Section
The sip
element provides additional configuration options that control how SIP calls are handled. The sip
element itself has one required attribute, serverPort
. Unless you have a specific reason to alter the port, you should leave it as 5060
, which is the default port for SIP. Modifying this can cause confusion, as many applications will expect the port to be 5060.
<sip serverPort="5060"> </sip>
There are several additional configuration sections under the sip
element:
registration
sets the SIP registration informationdialPlan
configures how the SIP connector interprets outbound toUser values entered by clients
Each of these is explored in detail in the following sections.
SIP Connectors require that port 5060 is forwarded if they are behind a firewall.
Registering with the SIP Server
You must have an account with a SIP provider to use the LiveSwitch SIP Connector. There are several providers that provide free trials, which you can use to get started. Once you have a provider, you can use the registration
element to tell the LiveSwitch SIP Connector how to register with them. The registration
element has three attributes, domain
, username
and password
. The values for these will be provided by your SIP provider. A sample configuration is demonstrated below:
<sip> <registration domain="sip.flowroute.com" username="15934744" password="ABzz4455ab3gd" /> </sip>
Again, you must find your own SIP provider and update these settings to the correct values.
Auth Id
Some SIP providers use an auth id in addition to the username and password. If provided, this is just another attribute in the registration xml element.
<registration domain="sip.flowroute.com" username="15934744" authId="yourauthid" password="ABzz4455ab3gd" />
Creating a Dial Plan
A SIP dial plan defines how a SIP call is routed when a client attempts to make a call to a specific user. These settings are contained in the dialPlan
element. The dialPlan
element has a child mappings
element, which is a collection of mappings that determine how SIP calls are routed. Each element that you add must have an applicationId
, a channelId
and a toUser
attribute. If a SIP call is made from a channel matching the channelId
attribute and an application matching the applicationId
attribute, then the call will be mapped to the user specified in the toUser
attribute.
Uniqueness of a mapping
Mappings must be unique according to the toUser
field. You should not add more than one mapping per user.
<sip serverPort="5060"> <dialPlan> <mappings> <add toUser="john@doe.com" channelId="1585" applicationId="my-other-app-id" /> <add toUser="16044248331" channelId="3337" applicationId="my-app-id" /> </mappings> </dialPlan> </sip>
You can also specify a webHookUrl
attribute in your dialPlan
element. This specifies an HTTP or HTTPS endpoint that will be hit when a call does not match any of the dial plan's mappings. The next snippet shows how to specify this:
<sip serverPort="5060"> <dialPlan webHookUrl="http://localhost:59763/sip" /> </sip>
The next section describes how to configure this webhook to receive and respond to SIP requests:
Setting up the Webhook
The SIP connector routes SIP requests that do not match any mappings in the dial plan to the webhook URL that you specify in your configuration, as mentioned above. You must now set up this webhook endpoint to receive and respond to these requests. When this endpoint is hit, several query parameters will be available:
- toUser: the user or phone number that is being called
- fromUser the user or phone number that initiated the call
- toHost: the host that is being called
- fromHost: the host that initiated the call
The HTTP request will be delivered as a POST request, with a Content-Type
of application/json
. The body of the request itself will contain a JSON object with information on the original SIP INVITE request. This object will contain two top-level keys, headers
and sdp
.
The headers
key is a nested JSON object, where each key represents a header from the INVITE request, and with each value representing the corresponding value of the SIP header. The sdp
key will contain the SDP message from the INVITE request. A request will look similar to the following example:
{ "headers": { "From": "\"Frozen Mountain\" <sip:frozenmountain@10.0.0.0>", "Content-Type": "application/sdp", ... }, "sdp": "v=0\r\no=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com..." }
Using the query parameters and the SIP INVITE request, your webhook must issue an HTTP response of type application/json
, containing the following parameters:
- applicationId: the application id to map the request to
- channelId: the channel id to map the request to
- userId: the user id to map the request to (optional)
Each of these parameters should be a top-level key of a JSON object. An example JSON response will look similar to the following:
{ "applicationId": "my-app", "channelId": "581233", "userId": "john@doe.com" }
Restarting the Service
Whenever you modify the configuration for the Connector service, you must restart the service for the changes to be applied. Use the following commands in either the Windows Command Prompt or a PowerShell window with administrator access:
net stop "LiveSwitch SipConnector" net start "LiveSwitch SipConnector"
Configuring Logging
LiveSwitch provides a rich logging framework that supports many different capabilities. You can configure LiveSwitch logging for the Media Server, the Gateway, or the SIP connector with the <log/>
element in the config of the given service:
<log level="info"> <file/> <!-- defaults to ProgramData --> <event/> <syslog/> <!-- defaults to 127.0.0.1 --> <cloudwatch /> </log>
- <file/> configures file system logging. You can configure multiple <file/> elements. You can change the default logging location by providing the path attribute to override where the log is written to. The defaults are:
%ProgramData%\Frozen Mountain Software\-service-\
for Windows, and/var/log/fm/liveswitch/-service-/
for Linux. <syslog/>
configures system message logging. You can configure multiple<syslog/>
elements.<syslog/>
has atarget
attribute which is the IP where the syslog is sent. The default is127.0.0.1
<event/>
turns on Windows event logging. There is no additional configuration required. If this element is present then event logging is turned on.<cloudwatch />
turns on AWS CloudWatch logging. See the Configuring the CloudWatch Log Provider section below for more information.
Levels
Logging statements have an associated level of severity. Possible log levels are: verbose,
debug
, info
, warn
, error
, fatal
, and none
. The severity level indicates the importance of the message. The levels are, from most severe to least severe:
- FATAL: The message indicates a fatal application error; the application will need to shut down and no recovery actions are possible.
- ERROR: The message indicates a normal application error (ie: timeouts, bad user input); this is a normal error state and usually it is possible to proceed.
- WARN: The message indicates that something abnormal occurred but that the application was not negatively affected; this is used for unexpected values, deprecations or abnormal application state.
- INFO: This is a trace message; it describes what is going on in the SDK.
- DEBUG: This is a diagnostic message; it is similar to the INFO level but is used for outputting detailed parameters or for "spammy" messages.
- VERBOSE: This is a diagnostic message; it is similar to the DEBUG level but is used for outputting extremely low level detailed tracing messages. It is generally not recommended to configure verbose logging.
The level
attribute allows you to set the level of logs you want displayed. By default, only INFO and more severe messages are displayed. In production, you may want to display no message, and in development, you may want to display all diagnostic messages. This is accomplished by setting the level
attribute. Note that there is one additional level here, NONE. You can set this if you want to suppress all log messages. A level can be configured for the <log>
element, and/or any of its child elements. A child element's level overrides the level configured for the <log>
element.
Configuring the CloudWatch Log Provider
The CloudWatchLogProvider
is a new log provider released with LiveSwitch 1.1.4. This provider dumps your logs directly into a specific AWS CloudWatch log group.
To enable it, add a new <cloudwatch />
element into the <logs></logs>
element on any of the LiveSwitch services.
<log level="info"> ... <cloudwatch region="us-west-2" group="myCloudWatchGroup" accessKey="..." secretKey="..." profileName="..." profilePath="..." /> </log>
Required attributes are:
region
: The CloudWatch region to send the logs.logGroup
: The CloudWatch log group to send the logs to.
Basic access configuration requires two attributes:
accessKey
: Your AWS accesskey.secretKey
: Your AWS secret key.
Alternatively, you can configure authentication via profile:
profileName
(required): The profile name in which to try and load from an AWS credential file.profilePath
(optional): The path to an AWS credential file. If missing, the default path is used.
Should you choose not to supply any of accessKey
, secretKey
, or the profile
attributes, then AWS auto-detection is turned on if the service is running on EC2 or ECS. For ECS, the task execution role is used, so you must ensure it has CloudWatch permissions. For EC2, the instance profile role is used if available.
Production Environments
Best Practice: For production environments, it is highly recommended to set your logging level to INFO or higher to minimize performance issues.
Creating an Auth Server
It is not advisable to create registration tokens in your client applications. This section will go into further detail about why this is a bad idea and will show you how to securely generate authorization tokens.
Why Not Generate Tokens Client Side?
To understand why you should not generate these tokens on the client, first consider how the LiveSwitch Gateway validates an authorization request. A authorization request is valid if the token parameters are valid values and the secret key in the token matches the secret key configured on the LiveSwitch Gateway. This means that if the client knows the secret key, that almost any request they make will be valid - there is no way for your application to stop a client from registering or joining a channel.
What this means is that the security of your application depends on keeping the secret key a secret. You can take this concept further by considering that for each token that you want to generate there are two types of data: things the client knows, and things the server knows. It's not always as black and white as that, but for our purposes here it's a useful distinction. Let's apply this concept to the parameters that are required to generate authorization tokens.
Application ID: As a unique identifier for the application, this value will come from the server. It can be hard-coded or, more commonly, the id may depend on a user's profile. Applications are associated with specific feature sets, so you do not want to allow the user to specify this as it can allow them to access features that they are not entitled to.
User ID: This value will also usually come from the server. Before a server issues an authorization token, it generally requires some form of authentication from the user. Because of this, the server knows who the user is already. You do not want to allow a client to specify this value, both because the server already knows what the value should be and because it may allow a client to impersonate another user.
Device ID: This value will generally come from the client. The device id is a unique identifier associated with a particular device. This is used in the case where a user might be connected on multiple devices, ie: through a web browser and a phone.
Client ID: This value will always come from the client. The client id is a unique identifier associated with a specific LiveSwitch instance, so it must be provided by the client.
Roles: Roles will generally come from a combination of both the client or the server. In the case where an application role is associated with a particular application privilege (ie: moderation abilities), then the role will be assigned by the server based on the user's profile. In the case where a role is not associated with any privileged action, (ie: a presenter in a conference), then the client should request this role. Of course, the server should validate all client roles based on their user profile.
Channel Claims: Channel claims usually come from the client, as they represent a claim that a client makes, ie: that they have the right to join the specified channel. The server may also add other channels as required if there are default channels or specific metadata channels associated with an application.
Shared Secret: This arbitrary string will always come from the server, as mentioned above.
Now that you have a better understanding of the concept of client information and server information, the next step is to set up your server.
Setting Things Up
To set up your auth server, you will need to set up a few things first. Generally, you will already have some sort of server to handle application-specific data such as user profiles. This is a good starting point for your auth server, as the application server will already have access to your user data, which you will need to authorize your client requests.
Generally, you will need to set up at least two additional endpoints on your server - one for generating registration tokens and one for generating channel tokens. This guide will assume a rest-like API, and will refer to these endpoints as "/token/register" and "/token/join"; meaning to register with the server, and to join a channel. In the following sections, some examples of server endpoints are provided using ASP .NET Core for C# and Spring 4 for Java.
Handling Registration Tokens
One of the main roles of an auth server is to authenticate clients and to return registration tokens so that these clients can connect to the LiveSwitch service. Registration tokens are typically generated from a "/token/register" endpoint on a REST-ful web service. In a normal registration request, the application id, user id and shared secret are provided by the server. The only remaining required parameters are the device id and the client id (because roles and channel claims are optional for a registration).
The following code snippet shows platform-specific examples of retrieving device and client ids and then making an http request to the server for a registration token. The device id and client id are provided via query parameters in a POST request.
var applicationId = "..."; var userId = "..."; var deviceId = Environment.MachineName; var client = new FM.LiveSwitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); var url = "https://app.server/token/register" + "?deviceId=" + client.DeviceId + "&clientId=" + client.Id; var httpClient = new HttpClient(); var response = await httpClient.PostAsync(url); var token = response.Content; client.Register(token).Then(...);
String applicationId = "..."; String userId = "..."; String deviceId = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID); fm.liveswitch.Client client = new fm.liveswitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); URL url = new URL("https://app.server/token/register" + "?deviceId=" + client.getDeviceId() + "&clientId=" + client.getId()); InputStream inputStream = url.openStream(); BufferedReader buffer = new BufferedReader(new InputStreamReader(input)); String token = buffer.lines.collect(Collectors.joining("\n")); client.register(token).then(...);
var applicationId = '...'; var userId = '...'; var deviceId = navigator.userAgent; var client = new fm.liveswitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); var url = 'https://app.server/token/register' + '?deviceId=' + client.getDeviceId() + '&clientId=' + client.getId(); var xhr = new XMLHttpRequest(); xhr.open('POST', url, true) xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { var token = xhr.response; client.register(token).then(...); } } xhr.send(null);
You will have to configure your application server to receive this request. The application server should then respond by generating a client registration token. You can generate this token using the values the client provides, and values available to the server. Pass all these values to the GenerateClientRegisterToken
static method of the FM.LiveSwitch.Token
class to return a token. This is demonstrated below.
[Route("[controller]")] public class TokenController : Controller { private String secret; private UserManager<AppUser> userManager; public TokenController(UserManager<AppUser> userManager) { this.userManager = userManager; } [HttpPost("register")] public ContentResult Register([FromQuery]string deviceId, [FromQuery]string clientId) { var user = await this.userManager.GetUserAsync(this.User); return Content(FM.LiveSwitch.Token.GenerateClientRegisterToken( user.ApplicationId, user.Id, deviceId, clientId, null, null this.secret )); } }
@Controller @RequestMapping("/token") public class TokenController { private String secret; @PostMapping @ResponseBody public String register( @RequestParam(value="deviceId", required=true) String deviceId, @RequestParam(value="clientId", required=true) String clientId) { UserDetails auth = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); applicationId = auth.getApplicationName(); userId = auth.getUsername(); return fm.liveswitch.Token.generateClientRegisterToken( applicationId, userId, deviceId, clientId, null, null, this.secret ); } }
Note that the details of how these controllers' shared secret property is provided is left up to you. A common way of doing this is through your dependency injection container. Another way of accessing it is to query your global application settings. Whatever way you select, it should not be accessible to any clients.
Handling Channel Tokens
The other main role of an auth server is to return channel tokens so that users who have already registered can join additional channels without needing to re-register. Channel tokens are usually generated from a "/token/join" endpoint on a REST-ful web service.
For a join token, the client must again provide its deviceId and clientId. It must also provide the id of a channel it wishes to join. The code samples below show how to make a REST-ful request with this information embedded in the query string.
var applicationId = "..."; var userId = "..."; var channelId = "...";' var deviceId = Environment.MachineName; var client = new FM.LiveSwitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); var url = "https://app.server/token/join" + "?deviceId=" + client.DeviceId + "&clientId=" + client.Id + "&channelId=" + channelId; var httpClient = new HttpClient(); var response = await httpClient.PostAsync(url); string token = response.Content; client.Join(channelId, token).Then(...);
String applicationId = "..."; String userId = "..."; String channelid = "..."; String deviceId = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID); Client client = new fm.liveswitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); URL url = new URL("https://app.server/token/join" + "?deviceId=" + client.getDeviceId() + "&clientId=" + client.getId()) + "&channelId=" + channelId; InputStream inputStream = url.openStream(); BufferedReader buffer = new BufferedReader(new InputStreamReader(input)); String token = buffer.lines.collect(Collectors.joining("\n")); client.join(channelId, token).then(...);
var applicationId = '...'; var userId = '...'; var channelId = '...'; var deviceId = navigator.userAgent; var client = new fm.liveswitch.Client("https://liveswitch.server:8443/sync", applicationId, userId, deviceId); var url = 'https://app.server/token/join' + '?deviceId=' + client.getDeviceId() + '&clientId=' + client.getId() + "&channelId=" + channelId; var xhr = new XMLHttpRequest(); xhr.open('POST', url, true) xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { var token = xhr.response; client.join(channelId, token); } } xhr.send(null);
Again, you will have to configure a "/token/join" endpoint to receive this request. The endpoint should respond with a join token that allows the client to join their requested channel. You generate this token using the GenerateClientJoinToken
static method of the FM.LiveSwitch.Token
class. Note that you must wrap the channel id provided by the client in an instance of FM.LiveSwitch.ChannelClaim
.
[Route("[controller]")] public class TokenController : Controller { private String secret; private UserManager<AppUser> userManager; public TokenController(UserManager<AppUser> userManager) { this.userManager = userManager; } [HttpPost("join")] public ContentResult Join([FromQuery]string deviceId, [FromQuery]string clientId, [FromQuery]string channelId) { var user = await this.userManager.GetUserAsync(this.User); return Content(FM.LiveSwitch.Token.GenerateClientJoinToken( user.ApplicationId, user.Id, deviceId, clientId, new FM.LiveSwitch.ChannelClaim(channelId), this.secret )); } }
@Controller @RequestMapping("/token") public class TokenController { @Value("${secret}") private String secret; @PostMapping @ResponseBody public String join( @RequestParam(value="deviceId") String deviceId, @RequestParam(value="clientId") String clientId, @RequestParam(value="channelId") String channelId) { UserDetails auth = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); applicationId = auth.getApplicationName(); userId = auth.getUsername(); return fm.liveswitch.Token.generateClientJoinToken( applicationId, userId, deviceId, clientId, new fm.liveswitch.ChannelClaim(channelId), this.secret ); } }
Upgrading your Server
LiveSwitch is a core product at Frozen Mountain Software and we are constantly making improvements to it. Because of this, it's important to keep your LiveSwitch server upgraded, so that you can take advantage of the latest features. We make this process as easy as possible. To upgrade to the latest version quickly and painlessly, simply download the latest LiveSwitch SDK version and run the installer. Your pre-existing settings will be saved, so you will not have to re-configure your servers.
Which Component Do I Upgrade First?
If your gateway and media server are on the same host, then you don't need to worry about this. The installer will update them both at the same time. If your services are on different hosts, then it is only a bit more complicated. The golden rule is to always upgrade your gateway service first. The gateway service is backwards compatible with older versions of LiveSwitch. By upgrading the gateway first, you ensure that there are no interruptions for any clients that are using older versions of LiveSwitch. If you upgrade your client applications or media servers before your gateway, then you run the risk of the gateway rejecting your connections because it has not been upgraded yet. The gateway service will never accept connections from newer versions of LiveSwitch clients because it cannot guarantee that it supports the same feature set expected by the client.
How many downtimes Can I Expect?
If all of your services are on one host, the downtime will be minimal. The installer will terminate each service, and restart it immediately after the upgrade. If your services are on separate hosts, the upgrade should be painless as well, provided you follow the upgrade order described above. There will be a brief interruption as each service is restarted, and the service will be available again afterwards. The next question addresses scenarios where seamless upgrading is required.
How do I Gracefully Terminate Services In Use?
To ensure minimal downtime for your users and also that your upgrade process does not interrupt their use of your service, you must have at least two gateway servers and at least two media servers. With two media servers, you can upgrade each one individually, ensuring that there are no service interruptions. Through the gateway administration panel, you can tell a media server to "deactivate" itself. This is a slow shutdown - the media server will not accept any new connections during this time. Once you have determined that it is safe to do so (no connections remaining) it is up to your Dev-ops team to shut down the media server. By design, media servers will not shut down automatically. Once the media server has been shut down you can upgrade it without interrupting any users' session. You can do this for each media server in turn, until all are upgraded.
For a seamless upgrade of gateway services, you must ensure that you have at least two gateway services and that they are both are configured to use a Redis backend, rather than the default in-memory backend. The Redis backend ensures that existing clients' connection information is propagated to all gateway servers. The extra gateway service ensures that when a gateway service goes down, your clients will seamlessly reconnect to the next available gateway service.
Are There Any Plans to Improve the Upgrade Process
Yes! We love automating things, and we realize that having to manually "drain" each media server before an upgrade can take unnecessary time out of a busy developer's day. In the future, we plan to make this the default upgrade behavior.
Upgrading Linux
The LiveSwitch Linux distribution does not have an installation script, so upgrading requires slightly more manual intervention. Follow the same basic guidelines for a Windows upgrade. However, instead of running the installer, stop each service using systemctl
, copy the new files to the LiveSwitch service's directory, and then restart the service.
In the future, we will provide both rpm and deb packages for easy installation.
Working with Pipes & Sinks
Pipes and Sinks provide a WebHook mechanism for the Gateway. They allow you to configure URLs (your WebHook callback URL) to handle messages that arrive at your Gateway sent from your clients.
Configure in your Gateway config:
<message> <sinks> <web url="..." types="message" /> </sinks> <pipes> <web url="..." types="message" /> </pipes> </message>
- Sinks are fire and forget, whereas pipes are synchronous.
types
is a comma-separated list of the LiveSwitch message types that will be sent to the hooks. See the MessageType class for possible options.- The '*' type is considered global and runs first for all messages.
- Order of operations: Global Sinks, Global pipes, Specific sinks, specific pipes.
Sinks and pipes send the same data to the configured URL (POST), the sink version just doesn't care about the response. The body is a JSON-ified LiveSwitch message in its entirety.
Pipes do different things depending on the response value. If returned 204
, the message moves on unmodified. If it returned a 403
, the message is considered blocked and an AccessMessageBlocked
error is returned to the client. If the response code is 200
, then the response body is parsed into a new LiveSwitch message and replaces the old one. If it cannot be parsed, the old message is used.
Currently, the Pipes and Sinks feature is possible for handling messages that originate on the client. It is not possible to use Pipes/Sinks to handle messages that originate from the Gateway at this time, but there is a feature request that you can vote on to show your interest. Please vote here.
Regionality
As of version 1.1.0, LiveSwitch now supports regionality. Regionality is a feature that improves clustering performance by allowing each client to select their preferred region. A region is an identifier that represents a specific geographic area. This allows each client to select a geographic area close to them, which ensures fast connection times and high throughput.
What regions are available?
The region identifiers are specific to your application. If your application has many users that are concentrated in a few areas, you will likely have regions with small geographic areas, such as "chicago" or "new-york". If, on the other hand, you have a global user base, you will instead want to use regions that represent larger geographic areas, such as "north-america" or "europe".
Where do I set the list of regions?
There is no canonical set of regions. Each media server can select its own region, and the set of available regions is taken from each media server that is connected to the current cluster.
How does region assignment work?
When a client requests a specific region, the gateway service will attempt to assign it to a media server in the same region. If there are no available media servers in the requested region, or all of the media servers in the requested region are over capacity, the gateway will ignore the region and assign the client to an available media server on a round-robin basis.
What this means is that a client's selected region is only a preference and that clients requesting a specific region can potentially be assigned to any media server in the global pool if no media servers for their region are available.
Setting up Regions
There are two steps to setting up regions. First, you must assign a region to each media server that will use the regionality feature. Second, you must add an extra parameter to your logic that generates the client registration token.
Assigning a region to a media server
To assign a region to a media server, set the region
property of the mediaServer
element in your FM.LiveSwitch.MediaServer.Service.config
file. After updating this property, you must restart the media server service for any change to take effect. As mentioned above, there is no canonical set of available regions. The set of available regions is always determined by whichever media servers are connected to the current cluster.
An example of assigning the region north-america to a media server is shown below.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <mediaServer region="north-america"> ... </mediaServer> </configuration>
Updating the client registration token
Once your pool of media servers have been assigned regions, you must update the client registration logic so that each client requests a specific region. You can review the client documentation for your specific platform to see how to generate tokens and join channels, but to request a specific region you simply specify the optional regionId
parameter when you create the client registration token. This parameter comes after the sharedSecret
parameter, and indicates the region that the client would like to be assigned to. An example of this is below:
You should never generate the token client-side in production. We're doing so for demo purposes ONLY. Refer to the section on Creating an Auth Server for more information.
var client = new FM.LiveSwitch.Client("http://localhost:8080/sync", applicationId, userId, deviceId); string token = FM.LiveSwitch.Token.GenerateClientRegisterToken( "my-application-id", client.UserId, client.DeviceId, client.Id, null, new[] { new FM.LiveSwitch.ChannelClaim(channelId) }, "--replaceThisWithYourOwnSharedSecret--", "north-america" );
TURN in the Media Server
Embedded TURN
LiveSwitch now integrates TURN server functionality directly into your Media Server. No longer do you have to maintain separate server infrastructure for your TURN and secure TURN (TURNS) servers. Now, you simply configure <turn>
bindings in your Media Server, and the rest takes care of itself.
As an added bonus, this improves the overall efficiency of relay connections, as the latency between the TURN server and the Media Server disappears, and thanks to Regionality, you can easily ensure that your clients are connecting to Media Servers that are nearby.
Configuring TURN Bindings
Media Servers now take a configuration option to specify a TURN binding. Add the following configuration to your <mediaserver>
node, and specify:
ipAddress
- this IP address is what will be bound to locally. It's here in case you have multiple network interfaces and want to dedicate one to external/TURN traffic. A wildcard "*" will bind to all available local network addresses.port
- traditionally 3478.Either the
publicIPAddress
of the Media Server, or in the case that you do not know this you can provide anexternalStunAddress
to be used to discover the public IP of the Media Server.transport
<turn> <binding ipAddress="{*|(string)}" port="{number}" /* 3478 */ externalStunAddress="{(empty)|(string)}" publicIPAddress="{(empty)|(string)}" transport="{udp|tcp}" /> ... </turn>
You can add as many bindings as necessary. For instance, you could add a binding for UDP on port 3478 to handle both STUN traffic and TURN traffic over UDP (recall that TURN servers are STUN servers by definition), and you could add a second binding for TCP on port 3478 to handle TURN traffic over TCP if necessary.
But what about secure TURN you ask? Read on to learn how to configure TURNS for your Media Server as well.
Configuring TURNS Bindings
Similarly to the <turn>
configuration discussed above, you can now provide a <turns>
configuration for your Media Server so that all of your TURNS needs are provided by the Media Server itself, with no reliance on an outside service.
The <turn>
and <turns>
bindings share some common attributes: ipAddress
, port
, publicIPAddress
, and externalStunAddress
. Traditionally you would use port 5349 for TURNS. In addition to these shared attributes you must provide your certificate hash and the path to your certificate. This is necessary for securing your relay connection.
Notice also that unlike the <turn>
binding you do not need to provide the transport
attribute for your <turns>
binding. This is because TURNS uses TCP by definition.
<turns> <binding ipAddress="{*|(string)}" port="{number}" /* 5349 */ externalStunAddress="{(empty)|(string)}" publicIPAddress="{(empty)|(string)}" publicHostname="(string)" certificateHash="(empty)|(string)" certificatePath="(empty)|(string)" /> ... </turns>
The final piece of the <turns>
binding to understand is the public hostname. This is necessary for TURNS because all traffic to the server must include this hostname, which is then compared to the hostname that is part of your certificate. Only when they match are TURNS connections possible.
This presents an interesting Operations problem: as you spin up Media Servers to accommodate the increasing load, you must also have a mechanism that creates the hostname for the embedded TURNS server this new Media Server will use. Your Ops process will have to create the necessary DNS record and also supply the new hostname as part of the TURNS configuration of the new Media Server. Conversely, as you tear down Media Servers you can clean up the DNS records for the TURNS domain names that are no longer required.
How Does it all Work?
No doubt you are well aware that it is typically clients that supply ICE servers, and provide these to a connection, which in turn gathers ICE candidates, etc. So, how is this all working? Obviously, the Media Server can make use of its own embedded TURN functionality, but how are clients gaining access when they have not yet opened a connection to the Media Server?
The simple answer is the Gateway. When your Media Server registers with the Gateway, it lets the Gateway know about its TURN configuration. Clients in turn get their ICE server credentials from the Gateway before opening a connection provided (a) you have not already supplied ICE servers to the pending connection, and (b) the connection has the DisableAutomaticIceServers
property set to false
.
Embedded TURN and P2P Connections
Ok, you’re thinking, this all makes a lot of sense for SFU/MCU connections, but what about my P2P connections, they do not connect with the Media Server. How are P2P connections getting access to the ICE servers they require?
It’s true that P2P connections do not even touch the Media Server, but they do register with the Gateway, and just like SFU/MCU connections, will query the Gateway for ICE servers before opening a connection. A very nice side effect here is that even P2P connections now benefit from your Regionality configuration. If you have configured Regionality properly, then clients will in turn use the region appropriate Media Server for relay (where the relay is necessary), which can in turn mitigate latency.
High Availability Architecture
Scaling LiveSwitch
LiveSwitch is built to be highly scalable, both horizontally and vertically. This section explains how to configure these features.
Clustering LiveSwitch
LiveSwitch supports media server clustering, which allows multiple media servers to work together to handle higher traffic volumes than they would be able to alone. Clustering is enabled by default, and all media servers that are connected to a gateway or pool of gateways will automatically attempt to cluster. This default configuration may not be optimal for your application. To get the most out of this feature, read through this brief guide to configuring the clustering feature.
Enabling Cluster Thresholds
Thresholds are configured within the <cluster/>
element. For convenience, we have provided the <enabled/>
element which enables/disables all thresholds for the cluster.
<mediaServer> ... <cluster> <enabled>true</enabled> ... </cluster> ... </mediaServer>
Setting Thresholds
Setting the CPU Threshold
The CPU threshold of a media server is the threshold at which a media server is considered "overcapacity". Once a media server is overcapacity, the gateway service will attempt to assign clients to a different media server instance. The CPU threshold value is expressed as a percentage of CPU use. By default, this is 80, which means that when a media server begins to use 80% or more of its CPU, then all gateways will stop forwarding it new connections. When the media server drops below the CPU threshold, gateway instances will again start to forward connections to the media server, in a round-robin fashion.
it will no longer accept new connections until either its CPU usage drops below 80%, or there are no other media server instances available.
To set the CPU threshold for a media server, add a cpuThreshold
element to the cluster
element, and use any value between 0 or 100. The example below sets the CPU threshold of a media server to 60%.
<mediaServer> ... <cluster> <enabled>true</enabled> <cpuThreshold>60</cpuThreshold> <!-- must be an integer --> ... </cluster> ... </mediaServer>
Setting the Memory Threshold
Similar to the CPU threshold, the memory threshold value of a media server determines when a media server is overcapacity. The memory threshold of a media server is expressed in bytes instead of a percentage. By default, there is no memory threshold. You can set one yourself by adding a memoryThreshold
element to the cluster
element.
The example below sets the memory threshold of a media server to 1073741824 bytes or 1 GB.
<mediaServer> ... <cluster> <enabled>true</enabled> <memoryThreshold>1073741824</memoryThreshold> ... </cluster> ... </mediaServer>
Setting the Bandwidth Threshold
The bandwidth threshold of a media server is another parameter that determines when the server is overcapacity. It is expressed in Mbps. By default, there is no bandwidth threshold. You can set one yourself by adding a bandwidthThreshold
element to the cluster
element. It is important to note that the bandwidth threshold is for both upstream and downstream bandwidth. If either of these exceeds the threshold values then the media server will be considered to be over capacity.
The example below sets the bandwidth threshold of a media server to 512 Mbps.
<mediaServer> ... <cluster> <enabled>true</enabled> <bandwidthThreshold>512</bandwidthThreshold> ... </cluster> ... </mediaServer>
Setting the Failed Connection Threshold
The failed connection threshold is an additional option for specifying media server capacity. You can use this to put your media server over capacity if your failed connections go over the configured value. There are two configurable properties here. The failedConnectionsThreshold
specifies the number of failed connections, and the failedConnectionsExpiry
specifies the time period to consider. For the example below 20 failed connections in a 10 second period will put the media server overcapacity and it will become unavailable.
<mediaServer> ... <cluster> <enabled>true</enabled> <failedConnectionsThreshold>20</failedConnectionsThreshold> <!-- The amount of failed connections allowed in a specific time period before server is over capacity --> <failedConnectionsExpiry>10</failedConnectionsExpiry> <!-- The time window (in seconds) for above --> ... </cluster> ... </mediaServer>
Specifying the Over Capacity Delay
The overCapacityDelay
configures how long your media server will remain unavailable before becoming available for new connections. You can imagine that without setting this delay you could easily end up with a server thrashing between being over capacity, recovering some resources, accepting more connections, and then going over capacity again. Configuring the overCapacityDelay
provides a buffer where the server will remain unavailable even as resources are recovered. This means that when the delay expires, and the server starts accepting new connections, the server is less likely to immediately go over capacity again. The longer the delay, the longer you are giving your server to recover.
<mediaServer> ... <cluster> <overCapacityDelay>60</overCapacityDelay> <!-- minimum time in seconds to be unavailable once triggered --> ... </cluster> ... </mediaServer>
Specifying a Default Configuration
Rather than specifying these thresholds on each individual media server, you can use the same configuration values in your gateway's configuration file. These values will then be used as default values by the media server. If a value exists in both the media server and gateway configuration files, then the value in the media server file will take precedence.
The example below shows how to specify each of these values in your gateway's configuration file.
<gateway> ... <cluster> <enabled>true</enabled> <cpuThreshold>60</cpuThreshold> <memoryThreshold>1073741824</memoryThreshold> <bandwidthThreshold>512</bandwidthThreshold> ... </cluster> ... </gateway>
Controlling the Cluster
LiveSwitch media servers in a cluster communicate with each other over TCP sockets. By default, clustering is performed on port 8445 for the cluster connection. If port 8445, is not available, media servers will incrementally try additional port values until either one is successful or until it is clear that clustering is not possible. For instance, if 8445 is available, a media server will try 8446, 8447, etc. in turn until one is successful. Media servers self-determine which IP addresses they bind to these sockets, and generally will use the first one of their interfaces that work.
This is not always ideal. You can configure the binding behavior by specifying the cidr
attribute on the cluster
element. If specified, a media server will only bind to IP addresses in the specified subnet. The following configuration snippet shows how to configure LiveSwitch to bind only to IP addresses in the 10.0.0.0/16 subnet.
<cluster cidr="10.0.0.0/16"> </cluster>
You can also change the default port that a media server will listen to by specifying the port
attribute. Note - if you specify a port attribute, a media server will only ever listen on a single port. This means that if the port is unavailable, the media server will not attempt to cluster on a different port.
The example below shows how you might set the clustering port to 8100.
<cluster port="8100"> </cluster>
Limitations of Clustering
A limitation of the clustering feature is that all media servers must exist on the same subnet. This means that if you want to link media servers from different data centres, you must implement bridging logic to connect them yourself, using a VPN or similar feature.
REST API
The REST API is hosted by the LiveSwitch gateway. You can access the REST API provided you have IP-level access to the Admin endpoint (/admin by default). For example:
curl -X GET https://v1.liveswitch.fm:9443/admin/api/v1.0/applications
FAQ
Do I need a STUN/TURN server with LiveSwitch?
In general, LiveSwitch does not require STUN or TURN for SFU/MCU connections. LiveSwitch does require STUN/TURN for guaranteed peer connections.
There are several use cases for STUN and/or TURN:
- STUN/TURN: When both endpoints in a streaming connection reside behind a NAT/router. In this case, STUN and/or TURN assists with NAT traversal.
- TURNS: When an ISP actively blocks real-time media using packet inspection. In this case, using the TLS-encrypted TURN transport bypasses prying eyes.
- STUN: When you want to know your own public IP address. In this case, the STUN server is used to simply echo back the public IP address of the client that made the request.
Use case 1 is why LiveSwitch requires STUN/TURN for peer connections. It also explains why STUN/TURN is generally not required for SFU/MCU connections. Since a publicly-accessible LiveSwitch media server is one of the endpoints in SFU/MCU connections, NAT traversal is not required. Note that if all endpoints are on the same local subnet, then STUN/TURN is generally not required.
Use case 2 is why we say that "in general" LiveSwitch doesn't require STUN or TURN for SFU/MCU connections. An Internet service provider can throw a wrench into the middle of things by actively blocking media packets. This happens more often on networks where anti-competitive practices are in place to ensure that customers use a pre-selected media provider (e.g. on hotel networks). Adding a TURNS server (and configuring clients to use it in their ICE server array), ensures a safe fallback.
Use case 3 only applies to the media server and is entirely optional. When deploying a LiveSwitch media server to the public Internet, there are two possible options for IP addressing:
- The server can bind directly to a private IP address, which is 1:1 port-mapped to a public IP address.
- The server can bind directly to a public IP address.
The LiveSwitch media server needs more information on clustering streaming routes with clients. If you are using option 1, which is typical of a cloud compute hosting provider, then the media server needs to either be configured manually with its public IP address or given the address of an external STUN server that can be used to auto-discover it. If you are using option 2, then the LiveSwitch media server can read the public IP address directly from the operating system - no special configuration is required.
To summarize:
- Clients require STUN/TURN(S) when creating peer connections (unless all peers belong to the same local subnet).
- Clients may require TURN(S) when creating SFU/MCU connections if their Internet service provider actively blocks real-time media.
- Media servers can optionally use STUN to auto-discover their public IP address if the server is configured to bind to a private IP address and manual configuration of the public IP address is not desired.
We want to run our STUN/TURN server behind a proxy. How do we accomplish this?
You don't! STUN/TURN cannot go behind a proxy. The entire purpose of TURN is to inspect the IP traffic directly. TURN requires access to the unmodified original IP headers to forward traffic between peers. This is not possible where a proxy is involved.
How many server instances will I need to support my user base?
Estimating exactly the number of servers you need depends very heavily on expected concurrency. It doesn't matter how many users you have - it matters how many users you expect to have active at the same time.
As a general best practice, we estimate concurrency using the 100:10:1 rule, which states that for every 1,000 named users, 100 are quite active, and about 10 are concurrent at any given point in time. Using 32,000 as a starting point, that means we need to plan for 3,200 active users and 320 concurrent at any moment in time.
In general, you can expect one Media Server, on the equivalent of an AWS-EC2 c4.large (2xCPU, 4GB RAM) instance, to handle ~100 concurrent connections in SFU mode with a standard 480p stream. So, to handle the load described here you would require at least four Media Servers in a clustered environment. More information on clustering is available here.
A single Gateway can handle a lot of traffic, far more than our example of 320 concurrent connections. We run our demo environment Gateway on the equivalent of an AWS-EC2 c5.xlarge (4xCPU, 8GB RAM) and it can handle thousands of concurrent clients. That said, we recommend running two Gateways behind a standard load balancer for redundancy purposes.
P2P Connections
For MCU and SFU connections relay is generally not required. See the FAQ question Do I Need a STUN/TURN Server with LiveSwitch? for an in-depth discussion on the reasons for this. That said, if you do use P2P connections then you can expect that some portion of these will be over relay. We estimate that about 20-30% of P2P connections will require TURN, so with the example numbers discussed here we need to plan for 96 concurrent TURN users. For this example load the recommended server is also the equivalent of an AWS-EC2 c5.xlarge. TURN is very demanding on bandwidth. A typical standard-definition audio/video stream is about 1mbps, so with 96 relayed connections, you can get away with a single server that has 100Mbps of available upstream/downstream bandwidth. For redundancy, you will need 2 servers. No load balancer is required - round-robin DNS to spread the load across the two is generally considered to be best practice.
I've set up a Gateway. Now how do I tell if it's running properly?
Your Gateway must be available over HTTPS. The simplest, debugging 101, test to see if your Gateway is available is to browse to it in a browser. Let's say you have a Gateway served from mydomain.liveswitch.com, if you browse to https://mydomain.liveswitch.com/sync then you should see a page that says "LiveSwitch", the version number, and nothing else. If you do not, then your Gateway is not being served properly. If you do see the message but still cannot get a connection, then something else is wrong and you should contact support@frozenmountain.com.
You can also start up your Gateway in interactive mode from the command line on the server where the Gateway service is installed. If you fire it up, it'll pop up a console window that shows the Media Server registration(s) that come in along with the client requests from client app(s). This is really useful for debugging. To do so you may need to stop the Gateway Windows service first since it's installed as a Windows service. Then, go to Program Files/Frozen Mountain Software/LiveSwitch/Gateway/FM.LiveSwitch.Gateway.Service.exe.
How do I enable bandwidth adaptation?
In your media server configuration, set the bandwidthAdaptationPolicy attribute to enabled on the connection element:
<connection bandwidthAdaptationPolicy="enabled" ... />
Native bandwidth adaptation is experimental
Note that in the native platforms bandwidth adaptation is still experimental and is not recommended for AudioStreams at this time.
I've set up a Media Server. Now how do I tell if it's running properly?
The Media Server can be started up from the command line in interactive mode on the server where the Media Server service is installed. If you fire it up, it'll pop up a console window that shows it's registering, then registered, (along with other info) then the reporting thread started. To do so you may need to stop the Media Server Windows service first since it's installed as a Windows service. Then, run the following - Program Files/Frozen Mountain Software/LiveSwitch/Media Server/MediaServer.exe