Minimum Viable Live Streaming

2021-08-28

With the ever increasing popularity of livestreaming one has to ask if there is a simple way to set up your own livestream website to make you more independent of large corporations. The good news is that it is relatively straightforward for anyone to set up if they can set up a simple website.

There are a few pieces to constructing a livestream from scratch:

  1. you need to get the video from your computer (eg. your webcam, or your screen) out to somewhere on the internet where people can see it
  2. you need to have a website built to display those video files (optionally with some bandwidth reduction technology)
  3. you need a livechat that you can interact with during the stream to take questions etc.
  4. you need some way that viewers can donate to the stream, including optional questions.

While there are endless ways to perform each of these tasks, we will recommend a barebones approach that can be expanded later (a minimum viable stream). We have this implemented as a demo.

Getting the video onto the internet from your computer

Probably the most universal streaming standard protocol is RTMP (Real time message protocol) you can use it to stream to proprietary platforms or preferrably to your own website. You will need a server capabale of receiving the stream and a way to generate the stream. On the server side the most simple thing is to use NGINX with the RTMP module.

First you'll need a VPS server. Then you will need to get NGINX. The module needed for RTMP is not included by default so we will need to compile our own NIGNX. That's easier than it sounds. First, get the NGINX source code along with the extension and save it somewhere sensible (and decompress them). Then inside the NGINX source directory configure and install NGINX:

./configure --add-module=/path/to/nginx-rtmp-module
make
make install

You can configure the server to convert the incoming RTMP stream into HLS (HTTP Live Streaming) for easy playback on your site:

worker_processes  auto;
events {
    worker_connections  1024;
}

# RTMP configuration
rtmp {
    server {
        listen 1935; # Listen on standard RTMP port

        application show {
            live on;
            record all;
            record_path /var/www/html;
            record_max_size 1K;

            # Turn on HLS
            hls on;
            hls_path /var/www/html/;
            hls_fragment 3;
            hls_playlist_length 60;
            # disable consuming the stream from nginx as rtmp
            # deny play all;
        }
    }
}

You might also want to make a systemd service for NGINX to easier manage reloading / starting the server.

[Unit]
Description=nginx - high performance web server
Documentation=https://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target

Now that we have the server set up we need to stream to the server. For that we will use ffmpeg from your local machine. ffmepg is a swiss army knife for dealing with video files, converting them, streaming them, reencoding them, etc. In our particular case we want to take some kind of input eg the desktop or a webcam (or both) and stream them to the RTMP server we just set up.

To start let's stream our desktop using x11window

ffmpeg -f x11grab -s 1920x1080 -i :0.0 \
-c:v libx264 -crf 21 -preset veryfast -qp 0 -video_size 1920x1080 \
-g 60 -r 60 -keyint_min 30 -force_key_frames "expr:gte(t,n_forced*2)" -sc_threshold 0 \
-c:a aac -b:a 128k -ac 2 \
-hls_time 4 -hls_playlist_type event \
-f flv rtmp://server.com/myshow/stream

This will start streaming to our server. The important part as far as streaming goes it ensuring that you configure the hls_time so that the video segments have a consistent duration (smoother playback). The server then re-encodes the stream to HLS which is saved per the NGINX configuration in the site directory and thus served on our domain.

Build a website to display the livestream

Now that we have the HLS stream playing on our site it would be nice if we could display it so users could watch the stream.

There is ~no way to watch an HLS stream without Javascript in the browser, and you almost have to use Hls.js as a dependency. You can watch an HLS stream with MPV directly if you have the link however, so it might be a good idea to give your viewers a link like https://mysite.com/myshow/stream.m3u8.

However, streaming to every user is incredibly inefficient and the bandwidth can add up quickly.

Take a simple calculation of a 720p stream with 5000 live viewers. Traditionally, if you have to stream to every viewer at a cost of $0.01 / GB. Then a 2 hour livestream will cost $100 to run. That can be a pretty heafty toll depending on how frequently audience members donate etc.

Peertube does better because it allows viewers to share segments of the video with each other peer to peer. However, Peertube is a somewhat bloated application if you just want to have a simple video website. Luckily, it isn't to hard to rip out just the pieces of Peertube we need and use them to run the stream. We have done this in a library called bitstream-js. It is a drop in replacement for Hls.js which will cut the bandwidth from the aforementioned P2P sharing mechanism. From our tests you should be able to cut the bandwidth more than 80%, but the exact number will depend on the connection quality and geographic distribution of the peers.

To show the video you just need a website that displays a simple html page with a few lines of Javascript:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/bitstream-js@latest"></script>
    <style>
      #video {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <video id="video" controls></video>

    <script>
      const video = document.getElementById("video");
      const stream = new Bitstream();
      stream.attachMedia(video);
      stream.loadSource("stream.m3u8");
    </script>
  </body>
</html>

Bitstream is a drop in replacement for HLS with some additional configuration options for P2P sharing.

One thing to note. The default setting uses a public tracker to coordinate which peer has which chunk. If you plan on doing a large amount of streams you might consider spinning up your own tracker so that you don't overburden a public / shared resource.

One additional thing you should do is set up certbot on the website to ensure that the traffic is encrypted (eg. HTTPS). This is fairly simple to do thanks to a program that reads through your NGINX configuration and generates the correct certificates and config to serve the traffic over HTTPS.

Adding a livechat to your website

The easiest way to add a livechat to a website is to not add one at all. Instead, start a room on an XMPP server and let viewers of the livestream know that chat is going on in that room. If you want users to be able to see the chat during the stream you can share part of your desktop using x11grab in the stream itself. Alternatively you can display the chat on the site using a bot like XMPPLive.

XMPPLive is a chatbot that sits in a particular XMPP chat room and then displays the messages on a web interface. It needs a tiny amount of Javascript to run in order to enable the expected scrolling / refreshing behavior of a livechat, but otherwise is vanilla html / css.

You can install XMPPLive following the readme instructions on a Debian server. The install script should take care of most things, but you will need to ensure that on the XMPP side you create the account and enable multi user chat.

Donations with questions (eg superchats)

Getting donations with questions without using proprietary software or Javascript is very hard. If you want to accept credit cards you must use both proprietary software and Javascript. One no Javascript method would be to take cash in the mail, you could even make it anonymous using a scheme like Mullvad does by generating a random code that can be sent along with the money. Looking at crypto currency, you can certainly accept cryptocurrency on the site, but you will need a side channel for the comment / confirming that the commenter was indeed the one who donated.

Doing this confirmation in real time while you are streaming does not seem feasible, and for certain cryptos like Bitcoin the confirmation time will take to long.