Video HTML streaming, videojs

Videojs – Quality selection (HLS)

This solution is for HLS – encrypting and playing video (videojs).

We can add quality options for videojs player. We have to add this javascript library:
videojs-contrib-quality-levels.js

Videojs options

It will iterate through video sources in master.m3u8 file and show buttons for them.

Example in typescript (javascript):

export function run() {
      var selectedBitrate = "auto";
      var lastPosition = 0;
      var qLevels = [];
 
      var options = {
         inactivityTimeout: 0,
         controls: true,
         autoplay: true,
         preload: "auto",
         notSupportedMessage: "Please use different browser (Mozzila Firefox, Google Chrome, Safari, Microsoft Edge)"
      };
 
      var srcOptions = {
         src: 'api/doc/' + DOCUMENT_ID + '/Video/master.m3u8',
         type: 'application/x-mpegurl'
      };
 
      var player = videojs('vid', options);
      player.qualityLevels().on('addqualitylevel'function (event) {
         event.qualityLevel.enabled = selectedBitrate === "auto" || event.qualityLevel.height.toString() === selectedBitrate;
      });
 
      player.on("loadedmetadata"function () {
         var qualityLevels = player.qualityLevels();
         for (var i = 0; i < qualityLevels.length; i++) {
            var quality = qualityLevels[i];
 
            if (quality.height != undefined && $.inArray(quality.height, qLevels) === -1) {
               qLevels.push(quality.height);
 
               if (!$('.quality_ul').length) {
                  var h = '< div class="quality_setting vjs-menu-button vjs-menu-button-popup vjs-control vjs-button">' +
                     '<button class="vjs-menu-button vjs-menu-button-popup vjs-button" type="button" aria-live="polite" aria-disabled="false" title="Quality" aria-haspopup="true" aria-expanded="false">' +
                     '<span aria-hidden="true" class="vjs-icon-cog"></span>' +
                     '<span class="vjs-control-text">Quality</span></button>' +
                     '< div class="vjs-menu"><ul class="quality_ul vjs-menu-content" role="menu"></ul></div></div>';
 
                  $(".vjs-fullscreen-control").before(h);
               } else {
                  $('.quality_ul').empty();
               }
 
               qLevels.sort();
               qLevels.reverse();
 
               var j = 0;
 
               $.each(qLevels, function (i, val) {
                  $(".quality_ul").append('<li class="vjs-menu-item" tabindex="' + i + '" role="menuitemcheckbox" aria-live="polite" aria-disabled="false" aria-checked="false" bitrate="' + val +
                     '"><span class="vjs-menu-item-text">' + val + 'p</span></li>');
 
                  j = i;
               });
 
               $(".quality_ul").append('<li class="vjs-menu-item vjs-selected" tabindex="' + (j + 1+ '" role="menuitemcheckbox" aria-live="polite" aria-disabled="false" aria-checked="true" bitrate="auto">' +
                  '<span class="vjs-menu-item-text">Auto</span></li>');
            }
         }
         player.currentTime(lastPosition);
      });
 
 
      $("body").on("click"".quality_ul li"function () {
         $(".quality_ul li").removeClass("vjs-selected");
         $(".quality_ul li").prop("aria-checked""false");
 
         $(this).addClass("vjs-selected");
         $(this).prop("aria-checked""true");
 
         selectedBitrate = $(this).attr("bitrate");
         lastPosition = player.currentTime();
         var levels = player.qualityLevels();
         var level = levels[levels.selectedIndex].height;
         if (selectedBitrate !== level.toString() && (selectedBitrate !== "auto" || levels.selectedIndex !== (levels.length - 1))) {
            player.src(srcOptions);
         }
         else {
            for (var i = 0; i < levels.length; i++)
               levels[i].enabled = selectedBitrate === "auto" || levels[i].height.toString() === selectedBitrate;
         }
      });
 
      player.on('fullscreenchange'function () {
         var levels = player.qualityLevels();
         if (selectedBitrate === "auto" && player.isFullscreen() && levels.selectedIndex !== (levels.length - 1)) {
            lastPosition = player.currentTime();
            player.src(srcOptions);
         }
      });
      player.src(srcOptions);
   }

Note: Please replace “< div” with “<div”. The formating is pissing me off!