player, Video HTML streaming

Cross browser video player

I tried using videojs for playing encrypted HLS video in all common browsers. It didn’t go well. I had a lot of trouble to get it working in Safari browser on MacOS (seeking). The best thing I did was writing my own video player. Here is an example of it.

Example of index.html:

<html>
<head>
   <title></title>
   <meta charset="utf-8" />
   <link rel="stylesheet" type="text/css" href="test.css" />
   <script src="Static/js/hls.js"></script>
   <script src="Static/js/test.js"></script>
</head>
<body>
   <div id ="video-container">
      <video id ="video" autoplay>
         <source src ="hls/master.m3u8" type="application/x-mpegURL" />
         Your browser does not support the HLS video or the video tag.
      </video>
   </div>
</body>
</html>

Example of test.js file:

var INIT_VOLUME = 0.3;
 
function isFullscreen() {
   if (document.fullscreenElement || document.webkitCurrentFullScreenElement ||
      document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {
      return true;
   }
   return false;
}
function requestFullscreen(el) {
   if (el.requestFullscreen) {
      el.requestFullscreen();
   }
   else if (el.mozRequestFullScreen) {
      el.mozRequestFullScreen();
   }
   else if (el.webkitRequestFullScreen) {
      el.webkitRequestFullScreen();
   }
   else if (el.webkitRequestFullscreen) {
      el.webkitRequestFullscreen();
   }
   else if (el.msRequestFullscreen) {
      el.msRequestFullscreen();
   }
}
function cancelFullscreen() {
   if (document.exitFullscreen) {
      document.exitFullscreen();
   }
   else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
   }
   else if (document.webkitCancelFullScreen) {
      document.webkitCancelFullScreen();
   }
   else if (document.msExitFullscreen) {
      document.msExitFullscreen();
   }
}
function fromSeconds(value) {
   var hours = Math.floor(value / 3600);
   value = value - hours * 3600;
   var minutes = Math.floor(value / 60);
   value = value - minutes * 60;
   var hoursStr = ("0" + hours).slice(-2);
   var minutesStr = ("0" + minutes).slice(-2);
   var secondsStr = ("0" + Math.round(value)).slice(-2);
   if (hours > 0)
      return hoursStr + ":" + minutesStr + ":" + secondsStr;
   return minutesStr + ":" + secondsStr;
}
function createControls() {
   $('#video-container').append('< div id="video-controls">' +
      '<button id="play"><i class="icon icon-play"></i></button>' +
      '<button id="volume-btn"><i class="icon icon-volume"></i></button>' +
      '<span id="current-time">00:00</span>' +
      '< div id="progress">' +
      '< div id="progress-buf"></div>' +
      '< div id="progress-bar"></div>' +
      '</div>' +
      '<span id="total-time"></span>' +
      '<button id="fullscreen"><i class="icon icon-fullscreen"></i></button>' +
      '<button id="levels">auto</button>' +
      '</div>' +
      '< div class="loader"></div>');
   var volumeBar = $('< div id="volume" style="display:none">< div id="volume-bar">< div id="volume-value" style="height: ' + INIT_VOLUME * 100 + '%"></div></div></div>');
   $('#video-controls').append(volumeBar);
   $('#volume-btn').click(function () {
      if (volumeBar.is(":visible")) {
         $(this).removeClass('active');
         volumeBar.hide();
      }
      else {
         $(this).addClass('active');
         volumeBar.show();
      }
   });
}
function createLevelButtons(hls) {
   var levels = hls.levelController._levels;
   var list = $('<ul id="video-levels" style="display:none">');
   for (var i = 0; i < levels.length; i++) {
      list.append('<li level="' + i + '">' + levels[i].height + 'p</li>');
   }
   list.append('<li level="-1" style="display:none">auto</li>');
   $('#video-controls').append(list);
   $("body").on("click""#video-levels li"function () {
      var level = parseInt($(this).attr("level"));
      hls.currentLevel = level; //immediate (nextLevel for later change)
      $('#levels').text($(this).text());
      list.hide();
      $("#video-levels li").show();
      $(this).hide();
      $('#levels').removeClass('active');
      $(".loader").show();
      $('#progress-buf').width(0);
   });
   $('#levels').click(function () {
      if (list.is(":visible")) {
         $(this).removeClass('active');
         list.hide();
      }
      else {
         $(this).addClass('active');
         list.show();
      }
   });
}
 
$(document).ready(function () {
   var video = document.getElementById('video');
   if (Hls.isSupported()) {
      createControls();
      var vid = $("#video");
      var getLoadedPercentage = function () {
         try {
            var range = 0;
            var bf = video.buffered;
            var time = video.currentTime;
            while (!(bf.start(range) <= time && time <= bf.end(range))) {
               range += 1;
            }
            return bf.end(range) / video.duration;
         }
         catch (e) {
            return 0;
         }
      };
      $(window).resize(function () {
         $('#video-controls').width(vid.width());
      });
      vid.bind('webkitfullscreenchange mozfullscreenchange msfullscreenchange fullscreenchange resize orientationchange'function () {
         $('#video-controls').width(vid.width());
      });
      var hls = new Hls();
      hls.loadSource('hls/master.m3u8');
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED, function () {
         createLevelButtons(hls);
         video.volume = INIT_VOLUME;
         video.play();
      });
      hls.on(Hls.Events.BUFFER_APPENDED, function () {
         var loadEndPercentage = getLoadedPercentage();
         $('#progress-buf').width((loadEndPercentage * 100+ "%");
      });
      $('#play').click(function () {
         if (video.paused)
            video.play();
         else
            video.pause();
      });
      $('#volume-bar').click(function (e) {
         var y = e.pageY - this.offsetTop - $(this).parent()[0].offsetTop - $(this).parent().parent()[0].offsetTop;
         var position = 1 - (y / $('#volume-bar').height());
         video.volume = position;
         $('#volume-value').css('height', (position * 100+ '%');
      });
      $('#progress').click(function (e) {
         var x = e.pageX - this.offsetLeft - $(this).parent()[0].offsetLeft;
         var position = x / $('#progress').width();
         video.currentTime = position * video.duration;
         $('#progress-bar').css('width', (position * 100+ '%');
      });
      $('#fullscreen').click(function () {
         if (isFullscreen()) {
            cancelFullscreen();
            $('body').removeClass("fullscreen");
            $(this).removeClass('active');
         }
         else {
            var videoContainerEl = videoContainer[0];
            requestFullscreen(videoContainerEl);
            $('body').addClass("fullscreen");
            $(this).addClass('active');
         }
      });
      vid.on('timeupdate'function () {
         var percentage = video.currentTime / video.duration;
         $('#progress-bar').css('width', (100 * percentage) + '%');
         $('#current-time').text(fromSeconds(video.currentTime));
      });
      vid.on('loadedmetadata'function () {
         $('#total-time').text(fromSeconds(video.duration));
         $('#video-controls').width(vid.width());
      });
      vid.on('playing'function () {
         $('#play').find('i').removeClass('icon-play').addClass('icon-pause');
         $(".loader").hide();
      });
      vid.on('seeking'function () {
         $(".loader").show();
      });
      vid.on('seeked'function () {
         $(".loader").hide();
      });
      vid.on('pause'function () {
         $('#play').find('i').removeClass('icon-pause').addClass('icon-play');
      });
      vid.on('loadstart'function (event) {
         $(".loader").show();
      });
      vid.on('canplay'function (event) {
         $(".loader").hide();
      });
      vid.on('waiting'function (event) {
         $(".loader").show();
      });
   }
   else {
      video.setAttribute("controls""controls");
   }
});
 

Example of test.css file:

.icon-fullscreen {
      background-imageurl('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDQ0IDQ0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA0NCA0NCIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KICA8cGF0aCBkPSJtMjIsMGMtMTIuMiwwLTIyLDkuOC0yMiwyMnM5LjgsMjIgMjIsMjIgMjItOS44IDIyLTIyLTkuOC0yMi0yMi0yMnptLTQsMzBoMWMwLjYsMCAxLDAuNCAxLDF2MmMwLDAuNi0wLjQsMS0xLDFoLThjLTAuNiwwLTEtMC40LTEtMXYtOGMwLTAuNiAwLjQtMSAxLTFoMmMwLjYsMCAxLDAuNCAxLDF2MWMwLDAuNCAwLjUsMC43IDAuOSwwLjRsMi0yYzAuNC0wLjQgMS0wLjQgMS40LDBsMS40LDEuNGMwLjQsMC40IDAuNCwxIDAsMS40bC0yLDJjLTAuMywwLjMtMC4xLDAuOCAwLjMsMC44em0yLTE3YzAsMC42LTAuNCwxLTEsMWgtMWMtMC40LDAtMC43LDAuNS0wLjQsMC45bDIsMmMwLjQsMC40IDAuNCwxIDAsMS40bC0xLjQsMS40Yy0wLjQsMC40LTEsMC40LTEuNCwwbC0yLTJjLTAuMy0wLjMtMC45LTAuMS0wLjksMC40djFjMCwwLjYtMC40LDEtMSwxaC0yYy0wLjYsMC0xLTAuNC0xLTF2LThjMC0wLjYgMC40LTEgMS0xaDhjMC42LDAgMSwwLjQgMSwxdjEuOWgwLjF6bTE0LDIwYzAsMC42LTAuNCwxLTEsMWgtOGMtMC42LDAtMS0wLjQtMS0xdi0yYzAtMC42IDAuNC0xIDEtMWgxYzAuNCwwIDAuNy0wLjUgMC40LTAuOWwtMi0yYy0wLjQtMC40LTAuNC0xIDAtMS40bDEuNC0xLjRjMC40LTAuNCAxLTAuNCAxLjQsMGwyLDJjMC4zLDAuMyAwLjksMC4xIDAuOS0wLjR2LTFjMC0wLjYgMC40LTEgMS0xaDJjMC42LDAgMSwwLjQgMSwxdjguMWgtMC4xem0wLTE0YzAsMC42LTAuNCwxLTEsMWgtMmMtMC42LDAtMS0wLjQtMS0xdi0xYzAtMC40LTAuNS0wLjctMC45LTAuNGwtMiwyYy0wLjQsMC40LTEsMC40LTEuNCwwbC0xLjQtMS40Yy0wLjQtMC40LTAuNC0xIDAtMS40bDItMmMwLjMtMC4zIDAuMS0wLjktMC40LTAuOWgtMWMtMC42LDAtMS0wLjQtMS0xdi0yYzAtMC42IDAuNC0xIDEtMWg4YzAuNiwwIDEsMC40IDEsMXY4LjFoMC4xeiIgZmlsbD0iI0ZGRkZGRiIvPgo8L3N2Zz4K');
   }
 
   .icon-play {
      background-imageurl('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAzMDAgMzAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzMDAgMzAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNMTUwLDBDNjcuMTU3LDAsMCw2Ny4xNjIsMCwxNTBjMCw4Mi44NDEsNjcuMTU3LDE1MCwxNTAsMTUwczE1MC02Ny4xNTksMTUwLTE1MEMzMDAsNjcuMTYyLDIzMi44NDMsMCwxNTAsMHogICAgIE0yMDUuODQ2LDE1OC4yNjZsLTg2LjU1Nyw0OS45NzFjLTEuMzIsMC43NjUtMi43OTksMS4xNDQtNC4yNzIsMS4xNDRjLTEuNDczLDAtMi45NDktMC4zNzktNC4yNzQtMS4xNDQgICAgYy0yLjY0LTEuNTI1LTQuMjY5LTQuMzQ3LTQuMjY5LTcuNDAyVjEwMC44OWMwLTMuMDUzLDEuNjMxLTUuODgsNC4yNjktNy40MDJjMi42NDgtMS41MjgsNS45MDYtMS41MjgsOC41NTEsMGw4Ni41NTcsNDkuOTc0ICAgIGMyLjY0NSwxLjUzLDQuMjc0LDQuMzUyLDQuMjY5LDcuNDAyQzIxMC4xMiwxNTMuOTE2LDIwOC40OTQsMTU2Ljc0MSwyMDUuODQ2LDE1OC4yNjZ6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==');
   }
 
   .icon-pause {
      background-imageurl('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAzMDAuMDAzIDMwMC4wMDMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMwMC4wMDMgMzAwLjAwMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTJweCIgaGVpZ2h0PSI1MTJweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTE1MC4wMDEsMGMtODIuODM4LDAtMTUwLDY3LjE1OS0xNTAsMTUwYzAsODIuODM4LDY3LjE2MiwxNTAuMDAzLDE1MCwxNTAuMDAzYzgyLjg0MywwLDE1MC02Ny4xNjUsMTUwLTE1MC4wMDMgICAgQzMwMC4wMDEsNjcuMTU5LDIzMi44NDYsMCwxNTAuMDAxLDB6IE0xMzQuNDEsMTk0LjUzOGMwLDkuNDk4LTcuNywxNy4xOTgtMTcuMTk4LDE3LjE5OHMtMTcuMTk4LTcuNy0xNy4xOTgtMTcuMTk4VjEwNS40NiAgICBjMC05LjQ5OCw3LjctMTcuMTk4LDE3LjE5OC0xNy4xOThzMTcuMTk4LDcuNywxNy4xOTgsMTcuMTk4VjE5NC41Mzh6IE0xOTguOTU1LDE5NC41MzhjMCw5LjQ5OC03LjcwMSwxNy4xOTgtMTcuMTk4LDE3LjE5OCAgICBjLTkuNDk4LDAtMTcuMTk4LTcuNy0xNy4xOTgtMTcuMTk4VjEwNS40NmMwLTkuNDk4LDcuNy0xNy4xOTgsMTcuMTk4LTE3LjE5OHMxNy4xOTgsNy43LDE3LjE5OCwxNy4xOThWMTk0LjUzOHoiIGZpbGw9IiNGRkZGRkYiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K');
   }
 
   .icon-volume {
      background-imageurl('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAzMDAuMDAzIDMwMC4wMDMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMwMC4wMDMgMzAwLjAwMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTJweCIgaGVpZ2h0PSI1MTJweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTE1MC4wMDUsMEM2Ny4xNjQsMCwwLjAwMSw2Ny4xNTksMC4wMDEsMTUwYzAsODIuODM4LDY3LjE2MiwxNTAuMDAzLDE1MC4wMDMsMTUwLjAwM1MzMDAuMDAyLDIzMi44MzgsMzAwLjAwMiwxNTAgICAgQzMwMC4wMDIsNjcuMTU5LDIzMi44NDQsMCwxNTAuMDA1LDB6IE0xOTcuMjQ1LDEwMC41MDZsMjcuMzU4LTE1Ljc5M2MzLjA4MS0xLjc3OSw3LjAxNi0wLjcyNCw4Ljc5NSwyLjM1NSAgICBjMS43NzcsMy4wODEsMC43MjQsNy4wMTgtMi4zNTUsOC43OTVsLTI3LjM1OCwxNS43OTVjLTMuMDc5LDEuNzc5LTcuMDE2LDAuNzI0LTguNzk1LTIuMzU1ICAgIEMxOTMuMTE0LDEwNi4yMjIsMTk0LjE2NywxMDIuMjg4LDE5Ny4yNDUsMTAwLjUwNnogTTE2NS44NjUsMjI0LjYyNGMtMS40MDMsMC42ODUtMi45MTgsMS4wMjItNC40MjUsMS4wMjIgICAgYy0yLjIxOCwwLTQuNDItMC43MzEtNi4yMzUtMi4xNTNMMTIyLjA0LDE5Ny41MWMtMS41MzUsMC4zNzktMy4xMzYsMC42MDctNC43OTYsMC42MDdIODQuMDEgICAgYy0xMC44OTgsMC0xOS43MzUtOC44MzYtMTkuNzM1LTE5LjczMnYtNTcuNTZjMC0xMC44OTYsOC44MzctMTkuNzM1LDE5LjczNS0xOS43MzVoMzMuMjM1YzEuOTMyLDAsMy43OTIsMC4yOSw1LjU2MSwwLjgwOSAgICBsMzIuMzk3LTI1LjM4OWMzLjA0Mi0yLjM4NCw3LjE4Ny0yLjgyNCwxMC42Ni0xLjEzMWMzLjQ3NSwxLjY5MSw1LjY4Myw1LjIxOCw1LjY4Myw5LjA4OHYxMzEuMDY5aDAuMDAyICAgIEMxNzEuNTQ3LDIxOS40MDYsMTY5LjMzNywyMjIuOTMzLDE2NS44NjUsMjI0LjYyNHogTTIzNC4yMzcsMjEzLjM4M2MtMS43NzksMy4wODEtNS43MTQsNC4xMzQtOC43OTUsMi4zNTVsLTI3LjM1OC0xNS43OTMgICAgYy0zLjA3OS0xLjc4Mi00LjEzMi01LjcxNi0yLjM1NS04Ljc5OGMxLjc3OS0zLjA4MSw1LjcxNi00LjEzNCw4Ljc5NS0yLjM1NWwyNy4zNTgsMTUuNzk1ICAgIEMyMzQuOTYxLDIwNi4zNjIsMjM2LjAxMSwyMTAuMjk5LDIzNC4yMzcsMjEzLjM4M3ogTTIzMS4xMzMsMTU2Ljc5NUgxOTkuNTVjLTMuNTU2LDAtNi40MzctMi44NzktNi40MzctNi40MzcgICAgYzAtMy41NTYsMi44ODItNi40MzcsNi40MzctNi40MzdoMzEuNTg1YzMuNTU2LDAsNi40MzcsMi44ODIsNi40MzcsNi40MzdDMjM3LjU3MywxNTMuOTE2LDIzNC42ODgsMTU2Ljc5NSwyMzEuMTMzLDE1Ni43OTV6IiBmaWxsPSIjRkZGRkZGIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==');
   }
 
   .icon {
      background-size30px;
      width30px;
      height30px;
   }
 
   #video-wrapper {
      positionabsolute;
      top0;
      left0;
      right0;
      bottom0;
      z-index2147483646;
      max-width100%;
      marginauto;
   }
 
   #video {
      height50%;
      positionabsolute;
      top0;
      left0;
      right0;
      bottom0;
      z-index2147483645;
      max-width100%;
      marginauto;
      background-colorblack;
   }
 
   #video-controls {
      positionabsolute;
      top75%;
      left0;
      right0;
      bottom0;
      z-index2147483647;
      max-width100%;
      marginauto;
      height40px;
      margin-top0;
      background-color#1d35e4;
      colorwhite;
   }
 
   body {
      text-aligncenter;
   }
 
      html,
      body,
      body.fullcreen > main {
         height100%;
      }
 
   #video-container {
      heightcalc(100% - 4px);
      margin0;
      padding0;
   }
 
   main {
      heightcalc(100% - 64px);
   }
 
   #progress {
      height10px;
      widthcalc(100% - 301px);
      background-colorwhite;
      displayinline-block;
      margin-left5px;
      cursorpointer;
   }
 
      #progress > #progress-bar {
         height100%;
         background-color#366ac7;
         width0;
         margin-top-10px;
      }
 
      #progress > #progress-buf {
         height100%;
         background-color#d8d4d4;
         width0;
      }
 
   #video-controls > span {
      width60px;
      displayinline-block;
      height40px;
      padding-top10px;
   }
 
   #video-controls > span > p{
      margin:0;
   }
 
   #video-controls > span#total-time {
      margin-left5px;
   }
 
   #video-controls > button {
      width40px;
      height40px;
      bordernone;
      padding0;
      background-colortransparent;
      floatleft;
   }
 
      #video-controls > button.active {
         background-color#19abd0;
      }
 
      #video-controls > button:hover {
         background-color#19abd0;
      }
 
      #video-controls > button#levels {
         floatright;
         width50px;
      }
 
      #video-controls > button#fullscreen {
         floatright;
      }
 
   body.fullcreen > header,
   body.fullcreen > main > .progress {
      displaynone;
   }
 
   body.fullscreen #video,
   body.fullscreen #video-wrapper {
      heightcalc(100% - 40px);
      bottom40px;
   }
 
   body.fullscreen #video-controls {
      width100% !important;
      topcalc(100% - 40px);
   }
 
   #video-levels {
      positionabsolute;
      right40px;
      bottom25px;
      background-color#19abd0;
      padding5px;
      width50px;
      z-index2147483647;
   }
 
      #video-levels > li {
         cursorpointer;
      }
 
         #video-levels > li:hover {
            background-color#1d35e4;
         }
 
   #volume {
      positionabsolute;
      width40px;
      height100px;
      background-color#19abd0;
      bottom40px;
      left41px;
   }
 
   #volume-bar {
      background-colorwhite;
      widthcalc(100% - 20px);
      heightcalc(100% - 20px);
      margin10px;
      positionrelative;
      cursorpointer;
   }
 
   #volume-value {
      background-color#366ac7;
      bottom0;
      positionabsolute;
      width100%;
   }
 
   .loader {
      border16px solid #f3f3f3/* Light grey */
      border-top16px solid #1d35e4/* Blue */
      border-radius50%;
      width120px;
      height120px;
      animationspin 2s linear infinite;
      positionabsolute;
      topcalc(50% - 60px);
      leftcalc(50% - 60px);
      z-index2147483647;
   }
 
   @keyframes spin {
      0% {
         transformrotate(0deg);
      }
 
      100% {
         transformrotate(360deg);
      }
   }

You can use it. There can be minor problems, because I parsed this code from my working solution and I don’t want to share everything. Please replace “< div” with “<div”. The wordpress formatting is shit.