Adaptive Bitrate VoD Server using Nginx on Ubuntu

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


If you are looking for a tutorial that guides you to build a VoD Server using Nginx with the Adaptive Bitrate feature, you come to the right place. In this tutorial, we will build Nginx from source and then put in the Kaltura Nginx VoD module to enable the HLS protocol that supports Adaptive Bitrate Capability.

Check Prerequisite

You need an Ubuntu Server. I tried to build this VoD 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 and Kaltura VoD module, we need some dependencies to install. Run this command to install them

apt update && apt install build-essential git libpcre3-dev libssl-dev zlib1g-dev ffmpeg libxml2-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 Kaltura Module

We need an Nginx vod module to build this VoD server, you can visit the Github here: https://github.com/kaltura/nginx-vod-module. Now, the latest version is 1.29, run this command to download the latest version of the module.

wget https://github.com/kaltura/nginx-vod-module/archive/refs/tags/1.29.tar.gz

Extract the downloaded file

tar -zxvf 1.29.tar.gz

You will get a folder named nginx-vod-module-1.29. Make sure this folder is at the same location as 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-vod-module and --with-cc-opt="-O3 -mpopcnt" to nginx config options. According to Nginx VoD Module Documentation, it is said that adding --with-cc-opt="-O3 -mpopcnt" will have 8% reduction in the mp4 parse time and frame processing time.

./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 \
    --with-cc-opt="-O3 -mpopcnt" \
    --add-module=../nginx-vod-module-1.29

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

Make sure there is --add-module=../nginx-vod-module-1.29 on the output

VoD 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 config file

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

events {
    worker_connections 4096;
}

http {
    server {
        listen 80;

        # vod mode
        vod_mode mapped;

        # vod caches
        vod_metadata_cache metadata_cache 512m;
        vod_response_cache response_cache 128m;
        vod_mapping_cache mapping_cache 5m;

        # gzip manifests
        gzip on;
        gzip_types application/vnd.apple.mpegurl;

        # file handle caching
        open_file_cache          max=1000 inactive=5m;
        open_file_cache_valid    2m;
        open_file_cache_min_uses 1;
        open_file_cache_errors   on;

        location ^~ /video/ {
            alias /etc/nginx/json/;
            vod hls;

            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
            add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
            add_header Access-Control-Allow-Origin '*';
            expires 100d;
        }
    }
}

With this configuration, we can make a request like this:

http://<server-ip-address>/video/<video-configuration-file>/master.m3u8

This request will be handled by location ^~ /video/ block. Inside this location block there is vod hls directive, this directive make the Nginx do a sub-request to /json/video/<json-file> to read the video-configuration-file. The sub-request will be handled by location ^~ /json/video/ block. This is where the nginx will read the video-configuration-file on the /etc/nginx/json folder.

After that, we must put the video-configuration-file to the /etc/nginx/json folder. In this tutorial, I will use transformers video to demonstrate this video server.

First, create the folder

mkdir /etc/nginx/json

Create video-configuration-file with transformers.json name inside the folder

nano /etc/nginx/json/transformers.json

Add this json code to the file

{
    "sequences": [
        {
            "clips": [
                {
                    "type": "source",
                    "path": "/etc/nginx/vod/transformers_720p.mp4"
                }
            ]
        },
        {
            "clips": [
                {
                    "type": "source",
                    "path": "/etc/nginx/vod/transformers_360p.mp4"
                }
            ]
        }
    ]
}

In this video configuration, we tell the Nginx that this transformers video will have 2 stream. One is 720p video and one is 360p video. Each of them is located in /etc/nginx/vod folder. Note that these are 2 identical videos with different resolutions.

With these 2 stream, the vod module will automatically generate a master playlist that will adapt to the user bandwidth when the video is requested. If the bandwidth is good, it will serve the 720p version, and if the bandwidth is bad, it will serve the 360p version.

If you want to add more streams, feel free to add a new object under "sequences" array.

After that, don’t forget to create /etc/nginx/vod folder

mkdir /etc/nginx/vod

And then put the video inside it with the name described on the transformers.json file. In my case, the video is transformers_720p.mp4 and transformers_360p.mp4.

root@facsiaginsa:/etc/nginx# ll /etc/nginx/vod/
total 154624
drwxr-xr-x 2 root root      4096 Mar  8 14:00 ./
drwxr-xr-x 5 root root      4096 Mar  8 13:59 ../
-rw-r--r-- 1 root root  29849139 Mar  8 14:00 transformers_360p.mp4
-rw-r--r-- 1 root root 128472680 Mar  8 14:00 transformers_720p.mp4

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

nginx -t

To summarize, this is the structure of our configuration file so far

/etc/nginx/
       |- nginx.conf
       |- json/
       |    |- transformers.json
       |- vod/
            |- transformers_720p.mp4
            |- transformers_360p.mp4

Create Systemd File

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

nano /lib/systemd/system/nginx.service

And then copy & paste this config to the file

[Unit]
Description=Nginx VoD Server
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 service using systemd, just like this:

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

Start your Nginx

service nginx start

If it is failed, please make sure that no other process is running on port 80.

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 the 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
}

Test the Vod Server

You can test the VoD server using VLC on your laptop by pointing the network URL to http://<your-server-ip>/video/<video-configuration-file>/master.m3u8 just like image below.

View VoD streaming via VLC

Congratulation! Now you can build a VoD server using Nginx with Adaptive Bitrate feature.

Lastly, if you want to know how to make you VoD Server stateless by adding Minio integration, I advice you to check my other tutorial: Nginx VoD Server Configuration: Minio Integration.