mistplayers.flv = { name: "HTML5 FLV Player", mimes: ["flash/7"], 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 (!window.MediaSource) { return false; } if (!MediaSource.isTypeSupported) { return true; } //we can't ask, but let's assume something will work try { //check if both audio and video have at least one playable track //gather track types and codec strings var playabletracks = {}; //var hassubtitles = false; for (var i in MistVideo.info.meta.tracks) { if (MistVideo.info.meta.tracks[i].type == "meta") { //if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; } continue; } if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) { playabletracks[MistVideo.info.meta.tracks[i].type] = {}; } playabletracks[MistVideo.info.meta.tracks[i].type][MistUtil.tracks.translateCodec(MistVideo.info.meta.tracks[i])] = 1; } var tracktypes = []; for (var type in playabletracks) { var playable = false; for (var codec in playabletracks[type]) { if (MediaSource.isTypeSupported("video/mp4;codecs=\""+codec+"\"")) { playable = true; break; } } if (playable) { tracktypes.push(type); } } source.supportedCodecs = tracktypes; /*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") { tracktypes.push("subtitle"); break; } } }*/ return tracktypes.length ? tracktypes : false; } catch(e){} return false; }, player: function(){ this.onreadylist = []; }, scriptsrc: function(host) { return host+"/flv.js"; } }; var p = mistplayers.flv.player; p.prototype = new MistPlayer(); p.prototype.build = function (MistVideo,callback) { this.onFLVLoad = function() { if (MistVideo.destroyed) { return; } MistVideo.log("Building flv.js player.."); var video = document.createElement("video"); video.setAttribute("playsinline",""); //iphones. effin' iphones. //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; } //send logging through our system flvjs.LoggingControl.applyConfig({ enableVerbose: false }); flvjs.LoggingControl.addLogListener(function(loglevel,message){ MistVideo.log("[flvjs] "+message); }); var opts = { type: "flv", url: MistVideo.source.url, //isLive: true, //not needed apparently hasAudio: false, hasVideo: false }; //if for example audio is not supported, send hasAudio = false flag or you get a bunch of errors ^_^ for (var i in MistVideo.source.supportedCodecs) { opts["has"+MistVideo.source.supportedCodecs[i].charAt(0).toUpperCase()+MistVideo.source.supportedCodecs[i].slice(1)] = true; } MistVideo.player.create = function(o){ o = MistUtil.object.extend({},o); //create a copy to force flv.js to recreate the segments key MistVideo.player.flvPlayer = flvjs.createPlayer(o,{ lazyLoad: false //if we let it lazyLoad, once it resumes, it will try to seek and fail miserably :) }); MistVideo.player.flvPlayer.attachMediaElement(video); MistVideo.player.flvPlayer.load(); MistVideo.player.flvPlayer.play(); if (!MistVideo.options.autoplay) { video.pause(); } } MistVideo.player.create(opts); MistVideo.player.api = {}; //redirect properties //using a function to make sure the "item" is in the correct scope function reroute(item) { Object.defineProperty(MistVideo.player.api,item,{ get: function(){ return video[item]; }, set: function(value){ return video[item] = value; } }); } var list = [ "volume" ,"buffered" ,"muted" ,"loop" ,"paused", ,"error" ,"textTracks" ,"webkitDroppedFrameCount" ,"webkitDecodedFrameCount" ]; if (MistVideo.info.type != "live") { list.push("duration"); } else { Object.defineProperty(MistVideo.player.api,"duration",{ get: function(){ if (!video.buffered.length) { return 0; } return video.buffered.end(video.buffered.length-1); }, }); } for (var i in list) { reroute(list[i]); } //redirect methods function redirect(item) { if (item in video) { MistVideo.player.api[item] = function(){ return video[item].call(video,arguments); }; } } var list = ["load","getVideoPlaybackQuality","play","pause"]; for (var i in list) { redirect(list[i]); } MistVideo.player.api.setSource = function(url){ if ((url != opts.url) && (url != "")) { MistVideo.player.flvPlayer.unload(); MistVideo.player.flvPlayer.detachMediaElement(); MistVideo.player.flvPlayer.destroy(); opts.url = url; MistVideo.player.create(opts); } }; MistVideo.player.api.unload = function(){ MistVideo.player.flvPlayer.unload(); MistVideo.player.flvPlayer.detachMediaElement(); MistVideo.player.flvPlayer.destroy(); } MistVideo.player.setSize = function(size){ video.style.width = size.width+"px"; video.style.height = size.height+"px"; }; //override seeking Object.defineProperty(MistVideo.player.api,"currentTime",{ get: function(){ return video.currentTime; }, set: function(value){ var keepaway = 0.5; //don't go closer to buffer end than this value [seconds] //check if this time is in the buffer for (var i = 0; i < video.buffered.length; i++) { if ((value >= video.buffered.start(i)) && (value <= video.buffered.end(i)-keepaway)) { //the desired seek time is in the buffer, go to it return video.currentTime = value; } } MistVideo.log("Seek attempted outside of buffer, but MistServer does not support seeking in progressive flash. Setting to closest available instead"); return video.currentTime = (video.buffered.length ? video.buffered.end(video.buffered.length-1)-keepaway : 0); } }); callback(video); } if ("flvjs" in window) { this.onFLVLoad(); } else { var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.flv.scriptsrc(MistVideo.options.host)),{ onerror: function(e){ var msg = "Failed to load flv.js"; if (e.message) { msg += ": "+e.message; } MistVideo.showError(msg); }, onload: MistVideo.player.onFLVLoad },MistVideo); } }