Embed rework
This commit is contained in:
parent
98054f268f
commit
ac7659614a
58 changed files with 8278 additions and 49187 deletions
|
@ -1,162 +1,216 @@
|
|||
mistplayers.dashjs = {
|
||||
name: 'Dash.js Player',
|
||||
version: '1.2',
|
||||
mimes: ['dash/video/mp4'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
name: "Dash.js player",
|
||||
mimes: ["dash/video/mp4"/*,"html5/application/vnd.ms-ss"*/],
|
||||
priority: MistUtil.object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype,source,options) {
|
||||
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; }
|
||||
return (('MediaSource' in window) && (location.protocol != 'file:'));
|
||||
isBrowserSupported: function (mimetype,source,MistVideo) {
|
||||
|
||||
//check for http/https mismatch
|
||||
if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
|
||||
MistVideo.log("HTTP/HTTPS mismatch for this source");
|
||||
return false;
|
||||
}
|
||||
|
||||
//don't use dashjs if this location is loaded over file://
|
||||
if (location.protocol == "file:") {
|
||||
MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ("MediaSource" in window);
|
||||
},
|
||||
player: function(){this.onreadylist = [];}
|
||||
player: function(){this.onreadylist = [];},
|
||||
scriptsrc: function(host) { return host+"/dashjs.js"; }
|
||||
};
|
||||
var p = mistplayers.dashjs.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var cont = document.createElement('div');
|
||||
cont.className = 'mistplayer';
|
||||
p.prototype.build = function (MistVideo,callback) {
|
||||
var me = this;
|
||||
|
||||
function onplayerload () {
|
||||
var ele = me.getElement('video');
|
||||
ele.className = '';
|
||||
cont.appendChild(ele);
|
||||
ele.width = options.width;
|
||||
ele.height = options.height;
|
||||
this.onDashLoad = function() {
|
||||
if (MistVideo.destroyed) { return; }
|
||||
|
||||
if (options.autoplay) {
|
||||
ele.setAttribute('autoplay','');
|
||||
}
|
||||
if (options.loop) {
|
||||
ele.setAttribute('loop','');
|
||||
}
|
||||
if (options.poster) {
|
||||
ele.setAttribute('poster',options.poster);
|
||||
}
|
||||
if (options.controls) {
|
||||
if (options.controls == 'stock') {
|
||||
ele.setAttribute('controls','');
|
||||
}
|
||||
else {
|
||||
me.buildMistControls();
|
||||
}
|
||||
}
|
||||
MistVideo.log("Building DashJS player..");
|
||||
|
||||
ele.addEventListener('error',function(e){
|
||||
var msg;
|
||||
if ('message' in e) {
|
||||
msg = e.message;
|
||||
}
|
||||
else {
|
||||
msg = 'readyState: ';
|
||||
switch (me.element.readyState) {
|
||||
case 0:
|
||||
msg += 'HAVE_NOTHING';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'HAVE_METADATA';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'HAVE_CURRENT_DATA';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'HAVE_FUTURE_DATA';
|
||||
break;
|
||||
case 4:
|
||||
msg += 'HAVE_ENOUGH_DATA';
|
||||
break;
|
||||
var ele = document.createElement("video");
|
||||
|
||||
if ("Proxy" in window) {
|
||||
var overrides = {
|
||||
get: {},
|
||||
set: {}
|
||||
};
|
||||
|
||||
MistVideo.player.api = new Proxy(ele,{
|
||||
get: function(target, key, receiver){
|
||||
if (key in overrides.get) {
|
||||
return overrides.get[key].apply(target, arguments);
|
||||
}
|
||||
var method = target[key];
|
||||
if (typeof method === "function"){
|
||||
return function () {
|
||||
return method.apply(target, arguments);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
},
|
||||
set: function(target, key, value) {
|
||||
if (key in overrides.set) {
|
||||
return overrides.set[key].call(target,value);
|
||||
}
|
||||
return target[key] = value;
|
||||
}
|
||||
msg += ' networkState: ';
|
||||
switch (me.element.networkState) {
|
||||
case 0:
|
||||
msg += 'NETWORK_EMPTY';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'NETWORK_IDLE';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'NETWORK_LOADING';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'NETWORK_NO_SOURCE';
|
||||
break;
|
||||
}
|
||||
}
|
||||
//prevent onerror loops
|
||||
if (e.target == me.element) {
|
||||
e.message = msg;
|
||||
}
|
||||
else {
|
||||
me.adderror(msg);
|
||||
}
|
||||
});
|
||||
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting'];
|
||||
for (var i in events) {
|
||||
ele.addEventListener(events[i],function(e){
|
||||
me.addlog('Player event fired: '+e.type);
|
||||
});
|
||||
|
||||
if (MistVideo.info.type == "live") {
|
||||
overrides.get.duration = function(){
|
||||
//this should indicate the end of Mist's buffer
|
||||
var buffer_end = 0;
|
||||
if (this.buffered.length) {
|
||||
buffer_end = this.buffered.end(this.buffered.length-1)
|
||||
}
|
||||
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
|
||||
return buffer_end + time_since_buffer + -1*MistVideo.player.api.liveOffset + 45;
|
||||
};
|
||||
overrides.set.currentTime = function(value){
|
||||
var offset = value - MistVideo.player.api.duration;
|
||||
//MistVideo.player.api.liveOffset = offset;
|
||||
|
||||
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
|
||||
|
||||
MistVideo.video.currentTime = value;
|
||||
//.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
|
||||
}
|
||||
MistUtil.event.addListener(ele,"progress",function(){
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
});
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
MistVideo.player.api.liveOffset = 0;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
me.api = ele;
|
||||
}
|
||||
|
||||
if (MistVideo.options.autoplay) {
|
||||
ele.setAttribute("autoplay","");
|
||||
}
|
||||
if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
|
||||
ele.setAttribute("loop","");
|
||||
}
|
||||
if (MistVideo.options.poster) {
|
||||
ele.setAttribute("poster",MistVideo.options.poster);
|
||||
}
|
||||
if (MistVideo.options.controls == "stock") {
|
||||
ele.setAttribute("controls","");
|
||||
}
|
||||
|
||||
var player = dashjs.MediaPlayer().create();
|
||||
player.getDebug().setLogToBrowserConsole(false);
|
||||
player.initialize(ele,options.src,true);
|
||||
//player.getDebug().setLogToBrowserConsole(false);
|
||||
player.initialize(ele,MistVideo.source.url,MistVideo.options.autoplay);
|
||||
|
||||
|
||||
me.dash = player;
|
||||
me.src = options.src;
|
||||
|
||||
//add listeners for events that we can log
|
||||
var skipEvents = ["METRIC_ADDED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];
|
||||
for (var i in dashjs.MediaPlayer.events) {
|
||||
if (skipEvents.indexOf(i) < 0) {
|
||||
me.dash.on(dashjs.MediaPlayer.events[i],function(e){
|
||||
MistVideo.log("Player event fired: "+e.type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
me.addlog('Built html');
|
||||
callback(cont);
|
||||
MistVideo.player.setSize = function(size){
|
||||
this.api.style.width = size.width+"px";
|
||||
this.api.style.height = size.height+"px";
|
||||
};
|
||||
MistVideo.player.api.setSource = function(url) {
|
||||
MistVideo.player.dash.attachSource(url);
|
||||
};
|
||||
|
||||
//trackswitching
|
||||
MistVideo.player.api.setTrack = function(type,id){
|
||||
var meta = MistUtil.tracks.parse(MistVideo.info.meta.tracks);
|
||||
if ((!(type in meta)) || ((!(id in meta[type]) && (id != 0)))) {
|
||||
MistVideo.log("Skipping trackselection of "+type+" track "+id+" because it does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
//figure out what the track number is
|
||||
//whyyyy did it have to be reverse order
|
||||
var n = me.dash.getBitrateInfoListFor("video").length - 1;
|
||||
for (var i in MistVideo.info.meta.tracks) {
|
||||
var t = MistVideo.info.meta.tracks[i];
|
||||
if (t.type == type) {
|
||||
if (t.trackid == id) { break; }
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
me.dash.setAutoSwitchQualityFor(type,false); //turn off ABR rules //TODO do we want this by default?
|
||||
me.dash.setFastSwitchEnabled(true); //show the new track asap
|
||||
me.dash.setQualityFor(type,n);
|
||||
//dash does change the track, but is appended to the buffer, so it seems to take a while..
|
||||
}
|
||||
|
||||
//react to automatic trackswitching
|
||||
me.dash.on("qualityChangeRendered",function(e){
|
||||
//the newQuality-th track of type mediaType is being selected
|
||||
|
||||
//figure out the track id
|
||||
//whyyyy did it have to be reverse order
|
||||
var n = me.dash.getBitrateInfoListFor("video").length - 1;
|
||||
var id;
|
||||
for (var i in MistVideo.info.meta.tracks) {
|
||||
var t = MistVideo.info.meta.tracks[i];
|
||||
if (t.type == e.mediaType) {
|
||||
if (e.newQuality == n) {
|
||||
id = t.trackid;
|
||||
break;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
//create an event to pass this to the skin
|
||||
MistUtil.event.send("playerUpdate_trackChanged",{
|
||||
type: e.mediaType,
|
||||
trackid: id
|
||||
},MistVideo.video);
|
||||
|
||||
});
|
||||
|
||||
//dashjs keeps on spamming the stalled icon >_>
|
||||
MistUtil.event.addListener(ele,"progress",function(e){
|
||||
if (MistVideo.container.getAttribute("data-loading") == "stalled") {
|
||||
MistVideo.container.removeAttribute("data-loading");
|
||||
}
|
||||
});
|
||||
|
||||
me.api.unload = function(){
|
||||
me.dash.reset();
|
||||
};
|
||||
|
||||
MistVideo.log("Built html");
|
||||
callback(ele);
|
||||
}
|
||||
|
||||
if ('dash' in window) {
|
||||
onplayerload();
|
||||
if ("dashjs" in window) {
|
||||
this.onDashLoad();
|
||||
}
|
||||
else {
|
||||
//load the dashjs player
|
||||
var scripttag = document.createElement('script');
|
||||
scripttag.src = options.host+'/dashjs.js';
|
||||
me.addlog('Retrieving dashjs player code from '+scripttag.src);
|
||||
document.head.appendChild(scripttag);
|
||||
scripttag.onerror = function(){
|
||||
me.askNextCombo('Failed to load dashjs.js');
|
||||
}
|
||||
scripttag.onload = function(){
|
||||
onplayerload();
|
||||
}
|
||||
|
||||
var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.dashjs.scriptsrc(MistVideo.options.host)),{
|
||||
onerror: function(e){
|
||||
var msg = "Failed to load dashjs.js";
|
||||
if (e.message) { msg += ": "+e.message; }
|
||||
MistVideo.showError(msg);
|
||||
},
|
||||
onload: me.onDashLoad
|
||||
},MistVideo);
|
||||
}
|
||||
}
|
||||
p.prototype.play = function(){ return this.element.play(); };
|
||||
p.prototype.pause = function(){ return this.element.pause(); };
|
||||
p.prototype.volume = function(level){
|
||||
if (typeof level == 'undefined' ) { return this.element.volume; }
|
||||
return this.element.volume = level;
|
||||
};
|
||||
p.prototype.play = function(){ return this.element.play(); };
|
||||
p.prototype.load = function(){ return this.element.load(); };
|
||||
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
|
||||
p.prototype.fullscreen = function(){
|
||||
if(this.element.requestFullscreen) {
|
||||
return this.element.requestFullscreen();
|
||||
} else if(this.element.mozRequestFullScreen) {
|
||||
return this.element.mozRequestFullScreen();
|
||||
} else if(this.element.webkitRequestFullscreen) {
|
||||
return this.element.webkitRequestFullscreen();
|
||||
} else if(this.element.msRequestFullscreen) {
|
||||
return this.element.msRequestFullscreen();
|
||||
}
|
||||
};
|
||||
}
|
||||
p.prototype.resize = function(size){
|
||||
this.element.width = size.width;
|
||||
this.element.height = size.height;
|
||||
};
|
||||
p.prototype.updateSrc = function(src){
|
||||
if (src == '') {
|
||||
this.dash.reset();
|
||||
return;
|
||||
}
|
||||
this.dash.attachSource(src);
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
mistplayers.flash_strobe = {
|
||||
name: 'Strobe Flash Media Playback',
|
||||
version: '1.1',
|
||||
mimes: ['flash/10','flash/11','flash/7'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
name: "Strobe Flash media playback",
|
||||
mimes: ["flash/10","flash/11","flash/7"],
|
||||
priority: MistUtil.object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype,source,options) {
|
||||
//check for http / https crossovers
|
||||
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; }
|
||||
isBrowserSupported: function (mimetype,source,MistVideo) {
|
||||
|
||||
//check for http/https mismatch
|
||||
if ((MistUtil.http.url.split(source.url).protocol.slice(0,4) == "http") && (location.protocol != MistUtil.http.url.split(source.url).protocol)) {
|
||||
MistVideo.log("HTTP/HTTPS mismatch for this source");
|
||||
return false;
|
||||
}
|
||||
|
||||
var version = 0;
|
||||
try {
|
||||
// check in the mimeTypes
|
||||
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
|
||||
var plugin = navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;
|
||||
if (plugin.version) { version = plugin.version.split(".")[0]; }
|
||||
else { version = plugin.description.replace(/([^0-9\.])/g, "").split(".")[0]; }
|
||||
} catch(e){}
|
||||
try {
|
||||
// for our special friend IE
|
||||
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
|
||||
version = new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/([^0-9\,])/g, "").split(",")[0];
|
||||
} catch(e){}
|
||||
|
||||
var mimesplit = mimetype.split('/');
|
||||
if (!version) {
|
||||
//flash is not enabled? Might need to ask permission first.
|
||||
//TODO how? just let it build?
|
||||
return false;
|
||||
}
|
||||
|
||||
var mimesplit = mimetype.split("/");
|
||||
|
||||
return Number(version) >= Number(mimesplit[mimesplit.length-1]);
|
||||
},
|
||||
|
@ -28,39 +39,53 @@ mistplayers.flash_strobe = {
|
|||
};
|
||||
var p = mistplayers.flash_strobe.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
function createParam(name,value) {
|
||||
var p = document.createElement('param');
|
||||
p.setAttribute('name',name);
|
||||
p.setAttribute('value',value);
|
||||
return p;
|
||||
}
|
||||
p.prototype.build = function (MistVideo,callback) {
|
||||
|
||||
|
||||
var ele = this.getElement('object');
|
||||
|
||||
ele.setAttribute('width',options.width);
|
||||
ele.setAttribute('height',options.height);
|
||||
|
||||
ele.appendChild(createParam('movie',options.host+options.source.player_url));
|
||||
var flashvars = 'src='+encodeURIComponent(options.src)+'&controlBarMode='+(options.controls ? 'floating' : 'none')+'&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3'+(options.live ? '&streamType=live' : '')+(options.autoplay ? '&autoPlay=true' : '' );
|
||||
ele.appendChild(createParam('flashvars',flashvars));
|
||||
ele.appendChild(createParam('allowFullScreen','true'));
|
||||
ele.appendChild(createParam('wmode','direct'));
|
||||
if (options.autoplay) {
|
||||
ele.appendChild(createParam('autoPlay','true'));
|
||||
}
|
||||
|
||||
var e = document.createElement('embed');
|
||||
var ele = document.createElement("object");
|
||||
var e = document.createElement("embed");
|
||||
ele.appendChild(e);
|
||||
e.setAttribute('src',options.source.player_url);
|
||||
e.setAttribute('type','application/x-shockwave-flash');
|
||||
e.setAttribute('allowfullscreen','true');
|
||||
e.setAttribute('width',options.width);
|
||||
e.setAttribute('height',options.height);
|
||||
e.setAttribute('flashvars',flashvars);
|
||||
|
||||
function build(source) {
|
||||
var options = MistVideo.options;
|
||||
function createParam(name,value) {
|
||||
var p = document.createElement("param");
|
||||
p.setAttribute("name",name);
|
||||
p.setAttribute("value",value);
|
||||
return p;
|
||||
}
|
||||
|
||||
MistUtil.empty(ele);
|
||||
ele.appendChild(createParam("movie",MistVideo.urlappend(options.host+MistVideo.source.player_url)));
|
||||
var flashvars = "src="+encodeURIComponent(source)+"&controlBarMode="+(options.controls ? "floating" : "none")+"&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3"+(options.live ? "&streamType=live" : "")+(options.autoplay ? "&autoPlay=true" : "" );
|
||||
ele.appendChild(createParam("flashvars",flashvars));
|
||||
ele.appendChild(createParam("allowFullScreen","true"));
|
||||
ele.appendChild(createParam("wmode","direct"));
|
||||
if (options.autoplay) {
|
||||
ele.appendChild(createParam("autoPlay","true"));
|
||||
}
|
||||
|
||||
e.setAttribute("src",MistVideo.urlappend(MistVideo.source.player_url));
|
||||
e.setAttribute("type","application/x-shockwave-flash");
|
||||
e.setAttribute("allowfullscreen","true");
|
||||
e.setAttribute("flashvars",flashvars);
|
||||
}
|
||||
build(MistVideo.source.url);
|
||||
|
||||
this.addlog('Built html');
|
||||
this.api = {};
|
||||
this.setSize = function(size){
|
||||
ele.setAttribute("width",size.width);
|
||||
ele.setAttribute("height",size.height);
|
||||
e.setAttribute("width",size.width);
|
||||
e.setAttribute("height",size.height);
|
||||
};
|
||||
this.setSize(MistVideo.calcSize());
|
||||
this.onready(function(){
|
||||
if (MistVideo.container) { MistVideo.container.removeAttribute("data-loading"); }
|
||||
});
|
||||
this.api.setSource = function(url){
|
||||
build(url);
|
||||
}
|
||||
|
||||
MistVideo.log("Built html");
|
||||
callback(ele);
|
||||
}
|
||||
|
|
|
@ -1,29 +1,87 @@
|
|||
mistplayers.html5 = {
|
||||
name: 'HTML5 video player',
|
||||
version: '1.1',
|
||||
mimes: ['html5/application/vnd.apple.mpegurl','html5/video/mp4','html5/video/ogg','html5/video/webm','html5/audio/mp3','html5/audio/webm','html5/audio/ogg','html5/audio/wav'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
name: "HTML5 video player",
|
||||
mimes: ["html5/application/vnd.apple.mpegurl","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],
|
||||
priority: MistUtil.object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype,source,options,streaminfo) {
|
||||
if ((['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) != -1) && (mimetype == 'html5/video/mp4')) { return false; }
|
||||
isBrowserSupported: function (mimetype,source,MistVideo) {
|
||||
|
||||
var support = false;
|
||||
var shortmime = mimetype.split('/');
|
||||
shortmime.shift();
|
||||
|
||||
if ((shortmime[0] == 'audio') && (streaminfo.height) && (!options.forceType) && (!options.forceSource)) {
|
||||
//claim you don't support audio-only playback if there is video data, unless this mime is being forced
|
||||
return false;
|
||||
//check for http/https mismatch
|
||||
if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
|
||||
if ((location.protocol == "file:") && (MistUtil.http.url.split(source.url).protocol == "http:")) {
|
||||
MistVideo.log("This page was loaded over file://, the player might not behave as intended.");
|
||||
}
|
||||
else {
|
||||
MistVideo.log("HTTP/HTTPS mismatch for this source");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var support = false;
|
||||
var shortmime = mimetype.split("/");
|
||||
shortmime.shift();
|
||||
|
||||
try {
|
||||
var v = document.createElement((shortmime[0] == 'audio' ? 'audio' : 'video'));
|
||||
shortmime = shortmime.join('/')
|
||||
if ((v) && (v.canPlayType(shortmime) != "")) {
|
||||
support = v.canPlayType(shortmime);
|
||||
shortmime = shortmime.join("/");
|
||||
|
||||
function test(mime) {
|
||||
var v = document.createElement("video");
|
||||
if ((v) && (v.canPlayType(mime) != "")) {
|
||||
support = v.canPlayType(mime);
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
if (shortmime == "video/mp4") {
|
||||
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 "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])] = 1;
|
||||
}
|
||||
}
|
||||
codecs = MistUtil.object.keys(codecs);
|
||||
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) {
|
||||
var s = test(shortmime+";codecs=\""+codecs[i]+"\"");
|
||||
if (s) {
|
||||
working++;
|
||||
}
|
||||
}
|
||||
return (working >= source.simul_tracks);
|
||||
}
|
||||
shortmime += ";codecs=\""+codecs.join(",")+"\"";
|
||||
}
|
||||
}
|
||||
|
||||
support = test(shortmime);
|
||||
} catch(e){}
|
||||
return support;
|
||||
},
|
||||
|
@ -34,206 +92,159 @@ mistplayers.html5 = {
|
|||
};
|
||||
var p = mistplayers.html5.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var cont = document.createElement('div');
|
||||
cont.className = 'mistplayer';
|
||||
var me = this; //to allow nested functions to access the player class itself
|
||||
|
||||
var shortmime = options.source.type.split('/');
|
||||
p.prototype.build = function (MistVideo,callback) {
|
||||
var shortmime = MistVideo.source.type.split("/");
|
||||
shortmime.shift();
|
||||
var video = document.createElement("video");
|
||||
|
||||
var ele = this.getElement((shortmime[0] == 'audio' ? 'audio' : 'video'));
|
||||
ele.className = '';
|
||||
cont.appendChild(ele);
|
||||
//TODO verify: not required if player is loaded from same domain as it should always be when not in dev mode?
|
||||
video.setAttribute("crossorigin","anonymous");//required for subs, breaks ogg?
|
||||
|
||||
if (options.source.type != "html5/video/ogg") {
|
||||
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load
|
||||
}
|
||||
var source = document.createElement("source");
|
||||
source.setAttribute("src",MistVideo.source.url);
|
||||
video.source = source;
|
||||
video.appendChild(source);
|
||||
source.type = shortmime.join("/");
|
||||
|
||||
if (shortmime[0] == 'audio') {
|
||||
this.setTracks = function() { return false; }
|
||||
this.fullscreen = false;
|
||||
cont.className += ' audio';
|
||||
}
|
||||
|
||||
this.addlog('Building HTML5 player..');
|
||||
|
||||
var source = document.createElement('source');
|
||||
source.setAttribute('src',options.src);
|
||||
this.source = source;
|
||||
ele.appendChild(source);
|
||||
source.type = shortmime.join('/');
|
||||
this.addlog('Adding '+source.type+' source @ '+options.src);
|
||||
|
||||
if ((this.tracks.subtitle.length) && (this.subtitle)) {
|
||||
for (var i in this.tracks.subtitle) {
|
||||
var t = document.createElement('track');
|
||||
ele.appendChild(t);
|
||||
t.kind = 'subtitles';
|
||||
t.label = this.tracks.subtitle[i].desc;
|
||||
t.srclang = this.tracks.subtitle[i].lang;
|
||||
t.src = this.subtitle+'?track='+this.tracks.subtitle[i].trackid;
|
||||
//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]));
|
||||
}
|
||||
}
|
||||
|
||||
ele.width = options.width;
|
||||
ele.height = options.height;
|
||||
ele.style.width = options.width+'px';
|
||||
ele.style.height = options.height+'px';
|
||||
|
||||
if (options.autoplay) {
|
||||
ele.setAttribute('autoplay','');
|
||||
if (MistVideo.options.controls == "stock") {
|
||||
video.setAttribute("controls","");
|
||||
}
|
||||
if (options.loop) {
|
||||
ele.setAttribute('loop','');
|
||||
}
|
||||
if (options.poster) {
|
||||
ele.setAttribute('poster',options.poster);
|
||||
}
|
||||
if (options.controls) {
|
||||
if ((options.controls == 'stock') || (!this.buildMistControls())) {
|
||||
//MistControls have failed to build in the if condition
|
||||
ele.setAttribute('controls','');
|
||||
}
|
||||
if (MistVideo.info.type == "live") {
|
||||
video.loop = false;
|
||||
}
|
||||
|
||||
cont.onclick = function(){
|
||||
if (ele.paused) { ele.play(); }
|
||||
else { ele.pause(); }
|
||||
};
|
||||
|
||||
this.addlog('Built html');
|
||||
|
||||
//forward events
|
||||
ele.addEventListener('error',function(e){
|
||||
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves
|
||||
if (("Proxy" in window) && ("Reflect" in window)) {
|
||||
var overrides = {
|
||||
get: {},
|
||||
set: {}
|
||||
};
|
||||
|
||||
if (options.live) {
|
||||
if ((ele.error) && (ele.error.code == 3)) {
|
||||
e.stopPropagation(); //dont let this error continue to prevent the core from trying to handle the error
|
||||
me.load();
|
||||
me.cancelAskNextCombo();
|
||||
e.message = 'Handled decoding error';
|
||||
me.addlog('Decoding error: reloading..');
|
||||
me.report({
|
||||
type: 'playback',
|
||||
warn: 'A decoding error was encountered, but handled'
|
||||
});
|
||||
return;
|
||||
MistVideo.player.api = new Proxy(video,{
|
||||
get: function(target, key, receiver){
|
||||
if (key in overrides.get) {
|
||||
return overrides.get[key].apply(target, arguments);
|
||||
}
|
||||
var method = target[key];
|
||||
if (typeof method === "function"){
|
||||
return function () {
|
||||
return method.apply(target, arguments);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
},
|
||||
set: function(target, key, value) {
|
||||
if (key in overrides.set) {
|
||||
return overrides.set[key].call(target,value);
|
||||
}
|
||||
return target[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var msg;
|
||||
if ('message' in e) {
|
||||
msg = e.message;
|
||||
if (MistVideo.source.type == "html5/audio/mp3") {
|
||||
overrides.set.currentTime = function(){
|
||||
MistVideo.log("Seek attempted, but MistServer does not currently support seeking in MP3.");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else if ((e.target.tagName == 'SOURCE') && (e.target.getAttribute('src') == '')) {
|
||||
e.stopPropagation();
|
||||
//this error is triggered because the unload function was fired
|
||||
return;
|
||||
if (MistVideo.info.type == "live") {
|
||||
|
||||
overrides.get.duration = function(){
|
||||
//this should indicate the end of Mist's buffer
|
||||
var buffer_end = 0;
|
||||
if (this.buffered.length) {
|
||||
buffer_end = this.buffered.end(this.buffered.length-1)
|
||||
}
|
||||
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
|
||||
return buffer_end + time_since_buffer - MistVideo.player.api.liveOffset;
|
||||
};
|
||||
overrides.set.currentTime = function(value){
|
||||
var offset = value - MistVideo.player.api.duration;
|
||||
MistVideo.player.api.liveOffset = offset;
|
||||
|
||||
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
|
||||
|
||||
MistVideo.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
|
||||
}
|
||||
MistUtil.event.addListener(video,"progress",function(){
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
});
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
MistVideo.player.api.liveOffset = 0;
|
||||
|
||||
|
||||
MistUtil.event.addListener(video,"pause",function(){
|
||||
MistVideo.player.api.pausedAt = new Date();
|
||||
});
|
||||
overrides.get.play = function(){
|
||||
return function(){
|
||||
if ((MistVideo.player.api.paused) && (MistVideo.player.api.pausedAt) && ((new Date()) - MistVideo.player.api.pausedAt > 5e3)) {
|
||||
video.load();
|
||||
MistVideo.log("Reloading source..");
|
||||
}
|
||||
|
||||
return video.play.apply(video, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
if (MistVideo.source.type == "html5/video/mp4") {
|
||||
overrides.get.currentTime = function(){
|
||||
return this.currentTime - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg = 'readyState: ';
|
||||
switch (me.element.readyState) {
|
||||
case 0:
|
||||
msg += 'HAVE_NOTHING';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'HAVE_METADATA';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'HAVE_CURRENT_DATA';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'HAVE_FUTURE_DATA';
|
||||
break;
|
||||
case 4:
|
||||
msg += 'HAVE_ENOUGH_DATA';
|
||||
break;
|
||||
}
|
||||
msg += ' networkState: ';
|
||||
switch (me.element.networkState) {
|
||||
case 0:
|
||||
msg += 'NETWORK_EMPTY';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'NETWORK_IDLE';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'NETWORK_LOADING';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'NETWORK_NO_SOURCE';
|
||||
break;
|
||||
if (!isFinite(video.duration)) {
|
||||
var duration = 0;
|
||||
for (var i in MistVideo.info.meta.tracks) {
|
||||
duration = Math.max(duration,MistVideo.info.meta.tracks[i].lastms);
|
||||
}
|
||||
overrides.get.duration = function(){
|
||||
if (isFinite(this.duration)) { return this.duration; }
|
||||
return duration * 1e-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
me.adderror(msg);
|
||||
});
|
||||
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
|
||||
for (var i in events) {
|
||||
ele.addEventListener(events[i],function(e){
|
||||
me.addlog('Player event fired: '+e.type);
|
||||
});
|
||||
}
|
||||
callback(cont);
|
||||
}
|
||||
p.prototype.play = function(){ return this.element.play(); };
|
||||
p.prototype.pause = function(){ return this.element.pause(); };
|
||||
p.prototype.volume = function(level){
|
||||
if (typeof level == 'undefined' ) { return this.element.volume; }
|
||||
return this.element.volume = level;
|
||||
};
|
||||
p.prototype.loop = function(bool){
|
||||
if (typeof bool == 'undefined') {
|
||||
return this.element.loop;
|
||||
}
|
||||
return this.element.loop = bool;
|
||||
};
|
||||
p.prototype.load = function(){
|
||||
var load;
|
||||
if (this.element.paused) {
|
||||
load = this.element.load();
|
||||
}
|
||||
else {
|
||||
//sometimes there is a play / pause interrupt: try again
|
||||
//TODO figure out if this happens on paused or on playing
|
||||
this.load();
|
||||
return;
|
||||
MistVideo.player.api = video;
|
||||
}
|
||||
|
||||
//this helps to prevent the player from just showing a black screen after a reload
|
||||
if (this.element.paused) {
|
||||
var me = this;
|
||||
var unpause = function(){
|
||||
if (me.element.paused) {
|
||||
me.element.play();
|
||||
}
|
||||
me.element.removeEventListener('progress',unpause);
|
||||
}
|
||||
this.element.addEventListener('progress',unpause);
|
||||
}
|
||||
|
||||
return load;
|
||||
};
|
||||
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
|
||||
p.prototype.fullscreen = function(){
|
||||
if(this.element.requestFullscreen) {
|
||||
return this.element.requestFullscreen();
|
||||
} else if(this.element.mozRequestFullScreen) {
|
||||
return this.element.mozRequestFullScreen();
|
||||
} else if(this.element.webkitRequestFullscreen) {
|
||||
return this.element.webkitRequestFullscreen();
|
||||
} else if(this.element.msRequestFullscreen) {
|
||||
return this.element.msRequestFullscreen();
|
||||
MistVideo.player.api.setSource = function(url) {
|
||||
if (url != this.source.src) {
|
||||
this.source.src = url;
|
||||
this.load();
|
||||
}
|
||||
};
|
||||
MistVideo.player.api.setSubtitle = function(trackmeta) {
|
||||
//remove previous subtitles
|
||||
var tracks = video.getElementsByTagName("track");
|
||||
for (var i = tracks.length - 1; i >= 0; i--) {
|
||||
video.removeChild(tracks[i]);
|
||||
}
|
||||
if (trackmeta) { //if the chosen track exists
|
||||
//add the new one
|
||||
var track = document.createElement("track");
|
||||
video.appendChild(track);
|
||||
track.kind = "subtitles";
|
||||
track.label = trackmeta.label;
|
||||
track.srclang = trackmeta.lang;
|
||||
track.src = trackmeta.src;
|
||||
track.setAttribute("default","");
|
||||
}
|
||||
};
|
||||
MistVideo.player.setSize = function(size){
|
||||
this.api.style.width = size.width+"px";
|
||||
this.api.style.height = size.height+"px";
|
||||
};
|
||||
|
||||
callback(video);
|
||||
}
|
||||
p.prototype.updateSrc = function(src){
|
||||
this.source.setAttribute('src',src);
|
||||
return true;
|
||||
};
|
||||
p.prototype.resize = function(size){
|
||||
this.element.width = size.width;
|
||||
this.element.height = size.height;
|
||||
};
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
mistplayers.img = {
|
||||
name: 'HTML img tag',
|
||||
version: '1.1',
|
||||
mimes: ['html5/image/jpeg'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype,source,options,streaminfo) {
|
||||
//only use this if we are sure we just want an image
|
||||
if ((options.forceType) || (options.forceSource) || (options.forcePlayer)) { return true; }
|
||||
return false;
|
||||
},
|
||||
player: function(){this.onreadylist = [];}
|
||||
};
|
||||
var p = mistplayers.img.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var ele = this.getElement('img');
|
||||
ele.src = options.src;
|
||||
ele.style.display = 'block';
|
||||
callback(ele);
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
mistplayers.jwplayer = {
|
||||
name: 'JWPlayer',
|
||||
version: '0.2',
|
||||
mimes: ['html5/video/mp4','html5/video/webm','dash/video/mp4','flash/10','flash/7','html5/application/vnd.apple.mpegurl','html5/audio/mp3','html5/audio/aac'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype) {
|
||||
//TODO like, actually check the browser or something?
|
||||
if (typeof jwplayer == 'function') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
player: function(){}
|
||||
};
|
||||
var p = mistplayers.jwplayer.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var ele = this.getElement('div');
|
||||
|
||||
this.jw = jwplayer(ele).setup({
|
||||
file: options.src,
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
autostart: options.autoplay,
|
||||
image: options.poster,
|
||||
controls: options.controls
|
||||
});
|
||||
|
||||
this.addlog('Built html');
|
||||
callback(ele);
|
||||
}
|
||||
p.prototype.play = function(){ return this.jw.play(); };
|
||||
p.prototype.pause = function(){ return this.jw.pause(); };
|
||||
p.prototype.volume = function(level){
|
||||
if (typeof level == 'undefined' ) { return this.jw.getVolume/100; }
|
||||
return this.jw.setVolume(level*100);
|
||||
};
|
|
@ -1,101 +0,0 @@
|
|||
mistplayers.polytrope = {
|
||||
name: 'Polytrope Flash Player',
|
||||
version: '0.2',
|
||||
mimes: ['flash/11','flash/10','flash/7'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype) {
|
||||
return false;
|
||||
|
||||
var version = 0;
|
||||
try {
|
||||
// check in the mimeTypes
|
||||
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
|
||||
} catch(e){}
|
||||
try {
|
||||
// for our special friend IE
|
||||
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
|
||||
} catch(e){}
|
||||
|
||||
var mimesplit = mimetype.split('/');
|
||||
|
||||
return Number(version) >= Number(mimesplit[mimesplit.length-1]);
|
||||
},
|
||||
player: function(){}
|
||||
};
|
||||
var p = mistplayers.polytrope.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
function createParam(name,value) {
|
||||
var p = document.createElement('param');
|
||||
p.setAttribute('name',name);
|
||||
p.setAttribute('value',value);
|
||||
return p;
|
||||
}
|
||||
|
||||
//TODO its not working.
|
||||
|
||||
/*
|
||||
this.swf = this.video_instance_el.flash({
|
||||
swf: "/shared/swf/videoplayer.swf?" + (new Date).getTime(),
|
||||
width: "100%",
|
||||
height: parseInt(this.options.element.height()),
|
||||
wmode: "opaque",
|
||||
menu: "false",
|
||||
allowFullScreen: "true",
|
||||
allowFullScreenInteractive: "true",
|
||||
allowScriptAccess: "always",
|
||||
id: "cucumbertv-swf-" + this.guid,
|
||||
expressInstall: "/shared/swf/expressInstall.swf",
|
||||
flashvars: {
|
||||
rtmp_url: "rtmp://" + this.options.stream_host + "/play/",
|
||||
stream_name: this.options.stream_name,
|
||||
poster: this.options.poster,
|
||||
autoplay: this.options.autoplay,
|
||||
color_1: "0x1d1d1d",
|
||||
color_2: "0xffffff",
|
||||
buffer_time: .1,
|
||||
is_streaming_url: "/api/user/is_streaming",
|
||||
username: this.options.username,
|
||||
mode: "v" == this.options.type ? "archive" : "live",
|
||||
guid: this.guid
|
||||
}
|
||||
})
|
||||
|
||||
<div>
|
||||
<object data="/shared/swf/videoplayer.swf?1468312898591" type="application/x-shockwave-flash" id="cucumbertv-swf-4dc64c18-59af-91a2-d0c5-ab8df4f45c65" width="100%" height="660">
|
||||
<param name="wmode" value="opaque">
|
||||
<param name="menu" value="false">
|
||||
<param name="allowFullScreen" value="true">
|
||||
<param name="allowFullScreenInteractive" value="true">
|
||||
<param name="allowScriptAccess" value="always">
|
||||
<param name="expressInstall" value="/shared/swf/expressInstall.swf">
|
||||
<param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&autoplay=true&color_1=0x1d1d1d&color_2=0xffffff&buffer_time=0.1&is_streaming_url=/api/user/is_streaming&username=SrA&mode=archive&guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65">
|
||||
<param name="movie" value="/shared/swf/videoplayer.swf?1468312898591">
|
||||
</object>
|
||||
</div>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
var ele = this.element('object');
|
||||
ele.data = 'players/polytrope.swf';
|
||||
ele.type = 'application/x-shockwave-flash';
|
||||
ele.width = options.width;
|
||||
ele.height = options.height;
|
||||
|
||||
/*
|
||||
ele.appendChild(createParam('allowFullScreen','true'));
|
||||
ele.appendChild(createParam('allowScriptAccess','always'));
|
||||
var flashvars = 'rtmp_url=rtmp://www.stickystage.com/play/&stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&autoplay=true&color_1=0x1d1d1d&color_2=0xffffff&buffer_time=0.1&is_streaming_url=/api/user/is_streaming&username=SrA&mode=archive&guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65';
|
||||
ele.appendChild(createParam('flashvars',flashvars));
|
||||
ele.appendChild(createParam('movie','players/polytrope.swf'));
|
||||
*/
|
||||
|
||||
ele.innerHTML = '<param name="wmode" value="opaque"> <param name="menu" value="false"> <param name="allowFullScreen" value="true"> <param name="allowFullScreenInteractive" value="true"> <param name="allowScriptAccess" value="always"> <param name="expressInstall" value="/shared/swf/expressInstall.swf"> <param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&poster=http://stickystage.com/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&autoplay=true&color_1=0x1d1d1d&color_2=0xffffff&buffer_time=0.1&is_streaming_url=/api/user/is_streaming&username=SrA&mode=archive&guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65"> <param name="movie" value="players/polytrope.swf">';
|
||||
|
||||
this.addlog('Built html');
|
||||
callback(ele);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
mistplayers.silverlight = {
|
||||
name: 'Silverlight',
|
||||
version: '1.1',
|
||||
mimes: ['silverlight'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype) {
|
||||
var plugin;
|
||||
try {
|
||||
// check in the mimeTypes
|
||||
plugin = navigator.plugins["Silverlight Plug-In"];
|
||||
return !!plugin;
|
||||
} catch(e){}
|
||||
try {
|
||||
// for our special friend IE
|
||||
plugin = new ActiveXObject('AgControl.AgControl');
|
||||
return true;
|
||||
} catch(e){}
|
||||
|
||||
return false;
|
||||
},
|
||||
player: function(){this.onreadylist = [];}
|
||||
};
|
||||
var p = mistplayers.silverlight.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
function createParam(name,value) {
|
||||
var p = document.createElement('param');
|
||||
p.setAttribute('name',name);
|
||||
p.setAttribute('value',value);
|
||||
return p;
|
||||
}
|
||||
|
||||
var ele = this.getElement('object');
|
||||
ele.setAttribute('data','data:application/x-silverlight,');
|
||||
ele.setAttribute('type','application/x-silverlight');
|
||||
ele.setAttribute('width',options.width);
|
||||
ele.setAttribute('height',options.height);
|
||||
ele.appendChild(createParam('source',encodeURI(options.src)+'/player.xap'));
|
||||
ele.appendChild(createParam('initparams','autoload=false,'+(options.autoplay ? 'autoplay=true' : 'autoplay=false')+',displaytimecode=false,enablecaptions=true,joinLive=true,muted=false'));
|
||||
|
||||
var a = document.createElement('a');
|
||||
ele.appendChild(a);
|
||||
a.setAttribute('href','http://go.microsoft.com/fwlink/?LinkID=124807');
|
||||
a.setAttribute('style','text-decoration: none;');
|
||||
var img = document.createElement('img');
|
||||
a.appendChild(img);
|
||||
img.setAttribute('src','http://go.microsoft.com/fwlink/?LinkId=108181');
|
||||
img.setAttribute('alt','Get Microsoft Silverlight');
|
||||
img.setAttribute('style','border-style: none;')
|
||||
|
||||
this.addlog('Built html');
|
||||
callback(ele);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
mistplayers.myplayer = {
|
||||
name: 'My video player',
|
||||
version: '0.1',
|
||||
mimes: ['my/mime/types'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype) {
|
||||
//TODO your code here
|
||||
return false;
|
||||
},
|
||||
player: function(){}
|
||||
};
|
||||
var p = mistplayers.myplayer.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var ele = this.element('object');
|
||||
|
||||
//TODO your code here
|
||||
|
||||
this.addlog('Built html');
|
||||
callback(ele);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
mistplayers.theoplayer = {
|
||||
name: 'TheoPlayer',
|
||||
version: '0.2',
|
||||
mimes: ['html5/application/vnd.apple.mpegurl','dash/video/mp4'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype) {
|
||||
//TODO like, actually check the browser or something?
|
||||
if (typeof theoplayer == 'function') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
player: function(){}
|
||||
};
|
||||
var p = mistplayers.theoplayer.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
var ele = this.getElement('video');
|
||||
|
||||
ele.src = options.src;
|
||||
ele.width = options.width;
|
||||
ele.height = options.height;
|
||||
|
||||
if (options.controls) {
|
||||
ele.setAttribute('controls','');
|
||||
}
|
||||
if (options.autoplay) {
|
||||
ele.setAttribute('autoplay','');
|
||||
}
|
||||
if (options.loop) {
|
||||
ele.setAttribute('loop','');
|
||||
}
|
||||
if (options.poster) {
|
||||
ele.setAttribute('poster',options.poster);
|
||||
}
|
||||
|
||||
this.theoplayer = theoplayer(ele);
|
||||
|
||||
this.addlog('Built html');
|
||||
callback(ele);
|
||||
}
|
||||
p.prototype.play = function(){ return this.theoplayer.play(); };
|
||||
p.prototype.pause = function(){ return this.theoplayer.pause(); };
|
||||
p.prototype.volume = function(level){
|
||||
if (typeof level == 'undefined' ) { return this.theoplayer.volume; }
|
||||
return this.theoplayer.volume = level;
|
||||
};
|
||||
p.prototype.fullscreen = function(){
|
||||
return this.theoplayer.requestFullscreen();
|
||||
};
|
|
@ -1,221 +1,208 @@
|
|||
mistplayers.videojs = {
|
||||
name: 'VideoJS player',
|
||||
version: '1.1',
|
||||
mimes: ['html5/video/mp4','html5/application/vnd.apple.mpegurl','html5/video/ogg','html5/video/webm'],
|
||||
priority: Object.keys(mistplayers).length + 1,
|
||||
name: "VideoJS player",
|
||||
mimes: ["html5/application/vnd.apple.mpegurl"],
|
||||
priority: MistUtil.object.keys(mistplayers).length + 1,
|
||||
isMimeSupported: function (mimetype) {
|
||||
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
|
||||
},
|
||||
isBrowserSupported: function (mimetype,source,options,streaminfo,logfunc) {
|
||||
isBrowserSupported: function (mimetype,source,MistVideo) {
|
||||
|
||||
//dont use https if the player is loaded over http
|
||||
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) {
|
||||
if (logfunc) { logfunc('HTTP/HTTPS mismatch for this source'); }
|
||||
//check for http/https mismatch
|
||||
if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
|
||||
MistVideo.log("HTTP/HTTPS mismatch for this source");
|
||||
return false;
|
||||
}
|
||||
|
||||
//dont use videojs if this location is loaded over file://
|
||||
if ((location.protocol == 'file:') && (mimetype == 'html5/application/vnd.apple.mpegurl')) {
|
||||
if (logfunc) { logfunc('This source ('+mimetype+') won\'t work if the page is run via file://'); }
|
||||
//don't use videojs if this location is loaded over file://
|
||||
if ((location.protocol == "file:") && (mimetype == "html5/application/vnd.apple")) {
|
||||
MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
|
||||
return false;
|
||||
}
|
||||
|
||||
//dont use HLS if there is an MP3 audio track, unless we're on apple or edge
|
||||
if ((mimetype == 'html5/application/vnd.apple.mpegurl') && (['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) == -1) && (navigator.userAgent.indexOf('Edge') == -1)) {
|
||||
var audio = false;
|
||||
var nonmp3 = false;
|
||||
for (var i in streaminfo.meta.tracks) {
|
||||
var t = streaminfo.meta.tracks[i];
|
||||
if (t.type == 'audio') {
|
||||
audio = true;
|
||||
if (t.codec != 'MP3') {
|
||||
nonmp3 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((audio) && (!nonmp3)) {
|
||||
if (logfunc) { logfunc('This source has audio, but only MP3, and this browser can\'t play MP3 via HLS'); }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ('MediaSource' in window);
|
||||
return ("MediaSource" in window);
|
||||
},
|
||||
player: function(){this.onreadylist = [];}
|
||||
player: function(){},
|
||||
scriptsrc: function(host) { return host+"/videojs.js"; }
|
||||
};
|
||||
var p = mistplayers.videojs.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (options,callback) {
|
||||
p.prototype.build = function (MistVideo,callback) {
|
||||
var me = this; //to allow nested functions to access the player class itself
|
||||
|
||||
function onplayerload () {
|
||||
me.addlog('Building VideoJS player..');
|
||||
function onVideoJSLoad () {
|
||||
if (MistVideo.destroyed) { return;}
|
||||
|
||||
var cont = document.createElement('div');
|
||||
cont.className = 'mistplayer';
|
||||
MistVideo.log("Building VideoJS player..");
|
||||
|
||||
var ele = me.getElement('video');
|
||||
cont.appendChild(ele);
|
||||
ele.className = '';
|
||||
if (options.source.type != "html5/video/ogg") {
|
||||
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load
|
||||
var ele = document.createElement("video");
|
||||
if (MistVideo.source.type != "html5/video/ogg") {
|
||||
ele.crossOrigin = "anonymous"; //required for subtitles, but if ogg, the video won"t load
|
||||
}
|
||||
|
||||
var shortmime = options.source.type.split('/');
|
||||
var shortmime = MistVideo.source.type.split("/");
|
||||
shortmime.shift();
|
||||
|
||||
var source = document.createElement('source');
|
||||
source.setAttribute('src',options.src);
|
||||
var source = document.createElement("source");
|
||||
source.setAttribute("src",MistVideo.source.url);
|
||||
me.source = source;
|
||||
ele.appendChild(source);
|
||||
source.type = shortmime.join('/');
|
||||
me.addlog('Adding '+source.type+' source @ '+options.src);
|
||||
if (source.type == 'application/vnd.apple.mpegurl') { source.type = 'application/x-mpegURL'; }
|
||||
source.type = shortmime.join("/");
|
||||
MistVideo.log("Adding "+source.type+" source @ "+MistVideo.source.url);
|
||||
if (source.type == "application/vnd.apple.mpegurl") { source.type = "application/x-mpegURL"; }
|
||||
|
||||
ele.className += ' video-js';
|
||||
ele.width = options.width;
|
||||
ele.height = options.height;
|
||||
ele.style.width = options.width+'px';
|
||||
ele.style.height = options.height+'px';
|
||||
MistUtil.class.add(ele,"video-js");
|
||||
|
||||
var vjsopts = {
|
||||
preload: 'auto'
|
||||
};
|
||||
var vjsopts = {};
|
||||
|
||||
if (options.autoplay) { vjsopts.autoplay = true; }
|
||||
if (options.loop) {
|
||||
if (MistVideo.options.autoplay) { vjsopts.autoplay = true; }
|
||||
if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
|
||||
vjsopts.loop = true;
|
||||
ele.loop = true;
|
||||
}
|
||||
if (options.poster) { vjsopts.poster = options.poster; }
|
||||
if (options.controls) {
|
||||
if ((options.controls == 'stock') || (!me.buildMistControls())) {
|
||||
//MistControls have failed to build in the if condition
|
||||
ele.setAttribute('controls',true);
|
||||
if (MistVideo.options.poster) { vjsopts.poster = MistVideo.options.poster; }
|
||||
if (MistVideo.options.controls == "stock") {
|
||||
ele.setAttribute("controls","");
|
||||
if (!document.getElementById("videojs-css")) {
|
||||
var style = document.createElement("link");
|
||||
style.rel = "stylesheet";
|
||||
style.href = MistVideo.options.host+"/skins/videojs.css";
|
||||
style.id = "videojs-css";
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
me.onready(function(){
|
||||
me.videojs = videojs(ele,vjsopts,function(){
|
||||
me.addlog('Videojs initialized');
|
||||
MistVideo.log("Videojs initialized");
|
||||
});
|
||||
|
||||
me.api.unload = function(){
|
||||
videojs(ele).dispose();
|
||||
};
|
||||
});
|
||||
|
||||
me.addlog('Built html');
|
||||
MistVideo.log("Built html");
|
||||
|
||||
//forward events
|
||||
ele.addEventListener('error',function(e){
|
||||
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves
|
||||
if (("Proxy" in window) && ("Reflect" in window)) {
|
||||
var overrides = {
|
||||
get: {},
|
||||
set: {}
|
||||
};
|
||||
|
||||
var msg;
|
||||
if ('message' in e) {
|
||||
msg = e.message;
|
||||
}
|
||||
else {
|
||||
msg = 'readyState: ';
|
||||
switch (me.element.readyState) {
|
||||
case 0:
|
||||
msg += 'HAVE_NOTHING';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'HAVE_METADATA';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'HAVE_CURRENT_DATA';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'HAVE_FUTURE_DATA';
|
||||
break;
|
||||
case 4:
|
||||
msg += 'HAVE_ENOUGH_DATA';
|
||||
break;
|
||||
MistVideo.player.api = new Proxy(ele,{
|
||||
get: function(target, key, receiver){
|
||||
if (key in overrides.get) {
|
||||
return overrides.get[key].apply(target, arguments);
|
||||
}
|
||||
var method = target[key];
|
||||
if (typeof method === "function"){
|
||||
return function () {
|
||||
return method.apply(target, arguments);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
},
|
||||
set: function(target, key, value) {
|
||||
if (key in overrides.set) {
|
||||
return overrides.set[key].call(target,value);
|
||||
}
|
||||
return target[key] = value;
|
||||
}
|
||||
msg += ' networkState: ';
|
||||
switch (me.element.networkState) {
|
||||
case 0:
|
||||
msg += 'NETWORK_EMPTY';
|
||||
break;
|
||||
case 1:
|
||||
msg += 'NETWORK_IDLE';
|
||||
break;
|
||||
case 2:
|
||||
msg += 'NETWORK_LOADING';
|
||||
break;
|
||||
case 3:
|
||||
msg += 'NETWORK_NO_SOURCE';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
me.adderror(msg);
|
||||
|
||||
});
|
||||
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
|
||||
for (var i in events) {
|
||||
ele.addEventListener(events[i],function(e){
|
||||
me.addlog('Player event fired: '+e.type);
|
||||
});
|
||||
|
||||
if (MistVideo.info.type == "live") {
|
||||
function getLastBuffer(video) {
|
||||
var buffer_end = 0;
|
||||
if (video.buffered.length) {
|
||||
buffer_end = video.buffered.end(video.buffered.length-1)
|
||||
}
|
||||
return buffer_end;
|
||||
}
|
||||
var HLSlatency = 90; //best guess..
|
||||
|
||||
overrides.get.duration = function(){
|
||||
return (MistVideo.info.lastms + (new Date()).getTime() - MistVideo.info.updated.getTime())*1e-3;
|
||||
};
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
MistVideo.player.api.liveOffset = 0;
|
||||
|
||||
MistUtil.event.addListener(ele,"progress",function(){
|
||||
MistVideo.player.api.lastProgress = new Date();
|
||||
});
|
||||
overrides.set.currentTime = function(value){
|
||||
var diff = MistVideo.player.api.currentTime - value;
|
||||
var offset = value - MistVideo.player.api.duration;
|
||||
//MistVideo.player.api.liveOffset = offset;
|
||||
|
||||
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
|
||||
MistVideo.video.currentTime -= diff;
|
||||
}
|
||||
overrides.get.currentTime = function(){
|
||||
return this.currentTime + MistVideo.info.lastms*1e-3 - MistVideo.player.api.liveOffset - HLSlatency;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
me.api = ele;
|
||||
}
|
||||
|
||||
callback(cont);
|
||||
MistVideo.player.setSize = function(size){
|
||||
if ("videojs" in MistVideo.player) {
|
||||
MistVideo.player.videojs.dimensions(size.width,size.height);
|
||||
|
||||
//for some reason, the videojs' container won't be resized with the method above.
|
||||
//so let's cheat and do it ourselves
|
||||
ele.parentNode.style.width = size.width+"px";
|
||||
ele.parentNode.style.height = size.height+"px";
|
||||
}
|
||||
this.api.style.width = size.width+"px";
|
||||
this.api.style.height = size.height+"px";
|
||||
};
|
||||
MistVideo.player.api.setSource = function(url) {
|
||||
if (!MistVideo.player.videojs) { return; }
|
||||
if (MistVideo.player.videojs.src() != url) {
|
||||
MistVideo.player.videojs.src({
|
||||
type: MistVideo.player.videojs.currentSource().type,
|
||||
src: url
|
||||
});
|
||||
}
|
||||
};
|
||||
MistVideo.player.api.setSubtitle = function(trackmeta) {
|
||||
//remove previous subtitles
|
||||
var tracks = ele.getElementsByTagName("track");
|
||||
for (var i = tracks.length - 1; i >= 0; i--) {
|
||||
ele.removeChild(tracks[i]);
|
||||
}
|
||||
if (trackmeta) { //if the chosen track exists
|
||||
//add the new one
|
||||
var track = document.createElement("track");
|
||||
ele.appendChild(track);
|
||||
track.kind = "subtitles";
|
||||
track.label = trackmeta.label;
|
||||
track.srclang = trackmeta.lang;
|
||||
track.src = trackmeta.src;
|
||||
track.setAttribute("default","");
|
||||
}
|
||||
};
|
||||
|
||||
callback(ele);
|
||||
}
|
||||
|
||||
if ('videojs' in window) {
|
||||
onplayerload();
|
||||
if ("videojs" in window) {
|
||||
onVideoJSLoad();
|
||||
}
|
||||
else {
|
||||
//load the videojs player
|
||||
var scripttag = document.createElement('script');
|
||||
scripttag.src = options.host+'/videojs.js';
|
||||
me.addlog('Retrieving videojs player code from '+scripttag.src);
|
||||
document.head.appendChild(scripttag);
|
||||
scripttag.onerror = function(){
|
||||
me.askNextCombo('Failed to load videojs.js');
|
||||
}
|
||||
scripttag.onload = function(){
|
||||
onplayerload();
|
||||
}
|
||||
|
||||
var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.videojs.scriptsrc(MistVideo.options.host)),{
|
||||
onerror: function(e){
|
||||
var msg = "Failed to load videojs.js";
|
||||
if (e.message) { msg += ": "+e.message; }
|
||||
MistVideo.showError(msg);
|
||||
},
|
||||
onload: onVideoJSLoad
|
||||
},MistVideo);
|
||||
|
||||
}
|
||||
}
|
||||
p.prototype.play = function(){ return this.element.play(); };
|
||||
p.prototype.pause = function(){ return this.element.pause(); };
|
||||
p.prototype.volume = function(level){
|
||||
if (typeof level == 'undefined' ) { return this.element.volume; }
|
||||
return this.element.volume = level;
|
||||
};
|
||||
p.prototype.loop = function(bool){
|
||||
if (typeof bool == 'undefined') {
|
||||
return this.element.loop;
|
||||
}
|
||||
return this.element.loop = bool;
|
||||
};
|
||||
p.prototype.load = function(){ return this.element.load(); };
|
||||
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
|
||||
p.prototype.fullscreen = function(){
|
||||
if(this.element.requestFullscreen) {
|
||||
return this.element.requestFullscreen();
|
||||
} else if(this.element.mozRequestFullScreen) {
|
||||
return this.element.mozRequestFullScreen();
|
||||
} else if(this.element.webkitRequestFullscreen) {
|
||||
return this.element.webkitRequestFullscreen();
|
||||
} else if(this.element.msRequestFullscreen) {
|
||||
return this.element.msRequestFullscreen();
|
||||
}
|
||||
};
|
||||
}
|
||||
p.prototype.updateSrc = function(src){
|
||||
if ("videojs" in this) {
|
||||
if (src == '') {
|
||||
this.videojs.dispose();
|
||||
return;
|
||||
}
|
||||
this.videojs.src({
|
||||
src: src,
|
||||
type: this.source.type
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
429
embed/wrappers/webrtc.js
Normal file
429
embed/wrappers/webrtc.js
Normal file
|
@ -0,0 +1,429 @@
|
|||
mistplayers.webrtc = {
|
||||
name: "WebRTC player",
|
||||
mimes: ["webrtc"],
|
||||
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)) || (!("RTCPeerConnection" 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
player: function(){}
|
||||
};
|
||||
var p = mistplayers.webrtc.player;
|
||||
p.prototype = new MistPlayer();
|
||||
p.prototype.build = function (MistVideo,callback) {
|
||||
var me = this;
|
||||
|
||||
if ((typeof WebRTCBrowserEqualizerLoaded == "undefined") || (!WebRTCBrowserEqualizerLoaded)) {
|
||||
//load it
|
||||
var scripttag = document.createElement("script");
|
||||
scripttag.src = MistVideo.urlappend(MistVideo.options.host+"/webrtc.js");
|
||||
MistVideo.log("Retrieving webRTC browser equalizer code from "+scripttag.src);
|
||||
document.head.appendChild(scripttag);
|
||||
scripttag.onerror = function(){
|
||||
MistVideo.showError("Failed to load webrtc browser equalizer",{nextCombo:5});
|
||||
}
|
||||
scripttag.onload = function(){
|
||||
me.build(MistVideo,callback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var video = document.createElement("video");
|
||||
|
||||
//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.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";
|
||||
};
|
||||
MistUtil.event.addListener(video,"loadeddata",correctSubtitleSync);
|
||||
MistUtil.event.addListener(video,"seeked",correctSubtitleSync);
|
||||
|
||||
var seekoffset = 0;
|
||||
var hasended = false;
|
||||
this.listeners = {
|
||||
on_connected: function() {
|
||||
seekoffset = 0;
|
||||
hasended = false;
|
||||
this.webrtc.play();
|
||||
},
|
||||
on_disconnected: function() {
|
||||
MistVideo.log("Websocket sent on_disconnect");
|
||||
/*
|
||||
If a VoD file ends, we receive an on_stop, but no on_disconnect
|
||||
If a live stream ends, we receive an on_disconnect, but no on_stop
|
||||
If MistOutWebRTC crashes, we receive an on_stop and then an on_disconnect
|
||||
*/
|
||||
if (hasended) {
|
||||
MistVideo.showError("Connection to media server ended unexpectedly.");
|
||||
}
|
||||
video.pause();
|
||||
},
|
||||
on_answer_sdp: function (ev) {
|
||||
if (!ev.result) {
|
||||
MistVideo.showError("Failed to open stream.");
|
||||
this.on_disconnected();
|
||||
return;
|
||||
}
|
||||
MistVideo.log("SDP answer received");
|
||||
},
|
||||
on_time: function(ev) {
|
||||
//timeupdate
|
||||
var oldoffset = seekoffset;
|
||||
seekoffset = ev.current*1e-3 - video.currentTime;
|
||||
if (Math.abs(oldoffset - seekoffset) > 1) { correctSubtitleSync(); }
|
||||
|
||||
var d = (ev.end == 0 ? Infinity : ev.end*1e-3);
|
||||
if (d != duration) {
|
||||
duration = d;
|
||||
MistUtil.event.send("durationchange",d,video);
|
||||
}
|
||||
},
|
||||
on_seek: function(){
|
||||
MistUtil.event.send("seeked",seekoffset,video);
|
||||
video.play();
|
||||
},
|
||||
on_stop: function(){
|
||||
MistVideo.log("Websocket sent on_stop");
|
||||
MistUtil.event.send("ended",null,video);
|
||||
hasended = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function WebRTCPlayer(){
|
||||
this.peerConn = null;
|
||||
this.localOffer = null;
|
||||
this.isConnected = false;
|
||||
var thisWebRTCPlayer = this;
|
||||
|
||||
this.on_event = function(ev) {
|
||||
//if (ev.type != "on_time") { console.log(ev); }
|
||||
switch (ev.type) {
|
||||
case "on_connected": {
|
||||
thisWebRTCPlayer.isConnected = true;
|
||||
break;
|
||||
}
|
||||
case "on_answer_sdp": {
|
||||
thisWebRTCPlayer.peerConn
|
||||
.setRemoteDescription({ type: "answer", sdp: ev.answer_sdp })
|
||||
.then(function(){}, function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "on_disconnected": {
|
||||
thisWebRTCPlayer.isConnected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ev.type in me.listeners) {
|
||||
return me.listeners[ev.type].call(me,ev);
|
||||
}
|
||||
MistVideo.log("Unhandled WebRTC event "+ev.type+": "+JSON.stringify(ev));
|
||||
return false;
|
||||
};
|
||||
|
||||
this.connect = function(callback){
|
||||
thisWebRTCPlayer.signaling = new WebRTCSignaling(thisWebRTCPlayer.on_event);
|
||||
thisWebRTCPlayer.peerConn = new RTCPeerConnection();
|
||||
thisWebRTCPlayer.peerConn.ontrack = function(ev) {
|
||||
video.srcObject = ev.streams[0];
|
||||
if (callback) { callback(); }
|
||||
};
|
||||
};
|
||||
|
||||
this.play = function(){
|
||||
if (!this.isConnected) {
|
||||
throw "Not connected, cannot play";
|
||||
}
|
||||
|
||||
this.peerConn
|
||||
.createOffer({
|
||||
offerToReceiveAudio: true,
|
||||
offerToReceiveVideo: true,
|
||||
})
|
||||
.then(function(offer){
|
||||
thisWebRTCPlayer.localOffer = offer;
|
||||
thisWebRTCPlayer.peerConn
|
||||
.setLocalDescription(offer)
|
||||
.then(function(){
|
||||
thisWebRTCPlayer.signaling.sendOfferSDP(thisWebRTCPlayer.localOffer.sdp);
|
||||
}, function(err){console.error(err)});
|
||||
}, function(err){ throw err; });
|
||||
};
|
||||
|
||||
this.stop = function(){
|
||||
if (!this.isConnected) { throw "Not connected, cannot stop." }
|
||||
this.signaling.send({type: "stop"});
|
||||
};
|
||||
this.seek = function(seekTime){
|
||||
if (!this.isConnected) { return; }
|
||||
this.signaling.send({type: "seek", "seek_time": seekTime*1e3});
|
||||
};
|
||||
this.pause = function(){
|
||||
if (!this.isConnected) { throw "Not connected, cannot pause." }
|
||||
this.signaling.send({type: "pause"});
|
||||
};
|
||||
this.setTrack = function(obj){
|
||||
if (!this.isConnected) { throw "Not connected, cannot set track." }
|
||||
obj.type = "tracks";
|
||||
this.signaling.send(obj);
|
||||
};
|
||||
this.getStats = function(callback){
|
||||
this.peerConn.getStats().then(function(d){
|
||||
var output = {};
|
||||
var entries = Array.from(d.entries());
|
||||
for (var i in entries) {
|
||||
var value = entries[i];
|
||||
if (value[1].type == "inbound-rtp") {
|
||||
output[value[0]] = value[1];
|
||||
}
|
||||
}
|
||||
callback(output);
|
||||
});
|
||||
};
|
||||
//input only
|
||||
/*
|
||||
this.sendVideoBitrate = function(bitrate) {
|
||||
this.send({type: "video_bitrate", video_bitrate: bitrate});
|
||||
};
|
||||
*/
|
||||
|
||||
this.connect();
|
||||
}
|
||||
function WebRTCSignaling(onEvent){
|
||||
this.ws = null;
|
||||
|
||||
this.ws = new WebSocket(MistVideo.source.url.replace(/^http/,"ws"));
|
||||
|
||||
this.ws.onopen = function() {
|
||||
onEvent({type: "on_connected"});
|
||||
};
|
||||
|
||||
this.ws.onmessage = function(e) {
|
||||
try {
|
||||
var cmd = JSON.parse(e.data);
|
||||
onEvent(cmd);
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Failed to parse a response from MistServer",err,e.data);
|
||||
}
|
||||
};
|
||||
|
||||
/* See http://tools.ietf.org/html/rfc6455#section-7.4.1 */
|
||||
this.ws.onclose = function(ev) {
|
||||
switch (ev.code) {
|
||||
default: {
|
||||
onEvent({type: "on_disconnected"});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.sendOfferSDP = function(sdp) {
|
||||
this.send({type: "offer_sdp", offer_sdp: sdp});
|
||||
};
|
||||
this.send = function(cmd) {
|
||||
if (!this.ws) {
|
||||
throw "Not initialized, cannot send "+JSON.stringify(cmd);
|
||||
}
|
||||
this.ws.send(JSON.stringify(cmd));
|
||||
}
|
||||
};
|
||||
|
||||
this.webrtc = new WebRTCPlayer();
|
||||
|
||||
this.api = {};
|
||||
|
||||
//override video duration
|
||||
var duration;
|
||||
Object.defineProperty(this.api,"duration",{
|
||||
get: function(){ return duration; }
|
||||
});
|
||||
|
||||
//override seeking
|
||||
Object.defineProperty(this.api,"currentTime",{
|
||||
get: function(){
|
||||
return seekoffset + video.currentTime;
|
||||
},
|
||||
set: function(value){
|
||||
seekoffset = value - video.currentTime;
|
||||
video.pause();
|
||||
me.webrtc.seek(value);
|
||||
MistUtil.event.send("seeking",value,video);
|
||||
}
|
||||
});
|
||||
|
||||
//redirect properties
|
||||
//using a function to make sure the "item" is in the correct scope
|
||||
function reroute(item) {
|
||||
Object.defineProperty(me.api,item,{
|
||||
get: function(){ return video[item]; },
|
||||
set: function(value){
|
||||
return video[item] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
var list = [
|
||||
"volume"
|
||||
,"muted"
|
||||
,"loop"
|
||||
,"paused",
|
||||
,"error"
|
||||
,"textTracks"
|
||||
,"webkitDroppedFrameCount"
|
||||
,"webkitDecodedFrameCount"
|
||||
];
|
||||
for (var i in list) {
|
||||
reroute(list[i]);
|
||||
}
|
||||
|
||||
//redirect methods
|
||||
function redirect(item) {
|
||||
if (item in video) {
|
||||
me.api[item] = function(){
|
||||
return video[item].call(video,arguments);
|
||||
};
|
||||
}
|
||||
}
|
||||
var list = ["load","getVideoPlaybackQuality"];
|
||||
for (var i in list) {
|
||||
redirect(list[i]);
|
||||
}
|
||||
|
||||
//redirect play
|
||||
me.api.play = function(){
|
||||
if (me.api.currentTime) {
|
||||
if ((!me.webrtc.isConnected) || (me.webrtc.peerConn.iceConnectionState != "completed")) {
|
||||
me.webrtc.connect(function(){
|
||||
me.webrtc.seek(me.api.currentTime);
|
||||
});
|
||||
}
|
||||
else {
|
||||
me.webrtc.seek(me.api.currentTime);
|
||||
}
|
||||
}
|
||||
else {
|
||||
video.play();
|
||||
}
|
||||
};
|
||||
|
||||
//redirect pause
|
||||
me.api.pause = function(){
|
||||
video.pause();
|
||||
try {
|
||||
me.webrtc.pause();
|
||||
}
|
||||
catch (e) {}
|
||||
MistUtil.event.send("paused",null,video);
|
||||
};
|
||||
|
||||
me.api.setTracks = function(obj){
|
||||
me.webrtc.setTrack(obj);
|
||||
};
|
||||
function correctSubtitleSync() {
|
||||
if (!me.api.textTracks[0]) { return; }
|
||||
var currentoffset = me.api.textTracks[0].currentOffset || 0;
|
||||
if (Math.abs(seekoffset - currentoffset) < 1) { return; } //don't bother if the change is small
|
||||
var newCues = [];
|
||||
for (var i = me.api.textTracks[0].cues.length-1; i >= 0; i--) {
|
||||
var cue = me.api.textTracks[0].cues[i];
|
||||
me.api.textTracks[0].removeCue(cue);
|
||||
if (!("orig" in cue)) {
|
||||
cue.orig = {start:cue.startTime,end:cue.endTime};
|
||||
}
|
||||
cue.startTime = cue.orig.start - seekoffset;
|
||||
cue.endTime = cue.orig.end - seekoffset;
|
||||
newCues.push(cue);
|
||||
}
|
||||
for (var i in newCues) {
|
||||
me.api.textTracks[0].addCue(newCues[i]);
|
||||
}
|
||||
me.api.textTracks[0].currentOffset = seekoffset;
|
||||
}
|
||||
me.api.setSubtitle = function(trackmeta) {
|
||||
//remove previous subtitles
|
||||
var tracks = video.getElementsByTagName("track");
|
||||
for (var i = tracks.length - 1; i >= 0; i--) {
|
||||
video.removeChild(tracks[i]);
|
||||
}
|
||||
if (trackmeta) { //if the chosen track exists
|
||||
//add the new one
|
||||
var track = document.createElement("track");
|
||||
video.appendChild(track);
|
||||
track.kind = "subtitles";
|
||||
track.label = trackmeta.label;
|
||||
track.srclang = trackmeta.lang;
|
||||
track.src = trackmeta.src;
|
||||
track.setAttribute("default","");
|
||||
|
||||
//correct timesync
|
||||
track.onload = correctSubtitleSync;
|
||||
}
|
||||
};
|
||||
|
||||
//loop
|
||||
MistUtil.event.addListener(video,"ended",function(){
|
||||
if (me.api.loop) {
|
||||
me.webrtc.connect();
|
||||
}
|
||||
});
|
||||
|
||||
if ("decodingIssues" in MistVideo.skin.blueprints) {
|
||||
//get additional dev stats
|
||||
var vars = ["nackCount","pliCount","packetsLost","packetsReceived","bytesReceived"];
|
||||
for (var j in vars) {
|
||||
me.api[vars[j]] = 0;
|
||||
}
|
||||
var f = function() {
|
||||
MistVideo.timers.start(function(){
|
||||
me.webrtc.getStats(function(d){
|
||||
for (var i in d) {
|
||||
for (var j in vars) {
|
||||
if (vars[j] in d[i]) {
|
||||
me.api[vars[j]] = d[i][vars[j]];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
f();
|
||||
},1e3);
|
||||
};
|
||||
f();
|
||||
}
|
||||
|
||||
me.api.unload = function(){
|
||||
try {
|
||||
me.webrtc.stop();
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
callback(video);
|
||||
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue