For a while now, I’ve been interested in self-hosting Twitch.tv-like streaming services to provide some of the features that Twitch doesn’t (i.e. private streaming) and for the fun of DIY. This resulted in an example authentication framework for arut’s excellent nginx-rtmp-module. However, one of the gross things about self-hosting a service like this was dealing with the web playback component. Until recently, the only real options for this were any of a variety of Flash-based web players, some of which worked better than others. Unfortunately, there were no good non-Flash options, which was a real drag, since nobody in their right mind likes Flash web components.

Recently, Dailymotion released hls.js, a JS library implementing HLS. Since nginx can output an incoming RTMP stream as HLS, we can use hls.js to get around the Flash problem.

Note: most of the following configs were shamelessly stolen from nginx-rtmp-module examples

All of this assumes an existing install of nginx with the rtmp module.

First, we need to configure nginx. Before that, though, we need a place for the HLS fragments and playlists to go. I used /tmp/hls. Note that the directory should exist before nginx tries doing stuff with it.

rtmp {
        server {
				# default RTMP port
                listen 1935;
                chunk_size 4000;
                
                application hls {
                        live on;
                        hls on;
						# directory for HLS fragments and m3u8 to be pushed to/served from
                        hls_path /tmp/hls;
                }
        }
}

http {
        server {
                listen 80;

				# Serve web player (assumes single-page super basic setup, change to whatever)
                location / {
                        root /usr/local/www/public;
                }
                
				# Serve HLS fragments
                location /hls {
                        types {
                                application/vnd.apple.mpegurl m3u8;
                                video/mp2t ts;
                        }
                        root /tmp;
                        add_header Cache-Control no-cache;
                }
        
        }
}

events { worker_connections 1024; }

The RTMP URL for clients to stream at in this example is rtmp://[hostname]/hls/ with an arbitrary stream key. Obviously, you’d want to configure this with some sort of authentication solution in a multi-user use case.

Now, we’ve got to set up the web player. Grab hls.js or hls.min.js from the Github project and drop them in (will probably need to create first) the dist directory in whatever directory you intend to serve the HTML containing the web player from.

At the most basic level, that HTML file would look like this:

<HTML>                                                                                                                              
   <HEAD>
      <TITLE>
         html5 rtmp demo
      </TITLE>
   </HEAD>
<BODY>
    <script src="dist/hls.js"></script>
    <video id="video"></video>
    <script>
        if(Hls.isSupported()) {
                var video = document.getElementById('video');
                var hls = new Hls();
                hls.loadSource('http://[hostname]/hls/[streamkey].m3u8');
            hls.attachMedia(video);
                hls.on(Hls.Events.MANIFEST_PARSED,function() {
                video.play();
        });
        }
    </script>
</BODY>
</HTML>

The stream key should match whatever key was used to stream with. hls in http://[hostname]/hls/[streamkey].m3u8 assumes the location HLS fragments are being served from is location /hls: if changed, change accordingly.