Social media services have become quite the control freaks recently… Media in general has taken a dive over the years too and I think now is about time we empower ourselves with this guide to create a live streaming service of our own, that has a player on a webpage to display the media, but also it can push the stream to any other third-party service that we choose… So, without further ado – let’s begin!
For this tutorial I have chosen to use a micro service with Ubuntu 20.10 on a load-balanced virtual machine inside the Google Cloud Platform. This will cost around $9(USD) a month, which I am happy to pay for this project as I will be utilising scaling options and other services as I develop this solution to serve a broad audience. However, and with less complexity, you can just as well use a local machine or microcontroller of your choice, if you don’t plan to publicise this service to many, or wish to forego the monthly financial overhead.
Service Prerequisites
Firstly, make sure we have the right tools to make the service!
sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev
Let’s go and grab the latest (stable) version of Nginx and extract it directly into our current directory.
At the time of this post, the latest stable version is nginx-1.18.0:
wget http://nginx.org/download/nginx-1.18.0.tar.gz && tar xzf nginx-1.18.0.tar.gz
Once that is done, we grab the zLib compression library and extract it similarly.
At the time of this post, the latest version is zlib-1.2.11:
wget http://www.zlib.net/zlib-1.2.11.tar.gz && tar xzf zlib-1.2.11.tar.gz
Now, we need to grab the RTMP module for Nginx, and clone to our current directory:
git clone git://github.com/sergey-dryabzhinsky/nginx-rtmp-module
Configure Service
So far so good?! Let’s change directory to the extracted Nginx application folder. Configure and make the service using the RTMP and HTTP SSL modules with zLib, then install that package to the system.
cd nginx-1.18.0/
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module --with-zlib=../zlib-1.2.11
make
sudo make install
Given everything compiled and returned without error, we should now set up STunnel in case we want to comply with any third-party services that might require this (Facebook, etc.). STunnel will convert unencrypted RTMP to encrypted RTMPS.
sudo apt-get install stunnel4 -y
Edit STunnel auto-boot configuration:
sudo nano /etc/default/stunnel4
Add this line or change the existing ‘ENABLE’ from ‘0’ to ‘1’:
ENABLE=1
Edit STunnel configuration
sudo nano /etc/stunnel/stunnel.conf
…Add the following lines to include Facebook live:
pid = /var/run/stunnel4/stunnel.pid
output = /var/log/stunnel4/stunnel.log
setuid = stunnel4
setgid = stunnel4
socket = r:TCP_NODELAY=1
socket = l:TCP_NODELAY=1
debug = 4
[fb-live]
client = yes
accept = 1936
connect = live-api-s.facebook.com:443
verifyChain = no
#[yt-live]
#client = yes
#accept = 1935
#connect = x.rtmps.youtube.com:443
#verifyChain = no
Enable STunnel after boot:
sudo systemctl enable stunnel4.service
Restart STunnel:
sudo systemctl restart stunnel4.service
Now, let’s replace our Nginx configuration file by removing it and then creating a new one:
sudo rm /usr/local/nginx/conf/nginx.conf && sudo nano /usr/local/nginx/conf/nginx.conf
…Add the following lines:
#user nobody;
worker_processes auto;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
rtmp {
server {
listen 1935;
listen [::]:1935 ipv6only=on;
ping 30s;
notify_method get;
chunk_size 4000;
# Configure HTTP Live Streaming (HLS).
application hls {
# Disable Nginx from consuming stream as RMTP.
deny play all;
# Enabled.
hls on;
# Directory to store HLS fragments.
hls_path /usr/local/nginx/html/hls;
# Fragment definitions.
hls_fragment 5s;
hls_playlist_length 60s;
}
# Handles incoming live stream requests.
application live {
# Enabled.
live on;
# Send to 'hls' application.
push rtmp://localhost:1935/hls;
# Send to 'pushit' application.
#push rtmp://localhost:1935/pushit;
# Send to 'recorder' application.
#push rtmp://localhost:1935/recorder;
}
# TV mode: one publisher, many subscribers.
application tv {
# Enabled.
live on;
# Publish only from localhost.
allow publish 127.0.0.1;
allow play all;
deny publish all;
# Recording enabled.
record all;
record_path /usr/local/nginx/html/rec;
record_max_size 100M;
record_unique off;
}
# Uses FFMPEG to manipulate the output of the live stream before sending it on to 'small',
# located beneath this application.
application big {
# Enabled.
live on;
# On every pusblished stream run this command (FFMPEG),
# with substitutions: $app/${app}, $name/${name} for application & stream name.
#
# FFMPEG can do anything with the stream like video/audio
# transcoding, resizing, altering container/codec params etc.
# Multiple exec lines can be specified.
exec /usr/bin/ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32 -f flv rtmp://localhost:1935/small/${name};
}
# Video with reduced resolution comes here from FFMPEG.
application small {
# Enabled.
live on;
}
application recorder {
# Enabled.
live on;
exec /usr/bin/ffmpeg -re -i /usr/local/nginx/html/rec/test.mp4 -c:v libx264 -c:a libfaac -ar 44100 -ac 1 -f flv rtmp://localhost:1935/live;
}
# Video on demand (FLV).
application vod {
play /usr/local/nginx/html/vod/flv;
}
# Video on demand (MP4).
application vod2 {
play /usr/local/nginx/html/vod/mp4;
}
# This application sends the live stream on to all social distribution network.
application pushit {
# Enabled.
live on;
# forward stream to third-party service (e.g. Facebook)
#push rtmp://localhost:1936/rtmp/ <-- PUT STATIC STREAM KEY AFTER THE SLASH
}
# Pull streams from remote machines to play locally
#application pullit {
# Enabled.
#live on;
#pull rtmp://rtmp3.example.com;
#}
}
}
# Landing page...
http {
access_log /usr/local/nginx/html/log/access-streaming.log;
error_log /usr/local/nginx/html/log/error-streaming.log;
include mime.types;
default_type application/octet-stream;
directio 512;
sendfile off;
keepalive_timeout 65;
tcp_nopush on;
server {
listen 80;
server_name localhost;
# Display HTTP error pages.
error_page 500 502 503 504 /50x.html;
error_page 404 /404.html;
location = /50x.html {
# Disable cache.
add_header 'Cache-Control' 'no-cache';
# CORS setup.
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length';
# allow CORS preflight requests.
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
types {
application/dash+xml mpd;
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root html;
}
# RTMP control.
location /control {
rtmp_control all;
}
# Serve HLS fragments.
location /hls {
alias /usr/local/nginx/html/hls;
}
# RTMP statistics.
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# you can move stat.xsl to a different location
root /usr/local/ngnix/html/stat.xsl;
}
}
}
# Multi-worker streaming.
rtmp_auto_push on;
rtmp {
server {
listen 1937;
chunk_size 4000;
# TV mode: one publisher, many subscribers.
application tv {
live on;
}
}
}
Once you have saved that, we should make sure we have all the folders created that we stated within the above Nginx configuration file. Let’s head over to the correct folder and make these extra directories:
cd /usr/local/nginx/html/
sudo mkdir hls/ log/ vod/
cd vod/
sudo mkdir flv/ mp4/
Now we need to copy the stats file to the directory set in our Nginx configuration file, and then start the Nginx service:
sudo cp ~/nginx-rtmp-module/stat.xsl /usr/local/nginx/html/ && sudo /usr/local/nginx/sbin/nginx
Configure Ports
Given Nginx starts up without error, and considering this is a completely fresh built server, before we try to access the web page we need to ensure ports on our firewall are configured. In this example we use the Uncomplicated Firewall (UFW).
sudo ufw allow OpenSSH && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw allow 1935/tcp && sudo ufw allow 1936/tcp && sudo ufw reload && sudo ufw enable
Create a Landing Page
At present you should be seeing a simple webpage by Nginx when you navigate your browser to your server IP address through port 80 (HTTP). However, you may want to display a player on this webpage instead to watch your livestreams. Thankfully, I have created a simple website that you can use to get you going, then, as with the Nginx.conf file above, you may customise to fit your requirement. For now, let’s download and apply the website template that I’ve created for you:
cd ~
git clone git://github.com/Enter7ainer/drumnbase
Now we can change in to the drumnbase directory and copy all of the contents to the Nginx directory serving our website:
cd drumnbase/
sudo cp * /usr/local/nginx/html/ -R
Test Livestream
Enter your server information, and any stream key of your choosing, in to Open Broadcaster Software (OBS) Studio or respective software solution that you’re using to broadcast a live stream through from your local machine, following this convention:
Stream server = rtmp://<IP ADDRESS OF YOUR SERVER>/live
Stream key = testbroadcast
If your connection is successful, go and check the website through your web browser, and within a few moments you should be able to click play on the embedded video player to see your stream! Alternatively, you may also input your stream server and stream key in to VLC Media Player, or an equivalent RMTP media player, following this convention:
rtmp://<IP ADDRESS OF YOUR SERVER>/live/testbroadcast
You should now have your own operational livestream service, with the ability to stream to multiple other services that accept RTMP or RTMPS. Congratulations! …But why stop there?! You may have noticed we created a ‘vod’ directory earlier, this could house videos that can be played on demand!
Conclusion
As you can see, it won’t take you much time to create your own live streaming service. Just be sure to factor in any scaling requirements at the start, if you plan to publicise this service, as the more viewers you have the greater the bandwidth you’ll need to serve the traffic! This could become quite costly if you do not plan ahead of time, you have been warned.
Finally, should you be having trouble with this tutorial and prefer to learn through visual assistance, I have created a video going through all of the steps above to create this service from scratch! Of course, I’d be very much grateful for a like, comment and subscribe! You might even like to become a patron. <3