Skip to Content

Live Video Streaming with Nginx RTMP & HLS on Ubuntu

In this tutorial, you'll learn how to set up a live video streaming server with Nginx, RTMP, and HLS on a Ubuntu 20.04 server. We'll go over the technologies used as well as a step-by-step guide for installing all of the components, configurations, and HTML and JavaScript required to display your live video streams.

Technologies Used

In our setup, we'll be utilizing three technologies for high-performance media streaming: Nginx, RTMP, and HLS.

  • Nginx is an open-source, high-performance web server created and publicly released by Igor Sysoev in 2004. It can also be used as a reverse proxy server, mail IMAP/POP3 proxy server, load balancer, and HTTP cache. In our case, we'll be using it for media streaming only.
  • RTMP, short for Real-Time Messaging Protocol, is a media communication protocol for live streaming video, audio, and data across the Internet. In our case, we'll be using it for live video streaming.
  • HLS, short for HTTP Live Streaming, is an HTTP-based bitrate streaming communications protocol that we'll use for high-quality video streaming. HLS was developed and released by Apple Inc. in 2009.

Let's get started with the installation!

Installing Nginx

First, we'll need to ensure we have the latest Ubuntu libraries installed and install Nginx on our Ubuntu server:

$ sudo apt update
$ sudo apt install nginx

Next, we'll start the Nginx server and enable it as a service so it always executes when the server boots up:

$ sudo systemctl start nginx
$ sudo systemctl enable nginx

Installing the Nginx RTMP Module

Now, we'll install the RTMP module needed to handle the live video streaming functionality:

$ sudo apt install libnginx-mod-rtmp

Once installed, we'll update the Nginx configuration file to include the necessary details needed for a successful live stream. This is quite lengthy, so I'll break it down into chunks, then provide the full configuration file at the end.

First, we need to load the RTMP module:

load_module "modules/ngx_rtmp_module.so";

Next, we'll define the user. The user we'll specify is the user profile created on your Ubuntu server. In this case, our username is ubuntu.

user ubuntu;

Now, we'll define the number of worker_processes or the number of processes that will spawn simultaneously. The default is 1 but is generally determined by the number of CPUs running on your machine:

worker_processes 1;

Next, we'll define the number of worker_connections or the number of connections that each process can manage. The default is 512, but most systems should be able to handle quite a few more. I usually make this number divisible by 1024 and the number of CPUs. Since we're running a single CPU, the value will be 1024:

events {
worker_connections 1024;
multi_accept on;
}

Now, we'll get into the RTMP server settings:

rtmp {
server {
...
}
}

Here, we'll define the port number with the listen directive as 1935 which is standard.

The chunk_size directive is the maximum chunk size for stream multiplexing. The default value is 4096, which means RTMP will send data in 4 KB blocks. The minimum value you can pass is 128 The larger the value, the lower the CPU overhead.

And the max_streams directive determines how many live streams can be running simultaneously:

listen 1935;
chunk_size 4096;
max_streams 8;

Next, we'll create two callback URLs, one for when the stream starts and the other for when the stream ends. Each callback URL receives the stream data in the body, including the stream key passed when starting and stopping the stream. You'll need to make sure you have a web server set up that can execute web page URLs. You can read this tutorial on setting up your own Apache web server.

on_publish http://domain.com/start;
on_publish_done http://domain.com/end;

Now, we'll define our application settings and set a path for our stream. In this case, we're using live as our path. So, when streaming, your URL would become rtmp://domain.com/live. We'll discuss that further later on:

application live {
...
}

Within our application settings, we'll add a live directive and set its value to on which allows for one-to-many live broadcasting. We'll also turn off the access log for now. You can use this setting for testing whenever you need it as it can report useful information for debugging purposes:

live on;
access_log off;

Next, we'll enable HLS. We'll ensure again that this is set up for live streaming, and break each fragment into small chunks. The hls_fragment directive defines the length of each of the TS files created, which holds simple data about the stream itself. In this case, we've set it to 3s, or three seconds for each fragment.

The hls_playlist_length directive defines the total length in time for the saved fragments. In this case, we've set our playlist length to 15s, or 15 seconds. For a fragment length of three seconds and a playlist length of 15 seconds, that means that only five TS files should be saved to the server at any given time. The TS files will cycle out, retaining the latest files only, as the live stream continues.

The hls_nested directive confirms whether or not the M3U8 file (the main live stream file) and associated TS fragment files will be saved within a directory or not. The directory name is generally the stream key provided by the user when starting the stream.

Finally, we have the hls_path directive which defines the location on the server where the directory and files will be stored:

hls on;
hls_type live;
hls_fragment 3s;
hls_playlist_length 15s;
hls_nested on;
hls_path /var/www/stream;

As a bonus, I decided to add some information on recording streams! You can do so with the following directives:

record all;
record_unique off;
record_interval 5m;
record_suffix .mp4;
record_path /var/www/archive;

In this case, the video will be saved as an MP4 file to the /var/www/archive path on the server. The length of the saved video will not exceed five minutes. For every five minutes reached, the video file will be deleted and re-recorded until the user has stopped the stream. Setting a value of all for the record directive just tells Nginx to save both audio and video to the MP4 file.

Here is the full contents of the nginx.conf file. Feel free to edit to your liking and server's specifications:

load_module "modules/ngx_rtmp_module.so";

user ubuntu;
worker_processes 1;

events {
worker_connections 1024;
multi_accept on;
}

rtmp {
server {
listen 1935;
chunk_size 4096;
max_streams 8;

on_publish http://domain.com/start;
on_publish_done http://domain.com/end;

application live {
live on;

access_log off;

hls on;
hls_type live;
hls_fragment 3s;
hls_playlist_length 15s;
hls_nested on;
hls_path /var/www/stream;

record all;
record_unique off;
record_interval 5m;
record_suffix .mp4;
record_path /var/www/archive;
}
}
}

Allow Incoming Traffic to Port 1935

Real quick, we'll need to allow incoming traffic to port 1935 so that our streaming software can communicate with the server properly. This can be done by executing the following command:

$ sudo ufw allow 1935/tcp

Restart the Nginx Service

To ensure we get everything up and running, let's restart the Nginx service once:

$ sudo systemctl reload nginx.service

Add HLS Support to HTML's video Tag

There are a couple different options for adding HLS support to your on-page video element:

  • Video.js: This is a widely known solution and has a lot of features built in.
  • HLS.js: This is a lightweight solution that works well and is my preferred method. We'll continue with examples using this method.

Make sure to download the preferred library and include the JS files on your site. In this case, we'll download the hls.min.js file, then include the following video tag:

<video id="stream" preload="auto" autoplay playsinline webkit-playsinline></video>

To start the HLS stream, you'll need the path on the server to the M3U8 file. The live stream path we set in our nginx.conf file above set the path to /var/www/stream, so we'll use that in this example:

var hls = new Hls();
var video = document.getElementById("stream");
var stream_key = "[get-user-stream-key]";

hls.loadSource("//domain.com/stream/" + stream_key + "/index.m3u8");
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});

Here, we're creating a new HLS instance and assigning to the hls variable, then loading our source M3U8 file, attaching it to the video element, then playing the video once it's loaded.

Choosing Your Streaming Software

There are a few options for live video streaming, but OBS Studio is by far the easiest to work with, especially if you're a beginner to live streaming.

To get going, you'll just need to go to File > Settings from the navigation menu in OBS, then click the Stream tab in the left pane to edit these two settings:

  • Server: The server where you Nginx application is running. As mentioned above, the application name is live, so the server name to use here would be rtmp://domain.com/live.
  • Stream Key: Set this to whatever you would like. If you're managing and securing streams with stream keys associated with user accounts on your web application, you could assign specific keys to each user. This is also the name of the directory created on the server that contains the stream files for each stream.

Conclusion

There's a ton of information to digest when considering setting up a live video streaming server, but this tutorial covers a lot of the basics to get you going right away!

You can download the configuration file from my GitHub repository and make updates as needed for your situation and your server resources.

Created: April 13, 2022