HTTPS HLS Server
HTTPS HLS Server
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
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.
1
sudo apt update && sudo apt upgrade -y
1
sudo apt-get install wget unzip software-properties-common dpkg-dev git make gcc automake build-essential
zlib1g-dev libpcre3 libpcre3-dev libssl-dev libxslt1-dev libxml2-dev libgd-dev libgeoip-dev libgoogle-
perftools-dev libperl-dev pkg-config autotools-dev gpac ffmpeg mediainfo mencoder lame libvorbisenc2
libvorbisfile3 libx264-dev libvo-aacenc-dev libmp3lame-dev libopus-dev -y
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.
1
sudo apt install nginx -y
2
sudo apt install libnginx-mod-rtmp python3-certbot-nginx -y
1
sudo mkdir -p /var/www/yourdomain/web/js/videojs
2
3
sudo mkdir -p /var/livestream/hls /var/livestream/dash /var/livestream/recordings /var/livestream/keys
4
5
sudo ln -s /var/livestream/hls /var/www/yourdomain/web/hls
6
sudo ln -s /var/livestream/dash /var/www/yourdomain/web/dash
7
8
sudo chown -R www-data:www-data /var/livestream /var/www/yourdomain
1
cd /usr/src
2
sudo git clone https://github.com/arut/nginx-rtmp-module
Now let’s copy the stat.xsl file to your website’s root folder, and also to the default web
folder .
1
sudo cp /usr/src/nginx-rtmp-module/stat.xsl /var/www/html/stat.xsl
2
sudo cp /usr/src/nginx-rtmp-module/stat.xsl /var/www/yourdomain/web/stat.xsl
Now we’re going to create a new file in he same folder and paste the lines below and save
the file.
1
sudo nano /var/www/html/crossdomain.xml
<?xml version="1.0"?>
-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy> Copy
1
sudo cp /var/www/html/crossdomain.xml /var/www/yourdomain/web/crossdomain.xml
The following will create a temporary index.html file in your root web folder that is needed
for setting up certificates later on.
1
sudo cp /var/www/html/index.nginx-debian.html /var/www/yourdomain/web/index.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.
1
sudo nano /etc/nginx/sites-available/yourdomain.conf
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”.
1
server {
2
listen 80;
3
listen [::]:80;
4
5
server_name YOUDOMAIN; # << EDIT THIS! (Example: hostname.example.com)
6
root /var/www/YOURDOMAIN/web; # << EDIT THIS! (Example: /var/www/example.com/web)
7
8
index index.html index-nginx.html index.htm index.php;
9
client_max_body_size 8192M;
10
add_header Strict-Transport-Security "max-age=63072000;";
11
add_header X-Frame-Options "DENY";
12
13
location / {
14
add_header Cache-Control no-cache;
15
add_header Access-Control-Allow-Origin *;
16
try_files $uri $uri/ =404;
17
}
18
location /crossdomain.xml {
19
root /var/www/html;
20
default_type text/xml;
21
expires 24h;
22
}
23
location /control {
24
rtmp_control all;
25
add_header Access-Control-Allow-Origin * always;
26
}
27
location /stat {
28
rtmp_stat all;
29
rtmp_stat_stylesheet stat.xsl;
30
#auth_basic Restricted Content; # Create a valid .htpasswd before uncommenting this.
31
#auth_basic_user_file .htpasswd; # Create a valid .htpasswd before uncommenting this.
32
}
33
location /stat.xsl {
34
root /var/www/YOURDOMAIN/web; # << EDIT THIS! (Example: /var/www/example.com/web)
35
}
36
location ~ /\.ht {
37
deny all;
38
}
39
location /hls {
40
types {
41
application/vnd.apple.mpegurl m3u8;
42
video/mp2t ts;
43
}
44
autoindex on;
45
alias /var/livestream/hls; # << Take note of this line. Change it when required.
46
47
expires -1;
48
add_header Strict-Transport-Security "max-age=63072000";
49
add_header Cache-Control no-cache;
50
add_header 'Access-Control-Allow-Origin' '*' always;
51
add_header 'Access-Control-Expose-Headers' 'Content-Length';
52
if ($request_method = 'OPTIONS') {
53
add_header 'Access-Control-Allow-Origin' '*';
54
add_header 'Access-Control-Max-Age' 1728000;
55
add_header 'Content-Type' 'text/plain charset=UTF-8';
56
add_header 'Content-Length' 0;
57
return 204;
58
}
59
}
60
location /dash {
61
types{
62
application/dash+xml mpd;
63
video/mp4 mp4;
64
}
65
autoindex on;
66
alias /var/livestream/dash; # << Take note of this line. Change it when required.
67
68
expires -1;
69
add_header Strict-Transport-Security "max-age=63072000";
70
add_header Cache-Control no-cache;
71
add_header 'Access-Control-Allow-Origin' '*' always;
72
add_header 'Access-Control-Expose-Headers' 'Content-Length';
73
if ($request_method = 'OPTIONS') {
74
add_header 'Access-Control-Allow-Origin' '*';
75
add_header 'Access-Control-Max-Age' 1728000;
76
add_header 'Content-Type' 'text/plain charset=UTF-8';
77
add_header 'Content-Length' 0;
78
return 204;
79
}
80
}
81
}
To enable your new site you must create a virtual link from the sites-enabled folder to your
config file like this:
1
sudo ln -s /etc/nginx/sites-available/YOURDOMAIN.conf /etc/nginx/sites-enabled/YOURDOMAIN.conf
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
1
sudo systemctl restart nginx
1
sudo certbot – nginx -d YOURDOMAIN
1
listen [::]:443 ssl ipv6only=on; # managed by Certbot
2
listen 443 ssl; # managed by Certbot
3
ssl_certificate /etc/letsencrypt/live/yourdomain/fullchain.pem; # managed by Certbot
4
ssl_certificate_key /etc/letsencrypt/live/yourdomain/privkey.pem; # managed by Certbot
5
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
6
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
7
8
}server {
9
if ($host = yourdomain) {
10
return 301 https://$host$request_uri;
11
} # managed by Certbot
12
13
listen 80;
14
listen [::]:80;
15
16
server_name yourdomain;
17
return 404; # managed by Certbot
18
}
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.
1
sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx-original.conf
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:
1
sudo wget -O /etc/nginx/nginx.conf https://raw.githubusercontent.com/ustoopia/Nginx-config-for-
livestreams-ABS-HLS-ffmpeg-transc-/main/etc/nginx/nginx.conf
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.
1
sudo nano /etc/nginx/nginx.conf
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.
1
##
====================================================================================
================== ##
2
## Visit this page for a list of all variables: https://github.com/arut/nginx-rtmp-module/wiki/Directives ##
3
## Visit this site for many more configuration examples: https://github.com/arut/nginx-rtmp-module ##
4
## This example file was put together by Andre "ustoopia" for usage on https://youtube.com/ustoopia ##
5
##
====================================================================================
================== ##
6
user www-data; # Only used on linux. Nginx will run under this username.
7
worker_processes 1; # Set this to how many processors/cores CPU has. Relates to "worker_connections"
8
pid /run/nginx.pid; # Sets the location of the process id file (used on linux only).
9
include /etc/nginx/modules-enabled/*.conf; # Include all the optional configuration files stored here.
10
events {
11
worker_connections 768; # Worker_processes * worker_connections = max clients. So in this setup: 1 * 768
= 768 max clients.
12
# multi_accept on; # "Off" will accept 1 new connection at a time. "On" will accept all new connections.
Default is off.
13
}
14
http {
15
sendfile off; # on|off. Toggles the use of sendfile. Default=off. For optimal HLS delivery disable this.
16
tcp_nodelay on; # on|off. Forces a socket to send the data in its buffer, whatever the packet size.
Default=on.
17
tcp_nopush on; # on|off. Sends the response header and beginning of a file in one packet. Default=off.
18
server_tokens off; # on|off|build. Toggles showing nginx version in the response header field. Default=on.
19
keepalive_timeout 65; # A keep-alive client connection will stay open for .. on the server side. Use 0 to
disable. Default=75s
20
types_hash_max_size 2048; # Sets the maximum size of the types hash tables. Default=1024.
21
server_name_in_redirect off; # Toggles the use of the primary server name, specified by the server_name
directive. Default=off.
22
server_names_hash_bucket_size 64; # Sets the bucket size for the server names hash tables depending on
processor's cache line, 32|64|128.
23
default_type application/octet-stream; # Emit this MIME type for all requests.
24
## Include configuration files from these locations ##
25
include /etc/nginx/mime.types;
26
include /etc/nginx/conf.d/*.conf;
27
include /etc/nginx/sites-enabled/*.conf; # Holds symlinks to the actual config files in /etc/nginx/sites-
available
28
## LOGGING - This section has many options. See https://github.com/arut/nginx-rtmp-
module/wiki/Directives#access-log ##
29
access_log /var/log/nginx/access.log; # off|path [format_name]. Default logging is done to same file as
HTTP logger.
30
error_log /var/log/nginx/error.log warn; # Set this here or in the virtual hosts config file in sites-available
folder.
31
gzip off; # on|off. Compresses responses using gzip method. Helps to reduce size of transmitted data by
half or more. Default=off
32
# gzip_vary on; # More info on zip variables is found here:
https://nginx.org/en/docs/http/ngx_http_gzip_module.html
33
gzip_proxied any;
34
# gzip_comp_level 6;
35
# gzip_buffers 16 8k;
36
gzip_http_version 1.1;
37
gzip_types text/plain text/css application/json application/javascript text/xml application/xml
application/xml+rss text/javascript;
38
}
39
## EITHER SET YOUR SERVER {} BLOCK HERE, OR RATHER IN A SEPERATE VIRTUAL HOST CONFIG FILE IN
/sites-available/yourdomain.conf ##
40
## RTMP CONFIGURATION ##
41
rtmp {
42
server {
43
listen 1935; # RTMP listen port. Open it in your router/firewall. Options: (addr[:port]|port|unix:path) [bind]
[ipv6only=on|off] [so_keepalive=on|off|keepidle:keepintvl:keepcnt|proxy_protocol]
44
application live { # Name it whatever you prefer. You will need at least one application, you can have many
more.
45
live on; # on|off. Enables this application and allowing live streaming to it. Default=on.
46
# max_streams 32; # Sets maximum number of RTMP streams. Default value is 32 which is usually ok for
most cases. Default=32.
47
# max_connections 100; # Sets maximum number of connections for rtmp engine. Default=off.
48
meta on; # on|copy|off. Receive metadata packets containing predefined fields like width, height etc.
Default=on.
49
interleave off; # on|off. Audio and video data is transmitted on the same RTMP chunk stream. Default=off.
50
wait_key on; # on|off. Makes video stream start with a key frame. Default=off.
51
wait_video off; # on|off. Disable audio until first video frame is sent (can cause delay). Default=off.
52
drop_idle_publisher 10s;# Drop publisher that has been idle for this time. Only works when connection is in
publish mode. Default=off
53
sync 300ms; # When timestamp difference exceeds the value specifiedan absolute frame is sent fixing that.
Default=300ms.
54
play_restart off; # on|off. If enabled sends "NetStream.Play.Start" and "NetStream.Play.Stop" every time
publishing starts or stops. Default=off.
55
idle_streams on; # on|off. If disabled prevents viewers from connecting to idle/nonexistent streams and
disconnects all. Default=on.
56
## NOTIFICATIONS - This section has too many options to include in this example config. ##
57
## Notifications use HTTP callback to inform subscribers that stream has started. You will need a website
that can handle these. ##
58
## These option go beyond the scope of this configuration file as it contains lots of info. Please visit this url
for more info: ##
59
## https://github.com/arut/nginx-rtmp-module/wiki/Directives#notify ##
60
publish_notify off; ## on|off. Send "NetStream.Play.PublishNotify" & "NetStream.Play.UnpublishNotify" to
subscribers. Default=off
61
# on_publish http://www.example.com/plugin/Live/on_publish.php;
62
# on_play http://www.example.com/plugin/Live/on_play.php;
63
# on_record_done http://www.example.com/plugin/Live/on_record_done.php';
64
## TRANSCODING USING FFMPEG EXEC ##
65
## EXEC - Many things are possible using exec. To learn more visit https://github.com/arut/nginx-rtmp-
module/wiki/Directives#exec ##
66
## You can either set this here, so all the incoming streams to /live/* get transcoded, or you can push to a
seperate recorder app. ##
67
## The following lines will take our incoming RTMP stream and transcode it to several different HLS streams
with variable bitrates ##
68
## This ffmpeg command takes the input and transforms the source into 4 or 5 different streams with
different bitrate and quality. ##
69
## 4 or 5 different streams with different bitrate and quality. P.S. The scaling done here respects the aspect
ratio of the input. ##
70
## If you enable adaptive bitrate streams here, make sure to disable the stream-push to /hls below, under #
STREAM RELAYING LOCAL # ##
71
#exec ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1 ## Transcoding can be enabled here, or
by using the abshls application below.
72
#-c:v libx264 -acodec copy -b:v 256k -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf
23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/$name_low
73
#-c:v libx264 -acodec copy -b:v 768k -vf "scale=720:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf
23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/$name_mid
74
#-c:v libx264 -acodec copy -b:v 1024k -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -
crf 23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/$name_high
75
#-c:v libx264 -acodec copy -b:v 1920k -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -
crf 23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/$name_higher
76
#-c copy -f flv rtmp://localhost/hls/$name_src;
77
## SET ACCESS RULES FOR APPLICATION /live ##
78
## PUBLISHING ##
79
allow 127.0.0.1; # If you decice to harden these rules, it is adviced to uncomment this line.
80
# allow publish 192.168.2.0/24; # Anybody from this local IP address range can stream to this application.
81
# allow publish 192.168.2.50; # Allow a single IP address to stream to this application (multiple lines with
different ip's are possible)
82
allow publish all; # Anybody from any location can stream to this application. Comment this out if you
want to use deny publish.
83
# deny publish 10.0.0.50; # Deny this specific IP address from streaming to this application. Can also be an IP
address range.
84
# deny publish all; # Nobody can stream to the server except those that you've set in previous lines.
85
## PLAYING ##
86
allow play 127.0.0.1; # Localhost can play the stream from this application. Must be set if you decide to use
push later on!
87
allow play 192.168.2.0/24; # Anybody in this IP range can play the RTMP directly.
88
allow play all; # Anybody can play the RTMP livestream directly. Comment this out if you decide to use
the next option.
89
# deny play all; # Nobody can play the RTMP stream except those that we've set in lines above it.
90
## RECORDING SETTINGS FOR /LIVE ##
91
record off; # off|all|audio|video|keyframes|manual. These options sorta speak for themselves.
92
record_path /var/livestream/recordings; # Folder location that will be used to store the recordings. YOU
SHOULD CHANGE THIS TO YOUR PREFERENCE!
93
record_suffix -%d-%b-%y-%T.flv; # Added to recorded filenames. Example uses 'strftime' format results: -24-
Apr-13-18:23:38.flv. Default=.flv
94
record_unique on; # on|off. Appends timestamp to recorded files. Otherwise the same file is re-written
each time. Default=-off
95
record_append off; # on|off. When turned on recorder appends new data to the old file with no gap.
Default=off
96
record_lock off; # on|off. When turned on recorded file gets locked with 'fcntl' call. Default=off.
97
record_notify off; # on|off. Toggles sending "NetStream.Record.Start" and "NetStream.Record.Stop" status
messages. Default=off.
98
# record_max_size 128K; # Set maximum file size of the recorded files. No default.
99
# record_max_frames 200; # Sets maximum number of video frames per recorded file. No default.
100
# record_interval 15m; # Restart recording after this number of (milli)seconds/minutes. Zero means no
delay. Default=off.
101
# recorder name {} # Create recorder{} block. Multiple recorders can be created withing single application.
Example:
102
recorder audio {
103
record audio;
104
record_suffix .audio.flv;
105
record_path /var/livestream/recordings/audio;
106
}
107
## This will automatically convert your .flv recordings to mp4 when the stream/recording is stopped.
WARNING! This will cause high CPU usage!! ##
108
# exec_record_done ffmpeg -i $path -f mp4 /var/livestream/recordings/$basename.mp4;
109
## RELAYING ##
110
## PULLING STREAMS ##
111
## For more info please visit: https://github.com/arut/nginx-rtmp-module/wiki/Directives#relay ##
112
## pull url [key=value]* # Creates pull relay. A stream is pulled from remote machine and becomes
available locally. ##
113
## URL Syntax: [rtmp://]host[:port][/app[/playpath]] ##
114
# pull rtmp://cdn.example.com/live/ch?id=1234 name=channel_a; # This is an example. Visit above url
for more info.
115
# session_relay off; # on|off. On=relay is destroyed when connection is closed. Off=relay is destroyed
when stream is closed. Default=off.
116
## PUSHING STREAMS ##
117
## Push has the same syntax as pull. Unlike pull, push directive publishes stream to remote server. ##
118
## This will push the stream from incoming /live application to these below to create HLS and/or Dash
streams, or to record or transcode automatically. ##
119
## Only enable HLS push if you are not using ABS options earlier or by using the encoder application ##
120
push rtmp://localhost/hls; # Remember to enable an app called HLS! Disable it if you use transcoding
using variable bitrates.
121
push rtmp://localhost/dash; # Remember to create the app dash! It is safe to disable this if you're not
interested in using Dash.
122
## PUSH TO TRANSCODER APPLICATION ##
123
# push rtmp://localhost/encoder; # Enable this if you're not using the ABS options earlier but want to use a
different application for it.
124
## PUSH TO RECORDER APPLICATION ##
125
# push rtmp://localhost/recorder; # Enable this if you want to record your stream and convert it to MP4
automatically when you stop the stream.
126
## STREAM RELAYING TO EXTERNAL PARTY'S ##
127
## Push can also be used to re-stream your stream to other platforms. You can do this directly or use an
additional application for this. ##
128
## Using an additional local application allows you to set up variables if you prefer. Or you can choose to
push to 3rd party directly. ##
129
## YOUTUBE PUSH OPTIONS ##
130
# push rmtp://localhost/youtube; # Uncomment this to use application on localhost (MUST BE SPECIFIED).
OR simply use the following line:
131
# push rtmp://a.rtmp.youtube.com/live2 /YOUR-LIVE-STREAM-KEY; # Your RTMP stream will be pushed
as it is to Youtube as an RTMP stream.
132
## TWITCH PUSH OPTIONS ##
133
# push rmtp://localhost/twitch; # Uncomment this to use application on localhost (MUST BE SPECIFIED).
OR simply use the following line:
134
# push rtmp://live-ams.twitch.tv/app /live_YOUR-LIVE-STREAM-KEY; # Your RTMP stream will be pushed
as it is to Twitch as an RTMP stream.
135
} # CLOSING BRACKET /live
136
## RECORDING APP ##
137
application recorder {
138
live on;
139
recorder all {
140
record all; # off|all|audio|video|keyframes|manual. These options speak for themselves.
141
record_path /var/livestream/recordings; # Folder location that will be used to store the recordings.
142
record_suffix all-%d-%b-%y-%T.flv; # Added to recorded filenames. Example uses 'strftime' format results: -
24-Apr-13-18:23:38.flv. Default=.flv
143
record_unique on; # on|off. Appends timestamp to recorded files. Otherwise the same file is re-written
each time. Default=-off
144
record_append off; # on|off. When turned on recorder appends new data to the old file with no gap.
Default=off
145
record_lock on; # on|off. When turned on recorded file gets locked with 'fcntl' call. Default=off.
146
record_notify off; # on|off. Toggles sending "NetStream.Record.Start" and "NetStream.Record.Stop"
status messages. Default=off.
147
# record_max_size 4096M; # Set maximum file size of the recorded files. No default.
148
# record_max_frames 200; # Sets maximum number of video frames per recorded file. No default.
149
# record_interval 15m; # Restart recording after this number of (milli)seconds/minutes. Zero means no
delay. Default=off.
150
} # Recorder closing bracket
151
## This will automatically convert your .flv recordings to mp4 when the stream/recording is stopped.
WARNING! This will cause high CPU useage!! ##
152
# exec_record_done ffmpeg -i $path -f mp4 /var/livestream/recordings/$basename.mp4;
153
}
154
## ENCODER APPLICATION ##
155
application encoder {
156
live on;
157
exec ffmpeg -i rtmp://localhost/encoder/$name -async 1 -vsync -1
158
-c:v libx264 -acodec copy -b:v 256k -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf
23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/encoder/$name_low
159
-c:v libx264 -acodec copy -b:v 768k -vf "scale=720:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf
23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/encoder/$name_mid
160
-c:v libx264 -acodec copy -b:v 1024k -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf
23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/encoder/$name_high
161
-c:v libx264 -acodec copy -b:v 1920k -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -
crf 23 -g 60 -hls_list_size 0 -f flv rtmp://localhost/hls/encoder/$name_higher
162
-c copy -f flv rtmp://localhost/hls/encoder/$name_src;
163
}
164
## HLS APPLICATION ##
165
application hls { # We enabled pushing stream from 'live' application to 'hls' we need to define it of course.
166
live on; # on|off. Enables this application and allowing live streaming to it. Default=on.
167
hls on; # on|off. Toggles HLS on or off for this application.
168
hls_type live; # live|event. Live plays from the current live position. Event plays from the start of the
playlist. Default=live.
169
hls_path /var/livestream/hls; # Location to store the video fragment files. Will be created if it doesn't exist.
170
hls_fragment 5s; # Sets HLS fragment length in seconds or minutes. Default=5s.
171
hls_playlist_length 30s; # Sets HLS playlist length in seconds or minutes. Default=30s.
172
hls_sync 2ms; # Timestamp sync threshold. Prevents crackling noise after conversion from low-res
(1KHz) to high-res(90KHz). Default=2ms.
173
hls_continuous off; # on|off. In this mode HLS sequence number is started from where it stopped last time.
Old fragments are kept. Default=off.
174
hls_nested on; # on|off. In this mode a subdirectory of hls_path is created for each stream. Default=off.
175
hls_cleanup on; # on|off. Nginx cache manager process removes old HLS fragments and playlist files from
hls_path. Default=on.
176
hls_fragment_naming system; # system = use system time. sequential = use increasing integers. timestamp =
use stream timestamp. Default=sequential.
177
hls_fragment_slicing plain; # plain|aligned. Plain: switch fragment when target duration is reached. Aligned:
switch fragment when incoming timestamp is a
178
# multiple of fragment duration. Makes it possible to generate identical fragments on different nginx
instances. Default=plain.
179
## ENCRYPTION KEYS ## !! Only works if you have certificates defined in your HTTP server block (Usually a
seperate file in /sites-available/yourfile.conf)
180
# hls_keys on; # on|off. Enables HLS encryption. AES-128 method is used to encrypt the HLS fragments.
Requires ssl module. Default=off.
181
hls_key_path /var/livestream/keys; # Sets the directory where auto-generated HLS keys are saved. Default=
hls_path.
182
# hls_key_url https://foo.bar/keys; # Sets url for HLS key file entries. When empty it assumes hls_path.
Default= empty.
183
# hls_fragments_per_key 100; # Sets the number of HLS fragments encrypted with the same key. 0 means
only one key is created at the publish start and
184
# all fragments within the session are encrypted with this key. Default=0.
185
## HLS_VARIANT - Used for variable bitrate streaming. Please read: https://github.com/arut/nginx-rtmp-
module/wiki/Directives#hls_variant ##
186
## When hls_variant suffix is matched on stream name then variant playlist is created for the current stream
with all entries specified by hls_variant
187
## directives in current application. Stripped name without suffix is used as variant stream name. The
original stream is processed as usual.
188
## Optional parameters following the suffix are appended to EXT-X-STREAM-INF in m3u8 playlist. See HLS
spec 3.3.10. EXT-X-STREAM-INF for full list.
189
# hls_variant _low BANDWIDTH=288000; # _low - Low bitrate, sub-SD resolution
190
# hls_variant _mid BANDWIDTH=448000; # _mid - Medium bitrate, SD resolution
191
# hls_variant _high BANDWIDTH=1152000; # _high - Higher-than-SD resolution
192
# hls_variant _higher BANDWIDTH=2048000; # _higher - High bitrate, HD 720p resolution
193
# hls_variant _src BANDWIDTH=4096000; # _src - Source bitrate, source resolution
194
}
195
## MPEG-DASH APPLICATION ##
196
application dash { # These variables will be used since we enabled pushing /live stream to this application.
197
live on; # on|off. Enables this application and allowing live streaming to it. Default=on.
198
dash on; # on|off. Toggles MPEG-DASH on the current application.
199
dash_path /var/livestream/dash; # Location to store the video fragment files. Will be created if it doesn't
exist.
200
dash_fragment 5s; # Sets DASH fragment length in seconds or minutes. Default= 5s.
201
dash_playlist_length 30s; # Sets MPEG-DASH playlist length. Defaults= 30s.
202
dash_nested on; # on|off. In this mode a subdirectory of dash_path is created for each stream. Default=off.
203
dash_cleanup on; # on|off. Nginx cache manager process removes old DASH fragments and playlist files
from dash_path. Default=on.
204
}
205
## YOUTUBE - Only required if you decide to re-stream using this application ##
206
#application youtube {
207
# live on;
208
# record off;
209
# allow publish 127.0.0.1;
210
# deny publish all;
211
# push rtmp://a.rtmp.youtube.com/live2 /YOUR-LIVE-STREAM-KEY;
212
#}
213
## TWITCH APPLICATION - Only required if you decide to re-stream using this application ##
214
# application twitch {
215
# live on;
216
# record off;
217
# allow publish 127.0.0.1;
218
# deny publish all;
219
# push rtmp://live-ams.twitch.tv/app /live_YOUR-LIVE-STREAM-KEY;
220
# }
221
}
222
}
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.
1
sudo wget -O /var/www/YOURDOMAIN/web/js/videojs/latest.zip
https://github.com/videojs/video.js/releases/download/v7.11.4/video-js-7.11.4.zip
1
cd /var/www/YOURDOMAIN/web/js/videojs
2
sudo unzip latest.zip
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.
1
sudo wget -O /var/www/YOURDOMAIN/web/videoplayer.html
https://raw.githubusercontent.com/ustoopia/Nginx-config-for-livestreams-ABS-HLS-ffmpeg-transc-
/main/var/www/yourdomain/videoplayer.html
1
nano /var/www/yourdomain/web/videoplayer.html
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset=utf-8 />
5
<title>LiveStream</title>
6
<link href="https://YOURDOMAIN/js/videojs/video-js.css" rel="stylesheet">
7
</head>
8
<body>
9
<center>
10
<video-js id="live_stream" class="vjs-default-skin" controls preload="auto" width="1280" height="auto">
11
<source src="https://YOURDOMAIN/hls/stream/index.m3u8" type="application/x-mpegURL">
12
</video-js>
13
<script src='https://YOURDOMAIN/js/videojs/video.js'></script>
14
<script src="https://YOURDOMAIN/js/videojs/videojs-http-streaming.js"></script>
15
<script>
16
var player = videojs('live_stream');
17
</script>
18
</center>
19
</body>
20
</html>
You have to edit 4 lines in there. Make sure Nginx can access all the files.
1
sudo chown -R www-data:www-data /var/www/yourdomain/web /var/www/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.
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.
1
sudo nano /etc/nginx/nginx.conf
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:
1
sudo systemctl restart nginx
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.
1
wget -O /var/www/YOURDOMAIN/web/abs.html https://raw.githubusercontent.com/ustoopia/Nginx-
config-for-livestreams-ABS-HLS-ffmpeg-transc-/main/var/www/yourdomain/abs.html
1
nano /var/www/YOURDOMAIN/web/abs.html
<!DOCTYPE html>
<html>
<head>
<title>LiveStream</title>
</head>
<body>
<center>
URL">
</video-js>
<script src='https://YOURDOMAIN/js/videojs/video.js'></script>
<script src="https://YOURDOMAIN/js/videojs/videojs-http-streaming.js"></s
cript>
<script>
</script>
</center>
</body>
</html> Copy
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!!
1
sudo apt install php7.4 php7.4-common php7.4-fpm php7.4-gd php7.4-mysql php7.4-imap php7.4-cli php7.4-
cgi php7.4-curl php7.4-intl php7.4-pspell php7.4-sqlite3 php7.4-tidy php7.4-xmlrpc php7.4-xsl php-
memcache php-imagick php7.4-zip php7.4-mbstring php-pear mcrypt imagemagick libruby memcached
It’s also an option to install PHP 8 instead. It’s really easy we only have to add the Ondrej
repository. Like this:
1
sudo apt install software-properties-common
2
sudo add-apt-repository ppa:ondrej/php
3
sudo apt update
1
sudo apt install php8.0-fpm -y
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.
1
sudo apt install -y php8.0 php8.0-common php8.0-gd php8.0-mysql php8.0-imap php8.0-cli php8.0-cgi
php8.0-curl php8.0-intl php8.0-pspell php8.0-sqlite3 php8.0-tidy php8.0-xmlrpc php8.0-xsl php8.0-zip
php8.0-mbstring php8.0-soap php8.0-opcache php-pear php-imagick php-apcu
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.
1
server {
2
# . . . your other code
3
location ~ \.php$ {
4
include snippets/fastcgi-php.conf;
5
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
6
# fastcgi_pass unix:/run/php/php8.0-fpm.sock;
7
}
8
}
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.
1
sudo sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/7.4/fpm/php.ini
1
sudo sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g' /etc/php/8.0/fpm/php.ini
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.
1
sudo sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 1024M/g' /etc/php/7.4/fpm/php.ini
2
sudo sed -i 's/max_input_time = 60/max_input_time = 300/g' /etc/php/7.4/fpm/php.ini
3
sudo sed -i 's/max_execution_time = 30/max_execution_time = 60/g' /etc/php/7.4/fpm/php.ini
4
sudo sed -i 's/;date.timezone =/date.timezone = "Europe\/Amsterdam"/g' /etc/php/7.4/fpm/php.ini
1
sudo systemctl restart php7.4-fpm nginx
2
sudo systemctl restart php8.0-fpm nginx
1
sudo apt install mariadb-server mariadb-client phpmyadmin -y
Create a symlink to PhpMyAdmin in your web root folder so you can use it
1
sudo ln -s /usr/share/phpmyadmin /var/www/yourhostname/web/phpmyadmin
1
mysql_secure_installation
Choose whatever suits your needs. Restart mysql once it is done. (sudo systemctl restart
mysql) and login to Mysql like this:
1
mysql -u root -p
Use the following commands to set up a database and database user for WordPress.
1
CREATE DATABASE wordpress;
2
grant all privileges on wordpress.* TO 'wordpress'@'localhost' identified by 'YourPassword';
3
FLUSH PRIVILEGES;
Since we’re at the mysql cli anyways, we might as well do this as well:
1
use mysql;
2
update user set plugin='' where User='root';
3
FLUSH PRIVILEGES;
4
quit;
1
sudo systemctl restart mysql
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
1
wget -O /var/www/YOURHOSTNAME/web/latest.zip https://wordpress.org/latest.zip
2
cd /var/www/YOURHOSTNAME/web
3
sudo unzip latest.zip
4
sudo mv /var/www/YOURHOSTNAME/web/wordpress/* /var/www/YOURHOSTNAME/web/
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.
1
sudo chown -R www-data: /var/www/yourhostname/web
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.
autoplay="true or false"
style="width:1280px;height:720px"
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://y
toplay="false"] Copy
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.
This example sets the player’s resolution in pixels. In this case 1280x720 with a gray border
of 1 pixel.
ps://yourdomain/static/video/index.m3u8"] Copy
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.