Desktop and media video streaming server with OBS, nginx, RTMP, HLS and DASH
Update: The article was extended with DASH and RTMP endpoint authentication after the initial publication.
Due to current circumstances, video streaming and desktop sharing has found an enormous increase in usage. This post contains a quick guide to set up an independent video streaming server with nginx and RTMP, which OBS Studio clients can stream to. Viewers can connect via a website and receive the stream in their browser HTML5 players. OBS is so unbelievably easy to use, it's fantastic for remote tutorials and application/game streaming where you wish to use multiple media inputs (microphone, desktop, webcam) and have good control of the output composition.
The following guide is based on Debian 10 (Buster), nginx 1.14.2 and OBS Studio 25.
Step 1: Install and set up nginx with RTMP module
Install the nginx package and the RTMP module with apt install nginx-full libnginx-mod-rtmp
. Configure the nginx.conf
http
section similar to the one shown below, e.g. enable gzip for certain file types and add/delete entries from the available TLS versions. On the top level, add the rtmp
section. See the nginx-rtmp-module directives reference for more information on the individual keywords.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
Then create the directories /var/www/hls
and /var/www/dash
, and let www-data
own it. With this configuration, nginx now receives incoming RTMP streams on port 1935 (automatically derived from rtmp://SERVER_NAME/live) and stores each stream as HLS and DASH fragments. Note that the module does not support RTMPS (RTMP over TLS), so everything is sent unencrypted (source: Debian module repository). For authentication, see section authentication.
Next, set up a virtual host in /etc/nginx/sites-enabled
, for example use the default
vhost that exists already. In it, the following server
content is needed. Be aware this server is configured to use HTTPS with a certificate. You can obtain automatically issued free certificates with certbot (package certbot
) for your hostname, but that's out of scope for now.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
The HLS/DASH fragments are now available at https://SERVER_NAME/hls and /dash. CORS configuration is needed if you embed this resource from other domains.
Step 2: Add HTML streaming client
If you finished step one, nginx is now ready to receive RTMP streams and to distribute these packets via HLS and DASH. Next, we will add an HTML page which offers a video client for viewers.
The page is pretty simple, just save it as index.html
in /var/www/html/
. You also need the video.js and dash.js libraries. video.min.js
and video-js.min.css
you can get from the video.js Github releases page, videojs-http-streaming.min.js
is available at unpkg.com/@videojs and dash.all.min.js
at the DASH Github repository.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Replace the SERVER_NAME placeholders with the hostname your nginx is reachable at (server_name
configuration entry). The STREAM_KEY URI part must also be replaced, the value being the name of the stream you wish to view in this video element. We will come back to this value in part three. In case you are sure to never need to serve Apple devices (iOS), you can also remove the HLS player entirely.
Step 3: Configure OBS
Now it is time to connect the streaming client. OBS supports a wide set of video streaming services like Twitch or YouTube Gaming out of the box, but we will use our own!
Start by going into the settings menu. In the stream settings dialog, use custom from the drop down service list. Two empty input fields will appear. In the server field enter rtmp://SERVER_NAME/live, the second field stream key being the name of the stream. For example, if you use mylivestream as the key, the video source URLs created by nginx (used in the HTML code above) would be https://SERVER_NAME/hls/mylivestream.m3u8 and https://SERVER_NAME/dash/mylivestream.mpd.
Don't forget to also configure your audio and video settings. I reached good performance with a video bitrate of 90% of my total upload speed, setting the resolution to native full HD 1920x1080 (no downscaling) and 30 FPS. If available, enable hardware-based video encoding (e.g. NVIDIA NVENC).
As soon as you hit the "Start Streaming" button, OBS connects to the RTMP endpoint, sends its packets and nginx transforms them into HLS/DASH fragments. A viewer can then visit the URL where the index.html
from step two is delivered at and receives a web page with the video players!
During recording/streaming the stats window allows you to view statistics about upload size and dropped frames. You might also notice that there might be a delay of up to 30 seconds between sending and receiving, which might be acceptable in most scenarios, but may interfere with your workflow if viewers have a possibility to comment your content live during the streaming session. There are ways to reduce this delay, with the cost of increasing traffic (more smaller packets mean more HTTP requests by clients). In a test I could reduce the time difference between sending and receiving to six seconds (key frame every two frames in OBS, HSL fragment size of 2 and list length 10). If you need realtime bidirectional conferencing, jitsi might be a better alternative.
Authentication for RTMP streamers
To not let everybody who knows the RTMP endpoint use the stream, we will also add authentication. This is done by utilising the notification feature of the nginx RTMP module. Extend your RTMP config in nginx as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Now every time someone connects with RTMP to begin streaming on port 1935, the http://localhost/rtmp_auth
URL will be called by nginx, with all parameters delivered by the OBS client. Next, the corresponding authentication route needs to be added. In this example we are using a very simple parameter check built into nginx. Here, if the client provides the token parameter with the MyVerySecurePassword123 value, nginx returns with code 200, which means authentication success. All other cases are returned with 403 error code (unauthorized). This works with every route returning 200/403 though, for example with another webapp which talks to a database, or an embedded Lua script (content_by_lua_block or content_by_lua_file).
1 2 3 4 5 6 7 8 9 10 11 |
|
If you already have a configuration for the localhost virtual server, add the snippet to this server block.
As the last thing, you need to adapt OBS to match the new config! Simply change your streaming server to rtmp://SERVER_NAME/live?token=MyVerySecurePassword123. The server will now accept your requests, but deny any others which have either no token parameter in their request, or which provide a wrong token (denied means a return code of 403).
Background
For reference, here's a list of terms mentioned in this article:
- RTMP: Real-Time Messaging Protocol, the sender's streaming protocol. Originally developed by Macromedia (Flash, now Adobe) and pretty old standard. Yet still used widely by many video streaming providers.
- HLS: HTTP Live Streaming, by Apple. Takes an incoming stream (e.g. RTMP) and chunks it into fragments, creating downloadable files for clients. HLS clients understand the .m3u8 playlist with its .ts entries and continously request the next fragments, generating an uninterrupted output stream.
- MPEG-DASH: Dynamic Adaptive Streaming over HTTP, similar to HLS, but standardised by the MPEG and backed by the DASH Industry Forum. It should therefore be your preferred protocol, because it's an open standard an browsers are more likely to implement DASH into the Media Source Extensions (MSE) API. Uses fragmenting as well, creating .m4v and .m4a files which are referenced in an .mpd playlist.
- SRT: Secure Reliable Transport, developed by Haivision and backed by the SRT Alliance community. New transport standard published under an open source license, with encryption, packet loss detection and dynamically adapting transport as core features. Support in OBS is experimental, referencing the state of implementation in ffmpeg in their forums.
Thanks for reading and happy streaming!
By the way, the OBS client used in this setup is running inside my virtualised Windows machine with PCI passthrough. I have blogged about this in the past: "Using a PCI graphics card in KVM/QEMU on Debian Stretch". All peripheral stuff like a webcam or USB microphone for OBS can be passed as USB host devices as well.