- updated videojs and dashjs
- combo selection algorithm now tries to find maximum simultracks
- when requesting stream info, add ?metaeverywhere=1 to the url to not count meta/subtitle tracks to simul_tracks and source priority sorting
- updated videojs, dashjs and hlsjs players
- improved html5 codec support testing
- urlappend: improved behaviour when url already contains search params
This commit is contained in:
Cat 2022-09-07 11:07:44 +02:00 committed by Thulinma
parent 6d86f98148
commit eee3595d46
26 changed files with 452 additions and 185 deletions

File diff suppressed because one or more lines are too long

View file

@ -82,7 +82,7 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top} svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5} .mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)} .mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em} .mist.muted{top:0;right:0;margin:1%;max-height:20%;width:auto}
.mistvideo-secondaryVideo{z-index:1;position:absolute;right:0;top:0;width:50%;height:50%;max-width:fit-content;max-height:fit-content} .mistvideo-secondaryVideo{z-index:1;position:absolute;right:0;top:0;width:50%;height:50%;max-width:fit-content;max-height:fit-content}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px} .mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1} .mistvideo-polling svg.icon.loading{z-index:0;opacity:1}

View file

@ -82,7 +82,7 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top} svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5} .mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)} .mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em} .mist.muted{top:0;right:0;margin:1%;max-height:20%;width:auto}
.mistvideo-secondaryVideo{z-index:1;position:absolute;right:0;top:0;width:50%;height:50%;max-width:fit-content;max-height:fit-content} .mistvideo-secondaryVideo{z-index:1;position:absolute;right:0;top:0;width:50%;height:50%;max-width:fit-content;max-height:fit-content}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px} .mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1} .mistvideo-polling svg.icon.loading{z-index:0;opacity:1}

View file

@ -1 +1 @@
mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return MistUtil.array.indexOf(this.mimes,t)==-1?false:true},isBrowserSupported:function(t,e,i){if(location.protocol!=MistUtil.http.url.split(e.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+t+") won't load if the page is run via file://");return false}var r={};for(var a in i.info.meta.tracks){if(i.info.meta.tracks[a].type!="meta"){r[i.info.meta.tracks[a].codec]=1}}r=MistUtil.object.keys(r);for(var a=r.length-1;a>=0;a--){if(r[a].substr(0,4)=="HEVC"){r.splice(a,1)}}if(r.length<e.simul_tracks){return false}return"MediaSource"in window},player:function(){this.onreadylist=[]},scriptsrc:function(t){return t+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var i=this;this.onDashLoad=function(){if(t.destroyed){return}t.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};t.player.api=new Proxy(r,{get:function(t,e,i){if(e in a.get){return a.get[e].apply(t,arguments)}var r=t[e];if(typeof r==="function"){return function(){return r.apply(t,arguments)}}return r},set:function(t,e,i){if(e in a.set){return a.set[e].call(t,i)}return t[e]=i}});if(t.info.type=="live"){a.get.duration=function(){var e=0;if(this.buffered.length){e=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-t.player.api.lastProgress.getTime())*.001;return e+i+-1*t.player.api.liveOffset+45};a.set.currentTime=function(e){var i=e-t.player.api.duration;t.log("Seeking to "+MistUtil.format.time(e)+" ("+Math.round(i*-10)/10+"s from live)");t.video.currentTime=e};MistUtil.event.addListener(r,"progress",function(){t.player.api.lastProgress=new Date});t.player.api.lastProgress=new Date;t.player.api.liveOffset=0}}else{i.api=r}if(t.options.autoplay){r.setAttribute("autoplay","")}if(t.options.loop&&t.info.type!="live"){r.setAttribute("loop","")}if(t.options.poster){r.setAttribute("poster",t.options.poster)}if(t.options.muted){r.muted=true}if(t.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,t.source.url,t.options.autoplay);i.dash=s;var o=["METRIC_ADDED","METRIC_UPDATED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];for(var n in dashjs.MediaPlayer.events){if(o.indexOf(n)<0){i.dash.on(dashjs.MediaPlayer.events[n],function(e){t.log("Player event fired: "+e.type)})}}t.player.setSize=function(t){this.api.style.width=t.width+"px";this.api.style.height=t.height+"px"};t.player.api.setSource=function(e){t.player.dash.attachSource(e)};var l=false;i.dash.on("allTextTracksAdded",function(){l=true});t.player.api.setSubtitle=function(e){if(!l){var r=function(){t.player.api.setSubtitle(e);i.dash.off("allTextTracksAdded",r)};i.dash.on("allTextTracksAdded",r);return}if(!e){i.dash.enableText(false);return}var a=i.dash.getTracksFor("text");for(var s in a){var o="idx"in e?e.idx:e.trackid;if(a[s].id==o){i.dash.setTextTrack(s);if(!i.dash.isTextEnabled()){i.dash.enableText()}return true}}return false};MistUtil.event.addListener(r,"progress",function(e){if(t.container.getAttribute("data-loading")=="stalled"){t.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};t.log("Built html");e(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(t.urlappend(mistplayers.dashjs.scriptsrc(t.options.host)),{onerror:function(e){var i="Failed to load dashjs.js";if(e.message){i+=": "+e.message}t.showError(i)},onload:i.onDashLoad},t)}}; mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,i){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+e+") won't load if the page is run via file://");return false}if(!("MediaSource"in window)){return false}if(!MediaSource.isTypeSupported){return true}var r={};var a=false;for(var s in i.info.meta.tracks){if(i.info.meta.tracks[s].type=="meta"){if(i.info.meta.tracks[s].codec=="subtitle"){a=true}continue}if(!(i.info.meta.tracks[s].type in r)){r[i.info.meta.tracks[s].type]={}}r[i.info.meta.tracks[s].type][MistUtil.tracks.translateCodec(i.info.meta.tracks[s])]=1}var o=[];for(var n in r){var l=false;for(var f in r[n]){if(MediaSource.isTypeSupported('video/mp4;codecs="'+f+'"')){l=true;break}}if(l){o.push(n)}}if(a){for(var s in i.info.source){if(i.info.source[s].type=="html5/text/vtt"){o.push("subtitle");break}}}return o.length?o:false},player:function(){this.onreadylist=[]},scriptsrc:function(e){return e+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=this;this.onDashLoad=function(){if(e.destroyed){return}e.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in a.get){return a.get[t].apply(e,arguments)}var r=e[t];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,t,i){if(t in a.set){return a.set[t].call(e,i)}return e[t]=i}});if(e.info.type=="live"){a.get.duration=function(){var t=0;if(this.buffered.length){t=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-e.player.api.lastProgress.getTime())*.001;return t+i+-1*e.player.api.liveOffset+45};a.set.currentTime=function(t){var i=t-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(i*-10)/10+"s from live)");e.video.currentTime=t};MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});e.player.api.lastProgress=new Date;e.player.api.liveOffset=0}}else{i.api=r}if(e.options.autoplay){r.setAttribute("autoplay","")}if(e.options.loop&&e.info.type!="live"){r.setAttribute("loop","")}if(e.options.poster){r.setAttribute("poster",e.options.poster)}if(e.options.muted){r.muted=true}if(e.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,e.source.url,e.options.autoplay);i.dash=s;var o=["METRIC_ADDED","METRIC_UPDATED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];for(var n in dashjs.MediaPlayer.events){if(o.indexOf(n)<0){i.dash.on(dashjs.MediaPlayer.events[n],function(t){e.log("Player event fired: "+t.type)})}}e.player.setSize=function(e){this.api.style.width=e.width+"px";this.api.style.height=e.height+"px"};e.player.api.setSource=function(t){e.player.dash.attachSource(t)};var l=false;i.dash.on("allTextTracksAdded",function(){l=true});e.player.api.setSubtitle=function(t){if(!l){var r=function(){e.player.api.setSubtitle(t);i.dash.off("allTextTracksAdded",r)};i.dash.on("allTextTracksAdded",r);return}if(!t){i.dash.enableText(false);return}var a=i.dash.getTracksFor("text");for(var s in a){var o="idx"in t?t.idx:t.trackid;if(a[s].id==o){i.dash.setTextTrack(s);if(!i.dash.isTextEnabled()){i.dash.enableText()}return true}}return false};MistUtil.event.addListener(r,"progress",function(t){if(e.container.getAttribute("data-loading")=="stalled"){e.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};e.log("Built html");t(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(e.urlappend(mistplayers.dashjs.scriptsrc(e.options.host)),{onerror:function(t){var i="Failed to load dashjs.js";if(t.message){i+=": "+t.message}e.showError(i)},onload:i.onDashLoad},e)}};

View file

@ -1 +1 @@
mistplayers.flv={name:"HTML5 FLV Player",mimes:["flash/7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,r){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){if(location.protocol=="file:"&&MistUtil.http.url.split(t.url).protocol=="http:"){r.log("This page was loaded over file://, the player might not behave as intended.")}else{r.log("HTTP/HTTPS mismatch for this source");return false}}if(!window.MediaSource){return false}try{function o(e){return window.MediaSource.isTypeSupported('video/mp4;codecs="'+e+'"')}function a(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"mp3";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 i={};for(var l in r.info.meta.tracks){if(r.info.meta.tracks[l].type!="meta"){i[a(r.info.meta.tracks[l])]=r.info.meta.tracks[l].type}}t.supportedCodecs=[];for(var l in i){var n=o(l);if(n){t.supportedCodecs.push(i[l])}}if(!r.options.forceType&&!r.options.forcePlayer){if(t.supportedCodecs.length<t.simul_tracks){r.log("Not enough playable tracks for this source");return false}}return t.supportedCodecs.length>0}catch(e){}return false},player:function(){this.onreadylist=[]},scriptsrc:function(e){return e+"/flv.js"}};var p=mistplayers.flv.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){this.onFLVLoad=function(){if(e.destroyed){return}e.log("Building flv.js player..");var r=document.createElement("video");r.setAttribute("playsinline","");var o=["autoplay","loop","poster"];for(var a in o){var i=o[a];if(e.options[i]){r.setAttribute(i,e.options[i]===true?"":e.options[i])}}if(e.options.muted){r.muted=true}if(e.options.controls=="stock"){r.setAttribute("controls","")}if(e.info.type=="live"){r.loop=false}flvjs.LoggingControl.applyConfig({enableVerbose:false});flvjs.LoggingControl.addLogListener(function(t,r){e.log("[flvjs] "+r)});var l={type:"flv",url:e.source.url,hasAudio:false,hasVideo:false};for(var a in e.source.supportedCodecs){l["has"+e.source.supportedCodecs[a].charAt(0).toUpperCase()+e.source.supportedCodecs[a].slice(1)]=true}e.player.create=function(t){t=MistUtil.object.extend({},t);e.player.flvPlayer=flvjs.createPlayer(t,{lazyLoad:false});e.player.flvPlayer.attachMediaElement(r);e.player.flvPlayer.load();e.player.flvPlayer.play();if(!e.options.autoplay){r.pause()}};e.player.create(l);e.player.api={};function n(t){Object.defineProperty(e.player.api,t,{get:function(){return r[t]},set:function(e){return r[t]=e}})}var s=["volume","buffered","muted","loop","paused",,"error","textTracks","webkitDroppedFrameCount","webkitDecodedFrameCount"];if(e.info.type!="live"){s.push("duration")}else{Object.defineProperty(e.player.api,"duration",{get:function(){if(!r.buffered.length){return 0}return r.buffered.end(r.buffered.length-1)}})}for(var a in s){n(s[a])}function p(t){if(t in r){e.player.api[t]=function(){return r[t].call(r,arguments)}}}var s=["load","getVideoPlaybackQuality","play","pause"];for(var a in s){p(s[a])}e.player.api.setSource=function(t){if(t!=l.url&&t!=""){e.player.flvPlayer.unload();e.player.flvPlayer.detachMediaElement();e.player.flvPlayer.destroy();l.url=t;e.player.create(l)}};e.player.api.unload=function(){e.player.flvPlayer.unload();e.player.flvPlayer.detachMediaElement();e.player.flvPlayer.destroy()};e.player.setSize=function(e){r.style.width=e.width+"px";r.style.height=e.height+"px"};Object.defineProperty(e.player.api,"currentTime",{get:function(){return r.currentTime},set:function(t){var o=.5;for(var a=0;a<r.buffered.length;a++){if(t>=r.buffered.start(a)&&t<=r.buffered.end(a)-o){return r.currentTime=t}}e.log("Seek attempted outside of buffer, but MistServer does not support seeking in progressive flash. Setting to closest available instead");return r.currentTime=r.buffered.length?r.buffered.end(r.buffered.length-1)-o:0}});t(r)};if("flvjs"in window){this.onFLVLoad()}else{var r=MistUtil.scripts.insert(e.urlappend(mistplayers.flv.scriptsrc(e.options.host)),{onerror:function(t){var r="Failed to load flv.js";if(t.message){r+=": "+t.message}e.showError(r)},onload:e.player.onFLVLoad},e)}}; mistplayers.flv={name:"HTML5 FLV Player",mimes:["flash/7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,r){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){if(location.protocol=="file:"&&MistUtil.http.url.split(t.url).protocol=="http:"){r.log("This page was loaded over file://, the player might not behave as intended.")}else{r.log("HTTP/HTTPS mismatch for this source");return false}}if(!window.MediaSource){return false}if(!MediaSource.isTypeSupported){return true}try{var o={};for(var a in r.info.meta.tracks){if(r.info.meta.tracks[a].type=="meta"){continue}if(!(r.info.meta.tracks[a].type in o)){o[r.info.meta.tracks[a].type]={}}o[r.info.meta.tracks[a].type][MistUtil.tracks.translateCodec(r.info.meta.tracks[a])]=1}var i=[];for(var l in o){var n=false;for(var s in o[l]){if(MediaSource.isTypeSupported('video/mp4;codecs="'+s+'"')){n=true;break}}if(n){i.push(l)}}t.supportedCodecs=i;return i.length?i:false}catch(e){}return false},player:function(){this.onreadylist=[]},scriptsrc:function(e){return e+"/flv.js"}};var p=mistplayers.flv.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){this.onFLVLoad=function(){if(e.destroyed){return}e.log("Building flv.js player..");var r=document.createElement("video");r.setAttribute("playsinline","");var o=["autoplay","loop","poster"];for(var a in o){var i=o[a];if(e.options[i]){r.setAttribute(i,e.options[i]===true?"":e.options[i])}}if(e.options.muted){r.muted=true}if(e.options.controls=="stock"){r.setAttribute("controls","")}if(e.info.type=="live"){r.loop=false}flvjs.LoggingControl.applyConfig({enableVerbose:false});flvjs.LoggingControl.addLogListener(function(t,r){e.log("[flvjs] "+r)});var l={type:"flv",url:e.source.url,hasAudio:false,hasVideo:false};for(var a in e.source.supportedCodecs){l["has"+e.source.supportedCodecs[a].charAt(0).toUpperCase()+e.source.supportedCodecs[a].slice(1)]=true}e.player.create=function(t){t=MistUtil.object.extend({},t);e.player.flvPlayer=flvjs.createPlayer(t,{lazyLoad:false});e.player.flvPlayer.attachMediaElement(r);e.player.flvPlayer.load();e.player.flvPlayer.play();if(!e.options.autoplay){r.pause()}};e.player.create(l);e.player.api={};function n(t){Object.defineProperty(e.player.api,t,{get:function(){return r[t]},set:function(e){return r[t]=e}})}var s=["volume","buffered","muted","loop","paused",,"error","textTracks","webkitDroppedFrameCount","webkitDecodedFrameCount"];if(e.info.type!="live"){s.push("duration")}else{Object.defineProperty(e.player.api,"duration",{get:function(){if(!r.buffered.length){return 0}return r.buffered.end(r.buffered.length-1)}})}for(var a in s){n(s[a])}function f(t){if(t in r){e.player.api[t]=function(){return r[t].call(r,arguments)}}}var s=["load","getVideoPlaybackQuality","play","pause"];for(var a in s){f(s[a])}e.player.api.setSource=function(t){if(t!=l.url&&t!=""){e.player.flvPlayer.unload();e.player.flvPlayer.detachMediaElement();e.player.flvPlayer.destroy();l.url=t;e.player.create(l)}};e.player.api.unload=function(){e.player.flvPlayer.unload();e.player.flvPlayer.detachMediaElement();e.player.flvPlayer.destroy()};e.player.setSize=function(e){r.style.width=e.width+"px";r.style.height=e.height+"px"};Object.defineProperty(e.player.api,"currentTime",{get:function(){return r.currentTime},set:function(t){var o=.5;for(var a=0;a<r.buffered.length;a++){if(t>=r.buffered.start(a)&&t<=r.buffered.end(a)-o){return r.currentTime=t}}e.log("Seek attempted outside of buffer, but MistServer does not support seeking in progressive flash. Setting to closest available instead");return r.currentTime=r.buffered.length?r.buffered.end(r.buffered.length-1)-o:0}});t(r)};if("flvjs"in window){this.onFLVLoad()}else{var r=MistUtil.scripts.insert(e.urlappend(mistplayers.flv.scriptsrc(e.options.host)),{onerror:function(t){var r="Failed to load flv.js";if(t.message){r+=": "+t.message}e.showError(r)},onload:e.player.onFLVLoad},e)}};

View file

@ -1 +1 @@
mistplayers.hlsjs={name:"HLS.js 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,t,s){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){s.log("HTTP/HTTPS mismatch for this source");return false}var l={};for(var r in s.info.meta.tracks){if(s.info.meta.tracks[r].type!="meta"){l[s.info.meta.tracks[r].codec]=1}}l=MistUtil.object.keys(l);for(var r=l.length-1;r>=0;r--){if(l[r].substr(0,4)=="HEVC"){l.splice(r,1)}}if(l.length<t.simul_tracks){return false}return true},player:function(){},scriptsrc:function(e){return e+"/hlsjs.js"}};var p=mistplayers.hlsjs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var s=this;var l=document.createElement("video");l.setAttribute("playsinline","");var r=["autoplay","loop","poster"];for(var i in r){var o=r[i];if(e.options[o]){l.setAttribute(o,e.options[o]===true?"":e.options[o])}}if(e.options.muted){l.muted=true}if(e.info.type=="live"){l.loop=false}if(e.options.controls=="stock"){l.setAttribute("controls","")}l.setAttribute("crossorigin","anonymous");this.setSize=function(e){l.style.width=e.width+"px";l.style.height=e.height+"px"};this.api=l;e.player.api.unload=function(){if(e.player.hls){e.player.hls.destroy();e.player.hls=false;e.log("hls.js instance disposed")}};function n(t){e.player.hls=new Hls({maxBufferLength:15,maxMaxBufferLength:60});e.player.hls.attachMedia(l);e.player.hls.on(Hls.Events.MEDIA_ATTACHED,function(){console.log("video and hls.js are now bound together !");e.player.hls.loadSource(t);e.player.hls.on(Hls.Events.MANIFEST_PARSED,function(e,t){console.log("manifest loaded, found "+t.levels.length+" quality level")})})}e.player.api.setSource=function(t){if(!e.player.hls){return}if(e.player.hls.url!=t){e.player.hls.destroy();n(t)}};e.player.api.setSubtitle=function(e){var t=l.getElementsByTagName("track");for(var s=t.length-1;s>=0;s--){l.removeChild(t[s])}if(e){var r=document.createElement("track");l.appendChild(r);r.kind="subtitles";r.label=e.label;r.srclang=e.lang;r.src=e.src;r.setAttribute("default","")}};function a(){n(e.source.url)}if("Hls"in window){a()}else{var p=e.urlappend(mistplayers.hlsjs.scriptsrc(e.options.host));MistUtil.scripts.insert(p,{onerror:function(t){var s="Failed to load hlsjs.js";if(t.message){s+=": "+t.message}e.showError(s)},onload:a},e)}t(l)}; mistplayers.hlsjs={name:"HLS.js player",mimes:["html5/application/vnd.apple.mpegurl","html5/application/vnd.apple.mpegurl;version=7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return this.mimes.indexOf(t)==-1?false:true},isBrowserSupported:function(t,e,r){if(location.protocol!=MistUtil.http.url.split(e.url).protocol){r.log("HTTP/HTTPS mismatch for this source");return false}if(!("MediaSource"in window)){return false}if(!MediaSource.isTypeSupported){return true}var i={};var s=false;for(var o in r.info.meta.tracks){if(r.info.meta.tracks[o].type=="meta"){if(r.info.meta.tracks[o].codec=="subtitle"){s=true}continue}if(!(r.info.meta.tracks[o].type in i)){i[r.info.meta.tracks[o].type]={}}i[r.info.meta.tracks[o].type][MistUtil.tracks.translateCodec(r.info.meta.tracks[o])]=1}var a=[];for(var l in i){var n=false;for(var p in i[l]){if(MediaSource.isTypeSupported('video/mp4;codecs="'+p+'"')){n=true;break}}if(n){a.push(l)}}if(s){for(var o in r.info.source){if(r.info.source[o].type=="html5/text/vtt"){a.push("subtitle");break}}}return a.length?a:false},player:function(){},scriptsrc:function(t){return t+"/hlsjs.js"}};var p=mistplayers.hlsjs.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var r=this;var i=document.createElement("video");i.setAttribute("playsinline","");var s=["autoplay","loop","poster"];for(var o in s){var a=s[o];if(t.options[a]){i.setAttribute(a,t.options[a]===true?"":t.options[a])}}if(t.options.muted){i.muted=true}if(t.info.type=="live"){i.loop=false}if(t.options.controls=="stock"){i.setAttribute("controls","")}i.setAttribute("crossorigin","anonymous");this.setSize=function(t){i.style.width=t.width+"px";i.style.height=t.height+"px"};this.api=i;t.player.api.unload=function(){if(t.player.hls){t.player.hls.destroy();t.player.hls=false;t.log("hls.js instance disposed")}};function l(e){t.player.hls=new Hls({maxBufferLength:15,maxMaxBufferLength:60});t.player.hls.attachMedia(i);t.player.hls.on(Hls.Events.MEDIA_ATTACHED,function(){t.player.hls.loadSource(e)})}t.player.api.setSource=function(e){if(!t.player.hls){return}if(t.player.hls.url!=e){t.player.hls.destroy();l(e)}};t.player.api.setSubtitle=function(t){var e=i.getElementsByTagName("track");for(var r=e.length-1;r>=0;r--){i.removeChild(e[r])}if(t){var s=document.createElement("track");i.appendChild(s);s.kind="subtitles";s.label=t.label;s.srclang=t.lang;s.src=t.src;s.setAttribute("default","")}};function n(){l(t.source.url)}if("Hls"in window){n()}else{var p=t.urlappend(mistplayers.hlsjs.scriptsrc(t.options.host));MistUtil.scripts.insert(p,{onerror:function(e){var r="Failed to load hlsjs.js";if(e.message){r+=": "+e.message}t.showError(r)},onload:n},t)}e(i)};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -105,7 +105,7 @@ function MistVideo(streamName,options) {
this.urlappend = function(url){ this.urlappend = function(url){
if (this.options.urlappend) { if (this.options.urlappend) {
url += this.options.urlappend; url = MistUtil.http.url.append(url,this.options.urlappend);
} }
return url; return url;
} }
@ -235,6 +235,39 @@ function MistVideo(streamName,options) {
return 0; //carry on! return 0; //carry on!
} }
var best = {
score: 0,
source_index: null,
player: null
};
function calcScore(tracktypes) {
//player.isBrowserSupported returns either true or an array of track types that are in the source and that it can play in this browser.
//loop over the returned track types and calculate a score of how good this player+source combo is
if (tracktypes === true) { return 1.9; } //something will play, but the player doesn't tell us what. Hopefully video will work?
var scores = {
video: 2,
audio: 1,
subtitle: 0.5
};
var score = 0;
for (var i in tracktypes) {
score += scores[tracktypes[i]];
}
return score;
}
//calculate the best possible score for this stream, so that we can break the loop early
var hastracktypes = {};
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type == "meta") {
hastracktypes[MistVideo.info.meta.tracks[i].codec] = 1;
}
else {
hastracktypes[MistVideo.info.meta.tracks[i].type] = 1;
}
}
var maxscore = calcScore(MistUtil.object.keys(hastracktypes));
outerloop: outerloop:
for (var n in variables[map.outer].list) { for (var n in variables[map.outer].list) {
variables[map.outer].current = n; variables[map.outer].current = n;
@ -255,18 +288,32 @@ function MistVideo(streamName,options) {
if (player.isMimeSupported(source.type)) { if (player.isMimeSupported(source.type)) {
//this player supports this mime //this player supports this mime
if (player.isBrowserSupported(source.type,source,MistVideo)) { var tracktypes = player.isBrowserSupported(source.type,source,MistVideo);
//this browser is supported if (tracktypes) {
return { var score = calcScore(tracktypes);
if (score > best.score) {
if (!quiet) MistVideo.log("Found a "+(best.score ? "better" : "working")+" combo: "+player.name+" with "+source.url+" (Score: "+score+")");
best = {
score: score,
player: p_shortname, player: p_shortname,
source: source, source: source,
source_index: variables.source.current source_index: variables.source.current
}; };
if (best.score == maxscore) {
//this is the max possible score, no need to continue searching
return best;
} }
} }
if (!quiet) { MistVideo.log("Checking "+player.name+" with "+source.type+".. Nope."); }
} }
} }
}
}
if (best.score) {
return best;
}
return false; return false;
} }
@ -280,7 +327,7 @@ function MistVideo(streamName,options) {
var player = mistplayers[result.player]; var player = mistplayers[result.player];
var source = result.source; var source = result.source;
MistVideo.log("Found a working combo: "+player.name+" with "+source.type+" @ "+source.url); MistVideo.log("Selected: "+player.name+" with "+source.type+" @ "+source.url);
MistVideo.playerName = result.player; MistVideo.playerName = result.player;
source = MistUtil.object.extend({},source); source = MistUtil.object.extend({},source);
source.index = result.source_index; source.index = result.source_index;
@ -1004,7 +1051,7 @@ function MistVideo(streamName,options) {
//switch to polling-mode if websockets are not supported //switch to polling-mode if websockets are not supported
function openWithGet() { function openWithGet() {
var url = MistVideo.urlappend(options.host+"/json_"+encodeURIComponent(MistVideo.stream)+".js"); var url = MistUtil.http.url.addParam(MistVideo.urlappend(options.host+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1});
MistVideo.log("Requesting stream info from "+url); MistVideo.log("Requesting stream info from "+url);
MistUtil.http.get(url,function(d){ MistUtil.http.get(url,function(d){
if (MistVideo.destroyed) { return; } if (MistVideo.destroyed) { return; }
@ -1023,7 +1070,7 @@ function MistVideo(streamName,options) {
function openSocket() { function openSocket() {
MistVideo.log("Opening stream status stream through websocket.."); MistVideo.log("Opening stream status stream through websocket..");
var url = MistVideo.options.host.replace(/^http/i,"ws"); var url = MistVideo.options.host.replace(/^http/i,"ws");
url = MistVideo.urlappend(url+"/json_"+encodeURIComponent(MistVideo.stream)+".js"); url = MistUtil.http.url.addParam(MistVideo.urlappend(url+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1});
var socket; var socket;
try { try {
socket = new WebSocket(url); socket = new WebSocket(url);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5,4 +5,7 @@ wget https://cdn.dashjs.org/latest/dash.all.min.js -O dash.all.min.js
echo "Videojs" echo "Videojs"
echo "You'll want to check for the latest version at https://videojs.com/getting-started/#download-cdn" echo "You'll want to check for the latest version at https://videojs.com/getting-started/#download-cdn"
wget https://vjs.zencdn.net/7.8.4/video.min.js -O video.min.js wget https://vjs.zencdn.net/8.0.4/video.min.js -O video.min.js
echo "HLSjs"
echo "Releases at https://github.com/video-dev/hls.js/ - download the .zip, extract, and replace hls.js with hls.min.js"

File diff suppressed because one or more lines are too long

View file

@ -90,7 +90,9 @@ svg.icon.timeout {
.mist.muted { .mist.muted {
top: 0; top: 0;
right: 0; right: 0;
margin: 1em; margin: 1%;
max-height: 20%;
width: auto;
} }
.mistvideo-secondaryVideo { .mistvideo-secondaryVideo {
z-index: 1; z-index: 1;

View file

@ -473,6 +473,30 @@ var MistUtil = {
if (splitparams.length) { ret.push(splitparams.join("&")); } if (splitparams.length) { ret.push(splitparams.join("&")); }
return ret.join("?"); return ret.join("?");
}, },
append: function(url,append){
var a = document.createElement("a");
a.href = url;
if (append[0] == "?") {
if (a.search == "") {
a.search = append;
}
else {
a.search += "&"+append.slice(1);
}
}
else if (append[0] == "&") {
if (a.search == "") {
a.search = "?"+append.slice(1);
}
else {
a.search += append;
}
}
else {
a.href += append;
}
return a.href;
},
split: function(url){ split: function(url){
var a = document.createElement("a"); var a = document.createElement("a");
a.href = url; a.href = url;
@ -895,6 +919,28 @@ var MistUtil = {
} }
return output; return output;
},
translateCodec: function(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 "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();
}
} }
}, },
isTouchDevice: function(){ isTouchDevice: function(){

View file

@ -19,22 +19,49 @@ mistplayers.dashjs = {
return false; return false;
} }
var codecs = {}; if (!("MediaSource" in window)) { return false; }
for (var i in MistVideo.info.meta.tracks) { if (!MediaSource.isTypeSupported) { return true; } //we can't ask, but let's assume something will work
if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[MistVideo.info.meta.tracks[i].codec] = 1;
}
}
codecs = MistUtil.object.keys(codecs);
//if there's a h265 track, remove it from the list of codecs
for (var i = codecs.length-1; i >= 0; i--) {
if (codecs[i].substr(0,4) == "HEVC") {
codecs.splice(i,1);
}
}
if (codecs.length < source.simul_tracks) { return false; } //if there's no longer enough playable tracks, skip this player
return ("MediaSource" in window); //check if both audio and video have at least one playable track
//gather track types and codec strings
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type == "meta") {
if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
continue;
}
if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) {
playabletracks[MistVideo.info.meta.tracks[i].type] = {};
}
playabletracks[MistVideo.info.meta.tracks[i].type][MistUtil.tracks.translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
var tracktypes = [];
for (var type in playabletracks) {
var playable = false;
for (var codec in playabletracks[type]) {
if (MediaSource.isTypeSupported("video/mp4;codecs=\""+codec+"\"")) {
playable = true;
break;
}
}
if (playable) {
tracktypes.push(type);
}
}
if (hassubtitles) {
//there is a subtitle track, check if there is a webvtt source
for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
tracktypes.push("subtitle");
break;
}
}
}
return tracktypes.length ? tracktypes : false;
}, },
player: function(){this.onreadylist = [];}, player: function(){this.onreadylist = [];},
scriptsrc: function(host) { return host+"/dashjs.js"; } scriptsrc: function(host) { return host+"/dashjs.js"; }

View file

@ -19,55 +19,53 @@ mistplayers.flv = {
} }
if (!window.MediaSource) { return false; } if (!window.MediaSource) { return false; }
if (!MediaSource.isTypeSupported) { return true; } //we can't ask, but let's assume something will work
try { try {
function test(mime) {
return window.MediaSource.isTypeSupported("video/mp4;codecs=\""+mime+"\"");
}
function translateCodec(track) {
function bin2hex(index) { //check if both audio and video have at least one playable track
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2); //gather track types and codec strings
} var playabletracks = {};
//var hassubtitles = false;
switch (track.codec) {
case "AAC":
return "mp4a.40.2";
case "MP3":
return "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) { for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type != "meta") { if (MistVideo.info.meta.tracks[i].type == "meta") {
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = MistVideo.info.meta.tracks[i].type; //if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
continue;
}
if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) {
playabletracks[MistVideo.info.meta.tracks[i].type] = {};
}
playabletracks[MistVideo.info.meta.tracks[i].type][MistUtil.tracks.translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
var tracktypes = [];
for (var type in playabletracks) {
var playable = false;
for (var codec in playabletracks[type]) {
if (MediaSource.isTypeSupported("video/mp4;codecs=\""+codec+"\"")) {
playable = true;
break;
} }
} }
source.supportedCodecs = []; if (playable) {
for (var i in codecs) { tracktypes.push(type);
//i is the long name (like mp4a.40.2), codecs[i] is the type (audio/video)
var s = test(i);
if (s) {
source.supportedCodecs.push(codecs[i]);
} }
} }
if ((!MistVideo.options.forceType) && (!MistVideo.options.forcePlayer)) { //unless we force mews, skip this player if not both video and audio are supported source.supportedCodecs = tracktypes;
if (source.supportedCodecs.length < source.simul_tracks) { /*if (hassubtitles) {
MistVideo.log("Not enough playable tracks for this source"); //there is a subtitle track, check if there is a webvtt source
return false; for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
tracktypes.push("subtitle");
break;
} }
} }
return source.supportedCodecs.length > 0; }*/
return tracktypes.length ? tracktypes : false;
} catch(e){} } catch(e){}
return false; return false;

View file

@ -12,22 +12,49 @@ mistplayers.hlsjs = {
return false; return false;
} }
var codecs = {}; if (!("MediaSource" in window)) { return false; }
for (var i in MistVideo.info.meta.tracks) { if (!MediaSource.isTypeSupported) { return true; } //we can't ask, but let's assume something will work
if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[MistVideo.info.meta.tracks[i].codec] = 1;
}
}
codecs = MistUtil.object.keys(codecs);
//if there's a h265 track, remove it from the list of codecs
for (var i = codecs.length-1; i >= 0; i--) {
if (codecs[i].substr(0,4) == "HEVC") {
codecs.splice(i,1);
}
}
if (codecs.length < source.simul_tracks) { return false; } //if there's no longer enough playable tracks, skip this player
return true; //check if both audio and video have at least one playable track
//gather track types and codec strings
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type == "meta") {
if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
continue;
}
if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) {
playabletracks[MistVideo.info.meta.tracks[i].type] = {};
}
playabletracks[MistVideo.info.meta.tracks[i].type][MistUtil.tracks.translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
var tracktypes = [];
for (var type in playabletracks) {
var playable = false;
for (var codec in playabletracks[type]) {
if (MediaSource.isTypeSupported("video/mp4;codecs=\""+codec+"\"")) {
playable = true;
break;
}
}
if (playable) {
tracktypes.push(type);
}
}
if (hassubtitles) {
//there is a subtitle track, check if there is a webvtt source
for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
tracktypes.push("subtitle");
break;
}
}
}
return tracktypes.length ? tracktypes : false;
}, },
player: function(){}, player: function(){},
scriptsrc: function(host) { return host+"/hlsjs.js"; } scriptsrc: function(host) { return host+"/hlsjs.js"; }
@ -81,13 +108,13 @@ p.prototype.build = function (MistVideo,callback) {
}); });
MistVideo.player.hls.attachMedia(video); MistVideo.player.hls.attachMedia(video);
MistVideo.player.hls.on(Hls.Events.MEDIA_ATTACHED, function () { MistVideo.player.hls.on(Hls.Events.MEDIA_ATTACHED, function () {
console.log("video and hls.js are now bound together !"); //console.log("video and hls.js are now bound together !");
//hls.loadSource("https://cattop/mist/cmaf/live/v9.m3u8"); //hls.loadSource("https://cattop/mist/cmaf/live/v9.m3u8");
//hls.loadSource("https://mira:4433/cmaf/live/v9.m3u8"); //hls.loadSource("https://mira:4433/cmaf/live/v9.m3u8");
MistVideo.player.hls.loadSource(url); MistVideo.player.hls.loadSource(url);
MistVideo.player.hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) { /*MistVideo.player.hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log("manifest loaded, found " + data.levels.length + " quality level"); console.log("manifest loaded, found " + data.levels.length + " quality level");
}); });*/
}); });
} }

View file

@ -33,27 +33,16 @@ mistplayers.html5 = {
try { try {
shortmime = shortmime.join("/"); shortmime = shortmime.join("/");
//works for mp4 but not for webm
function test(mime) {
var v = document.createElement("video");
if ((v) && (v.canPlayType(mime) != "")) {
support = v.canPlayType(mime);
}
return support;
}
function translateCodec(track) { function translateCodec(track) {
function bin2hex(index) { function bin2hex(index) {
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2); return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
} }
switch (track.codec) { switch (track.codec) {
case "AAC": case "AAC":
return "mp4a.40.2"; return "mp4a.40.2";
case "MP3": case "MP3":
return "mp3"; return "mp4a.40.34";
//return "mp4a.40.34";
case "AC3": case "AC3":
return "ec-3"; return "ec-3";
case "H264": case "H264":
@ -63,43 +52,64 @@ mistplayers.html5 = {
default: default:
return track.codec.toLowerCase(); return track.codec.toLowerCase();
} }
} }
var codecs = {}; var codecs = {};
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) { for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type != "meta") { if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = 1; codecs[translateCodec(MistVideo.info.meta.tracks[i])] = MistVideo.info.meta.tracks[i];
} }
else if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
} }
codecs = MistUtil.object.keys(codecs); var container = mimetype.split("/")[2];
if (shortmime == "video/mp4") { source.supportedCodecs = [];
if (codecs.length) {
if (codecs.length > source.simul_tracks) {
//not all of the tracks have to work
var working = 0;
for (var i in codecs) { for (var i in codecs) {
var s = test(shortmime+";codecs=\""+codecs[i]+"\""); //i is the long name (like mp4a.40.2), codecs[i] is the track meta, codecs[i].codec is the short name (like AAC)
var s = test(i);
if (s) { if (s) {
working++; source.supportedCodecs.push(codecs[i].codec);
playabletracks[codecs[i].type] = 1;
} }
} }
return (working >= source.simul_tracks); function test(codecs) {
var v = document.createElement("video");
if ((v) && (typeof v.canPlayType == "function")) {
var result;
switch (shortmime) {
case "video/webm": {
//if codecs are included here, at least chrome reports it as not working, even though it does. So we'll just assume it will play if webm returns maybe.
result = v.canPlayType(shortmime);
break;
} }
return test(shortmime+";codecs=\""+codecs.join(",")+"\""); case "video/mp4":
case "html5/application/vnd.apple.mpegurl":
default: {
result = v.canPlayType(shortmime+";codecs=\""+codecs+"\"");
break;
} }
} }
else { if (result != "") {
//if there's a h265 track, remove it from the list of codecs return result;
for (var i = codecs.length-1; i >= 0; i--) {
if (codecs[i].substr(0,4) == "hev1") {
codecs.splice(i,1);
} }
} }
if (codecs.length < source.simul_tracks) { return false; } //if there's no longer enough playable tracks, skip this player return false;
} }
support = test(shortmime);
if (hassubtitles) {
//there is a subtitle track, check if there is a webvtt source
for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
playabletracks.subtitle = 1;
break;
}
}
}
support = MistUtil.object.keys(playabletracks);
} catch(e){} } catch(e){}
return support; return support;
}, },

View file

@ -42,14 +42,17 @@ mistplayers.mews = {
} }
var codecs = {}; var codecs = {};
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) { for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type != "meta") { if (MistVideo.info.meta.tracks[i].type != "meta") {
if (MistVideo.info.meta.tracks[i].codec == "HEVC") { /*if (MistVideo.info.meta.tracks[i].codec == "HEVC") {
//the iPad claims to be able to play MP4/WS H265 tracks.. haha no. //the iPad claims to be able to play MP4/WS H265 tracks.. haha no.
continue; continue;
}*/
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = MistVideo.info.meta.tracks[i];
} }
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = MistVideo.info.meta.tracks[i].codec; else if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
}
} }
var container = mimetype.split("/")[2]; var container = mimetype.split("/")[2];
function test(codecs) { function test(codecs) {
@ -58,19 +61,25 @@ mistplayers.mews = {
} }
source.supportedCodecs = []; source.supportedCodecs = [];
for (var i in codecs) { for (var i in codecs) {
//i is the long name (like mp4a.40.2), codecs[i] is the short name (like AAC) //i is the long name (like mp4a.40.2), codecs[i] is the track meta, codecs[i].codec is the short name (like AAC)
var s = test(i); var s = test(i);
if (s) { if (s) {
source.supportedCodecs.push(codecs[i]); source.supportedCodecs.push(codecs[i].codec);
playabletracks[codecs[i].type] = 1;
} }
} }
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) { if (hassubtitles) {
MistVideo.log("Not enough playable tracks for this source"); //there is a subtitle track, check if there is a webvtt source
return false; for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
playabletracks.subtitle = 1;
break;
} }
} }
return source.supportedCodecs.length > 0; }
return MistUtil.object.keys(playabletracks);
}, },
player: function(){} player: function(){}
}; };

View file

@ -19,7 +19,7 @@ mistplayers.rawws = {
} }
for (var i in MistVideo.info.meta.tracks) { for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].codec == "HEVC") { return true; } if (MistVideo.info.meta.tracks[i].codec == "HEVC") { return ["video"]; }
} }
return false; return false;

View file

@ -19,22 +19,63 @@ mistplayers.videojs = {
return false; return false;
} }
var codecs = {}; function checkPlaybackOfTrackTypes(mime) {
for (var i in MistVideo.info.meta.tracks) { if (!MediaSource.isTypeSupported) { return true; } //we can't ask, but let's assume something will work
if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[MistVideo.info.meta.tracks[i].codec] = 1;
}
}
codecs = MistUtil.object.keys(codecs);
//if there's a h265 track, remove it from the list of codecs
for (var i = codecs.length-1; i >= 0; i--) {
if (codecs[i].substr(0,4) == "HEVC") {
codecs.splice(i,1);
}
}
if (codecs.length < source.simul_tracks) { return false; } //if there's no longer enough playable tracks, skip this player
return ("MediaSource" in window); //check if both audio and video have at least one playable track
//gather track types and codec strings
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type == "meta") {
if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
continue;
}
if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) {
playabletracks[MistVideo.info.meta.tracks[i].type] = {};
}
playabletracks[MistVideo.info.meta.tracks[i].type][MistUtil.tracks.translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
var tracktypes = [];
for (var type in playabletracks) {
var playable = false;
for (var codec in playabletracks[type]) {
if (MediaSource.isTypeSupported(mime+";codecs=\""+codec+"\"")) {
playable = true;
break;
}
}
if (playable) {
tracktypes.push(type);
}
}
if (hassubtitles) {
//there is a subtitle track, check if there is a webvtt source
for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
tracktypes.push("subtitle");
break;
}
}
}
return tracktypes.length ? tracktypes : false;
}
//can this browser play this natively?
if (document.createElement("video").canPlayType(mimetype.replace("html5/",""))) {
//we can't ask, but let's assume something will work
if (!("MediaSource" in window)) { return true; }
if (!MediaSource.isTypeSupported) { return true; }
return checkPlaybackOfTrackTypes(mimetype.replace("html5/",""));
}
if (!("MediaSource" in window)) { return false; }
return checkPlaybackOfTrackTypes("video/mp4");
}, },
player: function(){}, player: function(){},
scriptsrc: function(host) { return host+"/videojs.js"; } scriptsrc: function(host) { return host+"/videojs.js"; }

View file

@ -7,7 +7,7 @@ mistplayers.webrtc = {
}, },
isBrowserSupported: function (mimetype,source,MistVideo) { isBrowserSupported: function (mimetype,source,MistVideo) {
if ((!("WebSocket" in window)) || (!("RTCPeerConnection" in window))) { return false; } if ((!("WebSocket" in window)) || (!("RTCPeerConnection" in window) || (!("RTCRtpReceiver" in window)))) { return false; }
//check for http/https mismatch //check for http/https mismatch
if (location.protocol.replace(/^http/,"ws") != MistUtil.http.url.split(source.url.replace(/^http/,"ws")).protocol) { if (location.protocol.replace(/^http/,"ws") != MistUtil.http.url.split(source.url.replace(/^http/,"ws")).protocol) {
@ -15,7 +15,52 @@ mistplayers.webrtc = {
return false; return false;
} }
return true;
//check if both audio and video have at least one playable track
//gather track types and codec strings
var playabletracks = {};
var hassubtitles = false;
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type == "meta") {
if (MistVideo.info.meta.tracks[i].codec == "subtitle") { hassubtitles = true; }
continue;
}
if (!(MistVideo.info.meta.tracks[i].type in playabletracks)) {
playabletracks[MistVideo.info.meta.tracks[i].type] = {};
}
playabletracks[MistVideo.info.meta.tracks[i].type][MistVideo.info.meta.tracks[i].codec] = 1;
}
var tracktypes = [];
for (var type in playabletracks) {
var playable = false;
for (var codec in playabletracks[type]) {
var supported = RTCRtpReceiver.getCapabilities(type).codecs;
for (var i in supported) {
if (supported[i].mimeType.toLowerCase() == (type+"/"+codec).toLowerCase()) {
playable = true;
break;
}
}
}
if (playable) {
tracktypes.push(type);
}
}
if (hassubtitles) {
//there is a subtitle track, check if there is a webvtt source
for (var i in MistVideo.info.source) {
if (MistVideo.info.source[i].type == "html5/text/vtt") {
tracktypes.push("subtitle");
break;
}
}
}
return tracktypes.length ? tracktypes : false;
//return true;
}, },
player: function(){} player: function(){}
}; };