All-in-one RTMP/HLS streaming platform with adaptive bitrate easily deploayble via docker/caprover Includes NGINX configuration for optimal RTMP/HLS streaming, backend HTTP/WebSocket server to allow comunication from NGINX to the frontend, and realtime (moderatable) chat capabilities.
This application is designed to be deployed via docker, using the included Dockerfile
First step is to clone the repo and edit the file inside /path/to/repo/frontend
named .env.production
git clone repoUrl
cd /path/to/repo/frontend
nano .env.production
inside this file you will setup the administrator password that the client will use
VUE_APP_ADMIN_PASSWORD=SuperSafeAdminPassword
To run the app, we need to build a docker image giving it a name (only lowercase allowed)
cd /path/to/repo
docker build -t a-name .
while it's building we can create and set the permissions to the host folder where our streams will be recorded
mkdir /host/rec/folder
sudo chmod 777 /host/rec/folder
when the build process completes we can run the newly created image mapping the container's ports to the host's ports and the container's volume to the host's folder we just created
docker run -d -p 80:80 -p 1935:1935 -p 8080:8080 -v /host/rec/folder:/var/rec a-name
This app can be deployed to caprover wich is an:
Automated Scalable PaaS Package (automated Docker+nginx) - Heroku on Steroids
more info here
Deploying on caprover via the online dashboard is really easy:
- clone this repo
- set your administration password in:
/path/to/repo/frontend/.env.production
- add all the contents of the repo folder to a
.tar
archive - create a local directory where to save you stream and set permissions:
mkdir /host/rec/folder
sudo chmod 777 /host/rec/folder
- name and create a new app, tick the "Has persistent data" checkbox
- manage your newly created app, set the following port mappings
- 1935:1935 (RTMP port)
- 8080:8080 (express and socket.io port)
- add a persistent directory:
- Path in App:
/var/rec
- click on:
Set specific host path
- input the path of the directory created on step 4
- Path in App:
- go to the
deployment
tab, upload the.tar
archive created at step 3 - enjoy.
If you want to stream from anywhere outside your local network you will need to set up a port forwarding rule for port 1935
(RTMP) on your router to your host machine.
Same goes for port 80
, if you want your app to be reachable from outside your local network.
- Settings/Stream
- Service: custom
- Server: rtmp://yourHostName/live
- Stream Key: yourStreamName must be filled, no spaces/special characters allowed
You can watch the stream via the included client, just by visiting: http(s)://yourHostName
, the included client will show information about stream status in real time including number of connected clients, stream duration, and will also offer a very basic real time chat.
If you dont want to use the included client, you can find the .m3u8
playlists at these endpoints.
The backend of this application consists of 3 services:
-
NGINX configuration with RTMP and HLS module.
Features:
-
Adaptive bitrate streaming
The incoming stream is transcoded into 4 different variants:
- src: passthrough, same as incoming stream
- high: standard HD 720p variant
- mid: standard HQ 480p variant
- low: standard LQ 240p variant
each variant is then added to the master playlist. the played variant will automatically be determined (client-independent) according to the connection's bitrate.
The master playlist is served at:
http(s)://yourHostName/hls/yourStreamName.m3u8
if you want to test the individual hls variants you can open the relative playlist from:
http(s)://yourHostName/hls/yourStreamName_$variant$/index.m3u8
where
$variant$ can be any of the names mentioned above. -
Stream recording
By default NGINX is configured to record your live streams, those will be saved to the containers internal path:
/var/rec
-
-
Express and Socket.io
This application handles the comunication between the NGINX webserver hosting the RTMP stream and the hosted front-end
This application consists mainly of 2 servers:
- Express: Listens for POST requests coming from NGINX at specific endpoints, determines stream status and name.
- Socket.io: handles the comunication with the front-end and offers a chat server
Messaging protocol
Library: socket.io
Server => Client
Stream Status update
Event: 'status_update' emitted at every socket connection, and at a fixed interval (default: 1000ms).
{ streamName: String, online: Boolean, duration: Number, spectators: Number }
Joined Chat and Joined Ok
-
joined_chat
broadcasted when a client joins the chat with the 'join_chat' message. -
join_ok
is sent back to the client requesting to join the chat.
Payload for both is the chatUser object:
{ id: Number, //socket.id name: String, color: String, admin: Boolean, banned: Boolean }
Chat Message
Event
chat_message
emitted when a client sends a chat message via the 'send_chat_message' event.Payload is the chatMessage object:
{ id: Number, chatUser: { id: Number, name: String, color: String, admin: Boolean }, message: String, muted: Boolean }
Left Chat
Event
left_chat
emitted when a socket client, that previously joined the chat, disconnects.Payload is the chatUser object.
Banned User
Event
banned_user
is emitted when a socket client, with administrative priviledges, emits the 'ban_user' event.Payload is a chatUser object.
Muted Message
Event
muted_message
is emitted when a socket client, with administrative priviledges, emits the 'mute_message' event.Payload is a chatMessage object.
Client => Server
Join Chat
Expected event name is:
join_chat
, with the expected payload being a chatUser object.Administrator authentication is handled by the client
Send Chat Message
Expected event name is:
send_chat_message
Expected payload is a chatMessage object.
Ban User
Expected event name is:
ban_user
.Expected payload is: a chatUser object.
Mute Message
Expected event name is:
mute_message
.Expected payload is: a chatMessage object.
The frontend of this application consists of a SPA, built using the Vue.js framework, styling, inspired by the: "material design" specifications is handled by the Vuetify.js module, while the playback of the live stream and VoDs is handled by video.js player; the comunication with the backend is handled by a Socket.io client.
Application's status is managed using a Vuex store, mutations and actions due to the asyncronous nature of the operations.
A great part of the responsibilities of this application lie on the frontend. It has to be able to:
- handle comunication with the backend
- play both HLS live streams and
.mp4
VoDs - handle user authentication and authorization
- store user session
- look good both on desktop and mobile
- be able to "add to the homescreen"