NGINX – How to Set Up RTMP to HLS with Video.js Buffering

linuxnginxrtmpUbuntu

I am trying to setup a streaming server for a local school that runs on their local network. I setup Ubuntu 18.04 on a virtual machine and gave it it's own dedicated 1 Gigabit connection on the server. It has 4 processing cores and 8GB of memory. All the switches in the school are 1 Gigabit. I use a GoPro sending 480p video over RTMP to the server (running NGINX and rtmp module). On the same server I have the video.js configured to view the stream.

When we run 5-10 browsers with the stream going it seems just fine. Today they opened 50-60 which is about the max of what we will ever see. When we did that, slowly some of the browsers started "buffering" and would do it over and over. It almost looks like every 2 seconds it does it really quick.

I checked the server and it doesn't appear that CPU, Memory, Disk, or even Network is constrained. Although VMware does show the server getting to about 120mbps and peaking.

This is what my nginx.conf looks like. What can I do to get this to perform. I can't figure out if it's simply just too much bandwidth or if it's something along the lines of new players being turned on are trying to play from the playlist and holding up the process of the RTMP to HLS conversion. Hopefully that train of thought makes sense.

server {
        listen 8080;

        location / {
            # 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 /var/stream;
        }
}
rtmp {
        server {
                listen 1935;
                chunk_size 4096;

                application live {

                        record off;
                        live on;
                        # Turn on HLS
                        hls on;
                        hls_path /var/stream/hls/;
                        hls_fragment 3;
                        hls_playlist_length 60;
                        # disable consuming the stream from nginx as rtmp
                        deny play all;

                   }
        }

}

Here is the html for the player:

<video-js id="live_stream" class="video-js vjs-fluid vjs-default-skin vjs-big-play-centered" controls preload="auto" autoplay="true" width="auto" height="auto" poster="http://192.168.5.8/poster.jpg">
<source src="http://192.168.5.8:8080/hls/.m3u8" type="application/x-mpegURL">
    <p class='vjs-no-js'>
      To view this video please enable JavaScript, and consider upgrading to a web browser that
      <a href='https://videojs.com/html5-video-support/' target='_blank'>supports HTML5 video</a>
    </p>
</video-js>

Best Answer

This ended up being a matter of bandwidth. The GoPro feed is 480p to the RTMP server. Each consecutive stream was drawing about 3Mbps of bandwidth. What I ended up doing was using FFMPEG to transcode the video to a 500Kbps stream. The quality drops a little bit, but it is still very useable. With the same number of sessions going (60) the server is only using about 8Mbps of bandwidth. I will also add that the FFMPEG transcoding adds about 45-60 seconds of delay before the stream starts in the browser. I am sure this can be corrected, I just haven't taken it that far because for my scenario it doesn't matter.

Here is the RTMP section of my nginx.conf file:

rtmp {
        server {
                listen 1935;
                chunk_size 4096;

                application live {

                        record off;
                        live on;
                        # Turn on HLS
                        hls on;
                        hls_path /var/stream/hls/;
                        hls_fragment 3;
                        hls_playlist_length 60;
                        # disable consuming the stream from nginx as rtmp
                        allow play all;

                        exec ffmpeg -i rtmp://192.168.1.75/$app/$name -acodec copy -c:v libx264 -preset veryfast -profile:v baseline -vsync cfr -s 640x360 -b:v 400k -maxrate 500k -bufsize 500k -threads 0 -r 30 -f flv rtmp://192.168.1.75/mobile/$;
                   }

                application mobile {
                        allow play all;
                        live on;
                        hls on;
                        hls_nested on;
                        hls_path /var/stream/hls-mobile/;
                        hls_fragment 10s;
                }
        }

}

Then in my HTML5 viewer I use this code:

    <video-js id="live_stream" class="video-js vjs-fluid vjs-default-skin vjs-big-play-centered" controls preload="auto" autoplay="true" width="auto" height="auto" poster="http://192.168.5.8/poster.jpg">
<source src="http://192.168.5.8:8080/hls-mobile/index.m3u8" type="application/x-mpegURL">
    <p class='vjs-no-js'>
      To view this video please enable JavaScript, and consider upgrading to a web browser that
      <a href='https://videojs.com/html5-video-support/' target='_blank'>supports HTML5 video</a>
    </p>
</video-js>