diff --git a/CMakeLists.txt b/CMakeLists.txt index eca106d8..e3e442a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -482,6 +482,7 @@ add_executable(MistOutHTTP generated/dashjs.js.h generated/videojs.js.h generated/webrtc.js.h + generated/mews.js.h generated/player_dash.js.h generated/player_dash_lic.js.h generated/player_video.js.h @@ -574,6 +575,10 @@ add_custom_command(OUTPUT generated/webrtc.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js webrtc_js generated/webrtc.js.h DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js ) +add_custom_command(OUTPUT generated/mews.js.h + COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/mews.js mews_js generated/mews.js.h + DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/mews.js +) # players add_custom_command(OUTPUT generated/player_dash_lic.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js player_dash_lic_js generated/player_dash_lic.js.h diff --git a/embed/min/player.js b/embed/min/player.js index 4ab8b9d4..60cf37e3 100644 --- a/embed/min/player.js +++ b/embed/min/player.js @@ -1 +1 @@ -var MistUtil={format:{time:function(e,t){if(isNaN(e)||!isFinite(e))return e;t||(t={});var i=e<0?" ago":"";e=Math.abs(e);var r=Math.floor(e/86400);e-=86400*r;var n=Math.floor(e/3600);e-=3600*n;var a=Math.floor(e/60),s=Math.round(e%1*1e3);e=Math.floor(e-60*a);var o=[];return r&&(r=r+" day"+(r>1?"s":"")+", "),n||r?(o.push(n),o.push(("0"+a).slice(-2))):o.push(a),o.push(("0"+Math.floor(e)).slice(-2)),t.ms&&(o[o.length-1]+="."+("000"+s).slice(-3)),(r||"")+o.join(":")+i},ucFirst:function(e){return e.charAt(0).toUpperCase()+e.slice(1)},number:function(e){if(isNaN(Number(e))||0==e)return e;var t=Math.max(3,Math.ceil(Math.log(e)/Math.LN10)),i=Math.pow(10,t-Math.floor(Math.log(e)/Math.LN10)-1);if((e=Math.round(e*i)/i)>=1e4){number=e.toString().split(".");for(var r=/(\d+)(\d{3})/;r.test(number[0]);)number[0]=number[0].replace(r,"$1 $2");e=number.join(".")}return e},bytes:function(e){if(isNaN(Number(e)))return e;var t=["bytes","KB","MB","GB","TB","PB"];if(0==e)unit=t[0];else{var i=Math.floor(Math.log(Math.abs(e))/Math.log(1024));i<0?unit=t[0]:(e/=Math.pow(1024,i),unit=t[i])}return this.number(e)+unit},mime2human:function(e){switch(e){case"html5/video/webm":return"WebM";case"html5/application/vnd.apple.mpegurl":return"HLS (TS)";case"html5/application/vnd.apple.mpegurl;version=7":return"HLS (CMAF)";case"flash/10":return"Flash (RTMP)";case"flash/11":return"Flash (HDS)";case"flash/7":return"Flash (Progressive)";case"html5/video/mpeg":return"TS";case"html5/application/vnd.ms-sstr+xml":case"html5/application/vnd.ms-ss":return"Smooth Streaming";case"dash/video/mp4":return"DASH";case"webrtc":return"WebRTC";case"silverlight":return"Smooth streaming (Silverlight)";case"html5/text/vtt":return"VTT subtitles";case"html5/text/plain":return"SRT subtitles";default:return e.replace("html5/","").replace("video/","").replace("audio/","").toLocaleUpperCase()}}},class:{add:function(e,t){if("classList"in e)e.classList.add(t);else{var i=this.get(e);i.push(t),this.set(e,i)}},remove:function(e,t){if("classList"in e)e.classList.remove(t);else{for(var i=this.get(e),r=i.length-1;r>=0;r--)i[r]==t&&i.splice(r);this.set(e,i)}},get:function(e){var t=e.getAttribute("class");return t&&""!=t?t.split(" "):[]},set:function(e,t){e.setAttribute("class",t.join(" "))},has:function(e,t){return e.className.split(" ").indexOf(t)>=0}},object:{extend:function(e,t,i){for(var r in t)!i||"object"!=typeof t[r]||"nodeType"in t[r]?e[r]=t[r]:(r in e||(MistUtil.array.is(t[r])?e[r]=[]:e[r]={}),this.extend(e[r],t[r],!0));return e},keys:function(e,t){var i=[];for(var r in e)i.push(r);return t&&("function"!=typeof t&&(t=function(e,t){return e.localeCompare(t)}),i.sort(function(i,r){return t(i,r,e[i],e[r])})),i},values:function(e,t){var i=this.keys(e,t);for(var r in values=[],i)values.push(e[i[r]]);return values}},array:{indexOf:function(e,t){if(!(e instanceof Array))throw"Tried to use indexOf on something that is not an array";if("indexOf"in e)return e.indexOf(t);for(var i;i=0?r:i.length}if("function"==typeof e)return e(t);if("object"==typeof e){if(e instanceof Array)return i(t,e[0],e[1]);for(var r in e)return i(t,r,e[r])}if(e in t)return t[e];throw"Invalid sorting rule: "+e+". This should be a function, object or key of "+JSON.stringify(t)+"."}return e.sort(function(e,n){var a=0;for(var s in t){var o=t[s];if(0!=(a=i(r(o,e),r(o,n))))break}return a}),e}},createUnique:function(){var e="uid"+Math.random().toString().replace("0.","");return document.querySelector("."+e)?createUnique():e},http:{getpost:function(e,t,i,r,n){var a=new XMLHttpRequest;if(a.open(e,t,!0),"POST"==e&&a.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n&&(a.timeout=8e3),a.onload=function(){var e=a.status;e>=200&&e<300?r(a.response):n&&n(a)},n&&(a.onerror=function(){n(a)},a.ontimeout=a.onerror),"POST"==e){var s,o=[];for(var l in i)o.push(l+"="+encodeURIComponent(i[l]));o.length&&(s=o.join("&")),a.send(s)}else a.send()},get:function(e,t,i){this.getpost("GET",e,null,t,i)},post:function(e,t,i,r){this.getpost("POST",e,t,i,r)},url:{addParam:function(e,t){var i=e.split("?"),r=[i.shift()],n=[];for(var a in i.length&&(n=i[0].split("&")),t)n.push(a+"="+t[a]);return n.length&&r.push(n.join("&")),r.join("?")},split:function(e){var t=document.createElement("a");return t.href=e,{protocol:t.protocol,host:t.hostname,hash:t.hash,port:t.port,path:t.pathname.replace(/\/*$/,"")}},sanitizeHost:function(e){var t=MistUtil.http.url.split(e);return t.protocol+"//"+t.host+(t.port&&""!=t.port?":"+t.port:"")+(t.hash&&""!=t.hash?"#"+t.hash:"")+(t.path?"/"==t.path.charAt(0)?t.path:"/"+t.path:"")}}},css:{cache:{},load:function(e,t,i){var r=document.createElement("style");r.type="text/css",r.setAttribute("data-source",e),i&&(r.callback=i);var n=this.cache;function a(e){var i=MistUtil.css.applyColors(e,t);"callback"in r?r.callback(i):r.textContent=i}return e in n?n[e]instanceof Array?n[e].push(a):a(n[e]):(n[e]=[a],MistUtil.http.get(e,function(t){for(var i in n[e])n[e][i](t);n[e]=t},function(){var t="/*Failed to load*/";for(var i in n[e])n[e][i](t);n[e]=t})),r},applyColors:function(e,t){return e.replace(/\$([^\s^;^}]*)/g,function(e,i){var r=i.split("."),n=t;for(var a in r)n=n[r[a]];return n})},createStyle:function(e,t,i){var r=document.createElement("style");return r.type="text/css",e&&(t&&(e=this.prependClass(e,t,i)),r.textContent=e),r},prependClass:function(e,t,i){var r=!1;"string"!=typeof e&&("unprepended"in(r=e)||(r.unprepended=r.textContent),e=r.unprepended);var n=(e=e.replace(/\/\*.*?\*\//g,"")).match(/@[^}]*}/g);for(var a in n){e=e.replace(n[a],"@@#@@");for(var s=1;s0)s=r.bps>131072?Math.round(r.bps/1024/1024*8)+"mbps":Math.round(r.bps/1024*8)+"kbps",n[a]=s;break;case"fpks":r.fpks>0&&(n[a]=r.fpks/1e3+"fps");break;case"channels":r.channels>0&&(n[a]=1==r.channels?"Mono":2==r.channels?"Stereo":"Surround ("+r.channels+"ch)");break;case"rate":n[a]=Math.round(r.rate)+"Khz";break;case"language":"Undetermined"!=r[a]&&(n[a]=r[a]);break;case"codec":if("meta"==r.codec)continue;n[a]=r[a]}r.describe=n}for(var o in t){var l=!1;for(var i in t[o])if(l){if(MistUtil.object.keys(t[o]).length>1)for(var a in t[o][i].describe)l[a]!=t[o][i].describe[a]&&delete l[a]}else l=MistUtil.object.extend({},t[o][i].describe);for(var i in t[o]){var c={},d={};for(var a in t[o][i].describe)a in l?d[a]=t[o][i].describe[a]:c[a]=t[o][i].describe[a];t[o][i].different=c,t[o][i].same=d;var u=MistUtil.object.values(c);t[o][i].displayName=u.length?u.join(", "):MistUtil.object.values(t[o][i].describe).join(" ")}var p={};for(var i in t[o]){if(t[o][i].displayName in p){var h=1;for(var i in t[o])t[o][i].different.trackid=h+")",t[o][i].displayName="Track "+h+" ("+t[o][i].displayName+")",h++;break}p[t[o][i].displayName]=1}}return t}},isTouchDevice:function(){return"ontouchstart"in window||navigator.msMaxTouchPoints>0},getPos:function(e,t){e.currentStyle||window.getComputedStyle(e,null);for(var i=1,r=e;r;)r.style.zoom&&""!=r.style.zoom&&(i*=parseFloat(r.style.zoom,10)),r=r.parentElement;var n=e.getBoundingClientRect().left-(parseInt(e.borderLeftWidth,10)||0),a=e.getBoundingClientRect().width,s=Math.max(0,(t.clientX/i-n)/a);return s=Math.min(s,1)},createGraph:function(e,t){var i="http://www.w3.org/2000/svg",r=document.createElementNS(i,"svg");r.setAttributeNS(null,"height","100%"),r.setAttributeNS(null,"width","100%"),r.setAttributeNS(null,"class","mist icon graph"),r.setAttributeNS(null,"preserveAspectRatio","none");var n=e.x[0],a=e.y[0];if(t.differentiate)for(var s=1;st.x.count&&(l.shift(),d()),d(e.x-n,-1*e.y),this.setAttributeNS(null,"d","M"+l.join(" L")),h()}},r.addData=function(e){m.addData(e)},r},getBrowser:function(){var e=window.navigator.userAgent;return e.indexOf("MSIE ")>=0||e.indexOf("Trident/")>=0?"ie":e.indexOf("Edge/")>=0?"edge":e.indexOf("Opera")>=0||e.indexOf("OPR")>=0?"opera":e.indexOf("Chrome")>=0?"chrome":e.indexOf("Safari")>=0?"safari":e.indexOf("Firefox")>=0&&"firefox"},getAndroid:function(){var e=navigator.userAgent.toLowerCase().match(/android\s([\d\.]*)/i);return!!e&&e[1]}};if(void 0===MistSkins)var MistSkins={};if("undefined"!=typeof mistoptions&&"host"in mistoptions)var misthost=MistUtil.http.url.sanitizeHost(mistoptions.host);else misthost="..";function MistSkin(e){e.skin=this,this.applySkinOptions=function(t){var i;return"string"==typeof t&&t in MistSkins&&(t=MistUtil.object.extend({},MistSkins[t],!0)),i="inherit"in t&&t.inherit&&t.inherit in MistSkins?this.applySkinOptions(t.inherit):MistSkins.default,this.structure=MistUtil.object.extend({},i.structure),t&&"structure"in t&&MistUtil.object.extend(this.structure,t.structure),this.blueprints=MistUtil.object.extend({},i.blueprints),t&&"blueprints"in t&&MistUtil.object.extend(this.blueprints,t.blueprints),this.icons=MistUtil.object.extend({},i.icons,!0),t&&"icons"in t&&MistUtil.object.extend(this.icons.blueprints,t.icons),this.icons.build=function(t,i,r){i||(i=22);var n,a=this.blueprints[t];n="function"==typeof a.svg?a.svg.call(e,r):a.svg,"object"!=typeof i&&(i={height:i,width:i}),"object"!=typeof a.size&&(a.size={height:a.size,width:a.size}),(!("width"in i)&&"height"in i||!("height"in i)&&"width"in i)&&("width"in i&&(i.height=i.width*a.size.height/a.size.width),"height"in i&&(i.width=i.height*a.size.width/a.size.height));var s="";s+='',s+='',s+=n,s+="",s+="";var o=document.createElement("div");return o.innerHTML=s,o.firstChild},this.colors=MistUtil.object.extend({},i.colors),t&&"colors"in t&&MistUtil.object.extend(this.colors,t.colors,!0),this.css=MistUtil.object.extend({},i.css),t&&"css"in t&&MistUtil.object.extend(this.css,t.css),this},this.applySkinOptions("skin"in e.options?e.options.skin:"default");var t=[];for(var i in this.css)if("string"==typeof this.css[i]){var r=MistUtil.css.load(e.urlappend(this.css[i]),this.colors);t.push(r)}this.css=t}function MistUI(e,t){e.UI=this,this.elements=[],this.buildStructure=function(t){if("function"==typeof t&&(t=t.call(e)),"if"in t){var i=!1;if(t.if.call(e,t)?i=t.then:"else"in t&&(i=t.else),!i)return;for(var r in t)["if","then","else"].indexOf(r)<0&&(r in i?(i[r]instanceof Array||(i[r]=[i[r]]),i[r]=i[r].concat(t[r])):i[r]=t[r]);return this.buildStructure(i)}if("type"in t&&t.type in e.skin.blueprints){var n=e.skin.blueprints[t.type].call(e,t);if(!n)return;if(MistUtil.class.add(n,"mistvideo-"+t.type),"css"in t){var a=MistUtil.createUnique();for(var r in t.css=[].concat(t.css),t.css){var s=MistUtil.css.createStyle(t.css[r],a);n.appendChild(s)}MistUtil.class.add(n,a),n.uid=a}if("classes"in t)for(var r in t.classes)MistUtil.class.add(n,t.classes[r]);if("title"in t&&(n.title=t.title),"style"in t)for(var r in t.style)n.style[r]=t.style[r];if("children"in t)for(var r in t.children){var o=this.buildStructure(t.children[r]);o&&n.appendChild(o)}return e.UI.elements.push(n),n}return!1},this.build=function(){return this.buildStructure(t||e.skin.structure.main)};var i=this.build(),r=MistUtil.createUnique(),n=0;for(var a in e.skin.css.length&&(i.style.opacity=0),e.skin.css){var s=e.skin.css[a];s.callback=function(t){"/*Failed to load*/"==t?(this.textContent=t,e.showError("Failed to load CSS from "+this.getAttribute("data-source"))):this.textContent=MistUtil.css.prependClass(t,r,!0),n++,e.skin.css.length<=n&&(i.style.opacity="")},""!=s.textContent&&s.callback(s.textContent),i.appendChild(s)}MistUtil.class.add(i,r);var o=MistUtil.getBrowser();return o&&MistUtil.class.add(i,"browser-"+o),i}MistSkins.default={structure:{main:{if:function(){return!!this.info.hasVideo&&"audio"!=this.source.type.split("/")[1]},then:{type:"placeholder",classes:["mistvideo"],children:[{type:"hoverWindow",mode:"pos",style:{position:"relative"},transition:{hide:"left: 0; right: 0; bottom: -43px;",show:"bottom: 0;",viewport:"left:0; right: 0; top: -1000px; bottom: 0;"},button:{type:"videocontainer"},children:[{type:"loading"},{type:"error"}],window:{type:"controls"}}]},else:{type:"container",classes:["mistvideo"],style:{overflow:"visible"},children:[{type:"controls",classes:["mistvideo-novideo"],style:{width:"480px"}},{type:"loading"},{type:"error"},{if:function(){return"stock"==this.options.controls},then:{type:"video",style:{position:"absolute"}},else:{type:"video",style:{position:"absolute",display:"none"}}}]}},videocontainer:{type:"video"},controls:{if:function(){return!!(this.player&&this.player.api&&this.player.api.play)},then:{type:"container",classes:["mistvideo-column"],children:[{type:"progress",classes:["mistvideo-pointer"]},{type:"container",classes:["mistvideo-main","mistvideo-padding","mistvideo-row","mistvideo-background"],children:[{type:"play",classes:["mistvideo-pointer"]},{type:"currentTime"},{type:"totalTime"},{type:"container",classes:["mistvideo-align-right"],children:[{type:"container",children:[{type:"container",classes:["mistvideo-volume_container"],children:[{type:"volume",mode:"horizontal",size:{height:22},classes:["mistvideo-pointer"]}]},{type:"speaker",classes:["mistvideo-pointer"],style:{"margin-left":"-2px"}}]},{if:function(){return"size"in this&&this.size.width>200||!this.info.hasVideo||"audio"==this.source.type.split("/")[1]},then:{type:"container",children:[{type:"loop",classes:["mistvideo-pointer"]},{type:"fullscreen",classes:["mistvideo-pointer"]}]}},{type:"hoverWindow",mode:"pos",transition:{hide:"right: -1000px; bottom: 44px;",show:"right: 5px;",viewport:"right: 0; left: 0; bottom: 0; top: -1000px"},button:{type:"settings",classes:["mistvideo-pointer"]},window:{type:"submenu"}}]}]}]},else:{if:function(){return!(!this.player||!this.player.api)},then:{type:"hoverWindow",mode:"pos",transition:{hide:"right: -1000px; bottom: 44px;",show:"right: 2.5px;",viewport:"right: 0; left: -1000px; bottom: 0; top: -1000px"},style:{right:"5px",left:"auto"},button:{type:"settings",classes:["mistvideo-background","mistvideo-padding"]},window:{type:"submenu"}}}},submenu:{type:"container",style:{width:"80%",maxWidth:"25em",zIndex:2},classes:["mistvideo-padding","mistvideo-column","mistvideo-background"],children:[{type:"tracks"},{if:function(){return"size"in this&&this.size.width<=200},then:{type:"container",classes:["mistvideo-center"],children:[{type:"loop",classes:["mistvideo-pointer"]},{type:"fullscreen",classes:["mistvideo-pointer"]}]}}]},placeholder:{type:"container",classes:["mistvideo","mistvideo-delay-display"],children:[{type:"placeholder"},{type:"loading"},{type:"error"}]},secondaryVideo:function(e){return{type:"hoverWindow",classes:["mistvideo"],mode:"pos",transition:{hide:"left: 10px; bottom: -40px;",show:"bottom: 10px;",viewport:"left: 0; right: 0; top: 0; bottom: 0"},button:{type:"container",children:[{type:"videocontainer"}]},window:{type:"switchVideo",classes:["mistvideo-controls","mistvideo-padding","mistvideo-background","mistvideo-pointer"],containers:e}}}},css:{skin:misthost+"/skins/default.css"},icons:{blueprints:{play:{size:45,svg:''},largeplay:{size:45,svg:''},pause:{size:45,svg:''},speaker:{size:45,svg:''},volume:{size:{width:100,height:45},svg:function(){var e=MistUtil.createUnique();return''}},muted:{size:45,svg:''},fullscreen:{size:45,svg:''},loop:{size:45,svg:''},settings:{size:45,svg:''},loading:{size:100,svg:''},timeout:{size:25,svg:function(e){e&&e.delay||(e={delay:10});var t=e.delay,i=MistUtil.createUnique();return''}},popout:{size:45,svg:''},switchvideo:{size:45,svg:''}}},blueprints:{container:function(){return document.createElement("div")},video:function(){var e=this;return MistUtil.event.addListener(e.video,"contextmenu",function(t){t.preventDefault(),e.container.setAttribute("data-show-submenu",""),e.container.removeAttribute("data-hide-submenu"),e.container.removeAttribute("data-hidecursor");var i=function(){e.container.removeAttribute("data-show-submenu"),e.container.removeEventListener("mouseout",i)};MistUtil.event.addListener(e.container,"mouseout",i)}),e.video.hideTimer=!1,e.video.hideCursor=function(){this.hideTimer&&clearTimeout(this.hideTimer),this.hideTimer=e.timers.start(function(){e.container.setAttribute("data-hidecursor","");var t=e.container.querySelector(".mistvideo-controls");t&&t.parentNode.setAttribute("data-hidecursor","")},3e3)},MistUtil.event.addListener(e.video,"mousemove",function(){e.container.removeAttribute("data-hidecursor");var t=e.container.querySelector(".mistvideo-controls");t&&t.parentNode.removeAttribute("data-hidecursor"),e.video.hideCursor()}),MistUtil.event.addListener(e.video,"mouseout",function(){e.video.hideTimer&&e.timers.stop(e.video.hideTimer)}),e.options.autoplay&&MistUtil.event.addListener(e.video,"canplay",function(){if(e.player.api.paused){var t=e.player.api.play();t&&t.catch(function(t){if(!e.destroyed&&(e.log("Autoplay failed. Retrying with muted audio.."),e.info.hasVideo)){e.player.api.muted=!0,MistUtil.event.send("volumechange",null,e.video);var i=e.player.api.play();i&&i.catch(function(){if(!e.destroyed){e.log("Autoplay failed even with muted video. Unmuting and showing play button."),e.player.api.muted=!1;var t=e.skin.icons.build("largeplay",150);MistUtil.class.add(t,"mistvideo-pointer"),e.container.appendChild(t),MistUtil.event.addListener(t,"click",function(){e.player.api.paused&&e.player.api.play()});var i=function(){e.container.removeChild(t),e.video.removeEventListener("play",i)};MistUtil.event.addListener(e.video,"play",i)}}).then(function(){if(!e.destroyed){e.log("Autoplay worked! Video will be unmuted on mouseover if the page has been interacted with.");var t=e.skin.icons.build("muted",100);MistUtil.class.add(t,"mistvideo-pointer"),e.container.appendChild(t),MistUtil.event.addListener(t,"click",function(){e.player.api.muted=!1,e.container.removeChild(t)});var i=!1,r=function(){i=!0,document.body.removeEventListener("click",r)};MistUtil.event.addListener(document.body,"click",r,e.video);var n=function(){i&&(e.player.api.muted=!1,e.video.removeEventListener("mouseenter",n),e.log("Re-enabled sound"))};MistUtil.event.addListener(e.video,"mouseenter",n);var a=function(){e.video.muted||(t.parentNode&&e.container.removeChild(t),e.video.removeEventListener("volumechange",a),document.body.removeEventListener("click",r),e.video.removeEventListener("mouseenter",n))};MistUtil.event.addListener(e.video,"volumechange",a)}},function(){})}})}}),this.video},videocontainer:function(){return this.UI.buildStructure(this.skin.structure.videocontainer)},secondaryVideo:function(e){e||(e={}),e.options||(e.options={});var t=this;"secondary"in t||(t.secondary=[]);var i=MistUtil.object.extend({},t.options);i=MistUtil.object.extend(i,e.options),t.secondary.push(i);var r={primary:t,secondary:!1};i.target=document.createElement("div"),delete i.container;var n={};return i.MistVideoObject=n,MistUtil.event.addListener(i.target,"initialized",function(){var e=n.reference;i.MistVideo=e,r.secondary=e,e.player.api.muted=!0,e.player.api.loop=!1;for(var a=i.target.querySelectorAll(".mistvideo-controls"),s=0;s30)e.player.api.pausedesync=!0,e.player.api.currentTime=this.currentTime,e.log("Re-syncing with main video by seeking (desync: "+t+"s)");else if(i>.01){var r=.1;i<1&&(r=.05),(r=1+r*Math.sign(t))!=e.player.api.playbackRate&&e.log("Re-syncing by changing the playback rate (desync: "+Math.round(1e3*t)+"ms, rate: "+r+")"),e.player.api.playbackRate=r}else 1!=e.player.api.playbackRate&&(e.player.api.playbackRate=1,e.log("Sync with main video achieved (desync: "+Math.round(1e3*t)+"ms)"))}},i.target),MistUtil.event.addListener(e.video,"seeked",function(){e.player.api.pausedesync=!1})}),i.skin=MistUtil.object.extend({},t.skin,!0),i.skin.structure.main=MistUtil.object.extend({},t.skin.structure.secondaryVideo(r)),mistPlay(t.stream,i),i.target},switchVideo:function(e){var t=document.createElement("div");return t.appendChild(this.skin.icons.build("switchvideo")),MistUtil.event.addListener(t,"click",function(){var t=e.containers.primary,i=e.containers.secondary;function r(e,t){if(e.video.currentTarget==t)return e.video;if(e.secondary)for(var i=0;i .outer_window:not([data-hidecursor]) > .inner_window { "+e.transition.show+" }\n.hover_window_container > .outer_window { "+e.transition.viewport+" }\n.hover_window_container > .outer_window > .inner_window { "+e.transition.hide+" }")),t.classes.push(e.mode),this.UI.buildStructure(t)},draggable:function(e){var t=this.skin.blueprints.container(e),i=this,r=this.skin.icons.build("fullscreen",16);MistUtil.class.remove(r,"fullscreen"),MistUtil.class.add(r,"draggable-icon"),t.appendChild(r),r.style.alignSelf="flex-end",r.style.position="absolute",r.style.cursor="move";var n={},a=function(e){t.style.left=e.clientX-n.x+"px",t.style.top=e.clientY-n.y+"px"},s=function(e){window.removeEventListener("mousemove",a),window.removeEventListener("click",s),MistUtil.event.addListener(r,"click",o)},o=function(e){e.stopPropagation(),r.removeEventListener("click",o),n.x=i.container.getBoundingClientRect().left-(t.getBoundingClientRect().left-e.clientX),n.y=i.container.getBoundingClientRect().top-(t.getBoundingClientRect().top-e.clientY),t.style.position="absolute",t.style.right="auto",t.style.bottom="auto",i.container.appendChild(t),a(e),MistUtil.event.addListener(window,"mousemove",a,t),MistUtil.event.addListener(window,"click",s,t)};return MistUtil.event.addListener(r,"click",o),t},progress:function(){var e=document.createElement("div"),t=document.createElement("div");e.appendChild(t),t.kids={},t.kids.bar=document.createElement("div"),t.kids.bar.className="bar",t.appendChild(t.kids.bar);var i=this.video,r=this.player.api,n=this;t.updateBar=function(e){if(this.kids.bar){if(!isFinite(r.duration))return void(this.kids.bar.style.display="none");this.kids.bar.style.display="",w=Math.min(1,Math.max(0,this.time2perc(e))),this.kids.bar.style.width=100*w+"%"}},t.time2perc=function(e){if(!isFinite(r.duration))return 0;var t=0;if("live"==n.info.type){var i=.001*n.info.meta.buffer_window;t=(e-r.duration+i)/i}else t=e/r.duration;return Math.min(1,Math.max(0,t))},t.buildBuffer=function(e,t){var i=document.createElement("div");return i.className="buffer",i.style.left=100*this.time2perc(e)+"%",i.style.width=100*(this.time2perc(t)-this.time2perc(e))+"%",i},t.updateBuffers=function(e){for(var t=this.querySelectorAll(".buffer"),i=0;i.5?(r.right=100*(1-i)+"%",a.triangle.setMode("bottom","right")):(r.left=100*i+"%",a.triangle.setMode("bottom","left")),a.setPos(r)}else a.style.opacity=0},MistUtil.event.addListener(e,"mousemove",function(e){t.moveTooltip(e)});var s=!1;return MistUtil.event.addListener(e,"mousedown",function(i){if(1==i.which){s=!0,t.updateBar(t.getPos(i));var r=MistUtil.event.addListener(document,"mousemove",function(e){t.updateBar(t.getPos(e)),t.moveTooltip(e)},t),n=MistUtil.event.addListener(document,"mouseup",function(i){1==i.which&&(s=!1,MistUtil.event.removeListener(r),MistUtil.event.removeListener(n),a.style.opacity=0,(!i.path||MistUtil.array.indexOf(i.path,e)<0)&&t.seek(i))},t)}}),e},play:function(){var e=this,t=document.createElement("div");t.appendChild(this.skin.icons.build("play")),t.appendChild(this.skin.icons.build("pause")),t.setState=function(e){this.setAttribute("data-state",e)},t.setState("paused");var i=this.video;return MistUtil.event.addListener(i,"playing",function(){t.setState("playing"),e.options.autoplay=!0},t),MistUtil.event.addListener(i,"pause",function(){t.setState("paused")},t),MistUtil.event.addListener(i,"paused",function(){t.setState("paused")},t),MistUtil.event.addListener(i,"ended",function(){t.setState("paused")},t),MistUtil.event.addListener(t,"click",function(){e.player.api.error&&e.player.api.load(),e.player.api.paused?e.player.api.play():(e.player.api.pause(),e.options.autoplay=!1)}),e.player.api&&MistUtil.event.addListener(e.video,"click",function(){e.player.api.paused?e.player.api.play():MistUtil.isTouchDevice()||(e.player.api.pause(),e.options.autoplay=!1)},t),t},speaker:function(){var e=!1,t=this.info.meta.tracks;for(var i in t)if("audio"==t[i].type){e=!0;break}if(!e)return!1;var r=this.skin.icons.build("speaker"),n=this,a=this.video;return MistUtil.event.addListener(a,"volumechange",function(){n.player.api.volume&&!n.player.api.muted?MistUtil.class.remove(r,"off"):MistUtil.class.add(r,"off")},r),MistUtil.event.addListener(r,"click",function(e){n.player.api.muted=!n.player.api.muted}),r},volume:function(e){var t=!1,i=this.info.meta.tracks;for(var r in i)if("audio"==i[r].type){t=!0;break}if(!t)return!1;var n=document.createElement("div"),a=this.skin.icons.build("volume","size"in e&&e.size);n.appendChild(a);var s=this;a.mode="mode"in e?e.mode:"vertical","vertical"==a.mode&&(a.style.transform="rotate(90deg)"),a.margin={start:.15,end:.1};var o=this.video;a.set=function(e){100!=(e=100-100*Math.pow(1-e/100,2))&&0!=e&&(e=100*this.addPadding(e/100));for(var t=a.querySelectorAll(".slider"),i=0;ii(t)?1:i(e)t[i]?1:t[e]1&&"player"in e&&"api"in e.player&&("setTrack"in e.player.api||"setTracks"in e.player.api||"setSource"in e.player.api)){var b=document.createElement("select");if(b.title="Select another "+l+" track",r[l]=b,b.trackType=l,m.appendChild(b),"subtitle"!=l){var M=document.createElement("option");b.appendChild(M),M.value="",M.appendChild(document.createTextNode("Automatic"))}var k=g(c[MistUtil.object.keys(c)[0]].same);if(k.length)(T=document.createElement("span")).className="mistvideo-description",m.appendChild(T),m.appendChild(document.createTextNode(k.join(" ")));function w(e){return""==e?-1:Number(e)}var U=MistUtil.object.keys(c,function(e,t){return w(e)-w(t)});for(var u in U){var x=c[U[u]];M=document.createElement("option");b.appendChild(M),M.value="idx"in x?x.idx:x.trackid,MistUtil.object.keys(x.different).length?M.appendChild(document.createTextNode(g(x.different).join(" "))):M.appendChild(document.createTextNode("Track "+(Number(u)+1)))}if(MistUtil.event.addListener(e.video,"playerUpdate_trackChanged",function(t){t.message.type==l&&"none"!=t.message.trackid&&(b.value=t.message.trackid,e.log("Player selected "+l+" track with id "+t.message.trackid))},b),"subtitle"==l){if(MistUtil.event.addListener(b,"change",function(){try{localStorage.mistSubtitleLanguage=c[this.value].lang}catch(e){}if(""!=this.value){var t=MistUtil.object.extend({},c[this.value]);t.label=g(t.describe).join(" "),t.src=MistUtil.http.url.addParam(d,{track:this.value}),e.player.api.setSubtitle(t)}else e.player.api.setSubtitle()}),"localStorage"in window&&null!=localStorage&&"mistSubtitleLanguage"in localStorage)for(var u in c)if(c[u].lang==localStorage.mistSubtitleLanguage){b.value=u;var C=document.createEvent("Event");C.initEvent("change"),b.dispatchEvent(C);break}}else MistUtil.event.addListener(b,"change",function(){this.trackType in n&&(n[this.trackType].checked=!0),a(this.trackType,this.value)})}else{var T;(T=document.createElement("span")).className="mistvideo-description",m.appendChild(T),T.appendChild(document.createTextNode(g(c[y[0]].same).join(" ")))}}}},text:function(e){var t=document.createElement("span");return t.appendChild(document.createTextNode(e.text)),t},placeholder:function(){var e=document.createElement("div"),t=this.calcSize();return e.style.width=t.width+"px",e.style.height=t.height+"px",this.options.poster&&(e.style.background="url('"+this.options.poster+"') no-repeat 50%/contain"),e},timeout:function(e){if(!(!1 in e)){var t="delay"in e?e.delay:5,i=this.skin.icons.build("timeout",!1,{delay:t});return i.timeout=this.timers.start(function(){e.function()},1e3*t),i}},polling:function(){var e=document.createElement("div"),t=this.skin.icons.build("loading");return e.appendChild(t),e},loading:function(){var e=this,t=this.skin.icons.build("loading",50);if("player"in e&&e.player.api){var i=!1;function r(t){e.container.setAttribute("data-loading",t.type),function t(){i||(i=e.timers.start(function(){i=!1,e.monitor.vars&&e.monitor.vars.score>=.999?n():t()},1e3))}()}function n(){e.container.removeAttribute("data-loading"),i&&e.timers.stop(i),i=!1}var a=["waiting","seeking","stalled"];for(var s in a)MistUtil.event.addListener(e.video,a[s],function(t){!this.paused&&"container"in e&&r(t)},t);a=["seeked","playing","canplay","paused"];for(var s in a)MistUtil.event.addListener(e.video,a[s],function(t){"container"in e&&n()},t),MistUtil.event.addListener(e.video,"progress",function(t){"container"in e&&"monitor"in e&&"vars"in e.monitor&&"score"in e.monitor.vars&&e.monitor.vars.score>.99&&n()},t)}return t},error:function(){var e=this,t=document.createElement("div");t.message=function(t,i,r){MistUtil.empty(this);var n=document.createElement("div");if(n.className="message",this.appendChild(n),!r.polling&&!r.passive&&!r.hideTitle){var a=document.createElement("h3");n.appendChild(a),a.appendChild(document.createTextNode("The player has encountered a problem"))}var s=document.createElement("p");if(n.appendChild(s),n.update=function(e){MistUtil.empty(s),s.innerHTML=e},t){e.info.on_error&&(t=e.info.on_error.replace(/\/,t)),n.update(t);var o=document.createElement("p");if(o.className="details mistvideo-description",n.appendChild(o),i)o.appendChild(document.createTextNode(i));else if("decodingIssues"in e.skin.blueprints){if("player"in e&&"api"in e.player&&e.video){if(i=[],void 0!==e.state&&i.push(["Stream state:",e.state]),void 0!==e.player.api.currentTime&&i.push(["Current video time:",MistUtil.format.time(e.player.api.currentTime)]),"video"in e&&"getVideoPlaybackQuality"in e.video){var l=e.video.getVideoPlaybackQuality();"droppedVideoFrames"in l&&"totalVideoFrames"in l&&l.totalVideoFrames&&i.push(["Frames dropped/total:",MistUtil.format.number(l.droppedVideoFrames)+"/"+MistUtil.format.number(l.totalVideoFrames)]),"corruptedVideoFrames"in l&&l.corruptedVideoFrames&&i.push(["Corrupted frames:",MistUtil.format.number(l.corruptedVideoFrames)])}i.push({0:["NETWORK EMPTY:","not yet initialized"],1:["NETWORK IDLE:","resource selected, but not in use"],2:["NETWORK LOADING:","data is being downloaded"],3:["NETWORK NO SOURCE:","could not locate source"]}[e.video.networkState]);if(i.push({0:["HAVE NOTHING:","no information about ready state"],1:["HAVE METADATA:","metadata has been loaded"],2:["HAVE CURRENT DATA:","data for the current playback position is available, but not for the next frame"],3:["HAVE FUTURE DATA:","data for current and next frame is available"],4:["HAVE ENOUGH DATA:","can start playing"]}[e.video.readyState]),!r.passive){var c=document.createElement("table");for(var d in i){var u=document.createElement("tr");for(var p in c.appendChild(u),i[d]){var h=document.createElement("td");u.appendChild(h),h.appendChild(document.createTextNode(i[d][p]))}}o.appendChild(c)}}var m,f=document.createElement("div");f.className="mistvideo-container mistvideo-column",f.style.textAlign="left",f.style.marginBottom="1em",n.appendChild(f),(m=e.UI.buildStructure({type:"forcePlayer"}))&&f.appendChild(m),(m=e.UI.buildStructure({type:"forceType"}))&&f.appendChild(m)}}return n};var i,r=!1,n=!1,a={};if(this.showError=function(s,o){o||(o={softReload:!!(e.player&&e.player.api&&e.player.api.load),reload:!0,nextCombo:!!e.info,polling:!1,passive:!1});var l=o.type?o.type:s;if(!(l in a)){if(!0===o.reload&&(e.options.reloadDelay&&!isNaN(Number(e.options.reloadDelay))?o.reload=Number(e.options.reloadDelay):o.reload=10),o.passive){if(!0===r)return;if(r)return i.update(s),void(n=(new Date).getTime());t.setAttribute("data-passive","")}else t.removeAttribute("data-passive");r&&t.clear(),r=!o.passive||"passive",n=(new Date).getTime();var c=this.log(s,"error"),d=t.message(s,!1,o);i=d;var u=document.createElement("div");if(u.className="mistvideo-buttoncontainer",d.appendChild(u),MistUtil.empty(u),o.softReload){var p={type:"button",label:"Reload video",onclick:function(){e.player.api.load()}};isNaN(o.softReload+"")||(p.delay=o.softReload),u.appendChild(e.UI.buildStructure(p))}if(o.reload){p={type:"button",label:"Reload player",onclick:function(){e.reload()}};isNaN(o.reload+"")||(p.delay=o.reload),u.appendChild(e.UI.buildStructure(p))}if(o.nextCombo){p={type:"button",label:"Next source",onclick:function(){e.nextCombo()}};isNaN(o.nextCombo+"")||(p.delay=o.nextCombo),u.appendChild(e.UI.buildStructure(p))}if(o.ignore){p={type:"button",label:"Ignore",onclick:function(){this.clearError(),a[l]=!0}};isNaN(o.ignore+"")||(p.delay=o.ignore),u.appendChild(e.UI.buildStructure(p))}o.polling&&u.appendChild(e.UI.buildStructure({type:"polling"})),MistUtil.class.add(t,"show"),"container"in e&&e.container.removeAttribute("data-loading"),c.defaultPrevented&&t.clear()}},t.clear=function(){for(var i=t.querySelectorAll("svg.icon.timeout"),n=0;n=t.scrollHeight-5}),r.logs)o(r.logs[l].time,r.logs[l].message,r.logs[l].data);return MistUtil.event.addListener(r.options.target,"log",function(e){if(e.message){var t={};r.player&&r.player.api&&"currentTime"in r.player.api&&(t.currentTime=r.player.api.currentTime),o(new Date,e.message,t)}},e),MistUtil.event.addListener(r.options.target,"error",function(e){if(e.message){var t={type:"error"};r.player&&r.player.api&&"currentTime"in r.player.api&&(t.currentTime=r.player.api.currentTime),o(new Date,e.message,t)}},e),e},decodingIssues:function(){if(this.player){var e=this,t=document.createElement("div");if(e.player.api){var i={"Playback score":function(){if("monitor"in e){if("vars"in e.monitor&&"score"in e.monitor.vars&&e.monitor.vars.values.length){var t=e.monitor.vars.values[e.monitor.vars.values.length-1];if("score"in t){Math.min(1,Math.max(0,t.score));return{x:t.clock,y:Math.min(1,Math.max(0,t.score)),options:{y:{min:0,max:1},x:{count:10}},val:Math.round(100*Math.min(1,Math.max(0,e.monitor.vars.score)))+"%"}}}return 0}},"Corrupted frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return t.corruptedVideoFrames?{val:MistUtil.format.number(t.corruptedVideoFrames),x:.001*(new Date).getTime(),y:t.corruptedVideoFrames,options:{x:{count:10}}}:0}},"Dropped frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return t.droppedVideoFrames?MistUtil.format.number(t.droppedVideoFrames):0}},"Total frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return MistUtil.format.number(t.totalVideoFrames)}},"Decoded audio":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.webkitAudioDecodedByteCount)},"Decoded video":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.webkitVideoDecodedByteCount)},"Negative acknowledgements":function(){if(e.player.api)return MistUtil.format.number(e.player.api.nackCount)},"Picture losses":function(){return MistUtil.format.number(e.player.api.pliCount)},"Packets lost":function(){return MistUtil.format.number(e.player.api.packetsLost)},"Packets received":function(){return MistUtil.format.number(e.player.api.packetsReceived)},"Bytes received":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.bytesReceived)},"Local latency [ms]":function(){if(e.player.api&&"getLatency"in e.player.api){var t=e.player.api.getLatency();return t?new Promise(function(e,i){t.then(function(t){var i=[];for(var r in t)t[r]&&i.push(r[0]+Math.round(1e3*t[r]));i.length?e(i.join(" ")):e()},i)}):new Promise(function(e,t){e()},function(){})}}},r=[];for(var n in i)void 0!==i[n]()&&a({name:n,function:i[n]});t.update=function(){for(var i in r)r[i]();e.timers.start(function(){t.update()},1e3)},t.update()}return t}function a(e){var i=document.createElement("label");t.appendChild(i),i.style.display="none";var n=document.createElement("span");i.appendChild(n),n.appendChild(document.createTextNode(e.name+":")),n.className="mistvideo-description";var a=document.createElement("span");i.appendChild(a);var s=document.createTextNode(e.value?e.value:"");a.appendChild(s);var o=document.createElement("span");a.appendChild(o),i.set=function(e){if(0!==e&&(this.style.display=""),"object"==typeof e){if(e instanceof Promise)return void e.then(function(e){i.set(e)},function(){});if("val"in e&&(s.nodeValue=e.val,a.className="value"),o.children.length)return(t=o.children[0]).addData(e);var t=MistUtil.createGraph({x:[e.x],y:[e.y]},e.options);return o.style.display="",MistUtil.empty(o),o.appendChild(t)}return s.nodeValue=e},t.appendChild(i),r.push(function(){var t=e.function();i.set(t)})}},forcePlayer:function(){var e=document.createElement("label");e.title="Reload MistVideo and use the selected player";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force player: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");for(var a in r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic")),mistplayers){n=document.createElement("option");r.appendChild(n),n.value=a,n.appendChild(document.createTextNode(mistplayers[a].name))}return this.options.forcePlayer&&(r.value=this.options.forcePlayer),MistUtil.event.addListener(r,"change",function(){t.options.forcePlayer=""!=this.value&&this.value,t.options.forcePlayer!=t.playerName&&t.reload()}),e},forceType:function(){if(this.info){var e=document.createElement("label");e.title="Reload MistVideo and use the selected protocol";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force protocol: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic"));var a={};for(var s in t.info.source){var o=t.info.source[s];if(!(o.type in a)){a[o.type]=1;n=document.createElement("option");r.appendChild(n),n.value=o.type,n.appendChild(document.createTextNode(MistUtil.format.mime2human(o.type)))}}return this.options.forceType&&(r.value=this.options.forceType),MistUtil.event.addListener(r,"change",function(){t.options.forceType=""!=this.value&&this.value,t.source&&t.options.forceType==t.source.type||t.reload()}),e}},forceSource:function(){var e=document.createElement("label");e.title="Reload MistVideo and use the selected source";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force source: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");for(var a in r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic")),t.info.source){var s=t.info.source[a];n=document.createElement("option");r.appendChild(n),n.value=a,n.appendChild(document.createTextNode(s.url+" ("+MistUtil.format.mime2human(s.type)+")"))}return this.options.forceSource&&(r.value=this.options.forceSource),MistUtil.event.addListener(r,"change",function(){t.options.forceSource=""!=this.value&&this.value,t.options.forceSource!=t.source.index&&t.reload()}),e}}},MistSkins.dev.css={skin:misthost+"/skins/dev.css"},MistSkins.dev.structure.submenu=MistUtil.object.extend({},MistSkins.default.structure.submenu,!0),MistSkins.dev.structure.submenu.type="draggable",MistSkins.dev.structure.submenu.style.width="25em",MistSkins.dev.structure.submenu.children.unshift({type:"container",style:{flexShrink:1},classes:["mistvideo-column"],children:[{if:function(){return this.playerName&&this.source},then:{type:"container",classes:["mistvideo-description"],style:{display:"block"},children:[{type:"playername",style:{display:"inline"}},{type:"text",text:"is playing",style:{margin:"0 0.2em"}},{type:"mimetype"}]}},{type:"log"},{type:"decodingIssues"},{type:"container",classes:["mistvideo-column","mistvideo-devcontrols"],style:{"font-size":"0.9em"},children:[{type:"text",text:"Player control"},{type:"container",classes:["mistvideo-devbuttons"],style:{"flex-wrap":"wrap"},children:[{type:"button",title:"Build MistVideo again",label:"MistVideo.reload();",onclick:function(){this.reload()}},{type:"button",title:"Switch to the next available player and source combination",label:"MistVideo.nextCombo();",onclick:function(){this.nextCombo()}}]},{type:"forcePlayer"},{type:"forceType"}]}]});var mistplayers={};function MistPlayer(){}function mistPlay(e,t){return new MistVideo(e,t)}function MistVideo(e,t){var i=this;function r(e){if("meta"in e&&"tracks"in e.meta){var t=e.meta.tracks;for(var i in t)if("video"==t[i].type)return!0}return!1}function n(e){if(i.player&&i.player.api&&i.player.api.unload&&(i.log("Received new stream info while a player was already loaded: unloading player"),i.player.api.unload()),i.info=e,i.info.updated=new Date,MistUtil.event.send("haveStreamInfo",e,i.options.target),i.log("Stream info was loaded succesfully."),"error"in e){var n=e.error;return"on_error"in e&&(i.log(n),n=e.on_error),void i.showError(n,{reload:!0,hideTitle:!0})}if(i.calcSize=function(e){e||(e={width:!1,height:!1});var r=e.width||!!("width"in t&&t.width)&&t.width,n=e.height||!!("height"in t&&t.height)&&t.height;if(this.info&&"source"in this.info)if(this.info.hasVideo&&"audio"!=this.source.type.split("/")[1]){if(!r||!n){var a=i.info.width/i.info.height;if(r||n)r?n=r/a:r=n*a;else{var s="maxwidth"in t&&t.maxwidth?t.maxwidth:window.innerWidth,o="maxheight"in t&&t.maxheight?t.maxheight:window.innerHeight;r=i.info.width,n=i.info.height;function l(e){r/=e,n/=e}r<426&&l(r/426),n<240&&l(n/240),s&&r>s&&l(r/s),o&&n>o&&l(n/o)}}}else r||(r=480),n||(n=42);else r=640,n=480;return this.size={width:Math.round(r),height:Math.round(n)},this.size},e.hasVideo=r(e),"live"==e.type){var a=0;for(var s in i.info.meta.tracks)a=Math.max(a,i.info.meta.tracks[s].lastms);e.lastms=a}else{var o=i.resumeTime;if(o){var l=function(){i.player&&i.player.api&&(i.player.api.currentTime=o),this.removeEventListener("initialized",l)};MistUtil.event.addListener(i.options.target,"initialized",l)}}i.choosePlayer()?(i.player=new mistplayers[i.playerName].player,i.player.onreadylist=[],i.player.onready=function(e){this.onreadylist.push(e)},i.player.build(i,function(e){if(i.log("Building new player"),i.container.removeAttribute("data-loading"),i.video=e,"api"in i.player){i.monitor={MistVideo:i,delay:1,averagingSteps:20,threshold:function(){return"webrtc"==this.MistVideo.source.type?.95:.75},init:function(){if(!this.vars||!this.vars.active){this.MistVideo.log("Enabling monitor"),this.vars={values:[],score:!1,active:!0};var e=this;!function t(){e.vars&&e.vars.active&&(e.vars.timer=e.MistVideo.timers.start(function(){var i=e.calcScore();!1!==i&&e.check(i)&&e.action(),t()},1e3*e.delay))}()}},destroy:function(){this.vars&&this.vars.active&&(this.MistVideo.log("Disabling monitor"),this.MistVideo.timers.stop(this.vars.timer),delete this.vars)},reset:function(){this.vars&&this.vars.active?(this.MistVideo.log("Resetting monitor"),this.vars.values=[]):this.init()},calcScore:function(){var e=this.vars.values;if(e.push(this.getValue()),e.length<=1)return!1;var t=this.valueToScore(e[0],e[e.length-1]);return e.length>this.averagingSteps&&e.shift(),t=Math.max(t,e[e.length-1].score),this.vars.score=t,t},valueToScore:function(e,t){var i=1;return"player"in this.MistVideo&&"api"in this.MistVideo.player&&"playbackRate"in this.MistVideo.player.api&&(i=this.MistVideo.player.api.playbackRate),(t.video-e.video)/(t.clock-e.clock)/i},getValue:function(){var e={clock:.001*(new Date).getTime(),video:this.MistVideo.player.api.currentTime};return this.vars.values.length&&(e.score=this.valueToScore(this.vars.values[this.vars.values.length-1],e)),e},check:function(e){return!(this.vars.values.length<.5*this.averagingSteps)&&(e=2))for(var u in l[o.inner].list)if(l[o.inner].current=u,!(c(o.inner)>=1)){a=l.source.list[l.source.current];var p=l.player.list[l.player.current],h=mistplayers[p];if(h.isMimeSupported(a.type)&&h.isBrowserSupported(a.type,a,i))return{player:p,source:a,source_index:l.source.current};t||i.log("Checking "+h.name+" with "+a.type+".. Nope.")}return!1},this.choosePlayer=function(){i.log("Checking available players..");var e=this.checkCombo();if(!e)return!1;var t=mistplayers[e.player],r=e.source;return i.log("Found a working combo: "+t.name+" with "+r.type+" @ "+r.url),i.playerName=e.player,(r=MistUtil.object.extend({},r)).index=e.source_index,r.url=i.urlappend(r.url),i.source=r,MistUtil.event.send("comboChosen","Player/source combination selected",i.options.target),!0},i.calcSize=function(){return{width:640,height:480}},MistUtil.empty(i.options.target),new MistSkin(i),i.container=new MistUI(i,i.skin.structure.placeholder),i.options.target.appendChild(i.container),i.container.setAttribute("data-loading",""),"WebSocket"in window){!function e(){i.log("Opening stream status stream..");var t=i.options.host.replace(/^http/i,"ws"),s=new WebSocket(i.urlappend(t+"/json_"+encodeURIComponent(i.stream)+".js"));i.socket=s,s.die=!1,s.destroy=function(){this.die=!0,this.close()},s.onopen=function(e){this.wasConnected=!0},s.onclose=function(t){if(!this.die)return this.wasConnected?(i.log("Reopening websocket.."),void e()):void a()};var o=!1;s.addEventListener("message",function(e){var t=JSON.parse(e.data);if(t||i.showError("Error while parsing stream status stream. Obtained: "+e.data.toString(),{reload:!0}),"error"in t){var a;switch(e=t.error,"on_error"in t&&(i.log(e),e=t.on_error),i.state=t.error,t.error){case"Stream is offline":i.info=!1,i.player&&i.player.api&&i.player.api.currentTime&&(i.resumeTime=i.player.api.currentTime);case"Stream is initializing":case"Stream is booting":case"Stream is waiting for data":case"Stream is shutting down":case"Stream status is invalid?!":if(i.player&&i.player.api&&!i.player.api.paused)return i.log(t.error,"error"),o||(o=MistUtil.event.addListener(i.video,"ended",function(){i.showError(t.error,{polling:!0})})),void(o=MistUtil.event.addListener(i.video,"waiting",function(){i.showError(t.error,{polling:!0})}));a={polling:!0};break;default:a={reload:!0}}i.showError(e,a)}else{if(i.state="Stream is online",i.clearError(),o&&MistUtil.event.removeListener(o),!i.info)return void n(t);var s=function e(t,i){if(t==i)return!1;if("object"==typeof t&&void 0!==i){var r={};for(var n in t)if(!(MistUtil.array.indexOf(["lastms","hasVideo"],n)>=0)){var a=e(t[n],i[n]);a&&(r[n]=!0===a?[t[n],i[n]]:a)}for(var n in i)MistUtil.array.indexOf(["lastms","hasVideo"],n)>=0||n in t||(r[n]=[t[n],i[n]]);return!!MistUtil.object.keys(r).length&&r}return!0}(t,i.info);if(s){if("source"in s)return void("error"in i.info&&i.reload());i.info=MistUtil.object.extend(i.info,t),i.info.updated=new Date;var l=!1;for(var c in s)switch(c){case"meta":for(var d in s[c])switch(d){case"tracks":i.info.hasVideo=r(i.info),MistUtil.event.send("metaUpdate_tracks",t,i.video)}break;case"width":case"height":l=!0}l&&i.player.resize()}else i.log("Metachange: no differences detected")}})}()}else a();return this.unload=function(){if(!this.destroyed){for(var e in this.log("Unloading.."),this.destroyed=!0,this.timers.stop("all"),this.errorListeners){var t=this.errorListeners[e];if(t.src in MistUtil.scripts.list){var r=MistUtil.array.indexOf(MistUtil.scripts.list[t.src].subscribers);r>=0&&MistUtil.scripts.list[t.src].subscribers.splice(r,1)}}if("monitor"in i&&"destroy"in i.monitor&&i.monitor.destroy(),this.socket&&this.socket.destroy(),this.player&&this.player.api&&("pause"in this.player.api&&this.player.api.pause(),"setSource"in this.player.api&&this.player.api.setSource(""),"unload"in this.player.api))try{this.player.api.unload()}catch(n){}if(this.UI&&this.UI.elements)for(var e in this.UI.elements){var n=this.UI.elements[e];if("attachedListeners"in n)for(var e in n.attachedListeners)MistUtil.event.removeListener(n.attachedListeners[e]);n.parentNode&&n.parentNode.removeChild(n)}this.video&&MistUtil.empty(this.video),"container"in this&&(MistUtil.empty(this.container),delete this.container),MistUtil.empty(this.options.target),delete this.video}},this.reload=function(){var e="player"in this&&"api"in this.player&&this.player.api.currentTime;if(this.unload(),i=mistPlay(this.stream,this.options),e&&"live"!=this.info.type){var t=function(){i.player&&i.player.api&&(i.player.api.currentTime=e),this.removeEventListener("initialized",t)};MistUtil.event.addListener(this.options.target,"initialized",t)}return i},this.nextCombo=function(){var e=!1;"player"in this&&"api"in this.player&&(e=this.player.api.currentTime);var t={source:this.source.index,player:this.playerName};if(!this.checkCombo({startCombo:t},!0)){if(!this.checkCombo({startCombo:!1},!0))return;t=!1}this.unload();var r=this.options;if(r.startCombo=t,i=mistPlay(this.stream,r),e&&isFinite(e)&&"live"!=this.info.type){var n=function(){"player"in i&&"api"in i.player&&(i.player.api.currentTime=e),this.removeEventListener("initialized",n)};MistUtil.event.addListener(r.target,"initialized",n)}},this.onPlayerBuilt=function(){},t.MistVideoObject&&(t.MistVideoObject.reference=this),this} \ No newline at end of file +var MistUtil={format:{time:function(e,t){if(isNaN(e)||!isFinite(e))return e;t||(t={});var i=e<0?" ago":"";e=Math.abs(e);var r=Math.floor(e/86400);e-=86400*r;var n=Math.floor(e/3600);e-=3600*n;var a=Math.floor(e/60),s=Math.round(e%1*1e3);e=Math.floor(e-60*a);var o=[];return r&&(r=r+" day"+(r>1?"s":"")+", "),n||r?(o.push(n),o.push(("0"+a).slice(-2))):o.push(a),o.push(("0"+Math.floor(e)).slice(-2)),t.ms&&(o[o.length-1]+="."+("000"+s).slice(-3)),(r||"")+o.join(":")+i},ucFirst:function(e){return e.charAt(0).toUpperCase()+e.slice(1)},number:function(e){if(isNaN(Number(e))||0==e)return e;var t=Math.max(3,Math.ceil(Math.log(e)/Math.LN10)),i=Math.pow(10,t-Math.floor(Math.log(e)/Math.LN10)-1);if((e=Math.round(e*i)/i)>=1e4){number=e.toString().split(".");for(var r=/(\d+)(\d{3})/;r.test(number[0]);)number[0]=number[0].replace(r,"$1 $2");e=number.join(".")}return e},bytes:function(e){if(isNaN(Number(e)))return e;var t=["bytes","KB","MB","GB","TB","PB"];if(0==e)unit=t[0];else{var i=Math.floor(Math.log(Math.abs(e))/Math.log(1024));i<0?unit=t[0]:(e/=Math.pow(1024,i),unit=t[i])}return this.number(e)+unit},mime2human:function(e){switch(e){case"html5/video/webm":return"WebM";case"html5/application/vnd.apple.mpegurl":return"HLS (TS)";case"html5/application/vnd.apple.mpegurl;version=7":return"HLS (CMAF)";case"flash/10":return"Flash (RTMP)";case"flash/11":return"Flash (HDS)";case"flash/7":return"Flash (Progressive)";case"html5/video/mpeg":return"TS";case"html5/application/vnd.ms-sstr+xml":case"html5/application/vnd.ms-ss":return"Smooth Streaming";case"dash/video/mp4":return"DASH";case"webrtc":return"WebRTC";case"silverlight":return"Smooth streaming (Silverlight)";case"html5/text/vtt":return"VTT subtitles";case"html5/text/plain":return"SRT subtitles";default:return e.replace("html5/","").replace("video/","").replace("audio/","").toLocaleUpperCase()}}},class:{add:function(e,t){if("classList"in e)e.classList.add(t);else{var i=this.get(e);i.push(t),this.set(e,i)}},remove:function(e,t){if("classList"in e)e.classList.remove(t);else{for(var i=this.get(e),r=i.length-1;r>=0;r--)i[r]==t&&i.splice(r);this.set(e,i)}},get:function(e){var t=e.getAttribute("class");return t&&""!=t?t.split(" "):[]},set:function(e,t){e.setAttribute("class",t.join(" "))},has:function(e,t){return e.className.split(" ").indexOf(t)>=0}},object:{extend:function(e,t,i){for(var r in t)!i||"object"!=typeof t[r]||"nodeType"in t[r]?e[r]=t[r]:(r in e||(MistUtil.array.is(t[r])?e[r]=[]:e[r]={}),this.extend(e[r],t[r],!0));return e},keys:function(e,t){var i=[];for(var r in e)i.push(r);return t&&("function"!=typeof t&&(t=function(e,t){return e.localeCompare(t)}),i.sort(function(i,r){return t(i,r,e[i],e[r])})),i},values:function(e,t){var i=this.keys(e,t);for(var r in values=[],i)values.push(e[i[r]]);return values}},array:{indexOf:function(e,t){if(!(e instanceof Array))throw"Tried to use indexOf on something that is not an array";if("indexOf"in e)return e.indexOf(t);for(var i;i=0?r:i.length}if("function"==typeof e)return e(t);if("object"==typeof e){if(e instanceof Array)return i(t,e[0],e[1]);for(var r in e)return i(t,r,e[r])}if(e in t)return t[e];throw"Invalid sorting rule: "+e+". This should be a function, object or key of "+JSON.stringify(t)+"."}return e.sort(function(e,n){var a=0;for(var s in t){var o=t[s];if(0!=(a=i(r(o,e),r(o,n))))break}return a}),e}},createUnique:function(){var e="uid"+Math.random().toString().replace("0.","");return document.querySelector("."+e)?createUnique():e},http:{getpost:function(e,t,i,r,n){var a=new XMLHttpRequest;if(a.open(e,t,!0),"POST"==e&&a.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n&&(a.timeout=8e3),a.onload=function(){var e=a.status;e>=200&&e<300?r(a.response):n&&n(a)},n&&(a.onerror=function(){n(a)},a.ontimeout=a.onerror),"POST"==e){var s,o=[];for(var l in i)o.push(l+"="+encodeURIComponent(i[l]));o.length&&(s=o.join("&")),a.send(s)}else a.send()},get:function(e,t,i){this.getpost("GET",e,null,t,i)},post:function(e,t,i,r){this.getpost("POST",e,t,i,r)},url:{addParam:function(e,t){var i=e.split("?"),r=[i.shift()],n=[];for(var a in i.length&&(n=i[0].split("&")),t)n.push(a+"="+t[a]);return n.length&&r.push(n.join("&")),r.join("?")},split:function(e){var t=document.createElement("a");return t.href=e,{protocol:t.protocol,host:t.hostname,hash:t.hash,port:t.port,path:t.pathname.replace(/\/*$/,"")}},sanitizeHost:function(e){var t=MistUtil.http.url.split(e);return t.protocol+"//"+t.host+(t.port&&""!=t.port?":"+t.port:"")+(t.hash&&""!=t.hash?"#"+t.hash:"")+(t.path?"/"==t.path.charAt(0)?t.path:"/"+t.path:"")}}},css:{cache:{},load:function(e,t,i){var r=document.createElement("style");r.type="text/css",r.setAttribute("data-source",e),i&&(r.callback=i);var n=this.cache;function a(e){var i=MistUtil.css.applyColors(e,t);"callback"in r?r.callback(i):r.textContent=i}return e in n?n[e]instanceof Array?n[e].push(a):a(n[e]):(n[e]=[a],MistUtil.http.get(e,function(t){for(var i in n[e])n[e][i](t);n[e]=t},function(){var t="/*Failed to load*/";for(var i in n[e])n[e][i](t);n[e]=t})),r},applyColors:function(e,t){return e.replace(/\$([^\s^;^}]*)/g,function(e,i){var r=i.split("."),n=t;for(var a in r)n=n[r[a]];return n})},createStyle:function(e,t,i){var r=document.createElement("style");return r.type="text/css",e&&(t&&(e=this.prependClass(e,t,i)),r.textContent=e),r},prependClass:function(e,t,i){var r=!1;"string"!=typeof e&&("unprepended"in(r=e)||(r.unprepended=r.textContent),e=r.unprepended);var n=(e=e.replace(/\/\*.*?\*\//g,"")).match(/@[^}]*}/g);for(var a in n){e=e.replace(n[a],"@@#@@");for(var s=1;s0)s=r.bps>131072?Math.round(r.bps/1024/1024*8)+"mbps":Math.round(r.bps/1024*8)+"kbps",n[a]=s;break;case"fpks":r.fpks>0&&(n[a]=r.fpks/1e3+"fps");break;case"channels":r.channels>0&&(n[a]=1==r.channels?"Mono":2==r.channels?"Stereo":"Surround ("+r.channels+"ch)");break;case"rate":n[a]=Math.round(r.rate)+"Khz";break;case"language":"Undetermined"!=r[a]&&(n[a]=r[a]);break;case"codec":if("meta"==r.codec)continue;n[a]=r[a]}r.describe=n}for(var o in t){var l=!1;for(var i in t[o])if(l){if(MistUtil.object.keys(t[o]).length>1)for(var a in t[o][i].describe)l[a]!=t[o][i].describe[a]&&delete l[a]}else l=MistUtil.object.extend({},t[o][i].describe);for(var i in t[o]){var c={},d={};for(var a in t[o][i].describe)a in l?d[a]=t[o][i].describe[a]:c[a]=t[o][i].describe[a];t[o][i].different=c,t[o][i].same=d;var u=MistUtil.object.values(c);t[o][i].displayName=u.length?u.join(", "):MistUtil.object.values(t[o][i].describe).join(" ")}var p={};for(var i in t[o]){if(t[o][i].displayName in p){var h=1;for(var i in t[o])t[o][i].different.trackid=h+")",t[o][i].displayName="Track "+h+" ("+t[o][i].displayName+")",h++;break}p[t[o][i].displayName]=1}}return t}},isTouchDevice:function(){return"ontouchstart"in window||navigator.msMaxTouchPoints>0},getPos:function(e,t){e.currentStyle||window.getComputedStyle(e,null);for(var i=1,r=e;r;)r.style.zoom&&""!=r.style.zoom&&(i*=parseFloat(r.style.zoom,10)),r=r.parentElement;var n=e.getBoundingClientRect().left-(parseInt(e.borderLeftWidth,10)||0),a=e.getBoundingClientRect().width,s=Math.max(0,(t.clientX/i-n)/a);return s=Math.min(s,1)},createGraph:function(e,t){var i="http://www.w3.org/2000/svg",r=document.createElementNS(i,"svg");r.setAttributeNS(null,"height","100%"),r.setAttributeNS(null,"width","100%"),r.setAttributeNS(null,"class","mist icon graph"),r.setAttributeNS(null,"preserveAspectRatio","none");var n=e.x[0],a=e.y[0];if(t.differentiate)for(var s=1;st.x.count&&(l.shift(),d()),d(e.x-n,-1*e.y),this.setAttributeNS(null,"d","M"+l.join(" L")),h()}},r.addData=function(e){m.addData(e)},r},getBrowser:function(){var e=window.navigator.userAgent;return e.indexOf("MSIE ")>=0||e.indexOf("Trident/")>=0?"ie":e.indexOf("Edge/")>=0?"edge":e.indexOf("Opera")>=0||e.indexOf("OPR")>=0?"opera":e.indexOf("Chrome")>=0?"chrome":e.indexOf("Safari")>=0?"safari":e.indexOf("Firefox")>=0&&"firefox"},getAndroid:function(){var e=navigator.userAgent.toLowerCase().match(/android\s([\d\.]*)/i);return!!e&&e[1]}};if(void 0===MistSkins)var MistSkins={};if("undefined"!=typeof mistoptions&&"host"in mistoptions)var misthost=MistUtil.http.url.sanitizeHost(mistoptions.host);else misthost="..";function MistSkin(e){e.skin=this,this.applySkinOptions=function(t){var i;return"string"==typeof t&&t in MistSkins&&(t=MistUtil.object.extend({},MistSkins[t],!0)),i="inherit"in t&&t.inherit&&t.inherit in MistSkins?this.applySkinOptions(t.inherit):MistSkins.default,this.structure=MistUtil.object.extend({},i.structure),t&&"structure"in t&&MistUtil.object.extend(this.structure,t.structure),this.blueprints=MistUtil.object.extend({},i.blueprints),t&&"blueprints"in t&&MistUtil.object.extend(this.blueprints,t.blueprints),this.icons=MistUtil.object.extend({},i.icons,!0),t&&"icons"in t&&MistUtil.object.extend(this.icons.blueprints,t.icons),this.icons.build=function(t,i,r){i||(i=22);var n,a=this.blueprints[t];n="function"==typeof a.svg?a.svg.call(e,r):a.svg,"object"!=typeof i&&(i={height:i,width:i}),"object"!=typeof a.size&&(a.size={height:a.size,width:a.size}),(!("width"in i)&&"height"in i||!("height"in i)&&"width"in i)&&("width"in i&&(i.height=i.width*a.size.height/a.size.width),"height"in i&&(i.width=i.height*a.size.width/a.size.height));var s="";s+='',s+='',s+=n,s+="",s+="";var o=document.createElement("div");return o.innerHTML=s,o.firstChild},this.colors=MistUtil.object.extend({},i.colors),t&&"colors"in t&&MistUtil.object.extend(this.colors,t.colors,!0),this.css=MistUtil.object.extend({},i.css),t&&"css"in t&&MistUtil.object.extend(this.css,t.css),this},this.applySkinOptions("skin"in e.options?e.options.skin:"default");var t=[];for(var i in this.css)if("string"==typeof this.css[i]){var r=MistUtil.css.load(e.urlappend(this.css[i]),this.colors);t.push(r)}this.css=t}function MistUI(e,t){e.UI=this,this.elements=[],this.buildStructure=function(t){if("function"==typeof t&&(t=t.call(e)),"if"in t){var i=!1;if(t.if.call(e,t)?i=t.then:"else"in t&&(i=t.else),!i)return;for(var r in t)["if","then","else"].indexOf(r)<0&&(r in i?(i[r]instanceof Array||(i[r]=[i[r]]),i[r]=i[r].concat(t[r])):i[r]=t[r]);return this.buildStructure(i)}if("type"in t&&t.type in e.skin.blueprints){var n=e.skin.blueprints[t.type].call(e,t);if(!n)return;if(MistUtil.class.add(n,"mistvideo-"+t.type),"css"in t){var a=MistUtil.createUnique();for(var r in t.css=[].concat(t.css),t.css){var s=MistUtil.css.createStyle(t.css[r],a);n.appendChild(s)}MistUtil.class.add(n,a),n.uid=a}if("classes"in t)for(var r in t.classes)MistUtil.class.add(n,t.classes[r]);if("title"in t&&(n.title=t.title),"style"in t)for(var r in t.style)n.style[r]=t.style[r];if("children"in t)for(var r in t.children){var o=this.buildStructure(t.children[r]);o&&n.appendChild(o)}return e.UI.elements.push(n),n}return!1},this.build=function(){return this.buildStructure(t||e.skin.structure.main)};var i=this.build(),r=MistUtil.createUnique(),n=0;for(var a in e.skin.css.length&&(i.style.opacity=0),e.skin.css){var s=e.skin.css[a];s.callback=function(t){"/*Failed to load*/"==t?(this.textContent=t,e.showError("Failed to load CSS from "+this.getAttribute("data-source"))):this.textContent=MistUtil.css.prependClass(t,r,!0),n++,e.skin.css.length<=n&&(i.style.opacity="")},""!=s.textContent&&s.callback(s.textContent),i.appendChild(s)}MistUtil.class.add(i,r);var o=MistUtil.getBrowser();return o&&MistUtil.class.add(i,"browser-"+o),i}MistSkins.default={structure:{main:{if:function(){return!!this.info.hasVideo&&"audio"!=this.source.type.split("/")[1]},then:{type:"placeholder",classes:["mistvideo"],children:[{type:"hoverWindow",mode:"pos",style:{position:"relative"},transition:{hide:"left: 0; right: 0; bottom: -43px;",show:"bottom: 0;",viewport:"left:0; right: 0; top: -1000px; bottom: 0;"},button:{type:"videocontainer"},children:[{type:"loading"},{type:"error"}],window:{type:"controls"}}]},else:{type:"container",classes:["mistvideo"],style:{overflow:"visible"},children:[{type:"controls",classes:["mistvideo-novideo"],style:{width:"480px"}},{type:"loading"},{type:"error"},{if:function(){return"stock"==this.options.controls},then:{type:"video",style:{position:"absolute"}},else:{type:"video",style:{position:"absolute",display:"none"}}}]}},videocontainer:{type:"video"},controls:{if:function(){return!!(this.player&&this.player.api&&this.player.api.play)},then:{type:"container",classes:["mistvideo-column"],children:[{type:"progress",classes:["mistvideo-pointer"]},{type:"container",classes:["mistvideo-main","mistvideo-padding","mistvideo-row","mistvideo-background"],children:[{type:"play",classes:["mistvideo-pointer"]},{type:"currentTime"},{type:"totalTime"},{type:"container",classes:["mistvideo-align-right"],children:[{type:"container",children:[{type:"container",classes:["mistvideo-volume_container"],children:[{type:"volume",mode:"horizontal",size:{height:22},classes:["mistvideo-pointer"]}]},{type:"speaker",classes:["mistvideo-pointer"],style:{"margin-left":"-2px"}}]},{if:function(){return"size"in this&&this.size.width>200||!this.info.hasVideo||"audio"==this.source.type.split("/")[1]},then:{type:"container",children:[{type:"loop",classes:["mistvideo-pointer"]},{type:"fullscreen",classes:["mistvideo-pointer"]}]}},{type:"hoverWindow",mode:"pos",transition:{hide:"right: -1000px; bottom: 44px;",show:"right: 5px;",viewport:"right: 0; left: 0; bottom: 0; top: -1000px"},button:{type:"settings",classes:["mistvideo-pointer"]},window:{type:"submenu"}}]}]}]},else:{if:function(){return!(!this.player||!this.player.api)},then:{type:"hoverWindow",mode:"pos",transition:{hide:"right: -1000px; bottom: 44px;",show:"right: 2.5px;",viewport:"right: 0; left: -1000px; bottom: 0; top: -1000px"},style:{right:"5px",left:"auto"},button:{type:"settings",classes:["mistvideo-background","mistvideo-padding"]},window:{type:"submenu"}}}},submenu:{type:"container",style:{width:"80%",maxWidth:"25em",zIndex:2},classes:["mistvideo-padding","mistvideo-column","mistvideo-background"],children:[{type:"tracks"},{if:function(){return"size"in this&&this.size.width<=200},then:{type:"container",classes:["mistvideo-center"],children:[{type:"loop",classes:["mistvideo-pointer"]},{type:"fullscreen",classes:["mistvideo-pointer"]}]}}]},placeholder:{type:"container",classes:["mistvideo","mistvideo-delay-display"],children:[{type:"placeholder"},{type:"loading"},{type:"error"}]},secondaryVideo:function(e){return{type:"hoverWindow",classes:["mistvideo"],mode:"pos",transition:{hide:"left: 10px; bottom: -40px;",show:"bottom: 10px;",viewport:"left: 0; right: 0; top: 0; bottom: 0"},button:{type:"container",children:[{type:"videocontainer"}]},window:{type:"switchVideo",classes:["mistvideo-controls","mistvideo-padding","mistvideo-background","mistvideo-pointer"],containers:e}}}},css:{skin:misthost+"/skins/default.css"},icons:{blueprints:{play:{size:45,svg:''},largeplay:{size:45,svg:''},pause:{size:45,svg:''},speaker:{size:45,svg:''},volume:{size:{width:100,height:45},svg:function(){var e=MistUtil.createUnique();return''}},muted:{size:45,svg:''},fullscreen:{size:45,svg:''},loop:{size:45,svg:''},settings:{size:45,svg:''},loading:{size:100,svg:''},timeout:{size:25,svg:function(e){e&&e.delay||(e={delay:10});var t=e.delay,i=MistUtil.createUnique();return''}},popout:{size:45,svg:''},switchvideo:{size:45,svg:''}}},blueprints:{container:function(){return document.createElement("div")},video:function(){var e=this;return MistUtil.event.addListener(e.video,"contextmenu",function(t){t.preventDefault(),e.container.setAttribute("data-show-submenu",""),e.container.removeAttribute("data-hide-submenu"),e.container.removeAttribute("data-hidecursor");var i=function(){e.container.removeAttribute("data-show-submenu"),e.container.removeEventListener("mouseout",i)};MistUtil.event.addListener(e.container,"mouseout",i)}),e.video.hideTimer=!1,e.video.hideCursor=function(){this.hideTimer&&clearTimeout(this.hideTimer),this.hideTimer=e.timers.start(function(){e.container.setAttribute("data-hidecursor","");var t=e.container.querySelector(".mistvideo-controls");t&&t.parentNode.setAttribute("data-hidecursor","")},3e3)},MistUtil.event.addListener(e.video,"mousemove",function(){e.container.removeAttribute("data-hidecursor");var t=e.container.querySelector(".mistvideo-controls");t&&t.parentNode.removeAttribute("data-hidecursor"),e.video.hideCursor()}),MistUtil.event.addListener(e.video,"mouseout",function(){e.video.hideTimer&&e.timers.stop(e.video.hideTimer)}),e.options.autoplay&&MistUtil.event.addListener(e.video,"canplay",function(){if(e.player.api.paused){var t=e.player.api.play();t&&t.catch(function(t){if(!e.destroyed&&(e.log("Autoplay failed. Retrying with muted audio.."),e.info.hasVideo)){e.player.api.muted=!0,MistUtil.event.send("volumechange",null,e.video);var i=e.player.api.play();i&&i.catch(function(){if(!e.destroyed){e.log("Autoplay failed even with muted video. Unmuting and showing play button."),e.player.api.muted=!1;var t=e.skin.icons.build("largeplay",150);MistUtil.class.add(t,"mistvideo-pointer"),e.container.appendChild(t),MistUtil.event.addListener(t,"click",function(){e.player.api.paused&&e.player.api.play()});var i=function(){e.container.removeChild(t),e.video.removeEventListener("play",i)};MistUtil.event.addListener(e.video,"play",i)}}).then(function(){if(!e.destroyed){e.log("Autoplay worked! Video will be unmuted on mouseover if the page has been interacted with.");var t=e.skin.icons.build("muted",100);MistUtil.class.add(t,"mistvideo-pointer"),e.container.appendChild(t),MistUtil.event.addListener(t,"click",function(){e.player.api.muted=!1,e.container.removeChild(t)});var i=!1,r=function(){i=!0,document.body.removeEventListener("click",r)};MistUtil.event.addListener(document.body,"click",r,e.video);var n=function(){i&&(e.player.api.muted=!1,e.video.removeEventListener("mouseenter",n),e.log("Re-enabled sound"))};MistUtil.event.addListener(e.video,"mouseenter",n);var a=function(){e.video.muted||(t.parentNode&&e.container.removeChild(t),e.video.removeEventListener("volumechange",a),document.body.removeEventListener("click",r),e.video.removeEventListener("mouseenter",n))};MistUtil.event.addListener(e.video,"volumechange",a)}},function(){})}})}}),this.video},videocontainer:function(){return this.UI.buildStructure(this.skin.structure.videocontainer)},secondaryVideo:function(e){e||(e={}),e.options||(e.options={});var t=this;"secondary"in t||(t.secondary=[]);var i=MistUtil.object.extend({},t.options);i=MistUtil.object.extend(i,e.options),t.secondary.push(i);var r={primary:t,secondary:!1};i.target=document.createElement("div"),delete i.container;var n={};return i.MistVideoObject=n,MistUtil.event.addListener(i.target,"initialized",function(){var e=n.reference;i.MistVideo=e,r.secondary=e,e.player.api.muted=!0,e.player.api.loop=!1;for(var a=i.target.querySelectorAll(".mistvideo-controls"),s=0;s30)e.player.api.pausedesync=!0,e.player.api.currentTime=this.currentTime,e.log("Re-syncing with main video by seeking (desync: "+t+"s)");else if(i>.01){var r=.1;i<1&&(r=.05),(r=1+r*Math.sign(t))!=e.player.api.playbackRate&&e.log("Re-syncing by changing the playback rate (desync: "+Math.round(1e3*t)+"ms, rate: "+r+")"),e.player.api.playbackRate=r}else 1!=e.player.api.playbackRate&&(e.player.api.playbackRate=1,e.log("Sync with main video achieved (desync: "+Math.round(1e3*t)+"ms)"))}},i.target),MistUtil.event.addListener(e.video,"seeked",function(){e.player.api.pausedesync=!1})}),i.skin=MistUtil.object.extend({},t.skin,!0),i.skin.structure.main=MistUtil.object.extend({},t.skin.structure.secondaryVideo(r)),mistPlay(t.stream,i),i.target},switchVideo:function(e){var t=document.createElement("div");return t.appendChild(this.skin.icons.build("switchvideo")),MistUtil.event.addListener(t,"click",function(){var t=e.containers.primary,i=e.containers.secondary;function r(e,t){if(e.video.currentTarget==t)return e.video;if(e.secondary)for(var i=0;i .outer_window:not([data-hidecursor]) > .inner_window { "+e.transition.show+" }\n.hover_window_container > .outer_window { "+e.transition.viewport+" }\n.hover_window_container > .outer_window > .inner_window { "+e.transition.hide+" }")),t.classes.push(e.mode),this.UI.buildStructure(t)},draggable:function(e){var t=this.skin.blueprints.container(e),i=this,r=this.skin.icons.build("fullscreen",16);MistUtil.class.remove(r,"fullscreen"),MistUtil.class.add(r,"draggable-icon"),t.appendChild(r),r.style.alignSelf="flex-end",r.style.position="absolute",r.style.cursor="move";var n={},a=function(e){t.style.left=e.clientX-n.x+"px",t.style.top=e.clientY-n.y+"px"},s=function(e){window.removeEventListener("mousemove",a),window.removeEventListener("click",s),MistUtil.event.addListener(r,"click",o)},o=function(e){e.stopPropagation(),r.removeEventListener("click",o),n.x=i.container.getBoundingClientRect().left-(t.getBoundingClientRect().left-e.clientX),n.y=i.container.getBoundingClientRect().top-(t.getBoundingClientRect().top-e.clientY),t.style.position="absolute",t.style.right="auto",t.style.bottom="auto",i.container.appendChild(t),a(e),MistUtil.event.addListener(window,"mousemove",a,t),MistUtil.event.addListener(window,"click",s,t)};return MistUtil.event.addListener(r,"click",o),t},progress:function(){var e=document.createElement("div"),t=document.createElement("div");e.appendChild(t),t.kids={},t.kids.bar=document.createElement("div"),t.kids.bar.className="bar",t.appendChild(t.kids.bar);var i=this.video,r=this.player.api,n=this;t.updateBar=function(e){if(this.kids.bar){if(!isFinite(r.duration))return void(this.kids.bar.style.display="none");this.kids.bar.style.display="",w=Math.min(1,Math.max(0,this.time2perc(e))),this.kids.bar.style.width=100*w+"%"}},t.time2perc=function(e){if(!isFinite(r.duration))return 0;var t=0;if("live"==n.info.type){var i=.001*n.info.meta.buffer_window;t=(e-r.duration+i)/i}else t=e/r.duration;return Math.min(1,Math.max(0,t))},t.buildBuffer=function(e,t){var i=document.createElement("div");return i.className="buffer",i.style.left=100*this.time2perc(e)+"%",i.style.width=100*(this.time2perc(t)-this.time2perc(e))+"%",i},t.updateBuffers=function(e){for(var t=this.querySelectorAll(".buffer"),i=0;i1e3?(t.updateBuffers(r.buffered),a=(new Date).getTime()):s||(s=n.timers.start(function(){e(),s=!1},1e3))}()},t);var o=0,l=!1;MistUtil.event.addListener(i,"timeupdate",function(){!function e(){(new Date).getTime()-o>200&&!d?(t.updateBar(r.currentTime),o=(new Date).getTime()):l||(l=n.timers.start(function(){e(),l=!1},1e3))}()},t),MistUtil.event.addListener(i,"seeking",function(){t.updateBar(r.currentTime)},t),t.getPos=function(e){var t=MistUtil.getPos(this,e);return"live"==n.info.type?(t-1)*(.001*n.info.meta.buffer_window)+n.player.api.duration:!!isFinite(r.duration)&&t*r.duration},t.seek=function(e){var t=this.getPos(e);r.currentTime=t},MistUtil.event.addListener(e,"mouseup",function(e){1==e.which&&t.seek(e)});var c=n.UI.buildStructure({type:"tooltip"});c.style.opacity=0,t.appendChild(c),MistUtil.event.addListener(e,"mouseout",function(){d||(c.style.opacity=0)}),t.moveTooltip=function(e){var t=this.getPos(e);if(!1!==t){c.setText(MistUtil.format.time(t)),c.style.opacity=1;var i=MistUtil.getPos(this,e),r={bottom:20};i>.5?(r.right=100*(1-i)+"%",c.triangle.setMode("bottom","right")):(r.left=100*i+"%",c.triangle.setMode("bottom","left")),c.setPos(r)}else c.style.opacity=0},MistUtil.event.addListener(e,"mousemove",function(e){t.moveTooltip(e)});var d=!1;return MistUtil.event.addListener(e,"mousedown",function(i){if(1==i.which){d=!0,t.updateBar(t.getPos(i));var r=MistUtil.event.addListener(document,"mousemove",function(e){t.updateBar(t.getPos(e)),t.moveTooltip(e)},t),n=MistUtil.event.addListener(document,"mouseup",function(i){1==i.which&&(d=!1,MistUtil.event.removeListener(r),MistUtil.event.removeListener(n),c.style.opacity=0,(!i.path||MistUtil.array.indexOf(i.path,e)<0)&&t.seek(i))},t)}}),e},play:function(){var e=this,t=document.createElement("div");t.appendChild(this.skin.icons.build("play")),t.appendChild(this.skin.icons.build("pause")),t.setState=function(e){this.setAttribute("data-state",e)},t.setState("paused");var i=this.video;return MistUtil.event.addListener(i,"playing",function(){t.setState("playing"),e.options.autoplay=!0},t),MistUtil.event.addListener(i,"pause",function(){t.setState("paused")},t),MistUtil.event.addListener(i,"paused",function(){t.setState("paused")},t),MistUtil.event.addListener(i,"ended",function(){t.setState("paused")},t),MistUtil.event.addListener(t,"click",function(){e.player.api.error&&e.player.api.load(),e.player.api.paused?e.player.api.play():(e.player.api.pause(),e.options.autoplay=!1)}),e.player.api&&MistUtil.event.addListener(e.video,"click",function(){e.player.api.paused?e.player.api.play():MistUtil.isTouchDevice()||(e.player.api.pause(),e.options.autoplay=!1)},t),t},speaker:function(){var e=!1,t=this.info.meta.tracks;for(var i in t)if("audio"==t[i].type){e=!0;break}if(!e)return!1;var r=this.skin.icons.build("speaker"),n=this,a=this.video;return MistUtil.event.addListener(a,"volumechange",function(){n.player.api.volume&&!n.player.api.muted?MistUtil.class.remove(r,"off"):MistUtil.class.add(r,"off")},r),MistUtil.event.addListener(r,"click",function(e){n.player.api.muted=!n.player.api.muted}),r},volume:function(e){var t=!1,i=this.info.meta.tracks;for(var r in i)if("audio"==i[r].type){t=!0;break}if(!t)return!1;var n=document.createElement("div"),a=this.skin.icons.build("volume","size"in e&&e.size);n.appendChild(a);var s=this;a.mode="mode"in e?e.mode:"vertical","vertical"==a.mode&&(a.style.transform="rotate(90deg)"),a.margin={start:.15,end:.1};var o=this.video;a.set=function(e){100!=(e=100-100*Math.pow(1-e/100,2))&&0!=e&&(e=100*this.addPadding(e/100));for(var t=a.querySelectorAll(".slider"),i=0;ii(t)?1:i(e)t[i]?1:t[e]1&&"player"in e&&"api"in e.player&&("setTrack"in e.player.api||"setTracks"in e.player.api||"setSource"in e.player.api)){var b=document.createElement("select");if(b.title="Select another "+l+" track",r[l]=b,b.trackType=l,m.appendChild(b),"subtitle"!=l){var M=document.createElement("option");b.appendChild(M),M.value="",M.appendChild(document.createTextNode("Automatic"))}var k=g(c[MistUtil.object.keys(c)[0]].same);if(k.length)(T=document.createElement("span")).className="mistvideo-description",m.appendChild(T),m.appendChild(document.createTextNode(k.join(" ")));function w(e){return""==e?-1:Number(e)}var U=MistUtil.object.keys(c,function(e,t){return w(e)-w(t)});for(var u in U){var x=c[U[u]];M=document.createElement("option");b.appendChild(M),M.value="idx"in x?x.idx:x.trackid,MistUtil.object.keys(x.different).length?M.appendChild(document.createTextNode(g(x.different).join(" "))):M.appendChild(document.createTextNode("Track "+(Number(u)+1)))}if(MistUtil.event.addListener(e.video,"playerUpdate_trackChanged",function(t){t.message.type==l&&"none"!=t.message.trackid&&(b.value=t.message.trackid,e.log("Player selected "+l+" track with id "+t.message.trackid))},b),"subtitle"==l){if(MistUtil.event.addListener(b,"change",function(){try{localStorage.mistSubtitleLanguage=c[this.value].lang}catch(e){}if(""!=this.value){var t=MistUtil.object.extend({},c[this.value]);t.label=g(t.describe).join(" "),t.src=MistUtil.http.url.addParam(d,{track:this.value}),e.player.api.setSubtitle(t)}else e.player.api.setSubtitle()}),"localStorage"in window&&null!=localStorage&&"mistSubtitleLanguage"in localStorage)for(var u in c)if(c[u].lang==localStorage.mistSubtitleLanguage){b.value=u;var C=document.createEvent("Event");C.initEvent("change"),b.dispatchEvent(C);break}}else MistUtil.event.addListener(b,"change",function(){this.trackType in n&&(n[this.trackType].checked=!0),a(this.trackType,this.value)})}else{var T;(T=document.createElement("span")).className="mistvideo-description",m.appendChild(T),T.appendChild(document.createTextNode(g(c[y[0]].same).join(" ")))}}}},text:function(e){var t=document.createElement("span");return t.appendChild(document.createTextNode(e.text)),t},placeholder:function(){var e=document.createElement("div"),t=this.calcSize();return e.style.width=t.width+"px",e.style.height=t.height+"px",this.options.poster&&(e.style.background="url('"+this.options.poster+"') no-repeat 50%/contain"),e},timeout:function(e){if(!(!1 in e)){var t="delay"in e?e.delay:5,i=this.skin.icons.build("timeout",!1,{delay:t});return i.timeout=this.timers.start(function(){e.function()},1e3*t),i}},polling:function(){var e=document.createElement("div"),t=this.skin.icons.build("loading");return e.appendChild(t),e},loading:function(){var e=this,t=this.skin.icons.build("loading",50);if("player"in e&&e.player.api){var i=!1;function r(t){e.container.setAttribute("data-loading",t.type),function t(){i||(i=e.timers.start(function(){i=!1,e.monitor.vars&&e.monitor.vars.score>=.999?n():t()},1e3))}()}function n(){e.container.removeAttribute("data-loading"),i&&e.timers.stop(i),i=!1}var a=["waiting","seeking","stalled"];for(var s in a)MistUtil.event.addListener(e.video,a[s],function(t){!this.paused&&"container"in e&&r(t)},t);a=["seeked","playing","canplay","paused","ended"];for(var s in a)MistUtil.event.addListener(e.video,a[s],function(t){"container"in e&&n()},t),MistUtil.event.addListener(e.video,"progress",function(t){"container"in e&&"monitor"in e&&"vars"in e.monitor&&"score"in e.monitor.vars&&e.monitor.vars.score>.99&&n()},t)}return t},error:function(){var e=this,t=document.createElement("div");t.message=function(t,i,r){MistUtil.empty(this);var n=document.createElement("div");if(n.className="message",this.appendChild(n),!r.polling&&!r.passive&&!r.hideTitle){var a=document.createElement("h3");n.appendChild(a),a.appendChild(document.createTextNode("The player has encountered a problem"))}var s=document.createElement("p");if(n.appendChild(s),n.update=function(e){MistUtil.empty(s),s.innerHTML=e},t){e.info.on_error&&(t=e.info.on_error.replace(/\/,t)),n.update(t);var o=document.createElement("p");if(o.className="details mistvideo-description",n.appendChild(o),i)o.appendChild(document.createTextNode(i));else if("decodingIssues"in e.skin.blueprints){if("player"in e&&"api"in e.player&&e.video){if(i=[],void 0!==e.state&&i.push(["Stream state:",e.state]),void 0!==e.player.api.currentTime&&i.push(["Current video time:",MistUtil.format.time(e.player.api.currentTime)]),"video"in e&&"getVideoPlaybackQuality"in e.video){var l=e.video.getVideoPlaybackQuality();"droppedVideoFrames"in l&&"totalVideoFrames"in l&&l.totalVideoFrames&&i.push(["Frames dropped/total:",MistUtil.format.number(l.droppedVideoFrames)+"/"+MistUtil.format.number(l.totalVideoFrames)]),"corruptedVideoFrames"in l&&l.corruptedVideoFrames&&i.push(["Corrupted frames:",MistUtil.format.number(l.corruptedVideoFrames)])}i.push({0:["NETWORK EMPTY:","not yet initialized"],1:["NETWORK IDLE:","resource selected, but not in use"],2:["NETWORK LOADING:","data is being downloaded"],3:["NETWORK NO SOURCE:","could not locate source"]}[e.video.networkState]);if(i.push({0:["HAVE NOTHING:","no information about ready state"],1:["HAVE METADATA:","metadata has been loaded"],2:["HAVE CURRENT DATA:","data for the current playback position is available, but not for the next frame"],3:["HAVE FUTURE DATA:","data for current and next frame is available"],4:["HAVE ENOUGH DATA:","can start playing"]}[e.video.readyState]),!r.passive){var c=document.createElement("table");for(var d in i){var u=document.createElement("tr");for(var p in c.appendChild(u),i[d]){var h=document.createElement("td");u.appendChild(h),h.appendChild(document.createTextNode(i[d][p]))}}o.appendChild(c)}}var m,f=document.createElement("div");f.className="mistvideo-container mistvideo-column",f.style.textAlign="left",f.style.marginBottom="1em",n.appendChild(f),(m=e.UI.buildStructure({type:"forcePlayer"}))&&f.appendChild(m),(m=e.UI.buildStructure({type:"forceType"}))&&f.appendChild(m)}}return n};var i,r=!1,n=!1,a={};if(this.showError=function(s,o){o||(o={softReload:!!(e.player&&e.player.api&&e.player.api.load),reload:!0,nextCombo:!!e.info,polling:!1,passive:!1});var l=o.type?o.type:s;if(!(l in a)){if(!0===o.reload&&(e.options.reloadDelay&&!isNaN(Number(e.options.reloadDelay))?o.reload=Number(e.options.reloadDelay):o.reload=10),o.passive){if(!0===r)return;if(r)return i.update(s),void(n=(new Date).getTime());t.setAttribute("data-passive","")}else t.removeAttribute("data-passive");r&&t.clear(),r=!o.passive||"passive",n=(new Date).getTime();var c=this.log(s,"error"),d=t.message(s,!1,o);i=d;var u=document.createElement("div");if(u.className="mistvideo-buttoncontainer",d.appendChild(u),MistUtil.empty(u),o.softReload){var p={type:"button",label:"Reload video",onclick:function(){e.player.api.load()}};isNaN(o.softReload+"")||(p.delay=o.softReload),u.appendChild(e.UI.buildStructure(p))}if(o.reload){p={type:"button",label:"Reload player",onclick:function(){e.reload()}};isNaN(o.reload+"")||(p.delay=o.reload),u.appendChild(e.UI.buildStructure(p))}if(o.nextCombo){p={type:"button",label:"Next source",onclick:function(){e.nextCombo()}};isNaN(o.nextCombo+"")||(p.delay=o.nextCombo),u.appendChild(e.UI.buildStructure(p))}if(o.ignore){p={type:"button",label:"Ignore",onclick:function(){this.clearError(),a[l]=!0}};isNaN(o.ignore+"")||(p.delay=o.ignore),u.appendChild(e.UI.buildStructure(p))}o.polling&&u.appendChild(e.UI.buildStructure({type:"polling"})),MistUtil.class.add(t,"show"),"container"in e&&e.container.removeAttribute("data-loading"),c.defaultPrevented&&t.clear()}},t.clear=function(){for(var i=t.querySelectorAll("svg.icon.timeout"),n=0;n=t.scrollHeight-5}),r.logs)o(r.logs[l].time,r.logs[l].message,r.logs[l].data);return MistUtil.event.addListener(r.options.target,"log",function(e){if(e.message){var t={};r.player&&r.player.api&&"currentTime"in r.player.api&&(t.currentTime=r.player.api.currentTime),o(new Date,e.message,t)}},e),MistUtil.event.addListener(r.options.target,"error",function(e){if(e.message){var t={type:"error"};r.player&&r.player.api&&"currentTime"in r.player.api&&(t.currentTime=r.player.api.currentTime),o(new Date,e.message,t)}},e),e},decodingIssues:function(){if(this.player){var e=this,t=document.createElement("div");if(e.player.api){var i={"Playback score":function(){if("monitor"in e){if("vars"in e.monitor&&"score"in e.monitor.vars&&e.monitor.vars.values.length){var t=e.monitor.vars.values[e.monitor.vars.values.length-1];if("score"in t){Math.min(1,Math.max(0,t.score));return{x:t.clock,y:Math.min(1,Math.max(0,t.score)),options:{y:{min:0,max:1},x:{count:10}},val:Math.round(100*Math.min(1,Math.max(0,e.monitor.vars.score)))+"%"}}}return 0}},"Corrupted frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return t.corruptedVideoFrames?{val:MistUtil.format.number(t.corruptedVideoFrames),x:.001*(new Date).getTime(),y:t.corruptedVideoFrames,options:{x:{count:10}}}:0}},"Dropped frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return t.droppedVideoFrames?MistUtil.format.number(t.droppedVideoFrames):0}},"Total frames":function(){if(e.player.api&&"getVideoPlaybackQuality"in e.player.api){var t=e.player.api.getVideoPlaybackQuality();if(t)return MistUtil.format.number(t.totalVideoFrames)}},"Decoded audio":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.webkitAudioDecodedByteCount)},"Decoded video":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.webkitVideoDecodedByteCount)},"Negative acknowledgements":function(){if(e.player.api)return MistUtil.format.number(e.player.api.nackCount)},"Picture losses":function(){return MistUtil.format.number(e.player.api.pliCount)},"Packets lost":function(){return MistUtil.format.number(e.player.api.packetsLost)},"Packets received":function(){return MistUtil.format.number(e.player.api.packetsReceived)},"Bytes received":function(){if(e.player.api)return MistUtil.format.bytes(e.player.api.bytesReceived)},"Local latency [ms]":function(){if(e.player.api&&"getLatency"in e.player.api){var t=e.player.api.getLatency();return t?new Promise(function(e,i){t.then(function(t){var i=[];for(var r in t)t[r]&&i.push(r[0]+Math.round(1e3*t[r]));i.length?e(i.join(" ")):e()},i)}):new Promise(function(e,t){e()},function(){})}}},r=[];for(var n in i)void 0!==i[n]()&&a({name:n,function:i[n]});t.update=function(){for(var i in r)r[i]();e.timers.start(function(){t.update()},1e3)},t.update()}return t}function a(e){var i=document.createElement("label");t.appendChild(i),i.style.display="none";var n=document.createElement("span");i.appendChild(n),n.appendChild(document.createTextNode(e.name+":")),n.className="mistvideo-description";var a=document.createElement("span");i.appendChild(a);var s=document.createTextNode(e.value?e.value:"");a.appendChild(s);var o=document.createElement("span");a.appendChild(o),i.set=function(e){if(0!==e&&(this.style.display=""),"object"==typeof e){if(e instanceof Promise)return void e.then(function(e){i.set(e)},function(){});if("val"in e&&(s.nodeValue=e.val,a.className="value"),o.children.length)return(t=o.children[0]).addData(e);var t=MistUtil.createGraph({x:[e.x],y:[e.y]},e.options);return o.style.display="",MistUtil.empty(o),o.appendChild(t)}return s.nodeValue=e},t.appendChild(i),r.push(function(){var t=e.function();i.set(t)})}},forcePlayer:function(){var e=document.createElement("label");e.title="Reload MistVideo and use the selected player";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force player: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");for(var a in r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic")),mistplayers){n=document.createElement("option");r.appendChild(n),n.value=a,n.appendChild(document.createTextNode(mistplayers[a].name))}return this.options.forcePlayer&&(r.value=this.options.forcePlayer),MistUtil.event.addListener(r,"change",function(){t.options.forcePlayer=""!=this.value&&this.value,t.options.forcePlayer!=t.playerName&&t.reload()}),e},forceType:function(){if(this.info){var e=document.createElement("label");e.title="Reload MistVideo and use the selected protocol";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force protocol: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic"));var a={};for(var s in t.info.source){var o=t.info.source[s];if(!(o.type in a)){a[o.type]=1;n=document.createElement("option");r.appendChild(n),n.value=o.type,n.appendChild(document.createTextNode(MistUtil.format.mime2human(o.type)))}}return this.options.forceType&&(r.value=this.options.forceType),MistUtil.event.addListener(r,"change",function(){t.options.forceType=""!=this.value&&this.value,t.source&&t.options.forceType==t.source.type||t.reload()}),e}},forceSource:function(){var e=document.createElement("label");e.title="Reload MistVideo and use the selected source";var t=this,i=document.createElement("span");e.appendChild(i),i.appendChild(document.createTextNode("Force source: "));var r=document.createElement("select");e.appendChild(r);var n=document.createElement("option");for(var a in r.appendChild(n),n.value="",n.appendChild(document.createTextNode("Automatic")),t.info.source){var s=t.info.source[a];n=document.createElement("option");r.appendChild(n),n.value=a,n.appendChild(document.createTextNode(s.url+" ("+MistUtil.format.mime2human(s.type)+")"))}return this.options.forceSource&&(r.value=this.options.forceSource),MistUtil.event.addListener(r,"change",function(){t.options.forceSource=""!=this.value&&this.value,t.options.forceSource!=t.source.index&&t.reload()}),e}}},MistSkins.dev.css={skin:misthost+"/skins/dev.css"},MistSkins.dev.structure.submenu=MistUtil.object.extend({},MistSkins.default.structure.submenu,!0),MistSkins.dev.structure.submenu.type="draggable",MistSkins.dev.structure.submenu.style.width="25em",MistSkins.dev.structure.submenu.children.unshift({type:"container",style:{flexShrink:1},classes:["mistvideo-column"],children:[{if:function(){return this.playerName&&this.source},then:{type:"container",classes:["mistvideo-description"],style:{display:"block"},children:[{type:"playername",style:{display:"inline"}},{type:"text",text:"is playing",style:{margin:"0 0.2em"}},{type:"mimetype"}]}},{type:"log"},{type:"decodingIssues"},{type:"container",classes:["mistvideo-column","mistvideo-devcontrols"],style:{"font-size":"0.9em"},children:[{type:"text",text:"Player control"},{type:"container",classes:["mistvideo-devbuttons"],style:{"flex-wrap":"wrap"},children:[{type:"button",title:"Build MistVideo again",label:"MistVideo.reload();",onclick:function(){this.reload()}},{type:"button",title:"Switch to the next available player and source combination",label:"MistVideo.nextCombo();",onclick:function(){this.nextCombo()}}]},{type:"forcePlayer"},{type:"forceType"}]}]});var mistplayers={};function MistPlayer(){}function mistPlay(e,t){return new MistVideo(e,t)}function MistVideo(e,t){var i=this;function r(e){if("meta"in e&&"tracks"in e.meta){var t=e.meta.tracks;for(var i in t)if("video"==t[i].type)return!0}return!1}function n(e){if(i.player&&i.player.api&&i.player.api.unload&&(i.log("Received new stream info while a player was already loaded: unloading player"),i.player.api.unload()),i.info=e,i.info.updated=new Date,MistUtil.event.send("haveStreamInfo",e,i.options.target),i.log("Stream info was loaded succesfully."),"error"in e){var n=e.error;return"on_error"in e&&(i.log(n),n=e.on_error),void i.showError(n,{reload:!0,hideTitle:!0})}if(i.calcSize=function(e){e||(e={width:!1,height:!1});var r=e.width||!!("width"in t&&t.width)&&t.width,n=e.height||!!("height"in t&&t.height)&&t.height;if(this.info&&"source"in this.info)if(this.info.hasVideo&&"audio"!=this.source.type.split("/")[1]){if(!r||!n){var a=i.info.width/i.info.height;if(r||n)r?n=r/a:r=n*a;else{var s="maxwidth"in t&&t.maxwidth?t.maxwidth:window.innerWidth,o="maxheight"in t&&t.maxheight?t.maxheight:window.innerHeight;r=i.info.width,n=i.info.height;function l(e){r/=e,n/=e}r<426&&l(r/426),n<240&&l(n/240),s&&r>s&&l(r/s),o&&n>o&&l(n/o)}}}else r||(r=480),n||(n=42);else r=640,n=480;return this.size={width:Math.round(r),height:Math.round(n)},this.size},e.hasVideo=r(e),"live"==e.type){var a=0;for(var s in i.info.meta.tracks)a=Math.max(a,i.info.meta.tracks[s].lastms);e.lastms=a}else{var o=i.resumeTime;if(o){var l=function(){i.player&&i.player.api&&(i.player.api.currentTime=o),this.removeEventListener("initialized",l)};MistUtil.event.addListener(i.options.target,"initialized",l)}}i.choosePlayer()?(i.player=new mistplayers[i.playerName].player,i.player.onreadylist=[],i.player.onready=function(e){this.onreadylist.push(e)},i.player.build(i,function(e){if(i.log("Building new player"),i.container.removeAttribute("data-loading"),i.video=e,"api"in i.player){i.monitor={MistVideo:i,delay:1,averagingSteps:20,threshold:function(){return"webrtc"==this.MistVideo.source.type?.95:.75},init:function(){if(!this.vars||!this.vars.active){this.MistVideo.log("Enabling monitor"),this.vars={values:[],score:!1,active:!0};var e=this;!function t(){e.vars&&e.vars.active&&(e.vars.timer=e.MistVideo.timers.start(function(){var i=e.calcScore();!1!==i&&e.check(i)&&e.action(),t()},1e3*e.delay))}()}},destroy:function(){this.vars&&this.vars.active&&(this.MistVideo.log("Disabling monitor"),this.MistVideo.timers.stop(this.vars.timer),delete this.vars)},reset:function(){this.vars&&this.vars.active?(this.MistVideo.log("Resetting monitor"),this.vars.values=[]):this.init()},calcScore:function(){var e=this.vars.values;if(e.push(this.getValue()),e.length<=1)return!1;var t=this.valueToScore(e[0],e[e.length-1]);return e.length>this.averagingSteps&&e.shift(),t=Math.max(t,e[e.length-1].score),this.vars.score=t,t},valueToScore:function(e,t){var i=1;return"player"in this.MistVideo&&"api"in this.MistVideo.player&&"playbackRate"in this.MistVideo.player.api&&(i=this.MistVideo.player.api.playbackRate),(t.video-e.video)/(t.clock-e.clock)/i},getValue:function(){var e={clock:.001*(new Date).getTime(),video:this.MistVideo.player.api.currentTime};return this.vars.values.length&&(e.score=this.valueToScore(this.vars.values[this.vars.values.length-1],e)),e},check:function(e){return!(this.vars.values.length<.5*this.averagingSteps)&&(e=2))for(var p in c[l.inner].list)if(c[l.inner].current=p,!(d(l.inner)>=1)){a=c.source.list[c.source.current];var h=c.player.list[c.player.current].shortname,m=mistplayers[h];if(m.isMimeSupported(a.type)&&m.isBrowserSupported(a.type,a,i))return{player:h,source:a,source_index:c.source.current};t||i.log("Checking "+m.name+" with "+a.type+".. Nope.")}return!1},this.choosePlayer=function(){i.log("Checking available players..");var e=this.checkCombo();if(!e)return!1;var t=mistplayers[e.player],r=e.source;return i.log("Found a working combo: "+t.name+" with "+r.type+" @ "+r.url),i.playerName=e.player,(r=MistUtil.object.extend({},r)).index=e.source_index,r.url=i.urlappend(r.url),i.source=r,MistUtil.event.send("comboChosen","Player/source combination selected",i.options.target),!0},i.calcSize=function(){return{width:640,height:480}},MistUtil.empty(i.options.target),new MistSkin(i),i.container=new MistUI(i,i.skin.structure.placeholder),i.options.target.appendChild(i.container),i.container.setAttribute("data-loading",""),"WebSocket"in window){!function e(){i.log("Opening stream status stream..");var t=i.options.host.replace(/^http/i,"ws"),s=new WebSocket(i.urlappend(t+"/json_"+encodeURIComponent(i.stream)+".js"));i.socket=s,s.die=!1,s.destroy=function(){this.die=!0,this.close()},s.onopen=function(e){this.wasConnected=!0},s.onclose=function(t){if(!this.die)return this.wasConnected?(i.log("Reopening websocket.."),void e()):void a()};var o=!1;s.addEventListener("message",function(e){var t=JSON.parse(e.data);if(t||i.showError("Error while parsing stream status stream. Obtained: "+e.data.toString(),{reload:!0}),"error"in t){var a;switch(e=t.error,"on_error"in t&&(i.log(e),e=t.on_error),i.state=t.error,t.error){case"Stream is offline":i.info=!1,i.player&&i.player.api&&i.player.api.currentTime&&(i.resumeTime=i.player.api.currentTime);case"Stream is initializing":case"Stream is booting":case"Stream is waiting for data":case"Stream is shutting down":case"Stream status is invalid?!":if(i.player&&i.player.api&&!i.player.api.paused)return i.log(t.error,"error"),o||(o=MistUtil.event.addListener(i.video,"ended",function(){i.showError(t.error,{polling:!0})})),void(o=MistUtil.event.addListener(i.video,"waiting",function(){i.showError(t.error,{polling:!0})}));a={polling:!0};break;default:a={reload:!0}}i.showError(e,a)}else{if(i.state="Stream is online",i.clearError(),o&&MistUtil.event.removeListener(o),!i.info)return void n(t);var s=function e(t,i){if(t==i)return!1;if("object"==typeof t&&void 0!==i){var r={};for(var n in t)if(!(MistUtil.array.indexOf(["lastms","hasVideo"],n)>=0)){var a=e(t[n],i[n]);a&&(r[n]=!0===a?[t[n],i[n]]:a)}for(var n in i)MistUtil.array.indexOf(["lastms","hasVideo"],n)>=0||n in t||(r[n]=[t[n],i[n]]);return!!MistUtil.object.keys(r).length&&r}return!0}(t,i.info);if(s){if("source"in s)return void("error"in i.info&&i.reload());i.info=MistUtil.object.extend(i.info,t),i.info.updated=new Date;var l=!1;for(var c in s)switch(c){case"meta":for(var d in s[c])switch(d){case"tracks":i.info.hasVideo=r(i.info),MistUtil.event.send("metaUpdate_tracks",t,i.video)}break;case"width":case"height":l=!0}l&&i.player.resize()}else i.log("Metachange: no differences detected")}})}()}else a();return this.unload=function(){if(!this.destroyed){for(var e in this.log("Unloading.."),this.destroyed=!0,this.timers.stop("all"),this.errorListeners){var t=this.errorListeners[e];if(t.src in MistUtil.scripts.list){var r=MistUtil.array.indexOf(MistUtil.scripts.list[t.src].subscribers);r>=0&&MistUtil.scripts.list[t.src].subscribers.splice(r,1)}}if("monitor"in i&&"destroy"in i.monitor&&i.monitor.destroy(),this.socket&&this.socket.destroy(),this.player&&this.player.api&&("pause"in this.player.api&&this.player.api.pause(),"setSource"in this.player.api&&this.player.api.setSource(""),"unload"in this.player.api))try{this.player.api.unload()}catch(n){i.log("Error while unloading player: "+n.message)}if(this.UI&&this.UI.elements)for(var e in this.UI.elements){var n=this.UI.elements[e];if("attachedListeners"in n)for(var e in n.attachedListeners)MistUtil.event.removeListener(n.attachedListeners[e]);n.parentNode&&n.parentNode.removeChild(n)}this.video&&MistUtil.empty(this.video),"container"in this&&(MistUtil.empty(this.container),delete this.container),MistUtil.empty(this.options.target),delete this.video}},this.reload=function(){var e="player"in this&&"api"in this.player&&this.player.api.currentTime;if(this.unload(),i=mistPlay(this.stream,this.options),e&&"live"!=this.info.type){var t=function(){i.player&&i.player.api&&(i.player.api.currentTime=e),this.removeEventListener("initialized",t)};MistUtil.event.addListener(this.options.target,"initialized",t)}return i},this.nextCombo=function(){var e=!1;"player"in this&&"api"in this.player&&(e=this.player.api.currentTime);var t={source:this.source.index,player:this.playerName};if(!this.checkCombo({startCombo:t},!0)){if(!this.checkCombo({startCombo:!1},!0))return;t=!1}this.unload();var r=this.options;if(r.startCombo=t,i=mistPlay(this.stream,r),e&&isFinite(e)&&"live"!=this.info.type){var n=function(){"player"in i&&"api"in i.player&&(i.player.api.currentTime=e),this.removeEventListener("initialized",n)};MistUtil.event.addListener(r.target,"initialized",n)}},this.onPlayerBuilt=function(){},t.MistVideoObject&&(t.MistVideoObject.reference=this),this} \ No newline at end of file diff --git a/embed/min/skins/default.css b/embed/min/skins/default.css index a352bd0a..aeaab267 100644 --- a/embed/min/skins/default.css +++ b/embed/min/skins/default.css @@ -73,7 +73,7 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans .mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative} .mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px} .mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s} -.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2} +.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2;transition:width .2s} .mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill} .mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)} .mistvideo-play[data-state=playing] svg.play{display:none} diff --git a/embed/min/skins/dev.css b/embed/min/skins/dev.css index 8aa0d899..7cd77dba 100644 --- a/embed/min/skins/dev.css +++ b/embed/min/skins/dev.css @@ -73,7 +73,7 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans .mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative} .mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px} .mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s} -.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2} +.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2;transition:width .2s} .mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill} .mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)} .mistvideo-play[data-state=playing] svg.play{display:none} diff --git a/embed/min/wrappers/mews.js b/embed/min/wrappers/mews.js new file mode 100644 index 00000000..b37bcaef --- /dev/null +++ b/embed/min/wrappers/mews.js @@ -0,0 +1 @@ +mistplayers.mews={name:"MSE websocket player",mimes:["ws/video/mp4","ws/video/webm"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,t,i){if(!("WebSocket"in window)||!("MediaSource"in window)){return false}if(location.protocol.replace(/^http/,"ws")!=MistUtil.http.url.split(t.url.replace(/^http/,"ws")).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(navigator.platform.toUpperCase().indexOf("MAC")>=0){return false}function n(e){function t(t){return("0"+e.init.charCodeAt(t).toString(16)).slice(-2)}switch(e.codec){case"AAC":return"mp4a.40.2";case"MP3":return"mp4a.40.34";case"AC3":return"ec-3";case"H264":return"avc1."+t(1)+t(2)+t(3);case"HEVC":return"hev1."+t(1)+t(6)+t(7)+t(8)+t(9)+t(10)+t(11)+t(12);default:return e.codec.toLowerCase()}}var s={};for(var r in i.info.meta.tracks){if(i.info.meta.tracks[r].type!="meta"){s[n(i.info.meta.tracks[r])]=i.info.meta.tracks[r].codec}}var a=e.split("/")[2];function o(e){return MediaSource.isTypeSupported("video/"+a+';codecs="'+e+'"')}t.supportedCodecs=[];for(var r in s){var u=o(r);if(u){t.supportedCodecs.push(s[r])}}if(!i.options.forceType&&!i.options.forcePlayer){if(t.supportedCodecs.length0},player:function(){}};var p=mistplayers.mews.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=document.createElement("video");i.setAttribute("playsinline","");var n=["autoplay","loop","poster"];for(var s in n){var r=n[s];if(e.options[r]){i.setAttribute(r,e.options[r]===true?"":e.options[r])}}if(e.options.muted){i.muted=true}if(e.info.type=="live"){i.loop=false}if(e.options.controls=="stock"){i.setAttribute("controls","")}i.setAttribute("crossorigin","anonymous");this.setSize=function(e){i.style.width=e.width+"px";i.style.height=e.height+"px"};var a=this;function o(){if(a.ws.readyState==a.ws.OPEN&&a.ms.readyState=="open"&&a.sb){t(i);if(e.options.autoplay){a.api.play()}return true}}this.msinit=function(){return new Promise(function(e,t){a.ms=new MediaSource;i.src=URL.createObjectURL(a.ms);a.ms.onsourceopen=function(){e()};a.ms.onsourceclose=function(e){console.error("ms close",e);u({type:"stop"})};a.ms.onsourceended=function(e){console.error("ms ended",e);function t(e,t,n){var s,r;s=new Blob([e],{type:n});r=window.URL.createObjectURL(s);i(r,t);setTimeout(function(){return window.URL.revokeObjectURL(r)},1e3)}function i(e,t){var i;i=document.createElement("a");i.href=e;i.download=t;document.body.appendChild(i);i.style="display: none";i.click();i.remove()}if(a.debugging){var n=0;for(var s=0;s=500){s=0;a.sb._clean(10)}else{s++}var t=n.slice();n=[];for(var r in t){if(!a.sb){if(a.debugging){console.warn("I was doing on_updateend but the sb was reset")}break}if(a.sb.updating){n.concat(t.slice(r));if(a.debugging){console.warn("I was doing on_updateend but was interrupted")}break}t[r](r0&&!a.sb.updating&&!i.error){a.sb._append(this.queue.shift())}});a.sb.error=function(e){console.error("sb error",e)};a.sb.abort=function(e){console.error("sb abort",e)};a.sb._doNext=function(e){n.push(e)};a.sb._do=function(e){if(this.updating||this._busy){this._doNext(e)}else{e()}};a.sb._append=function(e){if(!e){return}if(!e.buffer){return}if(a.debugging){a.sb.appending=new Uint8Array(e)}if(a.sb._busy){if(a.debugging)console.warn("I wanted to append data, but now I won't because the thingy was still busy. Putting it back in the queue.");a.sb.queue.unshift(e);return}a.sb._busy=true;a.sb.appendBuffer(e)};if(a.msgqueue){if(a.msgqueue[0]){var r=false;if(a.msgqueue[0].length){for(var o in a.msgqueue[0]){if(a.sb.updating||a.sb.queue.length||a.sb._busy){a.sb.queue.push(a.msgqueue[0][o])}else{a.sb._append(a.msgqueue[0][o])}}}else{r=true}a.msgqueue.shift();if(a.msgqueue.length==0){a.msgqueue=false}e.log("The newly initialized source buffer was filled with data from a seperate message queue."+(a.msgqueue?" "+a.msgqueue.length+" more message queue(s) remain.":""));if(r){e.log("The seperate message queue was empty; manually triggering any onupdateend functions");a.sb.dispatchEvent(new Event("updateend"))}}}a.sb._clean=function(e){if(!e)e=180;if(i.currentTime>e){a.sb._do(function(){a.sb.remove(0,Math.max(.1,i.currentTime-e))})}};if(a.onsbinit.length){a.onsbinit.shift()()}};this.wsconnect=function(){return new Promise(function(t,n){this.ws=new WebSocket(e.source.url);this.ws.binaryType="arraybuffer";this.ws.onopen=function(){t()};this.ws.onerror=function(t){e.showError("MP4 over WS: websocket error")};this.ws.onclose=function(t){e.log("MP4 over WS: websocket closed")};this.ws.listeners={};this.ws.addListener=function(e,t){if(!(e in this.listeners)){this.listeners[e]=[]}this.listeners[e].push(t)};this.ws.removeListener=function(e,t){if(!(e in this.listeners)){return}var i=this.listeners[e].indexOf(t);if(i<0){return}this.listeners[e].splice(i,1);return true};a.msgqueue=false;var s=1;var r=[];this.ws.onmessage=function(t){if(!t.data){throw"Received invalid data"}if(typeof t.data=="string"){var n=JSON.parse(t.data);if(a.debugging&&n.type!="on_time"){console.log("ws message",n)}switch(n.type){case"on_stop":{var r;r=MistUtil.event.addListener(i,"waiting",function(e){MistUtil.event.send("ended",null,i);MistUtil.event.removeListener(r)});break}case"on_time":{var o=n.data.current-i.currentTime*1e3;var f=a.ws.serverDelay.get();var d=Math.max(500+f,f*2);if(e.info.type!="live"){d+=2e3}if(a.debugging)console.log("on_time received",n.data.current/1e3,"currtime",i.currentTime,s+"x","buffer",Math.round(o),"/",Math.round(d),e.info.type=="live"?"latency:"+Math.round(n.data.end-i.currentTime*1e3)+"ms":"","listeners",a.ws.listeners&&a.ws.listeners.on_time?a.ws.listeners.on_time:0,"msgqueue",a.msgqueue?a.msgqueue.length:0,n.data);if(!a.sb){e.log("Received on_time, but the source buffer is being cleared right now. Ignoring.");break}if(a.sb._duration!=n.data.end*.001){a.sb._duration=n.data.end*.001;MistUtil.event.send("durationchange",null,e.video)}e.info.meta.buffer_window=n.data.end-n.data.begin;a.sb.paused=false;if(e.info.type=="live"){if(s==1){if(n.data.play_rate_curr=="auto"){if(i.currentTime>0){if(o-d>d){s=1.1+Math.min(1,(o-d)/d)*.15;i.playbackRate*=s;e.log("Our buffer is big, so increase the playback speed to catch up.")}else if(o1){if(od){i.playbackRate/=s;s=1;e.log("Our buffer is big enough, so return to real time playback.")}}}else{if(s==1){if(n.data.play_rate_curr=="auto"){if(od){e.log("Our buffer is big, so request a slower download rate.");s=.5;u({type:"set_speed",play_rate:s})}}}else if(s>1){if(o>d){u({type:"set_speed",play_rate:"auto"});s=1;e.log("The buffer is big enough, so ask for realtime download rate.")}}else{if(o0){if(a.sb){a.sb._do(function(){a.sb.remove(0,n.data.current*.001)})}}e.log("Player switched tracks, keeping source buffer as codecs are the same as before.")}else{if(a.debugging){console.warn("Different codecs!");console.warn("video time",i.currentTime,"waiting until",n.data.current*.001)}a.last_codecs=n.data.codecs;if(a.msgqueue){a.msgqueue.push([])}else{a.msgqueue=[[]]}var l=function(){if(a&&a.sb){a.sb._do(function(t){if(!a.sb.updating){if(!isNaN(a.ms.duration))a.sb.remove(0,Infinity);a.sb.queue=[];a.ms.removeSourceBuffer(a.sb);a.sb=null;var s=(n.data.current*.001).toFixed(3);i.src="";a.ms.onsourceclose=null;a.ms.onsourceended=null;if(a.debugging&&t&&t.length){console.warn("There are do_on_updateend functions queued, which I *should* re-apply after clearing the sb.")}a.msinit().then(function(){a.sbinit(n.data.codecs);a.sb.do_on_updateend=t;var r=MistUtil.event.addListener(i,"loadedmetadata",function(){e.log("Buffer cleared, setting playback position to "+MistUtil.format.time(s,{ms:true}));var t=function(){i.currentTime=s;if(i.currentTimei.currentTime){var t=i.buffered.start(0);i.currentTime=t;if(i.currentTime!=t){e()}}}else{e()}})};e()}};t();MistUtil.event.removeListener(r)})})}else{l()}})}else{if(a.debugging){console.warn("sb not available to do clear")}a.onsbinit.push(l)}};if(!n.data.codecs||!n.data.codecs.length){e.showError("Track switch does not contain any codecs, aborting.");e.options.setTracks=false;l();break}if(a.debugging){console.warn("reached switching point",n.data.current*.001,MistUtil.format.time(n.data.current*.001))}l()}}}if(n.type in this.listeners){for(var p=this.listeners[n.type].length-1;p>=0;p--){this.listeners[n.type][p](n)}}return}var b=new Uint8Array(t.data);if(b){if(a.sb&&!a.msgqueue){if(a.sb.updating||a.sb.queue.length||a.sb._busy){a.sb.queue.push(b)}else{a.sb._append(b)}}else{if(!a.msgqueue){a.msgqueue=[[]]}a.msgqueue[a.msgqueue.length-1].push(b)}}else{e.log("Expecting data from websocket, but received none?!")}};this.ws.serverDelay={delays:[],log:function(e){var t=false;switch(e){case"seek":case"set_speed":{t=e;break}case"request_codec_data":{t="codec_data";break}default:{return}}if(t){var i=(new Date).getTime();function n(){a.ws.serverDelay.add((new Date).getTime()-i);a.ws.removeListener(t,n)}a.ws.addListener(t,n)}},add:function(e){this.delays.unshift(e);if(this.delays.length>5){this.delays.splice(5)}},get:function(){if(this.delays.length){let e=0;let t=0;for(null;t=3){break}e+=this.delays[t]}return e/t}return 500}}}.bind(this))};this.wsconnect().then(function(){var t=function(e){a.sbinit(e.data.codecs);o();a.ws.removeListener("codec_data",t)};this.ws.addListener("codec_data",t);u({type:"request_codec_data",supported_codecs:e.source.supportedCodecs})}.bind(this));function u(e){if(!a.ws){throw"No websocket to send to"}if(a.ws.readyState>=a.ws.CLOSING){a.wsconnect().then(function(){u(e)});return}if(a.debugging){console.log("ws send",e)}a.ws.serverDelay.log(e.type);a.ws.send(JSON.stringify(e))}a.findBuffer=function(e){var t=false;for(var n=0;n=e){t=n;break}}return t};this.api={play:function(t){return new Promise(function(n,s){var r=function(o){if(!a.sb){e.log("Attempting to play, but the source buffer is being cleared. Waiting for next on_time.");return}if(e.info.type=="live"){if(t||i.currentTime==0){var u=function(){if(i.buffered.length){var t=a.findBuffer(o.data.current*.001);if(t!==false){if(i.buffered.start(t)>i.currentTime||i.buffered.end(t)i.currentTime){a.sb.paused=false;i.play().then(n).catch(s);a.ws.removeListener("on_time",r)}};a.ws.addListener("on_time",r);var o={type:"play"};if(t){o.seek_time="live"}u(o)})},pause:function(){i.pause();u({type:"hold"});if(a.sb){a.sb.paused=true}},setTracks:function(e){e.type="tracks";e=MistUtil.object.extend({type:"tracks",audio:null,video:null,seek_time:Math.max(0,i.currentTime*1e3-(500+a.ws.serverDelay.get()))},e);u(e)},unload:function(){a.api.pause();a.sb._do(function(){a.sb.remove(0,Infinity);try{a.ms.endOfStream()}catch(e){}});a.ws.close();delete window.mistMewsOnVisibilityChange;document.removeEventListener("visibilitychange",l)}};Object.defineProperty(this.api,"currentTime",{get:function(){return i.currentTime},set:function(t){MistUtil.event.send("seeking",t,i);u({type:"seek",seek_time:Math.max(0,t*1e3-(250+a.ws.serverDelay.get()))});var n=function(e){a.ws.removeListener("seek",n);var s=function(e){a.ws.removeListener("on_time",s);t=(e.data.current*.001).toFixed(3);var n=function(){i.currentTime=t;if(i.currentTime!=t){if(a.debugging)console.log("Failed to set video.currentTime, wanted:",t,"got:",i.currentTime);a.sb._doNext(n)}};n()};a.ws.addListener("on_time",s)};a.ws.addListener("seek",n);i.currentTime=t;e.log("Seeking to "+MistUtil.format.time(t,{ms:true})+" ("+t+")")}});Object.defineProperty(this.api,"duration",{get:function(){return a.sb?a.sb._duration:1}});Object.defineProperty(this.api,"playbackRate",{get:function(){return i.playbackRate},set:function(e){var t=function(e){i.playbackRate=e.data.play_rate};a.ws.addListener("set_speed",t);u({type:"set_speed",play_rate:e==1?"auto":e})}});function f(e){Object.defineProperty(a.api,e,{get:function(){return i[e]},set:function(t){return i[e]=t}})}var d=["volume","buffered","muted","loop","paused",,"error","textTracks","webkitDroppedFrameCount","webkitDecodedFrameCount"];for(var s in d){f(d[s])}MistUtil.event.addListener(i,"ended",function(){if(a.api.loop){a.api.currentTime=0;a.sb._do(function(){a.sb.remove(0,Infinity)})}});var c=false;function l(){if(document.hidden){if(!a.sb.paused){a.api.pause();c=true;if(e.info.type=="live"){c="live"}e.log("Pausing the player as the tab is inactive.")}}else if(c){a.api.play(c=="live");c=false;e.log("Restarting the player as the tab is now active again.")}}if(!window.mistMewsOnVisibilityChange){window.mistMewsOnVisibilityChange=true;document.addEventListener("visibilitychange",l)}var p=false;MistUtil.event.addListener(i,"seeking",function(){p=true;var e=MistUtil.event.addListener(i,"seeked",function(){p=false;MistUtil.event.removeListener(e)})});MistUtil.event.addListener(i,"waiting",function(){if(p){return}var t=a.findBuffer(i.currentTime);if(t!==false){if(t+1=i.buffered.start(n)&&i.currentTime<=i.buffered.end(n)){t=true}e.push([i.buffered.start(n),i.buffered.end(n)])}console.log("waiting","currentTime",i.currentTime,"buffers",e,t?"contained":"outside of buffer","readystate",i.readyState,"networkstate",i.networkState);if(i.readyState>=2&&i.networkState>=2){console.error("Why am I waiting?!")}})}}; \ No newline at end of file diff --git a/embed/min/wrappers/videojs.js b/embed/min/wrappers/videojs.js index a04a1d3a..b91cf0ed 100644 --- a/embed/min/wrappers/videojs.js +++ b/embed/min/wrappers/videojs.js @@ -1 +1 @@ -mistplayers.videojs={name:"VideoJS player",mimes:["html5/application/vnd.apple.mpegurl","html5/application/vnd.apple.mpegurl;version=7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,i,t){if(location.protocol!=MistUtil.http.url.split(i.url).protocol){t.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"&&e=="html5/application/vnd.apple"){t.log("This source ("+e+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){},scriptsrc:function(e){return e+"/videojs.js"}};var p=mistplayers.videojs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,i){var t=this;var r;function o(){if(e.destroyed){return}e.log("Building VideoJS player..");r=document.createElement("video");if(e.source.type!="html5/video/ogg"){r.crossOrigin="anonymous"}var o=e.source.type.split("/");if(o[0]=="html5"){o.shift()}var s=document.createElement("source");s.setAttribute("src",e.source.url);t.source=s;r.appendChild(s);s.type=o.join("/");e.log("Adding "+s.type+" source @ "+e.source.url);MistUtil.class.add(r,"video-js");var n={};if(e.options.autoplay){n.autoplay=true}if(e.options.loop&&e.info.type!="live"){r.setAttribute("loop","")}if(e.options.muted){r.setAttribute("muted","")}if(e.options.poster){n.poster=e.options.poster}if(e.options.controls=="stock"){r.setAttribute("controls","");if(!document.getElementById("videojs-css")){var a=document.createElement("link");a.rel="stylesheet";a.href=e.options.host+"/skins/videojs.css";a.id="videojs-css";document.head.appendChild(a)}}else{n.controls=false}function l(){var e=navigator.userAgent.toLowerCase().match(/android\s([\d\.]*)/i);return e?e[1]:false}var d=MistUtil.getAndroid();if(d&&parseFloat(d)<7){e.log("Detected android < 7: instructing videojs to override native playback");n.html5={hls:{overrideNative:true}};n.nativeAudioTracks=false;n.nativeVideoTracks=false}t.onready(function(){e.log("Building videojs");t.videojs=videojs(r,n,function(){e.log("Videojs initialized")});t.api.unload=function(){if(t.videojs){videojs(r).dispose();t.videojs=false;e.log("Videojs instance disposed")}}});e.log("Built html");if("Proxy"in window&&"Reflect"in window){var p={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,i,t){if(i in p.get){return p.get[i].apply(e,arguments)}var r=e[i];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,i,t){if(i in p.set){return p.set[i].call(e,t)}return e[i]=t}});e.player.api.load=function(){};p.set.currentTime=function(i){e.player.videojs.currentTime(i)};if(e.info.type=="live"){function u(e){var i=0;if(e.buffered.length){i=e.buffered.end(e.buffered.length-1)}return i}var c=0;p.get.duration=function(){if(e.info){var i=(e.info.lastms+(new Date).getTime()-e.info.updated.getTime())*.001;return i}return 0};e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});p.set.currentTime=function(i){var t=e.player.api.currentTime-i;var r=i-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(i)+" ("+Math.round(r*-10)/10+"s from live)");e.player.videojs.currentTime(e.video.currentTime-t)};var f=0;p.get.currentTime=function(){if(e.info){f=e.info.lastms*.001}var i=this.currentTime+f-e.player.api.liveOffset-c;if(isNaN(i)){return 0}return i}}}else{t.api=r}e.player.setSize=function(i){if("videojs"in e.player){e.player.videojs.dimensions(i.width,i.height);r.parentNode.style.width=i.width+"px";r.parentNode.style.height=i.height+"px"}this.api.style.width=i.width+"px";this.api.style.height=i.height+"px"};e.player.api.setSource=function(i){if(!e.player.videojs){return}if(e.player.videojs.src()!=i){e.player.videojs.src({type:e.player.videojs.currentSource().type,src:i})}};e.player.api.setSubtitle=function(e){var i=r.getElementsByTagName("track");for(var t=i.length-1;t>=0;t--){r.removeChild(i[t])}if(e){var o=document.createElement("track");r.appendChild(o);o.kind="subtitles";o.label=e.label;o.srclang=e.lang;o.src=e.src;o.setAttribute("default","")}};if(e.info.type=="live"){var v=MistUtil.event.addListener(r,"loadstart",function(e){MistUtil.event.removeListener(v);MistUtil.event.send("canplay",false,this)});var y=MistUtil.event.addListener(r,"canplay",function(e){if(v){MistUtil.event.removeListener(v)}MistUtil.event.removeListener(y)})}i(r)}if("videojs"in window){o()}else{var s=false;function n(){try{e.video.pause()}catch(e){}e.showError("Error in videojs player");if(!window.mistplayer_videojs_failures){window.mistplayer_videojs_failures=1;e.reload()}else{if(!s){var i=.05*Math.pow(2,window.mistplayer_videojs_failures);e.log("Rate limiter activated: MistPlayer reload delayed by "+Math.round(i*10)/10+" seconds.","error");s=e.timers.start(function(){s=false;delete window.videojs;e.reload()},i*1e3);window.mistplayer_videojs_failures++}}}var a=e.urlappend(mistplayers.videojs.scriptsrc(e.options.host));var l;var d=function(e,i,t,r,o){if(!l){return}if(i==l.src){window.removeEventListener("error",d);n()}return false};window.addEventListener("error",d);var p=console.error;console.error=function(){if(arguments[0]=="VIDEOJS:"){console.error=p;n()}return p.apply(this,arguments)};l=MistUtil.scripts.insert(a,{onerror:function(i){var t="Failed to load videojs.js";if(i.message){t+=": "+i.message}e.showError(t)},onload:o},e)}}; \ No newline at end of file +mistplayers.videojs={name:"VideoJS player",mimes:["html5/application/vnd.apple.mpegurl","html5/application/vnd.apple.mpegurl;version=7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,i,t){if(location.protocol!=MistUtil.http.url.split(i.url).protocol){t.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"&&e=="html5/application/vnd.apple"){t.log("This source ("+e+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){},scriptsrc:function(e){return e+"/videojs.js"}};var p=mistplayers.videojs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,i){var t=this;var r;function o(){if(e.destroyed){return}e.log("Building VideoJS player..");r=document.createElement("video");if(e.source.type!="html5/video/ogg"){r.crossOrigin="anonymous"}r.setAttribute("playsinline","");var o=e.source.type.split("/");if(o[0]=="html5"){o.shift()}var s=document.createElement("source");s.setAttribute("src",e.source.url);t.source=s;r.appendChild(s);s.type=o.join("/");e.log("Adding "+s.type+" source @ "+e.source.url);MistUtil.class.add(r,"video-js");var n={};if(e.options.autoplay){n.autoplay=true}if(e.options.loop&&e.info.type!="live"){r.setAttribute("loop","")}if(e.options.muted){r.setAttribute("muted","")}if(e.options.poster){n.poster=e.options.poster}if(e.options.controls=="stock"){r.setAttribute("controls","");if(!document.getElementById("videojs-css")){var a=document.createElement("link");a.rel="stylesheet";a.href=e.options.host+"/skins/videojs.css";a.id="videojs-css";document.head.appendChild(a)}}else{n.controls=false}function l(){var e=navigator.userAgent.toLowerCase().match(/android\s([\d\.]*)/i);return e?e[1]:false}var d=MistUtil.getAndroid();if(d&&parseFloat(d)<7){e.log("Detected android < 7: instructing videojs to override native playback");n.html5={hls:{overrideNative:true}};n.nativeAudioTracks=false;n.nativeVideoTracks=false}t.onready(function(){e.log("Building videojs");t.videojs=videojs(r,n,function(){e.log("Videojs initialized")});t.api.unload=function(){if(t.videojs){videojs(r).dispose();t.videojs=false;e.log("Videojs instance disposed")}}});e.log("Built html");if("Proxy"in window&&"Reflect"in window){var p={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,i,t){if(i in p.get){return p.get[i].apply(e,arguments)}var r=e[i];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,i,t){if(i in p.set){return p.set[i].call(e,t)}return e[i]=t}});e.player.api.load=function(){};p.set.currentTime=function(i){e.player.videojs.currentTime(i)};if(e.info.type=="live"){function u(e){var i=0;if(e.buffered.length){i=e.buffered.end(e.buffered.length-1)}return i}var c=0;p.get.duration=function(){if(e.info){var i=(e.info.lastms+(new Date).getTime()-e.info.updated.getTime())*.001;return i}return 0};e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});p.set.currentTime=function(i){var t=e.player.api.currentTime-i;var r=i-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(i)+" ("+Math.round(r*-10)/10+"s from live)");e.player.videojs.currentTime(e.video.currentTime-t)};var f=0;p.get.currentTime=function(){if(e.info){f=e.info.lastms*.001}var i=this.currentTime+f-e.player.api.liveOffset-c;if(isNaN(i)){return 0}return i}}}else{t.api=r}e.player.setSize=function(i){if("videojs"in e.player){e.player.videojs.dimensions(i.width,i.height);r.parentNode.style.width=i.width+"px";r.parentNode.style.height=i.height+"px"}this.api.style.width=i.width+"px";this.api.style.height=i.height+"px"};e.player.api.setSource=function(i){if(!e.player.videojs){return}if(e.player.videojs.src()!=i){e.player.videojs.src({type:e.player.videojs.currentSource().type,src:i})}};e.player.api.setSubtitle=function(e){var i=r.getElementsByTagName("track");for(var t=i.length-1;t>=0;t--){r.removeChild(i[t])}if(e){var o=document.createElement("track");r.appendChild(o);o.kind="subtitles";o.label=e.label;o.srclang=e.lang;o.src=e.src;o.setAttribute("default","")}};if(e.info.type=="live"){var v=MistUtil.event.addListener(r,"loadstart",function(e){MistUtil.event.removeListener(v);MistUtil.event.send("canplay",false,this)});var y=MistUtil.event.addListener(r,"canplay",function(e){if(v){MistUtil.event.removeListener(v)}MistUtil.event.removeListener(y)})}i(r)}if("videojs"in window){o()}else{var s=false;function n(){try{e.video.pause()}catch(e){}e.showError("Error in videojs player");if(!window.mistplayer_videojs_failures){window.mistplayer_videojs_failures=1;e.reload()}else{if(!s){var i=.05*Math.pow(2,window.mistplayer_videojs_failures);e.log("Rate limiter activated: MistPlayer reload delayed by "+Math.round(i*10)/10+" seconds.","error");s=e.timers.start(function(){s=false;delete window.videojs;e.reload()},i*1e3);window.mistplayer_videojs_failures++}}}var a=e.urlappend(mistplayers.videojs.scriptsrc(e.options.host));var l;var d=function(e,i,t,r,o){if(!l){return}if(i==l.src){window.removeEventListener("error",d);n()}return false};window.addEventListener("error",d);var p=console.error;console.error=function(){if(arguments[0]=="VIDEOJS:"){console.error=p;n()}return p.apply(this,arguments)};l=MistUtil.scripts.insert(a,{onerror:function(i){var t="Failed to load videojs.js";if(i.message){t+=": "+i.message}e.showError(t)},onload:o},e)}}; \ No newline at end of file diff --git a/embed/min/wrappers/webrtc.js b/embed/min/wrappers/webrtc.js index 013cfa9b..d5a1ad2d 100644 --- a/embed/min/wrappers/webrtc.js +++ b/embed/min/wrappers/webrtc.js @@ -1 +1 @@ -mistplayers.webrtc={name:"WebRTC player",mimes:["webrtc"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,t,n){if(!("WebSocket"in window)||!("RTCPeerConnection"in window)){return false}if(location.protocol.replace(/^http/,"ws")!=MistUtil.http.url.split(t.url.replace(/^http/,"ws")).protocol){n.log("HTTP/HTTPS mismatch for this source");return false}return true},player:function(){}};var p=mistplayers.webrtc.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var n=this;if(typeof WebRTCBrowserEqualizerLoaded=="undefined"||!WebRTCBrowserEqualizerLoaded){var i=document.createElement("script");i.src=e.urlappend(e.options.host+"/webrtc.js");e.log("Retrieving webRTC browser equalizer code from "+i.src);document.head.appendChild(i);i.onerror=function(){e.showError("Failed to load webrtc browser equalizer",{nextCombo:5})};i.onload=function(){n.build(e,t)};return}var r=document.createElement("video");r.setAttribute("playsinline","");var o=["autoplay","loop","poster"];for(var s in o){var a=o[s];if(e.options[a]){r.setAttribute(a,e.options[a]===true?"":e.options[a])}}if(e.options.muted){r.muted=true}if(e.info.type=="live"){r.loop=false}if(e.options.controls=="stock"){r.setAttribute("controls","")}r.setAttribute("crossorigin","anonymous");this.setSize=function(e){r.style.width=e.width+"px";r.style.height=e.height+"px"};MistUtil.event.addListener(r,"loadeddata",g);MistUtil.event.addListener(r,"seeked",g);if(!e.options.autoplay){MistUtil.event.addListener(r,"canplay",function(){var t=MistUtil.event.addListener(r,"play",function(){e.log("Pausing because autoplay is disabled");var i=MistUtil.event.addListener(r,"pause",function(){e.options.autoplay=false;MistUtil.event.removeListener(i)});n.api.pause();MistUtil.event.removeListener(t)})})}var c=0;var l=false;var u=[];this.listeners={on_connected:function(){c=0;l=false;this.webrtc.play();MistUtil.event.send("webrtc_connected",null,r)},on_disconnected:function(){MistUtil.event.send("webrtc_disconnected",null,r);e.log("Websocket sent on_disconnect");if(!l){r.pause()}},on_answer_sdp:function(t){if(!t.result){e.showError("Failed to open stream.");this.on_disconnected();return}e.log("SDP answer received")},on_time:function(t){var n=c;c=t.current*.001-r.currentTime;if(Math.abs(n-c)>1){g()}if((!("paused"in t)||!t.paused)&&r.paused){r.play()}var i=t.end==0?Infinity:t.end*.001;if(i!=p){p=i;MistUtil.event.send("durationchange",i,r)}if(t.tracks&&u!=t.tracks){var o=e.info?MistUtil.tracks.parse(e.info.meta.tracks):[];for(var s in t.tracks){if(u.indexOf(t.tracks[s])<0){var a;for(var l in o){if(t.tracks[s]in o[l]){a=l;break}}if(!a){continue}MistUtil.event.send("playerUpdate_trackChanged",{type:a,trackid:t.tracks[s]},e.video)}}u=t.tracks}},on_seek:function(e){var t=this;MistUtil.event.send("seeked",c,r);if(e.live_point){t.webrtc.playbackrate("auto")}if("seekPromise"in this.webrtc.signaling){r.play().then(function(){if("seekPromise"in t.webrtc.signaling){t.webrtc.signaling.seekPromise.resolve("Play promise resolved")}}).catch(function(){if("seekPromise"in t.webrtc.signaling){t.webrtc.signaling.seekPromise.reject("Play promise rejected")}})}else{r.play()}},on_speed:function(e){this.webrtc.play_rate=e.play_rate_curr;MistUtil.event.send("ratechange",e,r)},on_stop:function(){e.log("Websocket sent on_stop");r.pause();MistUtil.event.send("ended",null,r);l=true}};function f(){this.peerConn=null;this.localOffer=null;this.isConnected=false;this.isConnecting=false;this.play_rate="auto";var t=this;this.on_event=function(i){switch(i.type){case"on_connected":{t.isConnected=true;t.isConnecting=false;break}case"on_answer_sdp":{t.peerConn.setRemoteDescription({type:"answer",sdp:i.answer_sdp}).then(function(){},function(e){console.error(e)});break}case"on_disconnected":{t.isConnected=false;break}}if(i.type in n.listeners){return n.listeners[i.type].call(n,i)}e.log("Unhandled WebRTC event "+i.type+": "+JSON.stringify(i));return false};this.connect=function(n){t.isConnecting=true;function i(e){var t=new Promise(function(t,n){function i(e){var r=RTCRtpReceiver.getCapabilities("video");for(var o=0;o0){setTimeout(function(){i(e-1)},100)}else{n("H264 not found :(")}}i(e)});return t}i(5).catch(function(){e.log("Beware: this device does not seem to be able to play H264.")}).finally(function(){t.signaling=new d(t.on_event);t.peerConn=new RTCPeerConnection;t.peerConn.ontrack=function(e){r.srcObject=e.streams[0];if(n){n()}}})};this.play=function(){if(!this.isConnected){throw"Not connected, cannot play"}this.peerConn.createOffer({offerToReceiveAudio:true,offerToReceiveVideo:true}).then(function(e){t.localOffer=e;t.peerConn.setLocalDescription(e).then(function(){t.signaling.sendOfferSDP(t.localOffer.sdp)},function(e){console.error(e)})},function(e){throw e})};this.stop=function(){if(!this.isConnected){throw"Not connected, cannot stop."}this.signaling.send({type:"stop"})};this.seek=function(n){var i=new Promise(function(i,r){if(!t.isConnected||!t.signaling){if(t.isConnecting){var o=MistUtil.event.addListener(e.video,"loadstart",function(){t.seek(n);MistUtil.event.removeListener(o)});return r("Not connected yet, will seek once connected")}else{return r("Failed seek: not connected")}}t.signaling.send({type:"seek",seek_time:n=="live"?"live":n*1e3});if("seekPromise"in t.signaling){t.signaling.seekPromise.reject("Doing new seek")}t.signaling.seekPromise={resolve:function(e){i("seeked");delete t.signaling.seekPromise},reject:function(e){r("Failed to seek: "+e);delete t.signaling.seekPromise}}});return i};this.pause=function(){if(!this.isConnected){throw"Not connected, cannot pause."}this.signaling.send({type:"pause"})};this.setTrack=function(e){if(!this.isConnected){throw"Not connected, cannot set track."}e.type="tracks";this.signaling.send(e)};this.playbackrate=function(e){if(typeof e=="undefined"){return n.webrtc.play_rate=="auto"?1:n.webrtc.play_rate}if(!this.isConnected){throw"Not connected, cannot change playback rate."}this.signaling.send({type:"set_speed",play_rate:e})};this.getStats=function(e){this.peerConn.getStats().then(function(t){var n={};var i=Array.from(t.entries());for(var r in i){var o=i[r];if(o[1].type=="inbound-rtp"){n[o[0]]=o[1]}}e(n)})};this.connect()}function d(t){this.ws=null;this.ws=new WebSocket(e.source.url.replace(/^http/,"ws"));var n=false;this.ws.onopen=function(){t({type:"on_connected"})};this.ws.onmessage=function(e){try{var n=JSON.parse(e.data);t(n)}catch(t){console.error("Failed to parse a response from MistServer",t,e.data)}};this.ws.onclose=function(e){switch(e.code){case 1006:{}default:{t({type:"on_disconnected",code:e.code});break}}};this.sendOfferSDP=function(e){this.send({type:"offer_sdp",offer_sdp:e})};this.send=function(e){if(!this.ws){throw"Not initialized, cannot send "+JSON.stringify(e)}this.ws.send(JSON.stringify(e))}}this.webrtc=new f;this.api={};var p;Object.defineProperty(this.api,"duration",{get:function(){return p}});Object.defineProperty(this.api,"currentTime",{get:function(){return c+r.currentTime},set:function(e){c=e-r.currentTime;r.pause();var t=n.webrtc.seek(e);MistUtil.event.send("seeking",e,r);if(t){t.catch(function(e){})}}});Object.defineProperty(this.api,"playbackRate",{get:function(){return n.webrtc.playbackrate()},set:function(e){return n.webrtc.playbackrate(e)}});function v(e){Object.defineProperty(n.api,e,{get:function(){return r[e]},set:function(t){return r[e]=t}})}var h=["volume","muted","loop","paused",,"error","textTracks","webkitDroppedFrameCount","webkitDecodedFrameCount"];for(var s in h){v(h[s])}function b(e){if(e in r){n.api[e]=function(){return r[e].call(r,arguments)}}}var h=["load","getVideoPlaybackQuality"];for(var s in h){b(h[s])}n.api.play=function(){var t;if(n.api.currentTime){t=n.api.currentTime}if(e.info&&e.info.type=="live"){t="live"}if(t){var i=new Promise(function(i,r){if(!n.webrtc.isConnected&&n.webrtc.peerConn.iceConnectionState!="completed"){if(!n.webrtc.isConnecting){e.log("Received call to play while not connected, connecting "+n.webrtc.peerConn.iceConnectionState);n.webrtc.connect(function(){n.webrtc.seek(t).then(function(e){i("played "+e)}).catch(function(e){r(e)})})}else{r("Still connecting")}}else{n.webrtc.seek(t).then(function(e){i("played "+e)}).catch(function(e){r(e)})}});return i}else{return r.play()}};n.api.getStats=function(){if(n.webrtc&&n.webrtc.isConnected){return new Promise(function(e,t){n.webrtc.peerConn.getStats().then(t=>{var n={audio:null,video:null};for(let e of t.values()){if(e.type=="track"){n[e.kind]=e}}e(n)})})}};n.api.getLatency=function(){var t=e.player.api.getStats();if(t){return new Promise(function(e,i){t.then(function(t){setTimeout(function(){var r=n.api.getStats();if(!r){i();return}r.then(function(n){var i={};for(var r in t){i[r]=t[r]&&n[r]?(n[r].jitterBufferDelay-t[r].jitterBufferDelay)/(n[r].jitterBufferEmittedCount-t[r].jitterBufferEmittedCount):null}e(i)},i)},1e3)},i)})}};n.api.pause=function(){r.pause();try{n.webrtc.pause()}catch(e){}MistUtil.event.send("paused",null,r)};n.api.setTracks=function(e){if(n.webrtc.isConnected){n.webrtc.setTrack(e)}else{var t=function(){n.webrtc.setTrack(e);MistUtil.event.removeListener({type:"webrtc_connected",callback:t,element:r})};MistUtil.event.addListener(r,"webrtc_connected",t)}};function g(){if(!n.api.textTracks[0]){return}var e=n.api.textTracks[0].currentOffset||0;if(Math.abs(c-e)<1){return}var t=[];for(var i=n.api.textTracks[0].cues.length-1;i>=0;i--){var r=n.api.textTracks[0].cues[i];n.api.textTracks[0].removeCue(r);if(!("orig"in r)){r.orig={start:r.startTime,end:r.endTime}}r.startTime=r.orig.start-c;r.endTime=r.orig.end-c;t.push(r)}for(var i in t){n.api.textTracks[0].addCue(t[i])}n.api.textTracks[0].currentOffset=c}n.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var n=t.length-1;n>=0;n--){r.removeChild(t[n])}if(e){var i=document.createElement("track");r.appendChild(i);i.kind="subtitles";i.label=e.label;i.srclang=e.lang;i.src=e.src;i.setAttribute("default","");i.onload=g}};MistUtil.event.addListener(r,"ended",function(){if(n.api.loop){if(e.state=="Stream is online"){n.webrtc.connect()}}});if("decodingIssues"in e.skin.blueprints){var w=["nackCount","pliCount","packetsLost","packetsReceived","bytesReceived"];for(var y in w){n.api[w[y]]=0}var k=function(){e.timers.start(function(){n.webrtc.getStats(function(e){for(var t in e){for(var i in w){if(w[i]in e[t]){n.api[w[i]]=e[t][w[i]]}}break}});k()},1e3)};k()}n.api.unload=function(){try{n.webrtc.stop();n.webrtc.signaling.ws.close()}catch(e){}};t(r)}; \ No newline at end of file +mistplayers.webrtc={name:"WebRTC player",mimes:["webrtc"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,t,n){if(!("WebSocket"in window)||!("RTCPeerConnection"in window)){return false}if(location.protocol.replace(/^http/,"ws")!=MistUtil.http.url.split(t.url.replace(/^http/,"ws")).protocol){n.log("HTTP/HTTPS mismatch for this source");return false}return true},player:function(){}};var p=mistplayers.webrtc.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var n=this;if(typeof WebRTCBrowserEqualizerLoaded=="undefined"||!WebRTCBrowserEqualizerLoaded){var i=document.createElement("script");i.src=e.urlappend(e.options.host+"/webrtc.js");e.log("Retrieving webRTC browser equalizer code from "+i.src);document.head.appendChild(i);i.onerror=function(){e.showError("Failed to load webrtc browser equalizer",{nextCombo:5})};i.onload=function(){n.build(e,t)};return}var r=document.createElement("video");r.setAttribute("playsinline","");var o=["autoplay","loop","poster"];for(var s in o){var a=o[s];if(e.options[a]){r.setAttribute(a,e.options[a]===true?"":e.options[a])}}if(e.options.muted){r.muted=true}if(e.info.type=="live"){r.loop=false}if(e.options.controls=="stock"){r.setAttribute("controls","")}r.setAttribute("crossorigin","anonymous");this.setSize=function(e){r.style.width=e.width+"px";r.style.height=e.height+"px"};MistUtil.event.addListener(r,"loadeddata",g);MistUtil.event.addListener(r,"seeked",g);if(!e.options.autoplay){MistUtil.event.addListener(r,"canplay",function(){var t=MistUtil.event.addListener(r,"play",function(){e.log("Pausing because autoplay is disabled");var i=MistUtil.event.addListener(r,"pause",function(){e.options.autoplay=false;MistUtil.event.removeListener(i)});n.api.pause();MistUtil.event.removeListener(t)})})}var c=0;var l=false;var u=[];this.listeners={on_connected:function(){c=0;l=false;this.webrtc.play();MistUtil.event.send("webrtc_connected",null,r)},on_disconnected:function(){MistUtil.event.send("webrtc_disconnected",null,r);e.log("Websocket sent on_disconnect");if(!l){r.pause()}},on_answer_sdp:function(t){if(!t.result){e.showError("Failed to open stream.");this.on_disconnected();return}e.log("SDP answer received")},on_time:function(t){var n=c;c=t.current*.001-r.currentTime;if(Math.abs(n-c)>1){g()}if((!("paused"in t)||!t.paused)&&r.paused){r.play()}var i=t.end==0?Infinity:t.end*.001;if(i!=p){p=i;MistUtil.event.send("durationchange",i,r)}if(t.tracks&&u!=t.tracks){var o=e.info?MistUtil.tracks.parse(e.info.meta.tracks):[];for(var s in t.tracks){if(u.indexOf(t.tracks[s])<0){var a;for(var l in o){if(t.tracks[s]in o[l]){a=l;break}}if(!a){continue}MistUtil.event.send("playerUpdate_trackChanged",{type:a,trackid:t.tracks[s]},e.video)}}u=t.tracks}},on_seek:function(e){var t=this;MistUtil.event.send("seeked",c,r);if(e.live_point){t.webrtc.playbackrate("auto")}if("seekPromise"in this.webrtc.signaling){r.play().then(function(){if("seekPromise"in t.webrtc.signaling){t.webrtc.signaling.seekPromise.resolve("Play promise resolved")}}).catch(function(){if("seekPromise"in t.webrtc.signaling){t.webrtc.signaling.seekPromise.reject("Play promise rejected")}})}else{r.play()}},on_speed:function(e){this.webrtc.play_rate=e.play_rate_curr;MistUtil.event.send("ratechange",e,r)},on_stop:function(){e.log("Websocket sent on_stop");r.pause();MistUtil.event.send("ended",null,r);l=true}};function f(){this.peerConn=null;this.localOffer=null;this.isConnected=false;this.isConnecting=false;this.play_rate="auto";var t=this;this.on_event=function(i){switch(i.type){case"on_connected":{t.isConnected=true;t.isConnecting=false;break}case"on_answer_sdp":{t.peerConn.setRemoteDescription({type:"answer",sdp:i.answer_sdp}).then(function(){},function(e){console.error(e)});break}case"on_disconnected":{t.isConnected=false;break}}if(i.type in n.listeners){return n.listeners[i.type].call(n,i)}e.log("Unhandled WebRTC event "+i.type+": "+JSON.stringify(i));return false};this.connect=function(n){t.isConnecting=true;function i(e){var t=new Promise(function(t,n){function i(e){try{var r=RTCRtpReceiver.getCapabilities("video");for(var o=0;o0){setTimeout(function(){i(e-1)},100)}else{n("H264 not found :(")}}catch(e){t("Checker unavailable")}}i(e)});return t}i(5).catch(function(){e.log("Beware: this device does not seem to be able to play H264.")}).finally(function(){t.signaling=new d(t.on_event);t.peerConn=new RTCPeerConnection;t.peerConn.ontrack=function(e){r.srcObject=e.streams[0];if(n){n()}}})};this.play=function(){if(!this.isConnected){throw"Not connected, cannot play"}this.peerConn.createOffer({offerToReceiveAudio:true,offerToReceiveVideo:true}).then(function(e){t.localOffer=e;t.peerConn.setLocalDescription(e).then(function(){t.signaling.sendOfferSDP(t.localOffer.sdp)},function(e){console.error(e)})},function(e){throw e})};this.stop=function(){if(!this.isConnected){throw"Not connected, cannot stop."}this.signaling.send({type:"stop"})};this.seek=function(n){var i=new Promise(function(i,r){if(!t.isConnected||!t.signaling){if(t.isConnecting){var o=MistUtil.event.addListener(e.video,"loadstart",function(){t.seek(n);MistUtil.event.removeListener(o)});return r("Not connected yet, will seek once connected")}else{return r("Failed seek: not connected")}}t.signaling.send({type:"seek",seek_time:n=="live"?"live":n*1e3});if("seekPromise"in t.signaling){t.signaling.seekPromise.reject("Doing new seek")}t.signaling.seekPromise={resolve:function(e){i("seeked");delete t.signaling.seekPromise},reject:function(e){r("Failed to seek: "+e);delete t.signaling.seekPromise}}});return i};this.pause=function(){if(!this.isConnected){throw"Not connected, cannot pause."}this.signaling.send({type:"pause"})};this.setTrack=function(e){if(!this.isConnected){throw"Not connected, cannot set track."}e.type="tracks";this.signaling.send(e)};this.playbackrate=function(e){if(typeof e=="undefined"){return n.webrtc.play_rate=="auto"?1:n.webrtc.play_rate}if(!this.isConnected){throw"Not connected, cannot change playback rate."}this.signaling.send({type:"set_speed",play_rate:e})};this.getStats=function(e){this.peerConn.getStats().then(function(t){var n={};var i=Array.from(t.entries());for(var r in i){var o=i[r];if(o[1].type=="inbound-rtp"){n[o[0]]=o[1]}}e(n)})};this.connect()}function d(t){this.ws=null;this.ws=new WebSocket(e.source.url.replace(/^http/,"ws"));var n=false;this.ws.onopen=function(){t({type:"on_connected"})};this.ws.onmessage=function(e){try{var n=JSON.parse(e.data);t(n)}catch(t){console.error("Failed to parse a response from MistServer",t,e.data)}};this.ws.onclose=function(e){switch(e.code){case 1006:{}default:{t({type:"on_disconnected",code:e.code});break}}};this.sendOfferSDP=function(e){this.send({type:"offer_sdp",offer_sdp:e})};this.send=function(e){if(!this.ws){throw"Not initialized, cannot send "+JSON.stringify(e)}this.ws.send(JSON.stringify(e))}}this.webrtc=new f;this.api={};var p;Object.defineProperty(this.api,"duration",{get:function(){return p}});Object.defineProperty(this.api,"currentTime",{get:function(){return c+r.currentTime},set:function(e){c=e-r.currentTime;r.pause();var t=n.webrtc.seek(e);MistUtil.event.send("seeking",e,r);if(t){t.catch(function(e){})}}});Object.defineProperty(this.api,"playbackRate",{get:function(){return n.webrtc.playbackrate()},set:function(e){return n.webrtc.playbackrate(e)}});function v(e){Object.defineProperty(n.api,e,{get:function(){return r[e]},set:function(t){return r[e]=t}})}var h=["volume","muted","loop","paused",,"error","textTracks","webkitDroppedFrameCount","webkitDecodedFrameCount"];for(var s in h){v(h[s])}function b(e){if(e in r){n.api[e]=function(){return r[e].call(r,arguments)}}}var h=["load","getVideoPlaybackQuality"];for(var s in h){b(h[s])}n.api.play=function(){var t;if(n.api.currentTime){t=n.api.currentTime}if(e.info&&e.info.type=="live"){t="live"}if(t){var i=new Promise(function(i,r){if(!n.webrtc.isConnected&&n.webrtc.peerConn.iceConnectionState!="completed"){if(!n.webrtc.isConnecting){e.log("Received call to play while not connected, connecting "+n.webrtc.peerConn.iceConnectionState);n.webrtc.connect(function(){n.webrtc.seek(t).then(function(e){i("played "+e)}).catch(function(e){r(e)})})}else{r("Still connecting")}}else{n.webrtc.seek(t).then(function(e){i("played "+e)}).catch(function(e){r(e)})}});return i}else{return r.play()}};n.api.getStats=function(){if(n.webrtc&&n.webrtc.isConnected){return new Promise(function(e,t){n.webrtc.peerConn.getStats().then(t=>{var n={audio:null,video:null};for(let e of t.values()){if(e.type=="track"){n[e.kind]=e}}e(n)})})}};n.api.getLatency=function(){var t=e.player.api.getStats();if(t){return new Promise(function(e,i){t.then(function(t){setTimeout(function(){var r=n.api.getStats();if(!r){i();return}r.then(function(n){var i={};for(var r in t){i[r]=t[r]&&n[r]?(n[r].jitterBufferDelay-t[r].jitterBufferDelay)/(n[r].jitterBufferEmittedCount-t[r].jitterBufferEmittedCount):null}e(i)},i)},1e3)},i)})}};n.api.pause=function(){r.pause();try{n.webrtc.pause()}catch(e){}MistUtil.event.send("paused",null,r)};n.api.setTracks=function(e){if(n.webrtc.isConnected){n.webrtc.setTrack(e)}else{var t=function(){n.webrtc.setTrack(e);MistUtil.event.removeListener({type:"webrtc_connected",callback:t,element:r})};MistUtil.event.addListener(r,"webrtc_connected",t)}};function g(){if(!n.api.textTracks[0]){return}var e=n.api.textTracks[0].currentOffset||0;if(Math.abs(c-e)<1){return}var t=[];for(var i=n.api.textTracks[0].cues.length-1;i>=0;i--){var r=n.api.textTracks[0].cues[i];n.api.textTracks[0].removeCue(r);if(!("orig"in r)){r.orig={start:r.startTime,end:r.endTime}}r.startTime=r.orig.start-c;r.endTime=r.orig.end-c;t.push(r)}for(var i in t){n.api.textTracks[0].addCue(t[i])}n.api.textTracks[0].currentOffset=c}n.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var n=t.length-1;n>=0;n--){r.removeChild(t[n])}if(e){var i=document.createElement("track");r.appendChild(i);i.kind="subtitles";i.label=e.label;i.srclang=e.lang;i.src=e.src;i.setAttribute("default","");i.onload=g}};MistUtil.event.addListener(r,"ended",function(){if(n.api.loop){if(e.state=="Stream is online"){n.webrtc.connect()}}});if("decodingIssues"in e.skin.blueprints){var w=["nackCount","pliCount","packetsLost","packetsReceived","bytesReceived"];for(var y in w){n.api[w[y]]=0}var k=function(){e.timers.start(function(){n.webrtc.getStats(function(e){for(var t in e){for(var i in w){if(w[i]in e[t]){n.api[w[i]]=e[t][w[i]]}}break}});k()},1e3)};k()}n.api.unload=function(){try{n.webrtc.stop();n.webrtc.signaling.ws.close()}catch(e){}};t(r)}; \ No newline at end of file diff --git a/embed/minimize_internal.sh b/embed/minimize_internal.sh index 6f924f26..58c3f8ad 100755 --- a/embed/minimize_internal.sh +++ b/embed/minimize_internal.sh @@ -42,6 +42,8 @@ echo " Minimizing wrappers.." echo " Minimizing webrtc"; terser -mn -o min/wrappers/webrtc.js -- wrappers/webrtc.js #fi + echo " Minimizing mews"; + terser -mn -o min/wrappers/mews.js -- wrappers/mews.js echo " Done."; echo " Minimizing CSS.."; diff --git a/embed/player.js b/embed/player.js index c23c3438..ac16201d 100644 --- a/embed/player.js +++ b/embed/player.js @@ -134,12 +134,16 @@ function MistVideo(streamName,options) { //retrieve and sort the players we can loop over var players; + //make sure all players have the shortname param + for (var i in mistplayers) { + mistplayers[i].shortname = i; + } if (options.forcePlayer) { - players = [options.forcePlayer]; + players = [mistplayers[options.forcePlayer]]; MistVideo.log("Forcing player "+options.forcePlayer); } else { - players = MistUtil.object.keys(mistplayers); + players = MistUtil.object.values(mistplayers); } @@ -155,7 +159,7 @@ function MistVideo(streamName,options) { a.origIndex = MistVideo.info.source.indexOf(a) return a.origIndex; }], - player: [{priority:-1}] + player: [{priority:1}] }; var map = { inner: "player", @@ -170,7 +174,7 @@ function MistVideo(streamName,options) { if ("player" in options.forcePriority) { if (!(options.forcePriority.player instanceof Array)) { throw "forcePriority.player is not an array."; } sortoptions.player = options.forcePriority.player.concat(sortoptions.player); //prepend - MistUtil.array.multiSort(players,sortoptions.players); + MistUtil.array.multiSort(players,sortoptions.player); } if ("first" in options.forcePriority) { sortopions.first = options.forcePriority.first; //overwrite @@ -223,7 +227,7 @@ function MistVideo(streamName,options) { if (checkStartCombo(map.inner) >= 1) { continue; } var source = variables.source.list[variables.source.current]; - var p_shortname = variables.player.list[variables.player.current]; + var p_shortname = variables.player.list[variables.player.current].shortname; var player = mistplayers[p_shortname]; if (player.isMimeSupported(source.type)) { @@ -579,7 +583,7 @@ function MistVideo(streamName,options) { //add event logging var events = [ - "abort","canplay","canplaythrough","durationchange","emptied","ended","loadeddata","loadedmetadata","loadstart","pause","play","playing","ratechange","seeked","seeking","stalled","volumechange","waiting","metaUpdate_tracks","resizing" + "abort","canplay","canplaythrough",/*"durationchange"*/,"emptied","ended","loadeddata","loadedmetadata","loadstart","pause","play","playing","ratechange","seeked","seeking","stalled","volumechange","waiting","metaUpdate_tracks","resizing" //,"timeupdate" ]; for (var i in events) { @@ -1142,7 +1146,12 @@ function MistVideo(streamName,options) { this.player.api.setSource(""); //this.element.load(); //don't use this.load() to avoid interrupting play/pause } - if ("unload" in this.player.api) { try { this.player.api.unload(); } catch (e) {} } + if ("unload" in this.player.api) { + try { this.player.api.unload(); } + catch (e) { + MistVideo.log("Error while unloading player: "+e.message); + } + } } if ((this.UI) && (this.UI.elements)) { for (var i in this.UI.elements) { diff --git a/embed/skins.js b/embed/skins.js index 291e538a..6636cc0c 100644 --- a/embed/skins.js +++ b/embed/skins.js @@ -785,11 +785,42 @@ MistSkins["default"] = { }; //obey video states + var lastBufferUpdate = 0; + var bufferTimer = false; MistUtil.event.addListener(video,"progress",function(){ - container.updateBuffers(api.buffered); + function updateBuffers(){ + //limit fire to once per second + if (new Date().getTime() - lastBufferUpdate > 1e3) { + container.updateBuffers(api.buffered); + lastBufferUpdate = new Date().getTime(); + } + else if (!bufferTimer) { + bufferTimer = MistVideo.timers.start(function(){ + updateBuffers(); + bufferTimer = false; + },1e3); + } + } + updateBuffers(); },container); + var lastBarUpdate = 0; + var barTimer = false; MistUtil.event.addListener(video,"timeupdate",function(){ - if (!dragging) { container.updateBar(api.currentTime); } + function updateBar(){ + //console.log(video.currentTime,"timeupdate"); + //limit fire to once per 0.2 second + if ((new Date().getTime() - lastBarUpdate > 200) && (!dragging)) { + container.updateBar(api.currentTime); + lastBarUpdate = new Date().getTime(); + } + else if (!barTimer) { + barTimer = MistVideo.timers.start(function(){ + updateBar(); + barTimer = false; + },1e3); + } + } + updateBar(); },container); MistUtil.event.addListener(video,"seeking",function(){ container.updateBar(api.currentTime); @@ -1698,7 +1729,7 @@ MistSkins["default"] = { },icon); } //remove loading icon - var events = ["seeked","playing","canplay","paused"]; + var events = ["seeked","playing","canplay","paused","ended"]; for (var i in events) { MistUtil.event.addListener(MistVideo.video,events[i],function(e){ if ("container" in MistVideo) { diff --git a/embed/skins/default.css b/embed/skins/default.css index 0965edcb..afc8f77f 100644 --- a/embed/skins/default.css +++ b/embed/skins/default.css @@ -41,6 +41,7 @@ border-right: inherit; background-color: $accent; z-index: 2; /*keep above buffer bar(s)*/ + transition: width 0.2s; } .mistvideo-progress .buffer { height: inherit; diff --git a/embed/wrappers/mews.js b/embed/wrappers/mews.js new file mode 100644 index 00000000..190054f2 --- /dev/null +++ b/embed/wrappers/mews.js @@ -0,0 +1,952 @@ +mistplayers.mews = { + name: "MSE websocket player", + mimes: ["ws/video/mp4","ws/video/webm"], + priority: MistUtil.object.keys(mistplayers).length + 1, + isMimeSupported: function (mimetype) { + return (this.mimes.indexOf(mimetype) == -1 ? false : true); + }, + isBrowserSupported: function (mimetype,source,MistVideo) { + + if ((!("WebSocket" in window)) || (!("MediaSource" in window))) { return false; } + + //check for http/https mismatch + if (location.protocol.replace(/^http/,"ws") != MistUtil.http.url.split(source.url.replace(/^http/,"ws")).protocol) { + MistVideo.log("HTTP/HTTPS mismatch for this source"); + return false; + } + + //it runs on MacOS, but breaks often on seek/track switch etc + if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) { + return false; + } + + //check (and save) codec compatibility + 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 = {}; + 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].codec; + } + } + var container = mimetype.split("/")[2]; + function test(codecs) { + //if (container == "webm") { return true; } + return MediaSource.isTypeSupported("video/"+container+";codecs=\""+codecs+"\""); + } + source.supportedCodecs = []; + for (var i in codecs) { + //i is the long name (like mp4a.40.2), codecs[i] is the short name (like AAC) + var s = test(i); + if (s) { + source.supportedCodecs.push(codecs[i]); + } + } + if ((!MistVideo.options.forceType) && (!MistVideo.options.forcePlayer)) { //unless we force mews, skip this players if not both video and audio are supported + if (source.supportedCodecs.length < source.simul_tracks) { + MistVideo.log("Not enough playable tracks for this source"); + return false; + } + } + return source.supportedCodecs.length > 0; + }, + player: function(){} +}; +var p = mistplayers.mews.player; +p.prototype = new MistPlayer(); +p.prototype.build = function (MistVideo,callback) { + + 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 + } + if (MistVideo.info.type == "live") { + video.loop = false; + } + if (MistVideo.options.controls == "stock") { + video.setAttribute("controls",""); + } + video.setAttribute("crossorigin","anonymous"); + this.setSize = function(size){ + video.style.width = size.width+"px"; + video.style.height = size.height+"px"; + }; + + var player = this; + //player.debugging = true; + + //this function is called both when the websocket is ready and the media source is ready - both should be open to proceed + function checkReady() { + if ((player.ws.readyState == player.ws.OPEN) && (player.ms.readyState == "open") && (player.sb)) { + callback(video); + if (MistVideo.options.autoplay) { + player.api.play(); + } + return true; + } + } + + this.msinit = function() { + return new Promise(function(resolve,reject){ + //prepare mediasource + player.ms = new MediaSource(); + video.src = URL.createObjectURL(player.ms); + player.ms.onsourceopen = function(){ + resolve(); + }; + player.ms.onsourceclose = function(e){ + console.error("ms close",e); + send({type:"stop"}); //stop sending data please something went wrong + }; + player.ms.onsourceended = function(e){ + console.error("ms ended",e); + + //for debugging + + function downloadBlob (data, fileName, mimeType) { + var blob, url; + blob = new Blob([data], { + type: mimeType + }); + url = window.URL.createObjectURL(blob); + downloadURL(url, fileName); + setTimeout(function() { + return window.URL.revokeObjectURL(url); + }, 1000); + }; + + function downloadURL (data, fileName) { + var a; + a = document.createElement('a'); + a.href = data; + a.download = fileName; + document.body.appendChild(a); + a.style = 'display: none'; + a.click(); + a.remove(); + }; + + if (player.debugging) { + var l = 0; + for (var i = 0; i < player.sb.appended.length; i++) { + l += player.sb.appended[i].length; + } + var d = new Uint8Array(l); + var l = 0; + for (var i = 0; i < player.sb.appended.length; i++) { + d.set(player.sb.appended[i],l); + l += player.sb.appended[i].length; + } + + downloadBlob(d, 'appended.mp4.bin', 'application/octet-stream'); + } + send({type:"stop"}); //stop sending data please something went wrong + }; + }); + } + this.msinit().then(function(){ + if (player.sb) { + MistVideo.log("Not creating source buffer as one already exists."); + return; + } + checkReady(); + }); + this.onsbinit = []; + this.sbinit = function(codecs){ + if (!codecs) { MistVideo.showError("Did not receive any codec: nothing to initialize."); return; } + + //console.log("sourcebuffers",player.ms.sourceBuffers.length); + //console.log("sb init","video/"+MistVideo.source.type.split("/")[2]+";codecs=\""+codecs.join(",")+"\""); + player.sb = player.ms.addSourceBuffer("video/"+MistVideo.source.type.split("/")[2]+";codecs=\""+codecs.join(",")+"\""); + player.sb.mode = "segments"; //the fragments will be put in the buffer at the correct time: much better behavior when seeking / not playing from 0s + + //save the current source buffer codecs + player.sb._codecs = codecs; + + player.sb._duration = 1; + player.sb._size = 0; + player.sb.queue = []; + var do_on_updateend = []; + player.sb.do_on_updateend = do_on_updateend; //so we can check it from the ws onmessage handler too + player.sb.appending = null; + player.sb.appended = []; + var n = 0; + player.sb.addEventListener("updateend",function(){ + if (!player.sb) { + MistVideo.log("Reached updateend but the source buffer is "+JSON.stringify(player.sb)+". "); + return; + } + //player.sb._busy = true; + //console.log("start updateend"); + + if (player.debugging) { + if (player.sb.appending) player.sb.appended.push(player.sb.appending); + player.sb.appending = null; + } + + //every 500 fragments, clean the buffer (about every 15 sec) + if (n >= 500) { + //console.log(n,video.currentTime - video.buffered.start(0)); + n = 0; + player.sb._clean(10); //keep 10 sec + } + else { + n++; + } + + var do_funcs = do_on_updateend.slice(); //clone the array + do_on_updateend = []; + for (var i in do_funcs) { + //console.log("do_funcs",Number(i)+1,"/",do_funcs.length); + if (!player.sb) { + if (player.debugging) { console.warn("I was doing on_updateend but the sb was reset"); } + break; + } + if (player.sb.updating) { + //it's updating again >_> + do_on_updateend.concat(do_funcs.slice(i)); //add the remaining functions to do_on_updateend + if (player.debugging) { console.warn("I was doing on_updateend but was interrupted"); } + break; + } + do_funcs[i](i < do_funcs.length-1 ? do_funcs.slice(i) : []); //pass remaining do_funcs as argument + } + + if (!player.sb) { return; } + + player.sb._busy = false; + //console.log("end udpateend"); + //console.log("onupdateend",player.sb.queue.length,player.sb.updating); + if (player.sb && player.sb.queue.length > 0 && !player.sb.updating && !video.error) { + //console.log("appending from queue"); + player.sb._append(this.queue.shift()); + } + }); + player.sb.error = function(e){ + console.error("sb error",e); + }; + player.sb.abort = function(e){ + console.error("sb abort",e); + }; + + player.sb._doNext = function(func) { + do_on_updateend.push(func); + }; + player.sb._do = function(func) { + if (this.updating || this._busy) { + this._doNext(func); + } + else { + func(); + } + } + player.sb._append = function(data) { + if (!data) { return; } + if (!data.buffer) { return; } + if (player.debugging) { player.sb.appending = new Uint8Array(data); } + if (player.sb._busy) { + if (player.debugging) console.warn("I wanted to append data, but now I won't because the thingy was still busy. Putting it back in the queue."); + player.sb.queue.unshift(data); + return; + } + player.sb._busy = true; + //console.log("appendBuffer"); + player.sb.appendBuffer(data); + } + + //we're initing the source buffer and there is a msg queue of data built up before the buffer was ready. Start by adding these data fragments to the source buffer + if (player.msgqueue) { + //There may be more than one msg queue, i.e. when rapidly switching tracks. Add only one msg queue and always add the oldest msg queue first. + if (player.msgqueue[0]) { + var do_do = false; //if there are no messages in the queue, make sure to execute any do_on_updateend functions right away + if (player.msgqueue[0].length) { + for (var i in player.msgqueue[0]) { + if (player.sb.updating || player.sb.queue.length || player.sb._busy) { + player.sb.queue.push(player.msgqueue[0][i]); + } + else { + //console.log("appending new data"); + player.sb._append(player.msgqueue[0][i]); + } + } + } + else { + do_do = true; + } + player.msgqueue.shift(); + if (player.msgqueue.length == 0) { player.msgqueue = false; } + MistVideo.log("The newly initialized source buffer was filled with data from a seperate message queue."+(player.msgqueue ? " "+player.msgqueue.length+" more message queue(s) remain." : "")); + if (do_do) { + MistVideo.log("The seperate message queue was empty; manually triggering any onupdateend functions"); + player.sb.dispatchEvent(new Event("updateend")); + } + } + } + + //remove everything keepaway secs before the current playback position to keep sourcebuffer from filling up + player.sb._clean = function(keepaway){ + if (!keepaway) keepaway = 180; + if (video.currentTime > keepaway) { + player.sb._do(function(){ + //make sure end time is never 0 + player.sb.remove(0,Math.max(0.1,video.currentTime - keepaway)); + }); + } + } + + if (player.onsbinit.length) { + player.onsbinit.shift()(); + } + //console.log("sb inited"); + }; + + this.wsconnect = function(){ + return new Promise(function(resolve,reject){ + //prepare websocket (both data and messages) + this.ws = new WebSocket(MistVideo.source.url); + this.ws.binaryType = "arraybuffer"; + + this.ws.onopen = function(){ + resolve(); + }; + this.ws.onerror = function(e){ + MistVideo.showError("MP4 over WS: websocket error"); + } + this.ws.onclose = function(e){ + MistVideo.log("MP4 over WS: websocket closed"); + }; + this.ws.listeners = {}; //kind of event listener list for websocket messages + this.ws.addListener = function(type,f){ + if (!(type in this.listeners)) { this.listeners[type] = []; } + this.listeners[type].push(f); + }; + this.ws.removeListener = function(type,f) { + if (!(type in this.listeners)) { return; } + var i = this.listeners[type].indexOf(f); + if (i < 0) { return; } + this.listeners[type].splice(i,1); + return true; + } + player.msgqueue = false; + var requested_rate = 1; + var serverdelay = []; + this.ws.onmessage = function(e){ + if (!e.data) { throw "Received invalid data"; } + if (typeof e.data == "string") { + var msg = JSON.parse(e.data); + if (player.debugging && (msg.type != "on_time")) { console.log("ws message",msg); } + switch (msg.type) { + case "on_stop": { + //the last fragment has been added to the buffer + var eObj; + eObj = MistUtil.event.addListener(video,"waiting",function(e){ + MistUtil.event.send("ended",null,video); + MistUtil.event.removeListener(eObj); + }); + + break; + } + case "on_time": { + var buffer = msg.data.current - video.currentTime*1e3; + var serverDelay = player.ws.serverDelay.get(); + var desiredBuffer = Math.max(500+serverDelay,serverDelay*2); + if (MistVideo.info.type != "live") { desiredBuffer += 2000; } //if VoD, keep an extra 2 seconds of buffer + if (player.debugging) console.log("on_time received",msg.data.current/1e3,"currtime",video.currentTime,requested_rate+"x","buffer",Math.round(buffer),"/",Math.round(desiredBuffer),(MistVideo.info.type == "live" ? "latency:"+Math.round(msg.data.end-video.currentTime*1e3)+"ms" : ""),"listeners",player.ws.listeners && player.ws.listeners.on_time ? player.ws.listeners.on_time : 0,"msgqueue",player.msgqueue ? player.msgqueue.length : 0,msg.data); + + if (!player.sb) { + MistVideo.log("Received on_time, but the source buffer is being cleared right now. Ignoring."); + break; + } + + if (player.sb._duration != msg.data.end*1e-3) { + player.sb._duration = msg.data.end*1e-3; + MistUtil.event.send("durationchange",null,MistVideo.video); + } + MistVideo.info.meta.buffer_window = msg.data.end - msg.data.begin; + player.sb.paused = false; + + if (MistVideo.info.type == "live") { + if (requested_rate == 1) { + if (msg.data.play_rate_curr == "auto") { + if (video.currentTime > 0) { //give it some time to seek to live first when starting up + //assume we want to be as live as possible + if (buffer - desiredBuffer > desiredBuffer) { + requested_rate = 1.1 + Math.min(1,((buffer-desiredBuffer)/desiredBuffer))*0.15; + video.playbackRate *= requested_rate; + MistVideo.log("Our buffer is big, so increase the playback speed to catch up."); + } + else if (buffer < desiredBuffer/2) { + requested_rate = 0.9; + video.playbackRate *= requested_rate; + MistVideo.log("Our buffer is small, so decrease the playback speed to catch up."); + } + } + } + } + else if (requested_rate > 1) { + if (buffer < desiredBuffer) { + video.playbackRate /= requested_rate; + requested_rate = 1; + MistVideo.log("Our buffer is small enough, so return to real time playback."); + } + } + else { + //requested rate < 1 + if (buffer > desiredBuffer) { + video.playbackRate /= requested_rate; + requested_rate = 1; + MistVideo.log("Our buffer is big enough, so return to real time playback."); + } + } + } + else { + //it's VoD, change the rate at which the server sends data to try and keep the buffer small + if (requested_rate == 1) { + if (msg.data.play_rate_curr == "auto") { + if (buffer < desiredBuffer/2) { + if (buffer < -10e3) { + //seek to play point + send({type: "seek", seek_time: video.currentTime*1e3}); + } + else { + //negative buffer? ask for faster delivery + requested_rate = 2; + MistVideo.log("Our buffer is negative, so request a faster download rate."); + send({type: "set_speed", play_rate: requested_rate}); + } + } + else if (buffer - desiredBuffer > desiredBuffer) { + MistVideo.log("Our buffer is big, so request a slower download rate."); + requested_rate = 0.5; + send({type: "set_speed", play_rate: requested_rate}); + } + } + } + else if (requested_rate > 1) { + if (buffer > desiredBuffer) { + //we have enough buffer, ask for real time delivery + send({type: "set_speed", play_rate: "auto"}); + requested_rate = 1; + MistVideo.log("The buffer is big enough, so ask for realtime download rate."); + } + } + else { //requested_rate < 1 + if (buffer < desiredBuffer) { + //we have a small enough bugger, ask for real time delivery + send({type: "set_speed", play_rate: "auto"}); + requested_rate = 1; + MistVideo.log("The buffer is small enough, so ask for realtime download rate."); + } + } + } + break; + } + case "tracks": { + //check if all codecs are equal to the ones we were using before + function checkEqual(arr1,arr2) { + if (!arr2) { return false; } + if (arr1.length != arr2.length) { return false; } + for (var i in arr1) { + if (arr2.indexOf(arr1[i]) < 0) { + return false; + } + } + return true; + } + + + if (checkEqual(player.last_codecs ? player.last_codecs : player.sb._codecs,msg.data.codecs)) { + if (player.debugging) console.log("reached switching point"); + if (msg.data.current > 0) { + if (player.sb) { //if sb is being cleared at the moment, don't bother + player.sb._do(function(){ //once the source buffer is done updating the current segment, clear the specified interval from the buffer + player.sb.remove(0,msg.data.current*1e-3); + }); + } + } + + MistVideo.log("Player switched tracks, keeping source buffer as codecs are the same as before."); + } + else { + if (player.debugging) { + console.warn("Different codecs!"); + console.warn("video time",video.currentTime,"waiting until",msg.data.current*1e-3); + } + player.last_codecs = msg.data.codecs; + //start gathering messages in a new msg queue. They won't be appended to the current source buffer + if (player.msgqueue) { + player.msgqueue.push([]); + } + else { + player.msgqueue = [[]]; + } + //play out buffer, then when we reach the starting timestamp of the new data, reset the source buffers + var clear = function(){ + //once the source buffer is done updating the current segment, clear the specified interval from the buffer + if (player && player.sb) { + player.sb._do(function(remaining_do_on_updateend){ + if (!player.sb.updating) { + if (!isNaN(player.ms.duration)) player.sb.remove(0,Infinity); + player.sb.queue = []; + player.ms.removeSourceBuffer(player.sb); + player.sb = null; + var t = (msg.data.current*1e-3).toFixed(3); //rounded because of floating point issues + video.src = ""; + player.ms.onsourceclose = null; + player.ms.onsourceended = null; + //console.log("sb murdered"); + if (player.debugging && remaining_do_on_updateend && remaining_do_on_updateend.length) { + console.warn("There are do_on_updateend functions queued, which I *should* re-apply after clearing the sb."); + } + + player.msinit().then(function(){ + player.sbinit(msg.data.codecs); + player.sb.do_on_updateend = remaining_do_on_updateend; + + var e = MistUtil.event.addListener(video,"loadedmetadata",function(){ + MistVideo.log("Buffer cleared, setting playback position to "+MistUtil.format.time(t,{ms:true})); + + var f = function() { + video.currentTime = t; + if (video.currentTime < t) { + player.sb._doNext(f); + if (player.debugging) { console.log("Could not set playback position"); } + } + else { + if (player.debugging) { console.log("Set playback position to "+MistUtil.format.time(t,{ms:true})); } + var p = function(){ + player.sb._doNext(function(){ + if (video.buffered.length) { + if (player.debugging) { + console.log(video.buffered.start(0),video.buffered.end(0),video.currentTime); + } + if (video.buffered.start(0) > video.currentTime) { + var b = video.buffered.start(0); + video.currentTime = b; + if (video.currentTime != b) { + p(); + } + } + } + else { + p(); + } + }); + }; + p(); + } + } + f(); + + MistUtil.event.removeListener(e); + }); + }); + } + else { + clear(); + } + }); + } + else { + if (player.debugging) { console.warn("sb not available to do clear"); } + player.onsbinit.push(clear); + } + }; + + if (!msg.data.codecs || !msg.data.codecs.length) { + MistVideo.showError("Track switch does not contain any codecs, aborting."); + //reset setTracks to auto + MistVideo.options.setTracks = false; + clear(); + break; + } + + if (player.debugging) { + console.warn("reached switching point",msg.data.current*1e-3,MistUtil.format.time(msg.data.current*1e-3)); + } + clear(); + + } + } + } + if (msg.type in this.listeners) { + for (var i = this.listeners[msg.type].length-1; i >= 0; i--) { //start at last in case the listeners remove themselves + this.listeners[msg.type][i](msg); + } + } + return; + } + var data = new Uint8Array(e.data); + if (data) { + if ((player.sb) && (!player.msgqueue)) { + if (player.sb.updating || player.sb.queue.length || player.sb._busy) { + player.sb.queue.push(data); + } + else { + //console.log("appending new data"); + player.sb._append(data); + } + } + else { + //There is no active source buffer or we're preparing for a track switch. + //Any data is kept in a seperate buffer and won't be appended to the source buffer until it is reinitialised. + if (!player.msgqueue) { player.msgqueue = [[]]; } + //There may be more than one seperate buffer (in case of rapid track switches), always append to the last of the buffers + player.msgqueue[player.msgqueue.length-1].push(data); + } + } + else { + //console.warn("no data, wut?",data,new Uint8Array(e.data)); + MistVideo.log("Expecting data from websocket, but received none?!"); + } + } + + + this.ws.serverDelay = { + delays: [], + log: function (type) { + var responseType = false; + switch (type) { + case "seek": + case "set_speed": { + //wait for cmd.type + responseType = type; + break; + } + case "request_codec_data": { + responseType = "codec_data"; + break; + } + default: { + //do nothing + return; + } + } + if (responseType) { + var starttime = new Date().getTime(); + function onResponse() { + player.ws.serverDelay.add(new Date().getTime() - starttime); + player.ws.removeListener(responseType,onResponse); + } + player.ws.addListener(responseType,onResponse); + } + }, + add: function(delay){ + this.delays.unshift(delay); + if (this.delays.length > 5) { + this.delays.splice(5); + } + }, + get: function(){ + if (this.delays.length) { + //return average of the last 3 recorded delays + let sum = 0; + let i = 0; + for (null; i < this.delays.length; i++){ + if (i >= 3) { break; } + sum += this.delays[i]; + } + return sum/i; + } + return 500; + } + }; + }.bind(this)); + }; + this.wsconnect().then(function(){ + //retrieve codec info + var f = function(msg){ + //got codec data, set up source buffer + + player.sbinit(msg.data.codecs); + + checkReady(); + player.ws.removeListener("codec_data",f); + }; + this.ws.addListener("codec_data",f); + send({type:"request_codec_data",supported_codecs:MistVideo.source.supportedCodecs}); + }.bind(this)); + + function send(cmd){ + if (!player.ws) { throw "No websocket to send to"; } + if (player.ws.readyState >= player.ws.CLOSING) { + //throw "WebSocket has been closed already."; + player.wsconnect().then(function(){ + send(cmd); + }); + return; + } + if (player.debugging) { console.log("ws send",cmd); } + + player.ws.serverDelay.log(cmd.type); + player.ws.send(JSON.stringify(cmd)); + } + + player.findBuffer = function (position) { + var buffern = false; + for (var i = 0; i < video.buffered.length; i++) { + if ((video.buffered.start(i) <= position) && (video.buffered.end(i) >= position)) { + buffern = i; + break; + } + } + return buffern; + }; + + this.api = { + play: function(skipToLive){ + return new Promise(function(resolve,reject){ + var f = function(e){ + if (!player.sb) { + MistVideo.log("Attempting to play, but the source buffer is being cleared. Waiting for next on_time."); + return; + } + if (MistVideo.info.type == "live") { + if (skipToLive || (video.currentTime == 0)) { + var g = function(){ + if (video.buffered.length) { + //is data.current contained within a buffer? is video.currentTime also contained in that buffer? if not, seek the video + var buffern = player.findBuffer(e.data.current*1e-3); + if (buffern !== false) { + if ((video.buffered.start(buffern) > video.currentTime) || (video.buffered.end(buffern) < video.currentTime)) { + video.currentTime = e.data.current*1e-3; + MistVideo.log("Setting live playback position to "+MistUtil.format.time(video.currentTime)); + } + video.play().then(resolve).catch(reject); + player.sb.paused = false; + player.sb.removeEventListener("updateend",g); + } + } + }; + player.sb.addEventListener("updateend",g); + } + else { + player.sb.paused = false; + video.play().then(resolve).catch(reject); + } + player.ws.removeListener("on_time",f); + } + else if (e.data.current > video.currentTime) { + player.sb.paused = false; + video.play().then(resolve).catch(reject); + player.ws.removeListener("on_time",f); + } + }; + player.ws.addListener("on_time",f); + + var cmd = {type:"play"}; + if (skipToLive) { cmd.seek_time = "live"; } + send(cmd); + }); + }, + pause: function(){ + video.pause(); + send({type: "hold",}); + if (player.sb) { player.sb.paused = true; } + }, + setTracks: function(obj){ + obj.type = "tracks"; + obj = MistUtil.object.extend({ + type: "tracks", + audio: null, + video: null, + seek_time: Math.max(0,video.currentTime*1e3-(500+player.ws.serverDelay.get())) + },obj); + send(obj); + }, + unload: function(){ + player.api.pause(); + player.sb._do(function(){ + player.sb.remove(0,Infinity); + try { + player.ms.endOfStream(); + + //it's okay if it fails + } catch (e) { } + }); + player.ws.close(); + delete window.mistMewsOnVisibilityChange; + document.removeEventListener("visibilitychange",onVisibilityChange); + } + }; + + //override seeking + Object.defineProperty(this.api,"currentTime",{ + get: function(){ + return video.currentTime; + }, + set: function(value){ + MistUtil.event.send("seeking",value,video); + send({type: "seek", seek_time: Math.max(0,value*1e3-(250+player.ws.serverDelay.get()))}); //safety margin for server latency + //set listener "seek" + var onseek = function(e){ + player.ws.removeListener("seek",onseek); + var ontime = function(e){ + player.ws.removeListener("on_time",ontime); + //in the first on_time, assume that the data were getting is where we want to be + value = (e.data.current*1e-3).toFixed(3); + var f = function() { + video.currentTime = value; + if (video.currentTime != value) { + if (player.debugging) console.log("Failed to set video.currentTime, wanted:",value,"got:",video.currentTime); + player.sb._doNext(f); + } + } + f(); + }; + player.ws.addListener("on_time",ontime); + } + player.ws.addListener("seek",onseek); + video.currentTime = value; + MistVideo.log("Seeking to "+MistUtil.format.time(value,{ms:true})+" ("+value+")"); + } + }); + //override duration + Object.defineProperty(this.api,"duration",{ + get: function(){ + return player.sb ? player.sb._duration : 1; + } + }); + Object.defineProperty(this.api,"playbackRate",{ + get: function(){ + return video.playbackRate; + }, + set: function(value){ + var f = function(msg){ + video.playbackRate = msg.data.play_rate; + }; + player.ws.addListener("set_speed",f); + send({type: "set_speed", play_rate: (value == 1 ? "auto" : value)}); + } + }); + + //redirect properties + //using a function to make sure the "item" is in the correct scope + function reroute(item) { + Object.defineProperty(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" + ]; + for (var i in list) { + reroute(list[i]); + } + + //loop + MistUtil.event.addListener(video,"ended",function(){ + if (player.api.loop) { + player.api.currentTime = 0; + player.sb._do(function(){ + player.sb.remove(0,Infinity); + }); + } + }); + //pause if tab is hidden to prevent buildup of frames + var autopaused = false; + //only add this once! + function onVisibilityChange() { + if (document.hidden) { + //check if we are playing (not video.paused! that already returns true) + if (!player.sb.paused) { + player.api.pause(); + autopaused = true; + if (MistVideo.info.type == "live") { + autopaused = "live"; //go to live point + //NB: even if the player wasn't near the live point when it was paused, we've likely exited the buffer while we were paused, so the current position probably won't exist anymore. Just skip to live. + } + MistVideo.log("Pausing the player as the tab is inactive."); + } + } + else if (autopaused) { + player.api.play(autopaused == "live"); + autopaused = false; + MistVideo.log("Restarting the player as the tab is now active again."); + } + } + if (!window.mistMewsOnVisibilityChange) { + window.mistMewsOnVisibilityChange = true; + document.addEventListener("visibilitychange",onVisibilityChange); + } + + var seeking = false; + MistUtil.event.addListener(video,"seeking",function(){ + seeking = true; + var seeked = MistUtil.event.addListener(video,"seeked",function(){ + seeking = false; + MistUtil.event.removeListener(seeked); + }); + }); + MistUtil.event.addListener(video,"waiting",function(){ + //check if there is a gap in the buffers, and if so, jump it + if (seeking) { return; } + var buffern = player.findBuffer(video.currentTime); + if (buffern !== false) { + if ((buffern+1 < video.buffered.length) && (video.buffered.start(buffern+1) - video.currentTime < 10e3)) { + MistVideo.log("Skipped over buffer gap (from "+MistUtil.format.time(video.currentTime)+" to "+MistUtil.format.time(video.buffered.start(buffern+1))+")"); + video.currentTime = video.buffered.start(buffern+1); + } + } + }); + + if (player.debugging) { + MistUtil.event.addListener(video,"waiting",function(){ + //check the buffer available + var buffers = []; + var contained = false; + for (var i = 0; i < video.buffered.length; i++) { + if ((video.currentTime >= video.buffered.start(i)) && (video.currentTime <= video.buffered.end(i))) { + contained = true; + } + buffers.push([ + video.buffered.start(i), + video.buffered.end(i), + ]); + } + console.log("waiting","currentTime",video.currentTime,"buffers",buffers,contained ? "contained" : "outside of buffer","readystate",video.readyState,"networkstate",video.networkState); + if ((video.readyState >= 2) && (video.networkState >= 2)) { + console.error("Why am I waiting?!"); + } + + }); + } +}; diff --git a/embed/wrappers/videojs.js b/embed/wrappers/videojs.js index 611198e9..e427bb6c 100644 --- a/embed/wrappers/videojs.js +++ b/embed/wrappers/videojs.js @@ -39,6 +39,7 @@ p.prototype.build = function (MistVideo,callback) { if (MistVideo.source.type != "html5/video/ogg") { ele.crossOrigin = "anonymous"; //required for subtitles, but if ogg, the video won"t load } + ele.setAttribute("playsinline",""); //for apple var shortmime = MistVideo.source.type.split("/"); if (shortmime[0] == "html5") { diff --git a/embed/wrappers/webrtc.js b/embed/wrappers/webrtc.js index 9f02a272..86485696 100644 --- a/embed/wrappers/webrtc.js +++ b/embed/wrappers/webrtc.js @@ -236,19 +236,23 @@ p.prototype.build = function (MistVideo,callback) { function checkH264(n){ var p = new Promise(function(resolve,reject){ function promise_body(n){ - var r = RTCRtpReceiver.getCapabilities("video"); - for (var i = 0; i < r.codecs.length; i++) { - if (r.codecs[i].mimeType == "video/H264") { - resolve("H264 found :)"); - return; + try { + var r = RTCRtpReceiver.getCapabilities("video"); + for (var i = 0; i < r.codecs.length; i++) { + if (r.codecs[i].mimeType == "video/H264") { + resolve("H264 found :)"); + return; + } } - } - if (n > 0) { setTimeout(function(){ - promise_body(n-1); - },100) } - else { - reject("H264 not found :("); - } + if (n > 0) { + setTimeout(function(){ + promise_body(n-1); + },100); + } + else { + reject("H264 not found :("); + } + } catch (e) { resolve("Checker unavailable"); } } promise_body(n); }); diff --git a/lsp/mist.js b/lsp/mist.js index 7118afcf..7af0c639 100644 --- a/lsp/mist.js +++ b/lsp/mist.js @@ -1,4 +1,3 @@ - $(function(){ UI.elements = { menu: $('nav > .menu'), diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index e74bfe3a..88aee59f 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -143,6 +143,7 @@ namespace Mist { capa["optional"]["wrappers"]["allowed"].append("videojs"); capa["optional"]["wrappers"]["allowed"].append("dashjs"); capa["optional"]["wrappers"]["allowed"].append("webrtc"); + capa["optional"]["wrappers"]["allowed"].append("mews"); capa["optional"]["wrappers"]["allowed"].append("flash_strobe"); capa["optional"]["wrappers"]["option"] = "--wrappers"; capa["optional"]["wrappers"]["short"] = "w"; @@ -797,9 +798,12 @@ namespace Mist { response.append((char*)webrtc_js, (size_t)webrtc_js_len); used = true; } - if (!used) { - WARN_MSG("Unknown player type: %s",it->asStringRef().c_str()); + if (it->asStringRef() == "mews"){ + #include "mews.js.h" + response.append((char*)mews_js, (size_t)mews_js_len); + used = true; } + if (!used){WARN_MSG("Unknown player type: %s", it->asStringRef().c_str());} } if ((rURL.substr(0, 7) == "/embed_") && (rURL.length() > 10) && (rURL.substr(rURL.length() - 3, 3) == ".js")){