Create a secure adaptive bit-rate HLS stream with Nginx/RTMP/Ffmpeg on Ubuntu 20.04 (2021)

6-8-2023 UPDATE: I wrote a new tutorial, suited for Ubuntu 22.04. Go to the new tutorial.

24-07-2023 UPDATE: Before you read/follow this tutorial, I want to inform you of something important. In case you follow this tutorial, there’s a big chance you won’t be able to get the adaptive streams to work correctly. I don’t know exactly why or what has changed, but I believe the audio codec is no longer used by FFmpeg. This issue can easily be resolved by using AAC codec instead.

Editing some lines in the nginx.conf file will accomplish this. They are located roughly around line number 84, if you used the example from this tutorial. Somewhere under the /live application search for the lines that look like the image.

Replace the lines starting at “exec ffmpeg” and ending with $name_src with the lines below:

HTML

Don’t forget to test your config by entering: nginx -t, and to restart it with: sudo systemctl restart nginx. I am currently putting together a new, improved and updated tutorial for Ubuntu 22.04, so keep an eye out for this during the next couple of weeks.


I wrote a new guide on setting up your own live-stream server. Mostly due to your requests. I took the opportunity to simplify the guide a bit. If you follow this guide step by step, and use your brain at times that it’s required, you’ll be fine. This guide has been tested on three occasions, and it works 100%.

YouTube player

Before we begin I want to make something clear. Creating your own live-stream server like this can not be compared by using a streaming platform like Youtube or Twitch. They use a worldwide network to deliver their content, and we’re just using 1 server. That means that it matters where the server is located. If you host a server in Japan, it may not be as fast as you hoped in Ireland. Also keep in mind that when you decide to host your server from your home, that you bandwidth is limited. This can seriously affect your upload speed, especially when you have more then a handful of viewers. To allow more viewers you should host it in a VPS or dedicated server preferably that is in a datacenter with a >500mbps connection. The downside of that is that it might no be powerful enough to do all the transcoding ánd deliver the content to viewers. In other words, this setup is not suited to host a live-stream server to completely replace Youtube or whatever platform. So what is it good for then? For me personally I just use it to re-stream. So I don’t need the transcoding of my stream since I can just forward the RTMP stream to Youtube & Twitch. Also this setup will be fine when you’re using it below 100 viewers, and if the hardware is not from the year 0. And, it can also be used as a primary hub that you stream to and from there it gets distributed to several other servers located at different parts of the world. This can be VPS servers that you rent, or an already existing CDN. That last thing is actually exactly what I’ve been fooling around with, just for fun. Anyways, hopefully all this meets your requirements.

Introduction

In this guide we’ll be setting up a live-stream server on Ubuntu 20.04 powered by Nginx+RTMP mod, and we’ll be using FFmpeg to transcode incoming RTMP streams to several outgoing HLS, adaptive bit-rate streams, or ABS in short, that will allow the most optimal, smooth playback on any type device/screen, even on slow, low-bandwidth internet connections.

Requirements

I’ll assume that you have already done a clean basic install of a server running at least Ubuntu 20.04. You can choose to run the server on an older, spare PC, or a Virtual Machine, or a rented VPS, or a dedicated server perhaps even, or… and I personally did this often times, use a laptop that you don’t really use anymore, one that has a h264 Nvenc capable Nvidia GPU chip in it gives you the option the do all the encoding on your server with the GPU, freeing up a lot of CPU power for other tasks. More on that in a future article. At least a Core i5 is advised, with either 4GB of RAM, but preferably 8GB, or more. More is always better! (One can never have too much RAM).

It is essential that you open or forward ports 80 and 443 in your firewall/router and point it to your server. If you want to allow others to stream to your server from the internet port 1935/udp for RTMP must also be pointing to your server. This is the very first thing you must do before you do anything else. Instructions on how to do this are beyond the scope of this guide. On this site you can test if you have successfully opened the specified ports: www.canyouseeme.org

0. Preparing the server

We’re going to set a proper host name for the server first, ideally a FQDN, as this will be a requirement to successfully set up certificates for HTTPS later on. You can get a dynamic DNS domain name for free at no-ip.org to name just one. In this how-to guide I’ll be using a dummy example addresses like: “yourdomain” or “example.com”. Now please make sure that from this point on you will replace every line that contains this address with the actual address of your server. I’m not going to repeat this so please keep this in mind and pay attention to it.

We’re gonna start by installing Nginx and the RTMP module for Nginx, and Certbot. We ‘re also installing a whole bunch of things that we will be needing or using later on. Let’s first get the server up to date.

HTML

1. Installing Nginx + RTMP module and a bunch other stuff

HTML

Time to install Nginx and the RTMP module and Certbot. Be sure you don’t have anything else like Apache running on port 80 as it will conflict resulting in that Nginx will not start.

HTML

2. Manually create these folders and files

We now have the libnginx-mod-rtmp module installed, but it doesn’t include a specific file that is needed to show a statistics web-page for you live-streams. Cause stats are cool of course, but more importantly, it is an essential tool to see if your streams are working. In case you ever need to troubleshoot an issue with a stream this is a handy tool. We’re going to obtain it from the official libnginx-mod-rtmp github repository. But first, we need to create the folder that we are going to copy this file to. This happens to be the same folder as we’ll be using as root folder for our video web-player files. And perhaps also a WordPress site later on. Oh yeah, you’re going to need a domain name that is pointing to the IP address of your server in DNS. Create these folders but be sure to replace “yourdomain” with whatever domain name you’ll be using.

HTML
HTML

Now let’s copy the stat.xsl file to your website’s root folder, and also to the default web folder .

HTML

Now we’re going to create a new file in he same folder and paste the lines below and save the file.

HTML
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
HTML

The following will create a temporary index.html file in your root web folder that is needed for setting up certificates later on.

HTML

We need to tell Nginx that it has to use a different config file for handling all the requests that it will receive when using your domain name in a browser. For this we’re going to create a separate, new virtual host config file. Continue with the next step to do so.

3. Set a basic Nginx setup for certificate requests

In order to get valid certificates we need to use a hostname and run a website on it so that Letsencrypt can validate it. An IP address won’t do, so make sure you actually use a hostname in the next config file.

HTML

Copy and paste the following in this new file, and pay attention to the code because you will need to replace a couple of lines so it shows your own domain name in stead of “yourdomain”.

HTML

To enable your new site you must create a virtual link from the sites-enabled folder to your config file like this:

HTML

You should always use the next command after making this many changes to Nginx’ configuration. It will check if your config is good, and if it contains errors it will show you what the error is.

sudo nginx -t

HTML

4. Use Certbot to request valid certificate for your domain.

You should now be able to see a default Nginx web page when you enter the IP address of your server in your web browser if you’re on the same network. If you connect through the internet to a remote server keep in mind that you may need to open or forward port 80 in your firewall / router. Doing that is beyond the scope of this article. If you are behind a modem/router and you want your server to be accessible from the internet, you must also make sure you setup up port forwarding. Don’t continue this guide until you’ve done so because you will not be able to successfully create certificates if your server is not reachable from the internet on pot 80 (and later on also port 443). Once you’ve confirmed that your server is reachable from the internet and is showing the default Nginx page, it is now possible to create our certificates that allow us to secure the server by employing Https. We can do this with Cerbot very easily. You can enter multiple domains in one line if you need to. See example below.

HTML

Example: sudo certbot –nginx -d example.com -d www.example.com -d subdomain.example.com

A new request will be queued while it asks you some questions like your email address and if you agree to something. Entering email is required by the way. If all went well, you should see a message at the end asking you if you want to have Certbot automatically change some lines in your virtual host config file and whether or not to forward all Http traffic to Https from now on. Choose yes unless you have a good reason not to do so. When you open /etc/nginx/sites-enabled/youdomain.conf you will notice that several lines have been added to the bottom of the config file. It will look something similar to this:

HTML

This means that you can now open your domain in your browser using a valid certificate. You may want to check if this is actually the case simply by opening your domain name in a web browser. You should not see a warning about certificates and when you investigate the certificate you will see it’s valid.

5. Edit the Nginx.conf to enable RTMP and set related variables like ABS.

Now is a good time to edit the main Nginx config file to add our config for the RTMP server and Ffmpeg encoding. It is safe to replace the current config file with the one below. Making a backups of it never hurts.

HTML

Either create a new file and paste what is shown in the oversized codeblock below, and save it after having added all the required lines. Or simply place it where it belong by pulling it directly from my github page, like so:

HTML

I strongly suggest to open this config file in a text editor and maximizing the window so the layout is not messed up due to line-breaks or wrapped long lines as it is on this page. It will look neat, like the screenshot, and that makes it easier to read the descriptive and informative comments behind the variables that we put together.

This is what the config file looks like without line-breaks and wrapped long lines.

After creating the new nginx.conf file, open it in your favorite text editor or directly from the command line editor Nano / Joe, and go through it carefully. Almost all the variables have a description that says what the variable exactly does, and what the other options for it are. So make any adjustments if you feel you need to. It will work as-is, without making any changes, so if you made changes that don’t work or that you don’t like, just replace it again with this one.

HTML

Use the small icon in the top right of the lines below to copy the whole content so you can paste it in a decent text editor so it becomes more easy to go through it.

HTML

IMPORTANT! Notice that the variables to enable the use of encryption keys for the stream are not enabled in this config. Also the part that handles the adaptive bit-rate streaming has been commented out as well (lines 84/89). You have to remove these comments if you want to enable ABS here. Before you touch the encryption keys variables make sure that you have created decent certificates before. To enable ABS just remove the # in front of the lines, and place a # at the beginning of the line that says: “push rtmp://localhost/hls;” (on line 138) and then restart Nginx. As an alternative you can leave them disabled, and use push to forward the stream to application /encoder. Everything that comes in at /encoder will automatically be encoded to the stream for ABS. The reason I decided to disable these options is so you can first test the server without it doing any encoding. Then, if that works, you can enable ABS by either using the push option I just mentioned, or by removing the comments I also just mentioned.

See if the config is good (sudo nginx -t) and then do a restart (sudo systemctl restart nginx) and now you’re done with the hardest part. Let’s see if the server accepts your stream. Use your favorite app (Like OBS or Wirecast) and in settings choose to use a custom server under server tab, and enter your server address here manually. Example: RTMP://yourIP/live or RTMP://youdomain/live. Under livestream-key field set: stream
Now start streaming! The fastest way to check if your stream works is to use the stat page at: https://yourdomain/stat or http://serverIP/stat

What your /stat page should look like when you started a stream

One way to check if everything is working as it should at this moment is by using VLC or PotPlayer to open the RTMP stream directly. The address will be the same as you’ve set in OBS/Wirecast with the streamkey added at the end. Like: rtmp://youdomain/live/stream. But you might as well skip this step because we should also have one HLS and one DASH stream that we may as well use to see if all is working. You’ll figure out how to do this yourself. Or not.. in that case just continue with the guide first and get it all over with.

6. Install and configure Video.JS web player

Video.JS is a web-video player that supports adaptive stream playback. It can do a lot more by utilizing plugins. Have a look at their website to get a glimpse of what the possibilities are. You can choose to follow these next few steps and download Video.JS and host the files from our own server, or you can choose to do it the smarter way, by not downloading anything but just pointing to a CDN hosted in your <script> . The smarter way is also faster, so do that. You can find out the exact URL’s of the most recent version of VideoJS on their github. Also the instructions on what to do. https://github.com/videojs/video.js

Here’s the old-fashioned way , that I for some reason prefer..

HTML
HTML

Create a new file in your website root folder called videoplayer.html and paste & edit the lines you see a bit below here. Or simply download it directly from my github and edit it. Whatever you choose, in both cases you will have to carefully inspect the code and replace all the things that are marked to to be replaced.

HTML
HTML
HTML

You have to edit 4 lines in there. Make sure Nginx can access all the files.

HTML

You are done!! You have successfully setup a live-stream server. Congratulations. You should now be able to see your own live-stream when you open the video player page in your browser. But only if you have of course started streaming.

Now you can have a closer look at your nginx.conf to see what all the variables can do, and edit them if you have specific wishes or needs. When you are streaming with ABS enabled, you should at first try to monitor your server to see how much CPU it will be using and how far you can go with adding other streams. I’ve said it before, encoding will be hard work for your CPU, no matter what generation, so always keep an eye out on what your setup does to your CPU when you are live-streaming, and adjust your config where necessary.

YouTube player
PART II

7. Adaptive Bit rate Streaming

When you’re ready to enable Adaptive Bitrate Streaming, you should open the /etc/nginx/nginx.conf file and make a few changes.

HTML

Remove the hashtags (#) from the lines that enable adaptive bitrate streaming, as shown in the video. Place a new hashtag in front of the default HLS push that I also show in the video. Continue to remove the hashtags from the lines that specify the variant streams. Again, like I show in the video, and in the three images below.

Save the file and test your configuration by using nginx -t. When the results come back successful go ahead and restart nginx like this:

HTML

You are now able to use the adaptive bitrate stream functionality! If you want to show this stream in a locally hosted html file, you can copy the file videoplayer.html that we created earlier, to a new file, like abs.html. Edit this new file and replace the URL that points to the .m3u8 playlist file in it. Change /hls/stream.m3u8 to /hls/stream/stream.m3u8 and save the file.

Now open it in your web browser by going to https://YOURDOMAIN/abs.html. It should show your HLS stream. You could also use the following command to copy it from Github, but you will also need to edit it first to add your domain name.

HTML
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>LiveStream</title>
<link href="https://YOURDOMAIN/js/videojs/video-js.css" rel="stylesheet">
</head>
<body>
<center>
  <video-js id="live_stream" class="vjs-default-skin" controls preload="auto" width="1280" height="auto" poster="https://YOURDOMAIN/poster.jpg">
    <source src="https://YOURDOMAIN/hls/stream.m3u8" type="application/x-mpegURL">
  </video-js>
  
  <script src='https://YOURDOMAIN/js/videojs/video.js'></script>
  <script src="https://YOURDOMAIN/js/videojs/videojs-http-streaming.js"></script>
  
  <script>
    var player = videojs('live_stream');
  </script>
 </center>
</body>
</html>

Using Bradmax Player that we’ll be installing later in this guide, it will be possible to manually switch between the different adaptive streams. I believe VideoJS also supports this but I’m not certain it will do so by default. You may also want to have a look at the browse-able folder that contains the video fragment files by entering in your browser: https://YOURDOMAIN/hls. This is where the m3u8 files should be pointing to.

You can stop following this how-to guide here if you like. You did a great job if you completed all the steps up to this point. Your server should now be running. The next steps are totally optional. Continue reading if you’re curious what else we’re gonna do!!

Additional, optional extra steps

8. Installing PHP (7.4 or 8.0)

Stick around if you want to host a (WordPress) website on your server that shows the live-streams. We’ll install PHP, MariaDB, WordPress and of course a nice video player plugin for WP that supports adaptive bitrate streaming playback.Let’s start with installing PHP 7.4.

HTML

It’s also an option to install PHP 8 instead. It’s really easy we only have to add the Ondrej repository. Like this:

HTML
HTML

It might be a good idea to install some common modules for PHP. I’ve put together a collection that is widely used. But feel free to skip this or choose only the ones you need.

HTML

Now we need to tell Nginx to use the PHP8.0-fpm sock. Place these lines in a virtual hosts file, or in the nginx.conf.

HTML

For Nginx we must edit a specific line in /etc/php7.4/fpm/php.ini. Search the file for “;cgi.fix_pathinfo=1” and replace it with “cgi.fix_pathinfo=0” and save the file. A more easy way to do this is by entering this command. The first one is if you use the default PHP 7.4 version. The second one is if you use PHP8.

HTML
HTML

That was the only thing we are required to change. But there are some other things that is advisable to change as well. Be sure to enter your timezone in the last sed option. The first ones below here are for PHP 7.4. Use your brain and just replace 7.4 in them with 8.0 if you want to make these changes and you’re using 8.0.

HTML
HTML

9. Installing MariaDB and PHPMyAdmin

Next up we’ll install MariaDB and PhpMyAdmin simply by using:

HTML

Create a symlink to PhpMyAdmin in your web root folder so you can use it

HTML

Follow the Mysql secure installation steps

HTML

Choose whatever suits your needs. Restart mysql once it is done. (sudo systemctl restart mysql) and login to Mysql like this:

HTML

Use the following commands to set up a database and database user for WordPress.

HTML

Since we’re at the mysql cli anyways, we might as well do this as well:

HTML
HTML

There are other guides out there that can show you what you can change in your Nginx configuration to optimize it specifically for WordPress. You should definitely look in to that because many of these options make your server more secure. It is beyond the scope of this guide to go in to that right now. The plan is to write a new guide on this subject in the future some time but until then, google it. 😛

10. Download and install WordPress

HTML

The files will be unpacked in a folder so you may want to move al these files to the folder that is below it. In other words, the files should be placed in the root folder for your website.

HTML

Now open your browser and enter your site’s domain name. You should see a language list. This is the first step of the WordPress installer. The rest is pretty explanatory. Remember to use the username and password we set a couple of steps ago during the database setup.

Create a new user for yourself and login to your brand new WordPress.

11. All right Sparky! Let’s add a video player

Once the setup is complete, you can log in to your new WordPress website. To add your stream to the site first install a plugin for this purpose. Now there’s plenty of video player plugins out there, but I strongly advice to use Bradmax Player.

You’ll be able to customize your player if you create an account at their website and from there you can create your customized player.

Once you did that you are asked if you want to generate the files for your player. Choose yes and download the zip. Open the zip and extract the bradmax.json file.

Now go to the settings page of Bradmax plugin in WordPress and upload the .json.This way the player on your site will look like what you’ve set up on the Bradmax site.

Now we need to add the shortcode for our player to a page or post. You have several variables you can use for this. Most importantly is the address of the .m3u8 playlist file. See the examples below to see what your options are.

bradmax_video url="url to your file or playlist"
poster="url to your poster file"
autoplay="true or false"
duration="length of video file in seconds"
style="width:1280px;height:720px"
style="border:solid 1px gray"

Let’s put together a proper shortcode that we can actually use. It can look like any of the examples below.

[bradmax_video url="https://yourdomain/hls/stream.m3u8" poster="https://yourdomain/wp-content/uploads/poster.jpg" style="border:solid 1px gray" autoplay="false"]

The example above points to the playlist of your HLS stream and it also adds a poster image for when there is no stream active when a person opens the player. Also it will add a border and will not automatically start playing.

[bradmax_video url="http://example.com/hls/hls_stream.m3u8" style="width:1280px;height:720px;border:solid 1px gray"]

This example sets the player’s resolution in pixels. In this case 1280×720 with a gray border of 1 pixel.

[bradmax_video url="https://yourdomain/static/video/index.mpd" url_2="https://yourdomain/static/video/index.m3u8"]

This example plays a Dash stream and as a fallback it will play a HLS stream. For more info on the shortcode variables that are available, check out the help pages on the Bradmax website.

In case you have any question about this guide, leave a comment below and I’ll respond as soon as I can.