Build an Adaptive Bitrate Streaming Server Using Nginx on Ubuntu

Written by: Bagus Facsi Aginsa
Published at: 05 Mar 2022


We can build a Streaming Server that supports adaptive bitrate feature using Nginx. For this, we need to use nginx-rtmp-module and also build the Nginx from source. This tutorial will help you to build a streaming server from scratch.

Check Prerequisite

  1. Ubuntu Server. I tried to build this streaming server in Ubuntu 20.04, but I believe that you can also build this in Ubuntu 18.04 with no problem.

Get Sudo Privileges

Before starting, make sure that we have no permission issue on the installation & configuration.

sudo su

Install Nginx Dependencies

To build Nginx from source, we need some dependencies. Run this command to install them

apt update && apt install build-essential git libpcre3-dev libssl-dev zlib1g-dev

Download Nginx Source Code

Before you download the Nginx source code, you can visit http://nginx.org/en/download.html to see the Nginx version available now. After that you can download them by running this command:

wget http://nginx.org/download/nginx-<version>.tar.gz

Now, the latest stable version is 1.20.2, so for me, I will download the nginx-1.20.2 version

wget http://nginx.org/download/nginx-1.20.2.tar.gz

Extract the downloaded file

tar -zxvf nginx-1.20.2.tar.gz

You will get a folder named nginx-1.20.2

Download RTMP Module

We need 3rd party RTMP module to build this streaming server, you can visit the Github here: https://github.com/arut/nginx-rtmp-module. Now, the latest version is 1.22.2, run this command to clone the latest version of the module.

wget https://codeload.github.com/arut/nginx-rtmp-module/tar.gz/refs/tags/v1.2.2

Extract the downloaded file

tar -zxvf v1.2.2

You will get a folder named nginx-rtmp-module-1.2.2. Make sure this folder is at the same location with the nginx-1.20.2 folder.

Build & Install Nginx

Go to the nginx-1.20.2 folder

cd nginx-1.20.2

After that, we configure the nginx and put in the nginx-rtmp-module to nginx

./configure \
    --prefix=/etc/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/run/nginx.pid \
    --sbin-path=/usr/sbin/nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_stub_status_module \
    --with-http_realip_module \
    --with-file-aio \
    --with-threads \
    --with-stream \
    --add-module=../nginx-rtmp-module-1.2.2

After that, run this command to build & install the nginx

make && make install

To verify the installation, you can check the Nginx version

nginx -V

Streaming Server Configuration

First, move our work directory to the Nginx configuration folder

cd /etc/nginx

Backup the default Nginx configuration file

mv nginx.conf nginx.conf.old

Create a new configuration file

nano nginx.conf

Add this configuration to the configuration file

user www-data;
worker_processes auto;
worker_rlimit_nofile 8192;
pid /run/nginx.pid;
rtmp_auto_push on;

events {
    worker_connections 4096;
}

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        max_message 1M;

        application streaming {
            live on;
            hls on;
            hls_nested on;
            hls_path /etc/nginx/live;

            hls_fragment 6s;
            hls_playlist_length 30s;
        }
    }
}

http {
    server {
        listen 8080;
        root /etc/nginx;

        location /live {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            add_header Cache-Control no-cache;
        }
    }
}

This is a simple streaming server configuration that receives rtmp video feed on port 1935 (default rtmp port) with /streaming path, transcode them to HLS inside /etc/nginx/live folder, and then the http block read the m3u8 and ts file inside /etc/nginx/live folder and serve them on port 8080.

Some values you can modify:

  1. max_message 1M, this is the size of 1 chunk of rtmp feed video. You need to increase this value especially if you have 4k or more rtmp feed video.
  2. hls_fragment 6s, this is tell the Nginx to divide the rtmp video to 6s hls fragment video. This mean each of the .ts video will have 6s duration. If you want a smaller fragment, you can change it to 3s.
  3. hls_playlist_length 30s, this will affect the index.m3u8 created by the Nginx. This is the length of 1 playlist defined in the index.m3u8. Now, because the hls_playlist_length is 30s and the hls_fragment is 6s, then the index.m3u8 will contain 5 .ts video. If you want you video to be more realtime, change this value to 18s paired with smaller hls_fragment 3s.

After the configuration is done, make sure that there is no error in the configuration by running this command

nginx -t

Create Systemd File

To make Nginx easier to manage, we can build a systemd file. First, create a new file in systemd folder:

nano /lib/systemd/system/nginx.service

And then copy & paste this config to the file

[Unit]
Description=Nginx Custom From Source
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Reload the systemd

systemctl daemon-reload

Enable Nginx service so it will auto start when the server boot

systemctl enable nginx

Now you can control Nginx using systemd, just like this:

service nginx start
service nginx reload
service nginx stop
service nginx restart

Start your Nginx

service nginx start

Create Logrotate File

Logrotate is useful for rotating the Nginx log so it will not write on a single file continuously. First, create a new file on logrotate folder

nano /etc/logrotate.d/nginx

Copy & Paste this code

/var/log/nginx/*.log {
    daily
    missingok
    rotate 7
    compress
    delaycompress
    notifempty
    create 0640 root root
    sharedscripts
    prerotate
            if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                    run-parts /etc/logrotate.d/httpd-prerotate; \
            fi \
    endscript
    postrotate
            invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

Check the Streaming Server

You can try to feed the streaming server using FFmpeg or OBS. In my case, I’m using OBS. Direct the rtmp output to rtmp://<your-server-ip>/streaming, for the stream key i will use avengers in this tutorial just like this image below. And then start the stream.

Feeding Streaming Server with OBS

And then, check the hls video using VLC by pointing the network URL to http://<your-server-ip>:8080/live/<stream key>/index.m3u8 just like the image below.

View HLS streaming via VLC

If the video is running well, then your basic streaming server is installed successfully!

Adaptive Bitrate Configuration

Now the advance part. If you want just a simple streaming server, then this step is not mandatory. This is where we add the adaptive bitrate feature to our streaming server. First, we must install FFmpeg.

apt install ffmpeg

Go back to nginx directory

cd /etc/nginx

After that, we must modify the rtmp block on the nginx.conf file. First, open the configuration file

nano nginx.conf

Change the rtmp block to this code below

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        max_message 1M;
    
        application streaming {
            live on;

            exec ffmpeg -i rtmp://localhost/streaming/$name
              -c:a aac -b:a 32k  -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low
              -c:a aac -b:a 64k  -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid
              -c:a aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi;
        }

        application hls {
            live on;
            hls on;
            hls_path /etc/nginx/live;
            hls_nested on;

            hls_fragment 6s;
            hls_playlist_length 30s;

            hls_variant _low BANDWIDTH=160000;
            hls_variant _mid BANDWIDTH=320000;
            hls_variant _hi  BANDWIDTH=640000;
        }
    }
}

This configuration will trigger FFmpeg to transcode all rtmp feed that comes in /streaming path to 3 rtmp stream with different bitrate and feed them to /hls path. And then the nginx will save each variant inside /etc/nginx/live folder as well as create master playlist file named <stream key>.m3u8.

Now your streaming server will adapt to the user’s available bandwidth based on the BANDWIDTH directive. You can add or reduce the variant and set the BANDWIDTH threshold based on your need.

After the configuration is done, make sure that there is no error in the configuration by running this command

nginx -t

After that, reload the nginx

service nginx reload

Check the Adaptive Bitrate Streaming Server

You can once again check your streaming server by feeding the video via OBS just like before and viewing them from VLC. But this time, change the network url on the VLC to http://<your-server-ip>:8080/live/<stream key>.m3u8 just like image below.

View Adaptive HLS streaming via VLC

Congratulation! Now you can build An Adaptive Bitrate Streaming Server Using Nginx.