WebRTC screening with authorization and goodies

The popularity of online education is increasing every month. And where there is an increase in popularity, there is an increase in competition and an improvement in quality: many authors of online schools realized that it is no longer enough to shoot some kind of mess of pixels on the front-facing of the fourth iPhone – you need to work on the quality of the picture, sound, presentation of material, etc. Not only has the pandemic proved that online salsa and crochet can be taught online, it also gave a very big push to improve the teaching materials + diversity in the labor market. Mothers on maternity leave can rejoice – now you can really earn one hundred thousand million a second while sitting at home.

In the last article, we talked about the new professions generated by quarantine and the tools for their work, and with the help of just 70 lines of code, we threw the skeleton of a multipoint conference with recording and screenshots.

Screensharing is generally a very popular thing, especially in modern realities, when various self-development courses are at the height of fashion. Do you want to lose weight, get fat, make millions, change your profession, raise your children correctly, achieve enlightenment, blackout, or get out of Feng Shui? The network has the mentor you need, and not even one. The main tool of such trainers is to conduct webinars, most often paid. And if you undertake to develop your own system of webinars, then several questions immediately arise:

  1. Where to place?

  2. How to implement?

  3. How do I restrict access?

We decided to collect all the answers together, and this is how this article turned out.

How to choose a server and where to host it?

To start streaming in operation, you need to assess the scale of the system.

  • number of streamer users;

  • number of incoming streams;

  • number of users subscribers;

  • number of outgoing streams;

  • parameters of video streams;

  • time periods;

  • geography of servers and subscribers.

And based on this data, choose the appropriate configuration for a physical server or virtual instance.

The approximate requirements for the characteristics of the server for typical tasks are indicated below:

Number of subscribers

CPUs

RAM, GB

Traffic, TB

Usage example

up to 200

four

eight

five

Video surveillance system

up to 500

eight

sixteen

6

Webinars

up to 1000

sixteen

64

nine

Video conference

up to 2000

twenty

96

10

HD video streaming

If your project plans to have a larger number of subscribers, it makes no sense to increase the technical characteristics of one server, since this will lead to a single point of failure. In this case, you can use technologies CDN at the rate of 1 Edge server for 2000 subscribers. Due to scaling, geographical and logical separation (with the allocation of transcoding and content delivery functions), you can flexibly determine the required level of performance for each of the servers. The load on the system resources of the server should not exceed 80%. In this case, all subscribers will receive video streams with acceptable quality.

Quite often, CDNs are deployed with a small number of users, if a significant amount of traffic is planned. The arithmetic is simple: 1 stream is about 1 Mbps of traffic, respectively 1000 streams – 1000 Mbps. There is not always a technical and financial opportunity to connect a 10Gbps channel to a server. Most Internet providers cannot provide a channel of more than 400 Mbps, therefore, to broadcast streams at a speed of 1000 Mbps, you will need three servers.

Let’s consider some WCS settings, which directly affect the final number of viewers and broadcast quality.

Configuring ports

By default, only 499 ports are available for WebRTC connections. This limitation is set in the file flashphoner.properties… If necessary, you can increase the range of ports in lines

media_port_from = 31001
media_port_to = 32000

The attentive reader, of course, would argue that there are 999 ports in the specified range. Yes, it is, but only even ports are used to transfer media traffic. Therefore, by default, only 499 WebRTC connections are available.

When expanding the range of media ports, check that the range does not overlap with other portsused in the server and range of dynamic Linux ports (you can change it if necessary)

Also note that media ports are only used for WebRTC connections. Therefore, if you send a stream via WebRTC, and viewers watch it via HLS on their iPhones, the problem of free ports becomes irrelevant.

Configuring the garbage collector

Streaming creates and destroys many data objects in memory. These objects are stored in the Java heap, except for the case of transcoding, in which pictures are stored in the server’s physical memory.

Heap is the main memory segment where the Java VM stores all your objects. When you want to create a new object, but there is no more space in the heap, the JVM performs garbage collection, which means that the JVM looks in memory for all objects that are no longer needed and gets rid of them. When garbage collection is running, the rest of the processes in the JVM are suspended. Therefore, for streaming tasks, it is imperative that this pause takes as little time as possible. To do this, Java has developed a new garbage collector – “Z Garbage Collector” (ZGC), which allows you to provide low latency during garbage collection without stopping the execution of application threads for more than 10 milliseconds, even when working with a very large memory heap.

It is recommended to allocate at least 1/2 of the server’s physical memory for Java memory heap. For example, if the server has 32 GB of RAM, it is recommended to allocate 16 GB. Java memory heap size is specified in the wcs-core.properties file in lines

### JVM OPTIONS ###
-Xmx1024M

The default is 1024 MB. To allocate 16 GB for Java memory heap, specify

-Xmx16g
-Xms16g

Install Z Garbage Collector as part of OpenJDK 12.

1.Download the latest build of OpenJDK 12 from the page http://jdk.java.net/12/:

wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz

2.Unpack the resulting file and move its contents to the working directory:

tar xvf openjdk-12.0.2_linux-x64_bin.tar.gz
mv jdk-12.0.2 /usr/java/jdk-12.0.2

3. Create symbolic links for OpenJDK 12:

ln -sfn /usr/java/jdk-12.0.2 /usr/java/default
ln -sfn /usr/java/default/bin/java /usr/bin/java
ln -sfn /usr/java/default/bin/jstack /usr/bin/jstack
ln -sfn /usr/java/default/bin/jcmd /usr/bin/jcmd
ln -sfn /usr/java/default/bin/jmap /usr/bin/jmap

4.If WCS was installed earlier, comment or delete the following lines in the wcs-core.properties file:

-XX:+UseConcMarkSweepGC
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails

and replace the line

-Xloggc:/usr/local/FlashphonerWebCallServer/logs/gc-core-

on the

5.Adding settings for ZGC and logging:

# ZGC
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms24g -Xmx24g -XX:+UseLargePages -XX:ZPath=/hugepages
 
# Log
-Xlog:gc*:/usr/local/FlashphonerWebCallServer/logs/gc-core.log
-XX:ErrorFile=/usr/local/FlashphonerWebCallServer/logs/error%p.log

Usually, these settings are sufficient to improve server performance and connect a large number of viewers.

So, we have decided on the choice of a server or virtual instance for WCS. Now let’s go directly to writing the code of the screening function for further adding to the site.

We program

Preparation for development

Solution architecture for this article:

  1. Front-end Web server – organizes an interface with a streamer and viewers.

  2. WCS – processes the incoming video stream from the streamer and gives it to viewers for playback.

Let’s start our deployment by installing WCS. Follow the directions instructions or launch a virtual instance on Amazon, Google cloud or DigitalOcean… WCS can also be run as a container in Docker… We configure the server according to the previous chapter and recommendations industrial preparation instructions

Now we start the web server. In this article, we use Nginx on CentOS 7… On Apache, everything will be configured in a similar way. If you already have a site to which you plan to add screening functionality, then you do not need to prepare the web server separately. Or you can deploy a web server on the same machine as WCS.

We create two files on the web server: a page for the future screening interface and a script that will implement the work. We have these files – “screen-sharing-min.html” and “screen-sharing-min.js”. Next to these files, you need to place the script of the main API – “https://habr.com/ru/company/flashphoner/blog/564730/flashphoner.js”, which can be downloaded here… Or specify the path to this file if the work is carried out on the same machine where WCS is installed.

The code

Let’s start by preparing an HTML page with all the necessary elements. We connect scripts:

<script type="text/javascript" src="https://habr.com/ru/company/flashphoner/blog/564730/flashphoner.js"></script> 
<script type="text/javascript" src="screen-sharing-min.js"></script> 

We hang the API launch function on loading the page body:

<body onload="init_api()">

And add a div element for the preview of the screenshot flow and a button for launching:

<div id="screen-sharing" style="width:320px;height:240px;border: solid 1px"></div>
<input type="button" onclick="connect()" value="Share Screen"/>

There is very little HTML code for the page:

<!DOCTYPE html>
<html lang="en">
<head>
   <script type="text/javascript" src="https://habr.com/ru/company/flashphoner/blog/564730/flashphoner.js"></script>
   <script type="text/javascript" src="screen-sharing-min.js"></script>
</head>
<body onload="init_api()">
    <div id="screen-sharing" style="width:320px;height:240px;border: solid 1px"></div>
    <input type="button" onclick="connect()" value="Share Screen"/>
</body>
</html>

Now let’s move on to creating a JS script for screening work.

The “init_api ()” function which is called when the HTML page is loaded initializes the main API

function init_api() {
    Flashphoner.init({});
}

When you click on the “Share Screen” button, two functions of our script will be executed sequentially – the “connect ()” function, which will establish a connection to WCS via WebSocket

function connect() {
    session = Flashphoner.createSession({
        urlServer: "wss://demo.flashphoner.com"
    }).on(SESSION_STATUS.ESTABLISHED, function(session) {
        startStreaming(session);
    });
}

and the “startStreaming ()” function, which forms and publishes the screenshot stream to WCS

function startStreaming(session) {
    var constraints = {
        video: {}
    };
    constraints.video.type = "screen";
    constraints.video.withoutExtension = true;
    session.createStream({
        name: "mystream",
        display: document.getElementById("screensharing"),
        constraints: constraints
    }).publish();
}

An important difference from regular webcam streaming is that in order to capture the screen, not the camera, two parameters must be explicitly specified in constraints:

constraints.video.type = "screen";
constraints.video.withoutExtension = true;

The script file is also quite compact. Only 34 lines with comments.

//Status constants
var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS;
var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS;
 
//Websocket session 
var session;
 
//Init Flashphoner API on page load
function init_api() {
    Flashphoner.init({});
}
 
//Connect to WCS server over websockets
function connect() {
    session = Flashphoner.createSession({
        urlServer: "wss://demo.flashphoner.com"
    }).on(SESSION_STATUS.ESTABLISHED, function(session) {
        startStreaming(session);
    });
}

//Publishing Share Screen
function startStreaming(session) {
    var constraints = {
        video: {}
    };
    constraints.video.type = "screen";
    constraints.video.withoutExtension = true;
    session.createStream({
        name: "mystream",
        display: document.getElementById("screensharing"),
        constraints: constraints
    }).publish();
}

We save, run and check the work.

Testing

Click on the “Share Screen” button and tell the browser what exactly we are going to fumble about: the whole screen, the application, or a specific browser tab. The preview of the screenshot flow will be displayed in a div element on the HTML page.

In the script code, we have hard-coded the name of the published stream – “mystream”

session.createStream({
        name: "mystream",

This flow can then be recorded by, reproduced, transcoded or relayed by any of the technologies supported by WCS

So everything works fine. Now I propose to consider how you can implement a simple measure to protect your content – access to the stream using Basic Auth.

Protecting

We configure the web server to use Basic Auth.

Create a .htpasswd file:

sudo yum install apache2-utils
sudo htpasswd -c /etc/nginx/conf.d/.htpasswd admin

The first command will install a utility on the system to generate the .htpasswd file. The second command will create a file at the specified path and ask for a password for the “admin” user. Key “-c“is only needed when generating the file for the first time. It is not needed to write other accounts to the .htpasswd file.

Next, let’s move on to the Nginx configs. We did not separate the site settings into a separate config, so we make the settings in the main configuration file /etc/nginx/nginx.conf

In the http section, add the following lines:

auth_basic "Enter password";
auth_basic_user_file /etc/nginx/.htpasswd;

The first line is the greeting line, the second is the path to the .htpasswd file

At this stage, you can restart Nginx and see if the password request worked when opening the web interface.

Next, we configure the web server to work over the HTTPS protocol. Add the following lines to the “Server” section in the nginx.conf file

listen 443 ssl;
    ssl_certificate /etc/pki/tls/yourdomain/yourdomain.crt;
    ssl_certificate_key /etc/pki/tls/yourdomain/yourdomain.key;
    server_name wcs.yourdomain.com;
    server_tokens off;
    client_max_body_size 500m;
    proxy_read_timeout 10m;

Configuring reverse proxying on the WCS WebSocket port

location /wss {
    proxy_set_header Host $host;
    proxy_pass http://IP:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
}

Restart Nginx again. Now we will slightly modify the code that we wrote and tested in the previous chapter to transfer the login and password. In the “urlServer” parameter of the “Connect” function, specify a request to the server in the form “wss: // login: password@wcs.yourdomain.com: 443 / wss”

function connect() {
    session = Flashphoner.createSession({
        urlServer: "wss://login:password@wcs.yourdomain.com:443/wss"
    }).on(SESSION_STATUS.ESTABLISHED, function(session) {
        startStreaming(session);
    });
}

Now, when accessing the “screen-sharing-min.html” HTML page, the browser will ask for a username and password. And further access via the WebSocket protocol to the WCS server will also use the login and password that we set in the code.

Thus, we deployed and tested an example of a screenshot and attached the minimum authorization using the Basic Auth technology to it. Why, in 2021, you do not need to create and register your extension for Google Chrome and bother end users with installing some additional components – everything works out of the box.

Happy screensharing!

Links

Our demo server

WCS on Amazon EC2 – Fast deployment of Amazon-based WCS

WCS on DigitalOcean – Rapid deployment of WCS powered by DigitalOcean

WCS in Docker – Running WCS as a Docker container

Screen broadcast via WebRTC – Function of demonstration and broadcasting of the screen from browsers

Description of the flashphoner.properties settings file

Description of the wcs-core.properties settings file

Java Memory Management Documentation

Recommendations for fine-tuning the server

WCS Preparation Documentation

Screen Sharing in the browser via WebRTC

Screen Sharing Web SDK Documentation

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *