Introduction

I’ve been working with live video streaming in some capacity for several years. Everything from simple Periscope or Meerkat clones, to very large-scale live sports productions (Super Bowl, FIFA World Cup). There are many open source tools available to build services like this yourself if you know what they are and how to use them. This guide attempts to introduce you to the tech behind live streaming by walking through the construction of a Twitch.tv-like website all the way from bare metal (ok, VPS) to backend web application and HTML/CSS.

This is a multipart guide that follows (roughly) this schedule:

  • Part 1 - Start Streaming!
  • Part 2 - The Application
  • Part 3 - DRM
  • Part 4 - We’re big now! Adding a CDN
  • Part 5 - Bringing it all together

WARNING

Live video streaming is one of the most difficult (and expensive!) things you can do at scale on the web today. Due to the extreme bandwidth costs involved, almost all streaming sites fail without a very solid, sustainable product + business model behind it. User-gen streaming sites are also havens for “pirate streamers” – people that re-broadcast Pay-Per-View, live sports, and other copyrighted content. If you choose to use this guide to launch such a site then you should be prepared for these users and the responsibility of responding to DMCA takedown requests.

Let’s get started!

Step 1 - The Server

You need a server

It doesn’t matter what VPS host you choose, or even if you choose to run it locally via Vagrant or other VM. I chose DigitalOcean but you can use whatever host you’re comfortable with.

You need a Linux OS

This guide is very opinionated about almost everything and especially assumes it’s being deployed on a fresh CentOS 7 install. But if you’re savvy enough to apply this guide to your own favorite distro then I’m sure it will work just fine.

However, from here on this guide assumes a newly launched CentOS 7 server hosted on DigitalOcean.

You need a domain name

OK this one isn’t strictly required but makes everything a lot easier to work with. I’m using boltstream.me for this guide, but you can use any subdomain of a domain you already own, or even just an IP address if you want.

Prepare the server

Bring the system up-to-date:

$ yum update -y

You need to allow some services through the firewall. On CentOS 7 you need to install the iptables-services package:

$ yum install -y iptables-services
$ systemctl enable iptables

Specifically, the ports you need are:

  • tcp/22 (SSH)
  • tcp/80 (HTTP)
  • tcp/443 (HTTPS)
  • tcp/1935 (RTMP)

Put this file at /etc/sysconfig/iptables

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT -m comment --comment "ssh"
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT -m comment --comment "http"
-A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT -m comment --comment "https"
-A INPUT -m state --state NEW -m tcp -p tcp --dport 1935 -j ACCEPT -m comment --comment "rtmp"
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

# DO NOT REMOVE THIS LINE

And then run:

$ systemctl restart iptables

Step 2 - NGINX

I don’t like installing packages from source but sometimes it’s necessary. In this case we need to compile NGINX from source in order to add support for a few 3rd-party modules that we need. However, we’re not going to do just straight source installs, but rather we’re going to rebuild the NGINX source RPMs with the required modules.

Building NGINX with RTMP support

$ yum install -y epel-release yum-utils rpm-build wget gcc

Install the NGINX build dependencies

$ yum-builddep -y nginx

We’re going to rebuild the NGINX source RPMs so we need to add the mockbuild user:

$ useradd mockbuild
$ su mockbuild
$ cd

Download the NGINX source RPM

$ yumdownloader --source nginx

Install the NGINX source RPM

$ rpm -U nginx-1.12.2-1.el7.src.rpm

This installed the NGINX source RPM in /home/mockbuild/rpmbuild:

$ find rpmbuild/
rpmbuild/
rpmbuild/SOURCES
rpmbuild/SOURCES/404.html
rpmbuild/SOURCES/50x.html
rpmbuild/SOURCES/README.dynamic
rpmbuild/SOURCES/UPGRADE-NOTES-1.6-to-1.10
rpmbuild/SOURCES/index.html
rpmbuild/SOURCES/nginx-1.12.2.tar.gz
rpmbuild/SOURCES/nginx-1.12.2.tar.gz.asc
rpmbuild/SOURCES/nginx-auto-cc-gcc.patch
rpmbuild/SOURCES/nginx-logo.png
rpmbuild/SOURCES/nginx-upgrade
rpmbuild/SOURCES/nginx-upgrade.8
rpmbuild/SOURCES/nginx.conf
rpmbuild/SOURCES/nginx.logrotate
rpmbuild/SOURCES/nginx.service
rpmbuild/SOURCES/poweredby.png
rpmbuild/SPECS
rpmbuild/SPECS/nginx.spec

The file that we’re interested in is nginx.spec. In order to add RTMP support to our NGINX install, we need to copy that file to rpmbuild/SPECS/nginx-rtmp.spec and modify it.

$ cp rpmbuild/SPECS/nginx.spec rpmbuild/SPECS/nginx-rtmp.spec

But first we need to get nginx-rtmp-module.

$ wget -qO- https://github.com/sergey-dryabzhinsky/nginx-rtmp-module/archive/dev.tar.gz | tar zx

This is a fork of nginx-rtmp-module from https://github.com/arut/nginx-rtmp-module that adds a few useful features that we want.

Find and add this line in nginx-rtmp.spec:

    ...
    --with-pcre \
    --with-pcre-jit \
    --with-stream=dynamic \
    --with-stream_ssl_module \
    # add this line: 
    --add-module=/home/mockbuild/nginx-rtmp-module-dev \
%if 0%{?with_gperftools}
    --with-google_perftools_module \
%endif
    ...

Now build NGINX with RTMP support:

$ rpmbuild -bb rpmbuild/SPECS/nginx-rtmp.spec

Exit out of the mockbuild shell by pressing Ctrl-D or type exit

Install our new NGINX RPMs

$ find /home/mockbuild/rpmbuild/RPMS -name 'nginx-*.rpm' | xargs rpm -U --force 

Configuring NGINX for RTMP ingest

First we need to disable SELinux so that NGINX can listen on port 1935 (don’t worry, we’re going to re-enable it later):

$ setenforce 0

Create the necessary web directories

$ mkdir -pZ /var/www/live
$ chown -R nginx:nginx /var/www

Copy the following to /etc/nginx/nginx.conf

user nginx;

# I'll explain why we only have 1 worker process later
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    use epoll;
    worker_connections 1024;
}

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" $request_time';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server {
        listen 80 default_server;
        server_name _;
        root /var/www;
        expires -1d;

        location ~ ^/live/.+\.ts$ {
            # MPEG-TS segments can be cached upstream indefinitely
            expires max;
        }
    }
}

rtmp {
    server {
        listen 1935;

        application app {
            live on;

            # Don't allow RTMP playback
            deny play all;

            # Package streams as HLS
            hls on;
            hls_path /var/www/live;
            hls_nested on;
            hls_fragment_naming system;
            hls_datetime system;
        }
    }
}

Start and enable NGINX

$ systemctl enable nginx
$ systemctl start nginx

Now you can use OBS Studio, XSplit, or even just FFMPEG to stream to your site.

$ ffmpeg -re -i <file.mp4> -c copy -f flv rtmp://boltstream.me/app/mystream

And play it back with VLC or another player:

$ ffplay http://boltstream.me/live/mystream/index.m3u8

In the next part we’re going to create a simple Django-based web application so that users can sign up and live-stream on their own page.

  • Part 1 - Start Streaming!
  • Part 2 - The Application
  • Part 3 - DRM
  • Part 4 - We’re big now! Adding a CDN
  • Part 5 - Bringing it all together