mistplayers.html5 = {
  name: "HTML5 video player",
  mimes: ["html5/application/vnd.apple.mpegurl","html5/application/vnd.apple.mpegurl;version=7","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],
  priority: MistUtil.object.keys(mistplayers).length + 1,
  isMimeSupported: function (mimetype) {
    return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
  },
  isBrowserSupported: function (mimetype,source,MistVideo) {
    
    //check for http/https mismatch
    if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
      if ((location.protocol == "file:") && (MistUtil.http.url.split(source.url).protocol == "http:")) {
        MistVideo.log("This page was loaded over file://, the player might not behave as intended.");
      }
      else {
        MistVideo.log("HTTP/HTTPS mismatch for this source");
        return false;
      }
    }
    
    if (mimetype == "html5/application/vnd.apple.mpegurl") {
      var android = MistUtil.getAndroid();
      if (android && (parseFloat(android) < 7)) { 
        MistVideo.log("Skipping native HLS as videojs will do better");
        return false;
      }
    }
    
    
    var support = false;
    var shortmime = mimetype.split("/");
    shortmime.shift();
    
    try {
      shortmime = shortmime.join("/");
      //works for mp4 but not for webm
      function translateCodec(track) {
        function bin2hex(index) {
          return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
        }
        switch (track.codec) {
          case "AAC":
            return "mp4a.40.2";
          case "MP3":
            return "mp4a.40.34";
          case "AC3":
            return "ec-3";
          case "H264":
            return "avc1."+bin2hex(1)+bin2hex(2)+bin2hex(3);
          case "HEVC":
            return "hev1."+bin2hex(1)+bin2hex(6)+bin2hex(7)+bin2hex(8)+bin2hex(9)+bin2hex(10)+bin2hex(11)+bin2hex(12);
          default:
            return track.codec.toLowerCase();
        }
      }

      var codecs = {};
      var playabletracks = {};
      var hassubtitles = false;
      for (var i in MistVideo.info.meta.tracks) {
        if (MistVideo.info.meta.tracks[i].type != "meta") {
          codecs[translateCodec(MistVideo.info.meta.tracks[i])] = MistVideo.info.meta.tracks[i];
        }
        else if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
      }
      var container = mimetype.split("/")[2];

      source.supportedCodecs = [];
      for (var i in codecs) {
        //i is the long name (like mp4a.40.2), codecs[i] is the track meta, codecs[i].codec is the short name (like AAC)
        var s = test(i);
        if (s) {
          source.supportedCodecs.push(codecs[i].codec);
          playabletracks[codecs[i].type] = 1;
        }
      }
      function test(codecs) {
        var v = document.createElement("video");
        if ((v) && (typeof v.canPlayType == "function")) {
          var result;
          switch (shortmime) {
            case "video/webm": {
              //if codecs are included here, at least chrome reports it as not working, even though it does. So we'll just assume it will play if webm returns maybe.
              result = v.canPlayType(shortmime);
              break;
            }
            case "video/mp4":
            case "html5/application/vnd.apple.mpegurl":
            default: {
              result = v.canPlayType(shortmime+";codecs=\""+codecs+"\"");
              break;
            }
          }
          if (result != "") {
            return result;
          }
        }
        return false;
      }


      if (hassubtitles) {
        //there is a subtitle track, check if there is a webvtt source
        for (var i in MistVideo.info.source) {
          if (MistVideo.info.source[i].type == "html5/text/vtt") {
            playabletracks.subtitle = 1;
            break;
          }
        }
      }
      
      support = MistUtil.object.keys(playabletracks);
    } catch(e){}
    return support;
  },
  player: function(){
    this.onreadylist = [];
  },
  mistControls: true
};
var p = mistplayers.html5.player;
p.prototype = new MistPlayer();
p.prototype.build = function (MistVideo,callback) {
  var shortmime = MistVideo.source.type.split("/");
  shortmime.shift();
  var video = document.createElement("video");
  
  //TODO verify: not required if player is loaded from same domain as it should always be when not in dev mode?
  video.setAttribute("crossorigin","anonymous");//required for subs, breaks ogg?
  
  video.setAttribute("playsinline",""); //iphones. effin' iphones.
  
  var source = document.createElement("source");
  source.setAttribute("src",MistVideo.source.url);
  video.source = source;
  video.appendChild(source);
  source.type = shortmime.join("/");
  
  //apply options
  var attrs = ["autoplay","loop","poster"];
  for (var i in attrs) {
    var attr = attrs[i];
    if (MistVideo.options[attr]) {
      video.setAttribute(attr,(MistVideo.options[attr] === true ? "" : MistVideo.options[attr]));
    }
  }
  if (MistVideo.options.muted) {
    video.muted = true; //don't use attribute because of Chrome bug: https://stackoverflow.com/questions/14111917/html5-video-muted-but-stilly-playing?rq=1
  }
  if (MistVideo.options.controls == "stock") {
    video.setAttribute("controls","");
  }
  if (MistVideo.info.type == "live") {
    video.loop = false;
  }
  
  if (("Proxy" in window) && ("Reflect" in window)) {
    var overrides = {
      get: {},
      set: {}
    };
    
    MistVideo.player.api = new Proxy(video,{
      get: function(target, key, receiver){
        if (key in overrides.get) {
          return overrides.get[key].apply(target, arguments);
        }
        var method = target[key];
        if (typeof method === "function"){
          return function () {
            return method.apply(target, arguments);
          }
        }
        return method;
      },
      set: function(target, key, value) {
        if (key in overrides.set) {
          return overrides.set[key].call(target,value);
        }
        return target[key] = value;
      }
    });
    
    if (MistVideo.source.type == "html5/audio/mp3") {
      overrides.set.currentTime = function(){
        MistVideo.log("Seek attempted, but MistServer does not currently support seeking in MP3.");
        return false;
      }
      
    }
    if (MistVideo.info.type == "live") {
      
      overrides.get.duration = function(){
        //this should indicate the end of Mist's buffer
        var buffer_end = 0;
        if (this.buffered.length) {
          buffer_end = this.buffered.end(this.buffered.length-1)
        }
        var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
        return buffer_end + time_since_buffer - MistVideo.player.api.liveOffset;
      };
      overrides.set.currentTime = function(value){
        var offset = value - MistVideo.player.api.duration;
        
        if (offset > 0) {offset = 0;} //don't allow positive numbers, as Mist will interpret them as unix timestamps
        
        MistVideo.player.api.liveOffset = offset;
        
        MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
        
        var params = {startunix:offset};
        if (offset == 0) {
          params = {};
        }
        
        MistVideo.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,params));
      }

      MistUtil.event.addListener(video,"progress",function(){
        MistVideo.player.api.lastProgress = new Date();
      });
      MistVideo.player.api.lastProgress = new Date();
      MistVideo.player.api.liveOffset = 0;
      
      
      MistUtil.event.addListener(video,"pause",function(){
        MistVideo.player.api.pausedAt = new Date();
      });
      overrides.get.play = function(){
        return function(){
          if ((MistVideo.player.api.paused) && (MistVideo.player.api.pausedAt) && ((new Date()) - MistVideo.player.api.pausedAt > 5e3)) {
            video.load();
            MistVideo.log("Reloading source..");
          }
          
          return video.play.apply(video, arguments);
        }
      };
      
      if (MistVideo.source.type == "html5/video/mp4") {
        var otherdurationoverride = overrides.get.duration;
        overrides.get.duration = function(){
          return otherdurationoverride.apply(this,arguments) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
        }
        overrides.get.currentTime = function(){
          return this.currentTime - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
        }
        overrides.get.buffered = function(){
          var video = this;
          return {
            length: video.buffered.length,
            start: function(i) { return video.buffered.start(i) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3; },
            end: function(i) { return video.buffered.end(i) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3; }
          }
        };
      }
    }
    else {
      if (!isFinite(video.duration)) {
        var duration = 0;
        for (var i in MistVideo.info.meta.tracks) {
          duration = Math.max(duration,MistVideo.info.meta.tracks[i].lastms);
        }
        overrides.get.duration = function(){
          if (isFinite(this.duration)) { return this.duration; }
          return duration * 1e-3;
        }
      }
    }
    
  }
  else {
    MistVideo.player.api = video;
  }
  MistVideo.player.api.setSource = function(url) {
    if (url != this.source.src) {
      this.source.src = url;
      this.load();
    }
  };
  MistVideo.player.api.setSubtitle = function(trackmeta) {
    //remove previous subtitles
    var tracks = video.getElementsByTagName("track");
    for (var i = tracks.length - 1; i >= 0; i--) {
      video.removeChild(tracks[i]);
    }
    if (trackmeta) { //if the chosen track exists
      //add the new one
      var track = document.createElement("track");
      video.appendChild(track);
      track.kind = "subtitles";
      track.label = trackmeta.label;
      track.srclang = trackmeta.lang;
      track.src = trackmeta.src;
      track.setAttribute("default","");
    }
  };
  MistVideo.player.setSize = function(size){
    this.api.style.width = size.width+"px";
    this.api.style.height = size.height+"px";
  };
  
  callback(video);
}