Embed: Error handling for media resource based players; many small edits

This commit is contained in:
Cat 2016-12-19 16:18:42 +01:00 committed by Thulinma
parent c626fb2855
commit de3043a8e3
10 changed files with 188 additions and 47 deletions

View file

@ -3,6 +3,7 @@
///////////////////////////////////// /////////////////////////////////////
var mistplayers = {}; var mistplayers = {};
var mistplayer_session_id = Math.round(Math.random()*1e12);
function MistPlayer() {}; function MistPlayer() {};
MistPlayer.prototype.sendEvent = function(type,message,target) { MistPlayer.prototype.sendEvent = function(type,message,target) {
@ -40,7 +41,7 @@ MistPlayer.prototype.build = function () {
return err; return err;
} }
//creates the player element, including custom functions //creates the player element, including custom functions
MistPlayer.prototype.element = function(tag){ MistPlayer.prototype.getElement = function(tag){
var ele = document.createElement(tag); var ele = document.createElement(tag);
ele.className = 'mistplayer'; ele.className = 'mistplayer';
this.element = ele; this.element = ele;
@ -155,11 +156,7 @@ MistPlayer.prototype.buildMistControls = function(){
secs = Math.floor(secs - mins * 60); secs = Math.floor(secs - mins * 60);
var str = []; var str = [];
if (hours) { if (hours) {
var h = '0'+hours; str.push(hours);
if (hours < 100) {
h.slice(-2);
}
str.push(h);
} }
str.push(('0'+mins).slice(-2)); str.push(('0'+mins).slice(-2));
str.push(('0'+secs).slice(-2)); str.push(('0'+secs).slice(-2));
@ -176,8 +173,10 @@ MistPlayer.prototype.buildMistControls = function(){
}; };
function whileLivePlaying(track) { function whileLivePlaying(track) {
var playtime = (new Date()) - options.initTime; //var playtime = (new Date()) - options.initTime;
var playtime = ele.currentTime*1e3;
timestampValue.nodeValue = formatTime((playtime + track.lastms)/1e3); timestampValue.nodeValue = formatTime((playtime + track.lastms)/1e3);
setTimeout(function(){ setTimeout(function(){
if (!ele.paused) { if (!ele.paused) {
@ -577,12 +576,14 @@ MistPlayer.prototype.buildMistControls = function(){
return true; return true;
} }
MistPlayer.prototype.askNextCombo = function(){ MistPlayer.prototype.askNextCombo = function(msg){
var me = this; var me = this;
if (me.errorstate) { return; }
me.errorstate = true; me.errorstate = true;
me.addlog('Showing error window');
var err = document.createElement('div'); var err = document.createElement('div');
var msgnode = document.createTextNode('Player or stream error detected'); var msgnode = document.createTextNode(msg ? msg : 'Player or stream error detected');
err.appendChild(msgnode); err.appendChild(msgnode);
err.className = 'error'; err.className = 'error';
var button = document.createElement('button'); var button = document.createElement('button');
@ -609,22 +610,76 @@ MistPlayer.prototype.askNextCombo = function(){
}; };
MistPlayer.prototype.cancelAskNextCombo = function(){ MistPlayer.prototype.cancelAskNextCombo = function(){
if (this.errorstate) { if (this.errorstate) {
this.errorstate = false;
this.addlog('Removing error window');
this.element.style.opacity = 1; this.element.style.opacity = 1;
var err = this.target.querySelector('.error'); var err = this.target.querySelector('.error');
if (err) { if (err) {
this.target.removeChild(err); this.target.removeChild(err);
} }
this.errorstate = false;
} }
}; };
MistPlayer.prototype.reload = function(){ MistPlayer.prototype.reload = function(){
this.unload();
mistPlay(this.mistplaySettings.streamname,this.mistplaySettings.options); mistPlay(this.mistplaySettings.streamname,this.mistplaySettings.options);
}; };
MistPlayer.prototype.nextCombo = function(){ MistPlayer.prototype.nextCombo = function(){
this.unload();
var opts = this.mistplaySettings.options; var opts = this.mistplaySettings.options;
opts.startCombo = this.mistplaySettings.startCombo; opts.startCombo = this.mistplaySettings.startCombo;
mistPlay(this.mistplaySettings.streamname,opts); mistPlay(this.mistplaySettings.streamname,opts);
}; };
///send information back to mistserver
///\param msg object containing the information to report
MistPlayer.prototype.report = function(msg) {
///send a http post request
///\param url (string) url to send to
///\param params object containing post parameters
function httpPost(url,params) {
var http = new XMLHttpRequest();
var postdata = [];
for (var i in params) {
postdata.push(i+'='+params[i]);
}
http.open("POST", url, true);
http.send(postdata.join('&'));
}
//add some extra information
msg.userinfo = {
userAgent: navigator.userAgent,
page: location.href,
stream: this.streamname,
session: mistplayer_session_id
};
if ('index' in this) { msg.userinfo.playerindex = this.index; }
if ('playername' in this) { msg.userinfo.player = this.playername; }
if ('options' in this) {
if ('source' in this.options) {
msg.userinfo.source = {
src: this.options.source.url,
type: this.options.source.type
};
}
msg.userinfo.resolution = this.options.width+'x'+this.options.height;
}
try {
httpPost(this.options.host+'/report',{
report: JSON.stringify(msg)
});
}
catch (e) { }
}
MistPlayer.prototype.unload = function(){
if (('pause' in this) && (this.pause)) { this.pause(); }
if ('updateSrc' in this) { this.updateSrc(''); }
//delete this.element;
this.target.innerHTML = '';
};
///////////////////////////////////////////////// /////////////////////////////////////////////////
// SELECT AND ADD A VIDEO PLAYER TO THE TARGET // // SELECT AND ADD A VIDEO PLAYER TO THE TARGET //
@ -633,6 +688,7 @@ MistPlayer.prototype.nextCombo = function(){
function mistPlay(streamName,options) { function mistPlay(streamName,options) {
var protoplay = new MistPlayer(); var protoplay = new MistPlayer();
protoplay.streamname = streamName;
function embedLog(msg) { function embedLog(msg) {
protoplay.sendEvent('log',msg,options.target); protoplay.sendEvent('log',msg,options.target);
} }
@ -710,6 +766,10 @@ function mistPlay(streamName,options) {
options.target.innerHTML = ''; options.target.innerHTML = '';
options.target.removeAttribute('data-loading'); options.target.removeAttribute('data-loading');
mistError('Error while loading stream info.'); mistError('Error while loading stream info.');
protoplay.report({
type: 'init',
error: 'Failed to load '+info.src
});
} }
info.onload = function(){ info.onload = function(){
options.target.innerHTML = ''; options.target.innerHTML = '';
@ -726,6 +786,10 @@ function mistPlay(streamName,options) {
if (!('source' in streaminfo)) { if (!('source' in streaminfo)) {
mistError('Error while loading stream info.'); mistError('Error while loading stream info.');
protoplay.report({
type: 'init',
error: 'No sources'
});
return; return;
} }
@ -775,7 +839,6 @@ function mistPlay(streamName,options) {
embedLog('Checking available players..'); embedLog('Checking available players..');
var source = false; var source = false;
var mistPlayer = false;
function checkPlayer(p_shortname) { function checkPlayer(p_shortname) {
if ((startCombo) && (!startCombo.started.player)) { if ((startCombo) && (!startCombo.started.player)) {
@ -864,6 +927,7 @@ function mistPlay(streamName,options) {
} }
if (mistPlayer) { if (mistPlayer) {
//create the options to send to the player //create the options to send to the player
var playerOpts = { var playerOpts = {
src: source.url+(('urlappend' in options) && (options.urlappend) ? options.urlappend : '' ), src: source.url+(('urlappend' in options) && (options.urlappend) ? options.urlappend : '' ),
@ -925,6 +989,7 @@ function mistPlay(streamName,options) {
//save the objects for future reference //save the objects for future reference
var player = new mistplayers[mistPlayer].player(); var player = new mistplayers[mistPlayer].player();
player.playername = mistPlayer;
player.target = options.target; player.target = options.target;
if (!('embedded' in streaminfo)) { streaminfo.embedded = []; } if (!('embedded' in streaminfo)) { streaminfo.embedded = []; }
streaminfo.embedded.push({ streaminfo.embedded.push({
@ -933,6 +998,7 @@ function mistPlay(streamName,options) {
player: player, player: player,
playerOptions: playerOpts playerOptions: playerOpts
}); });
player.index = streaminfo.embedded.length-1;
if (player.setTracks(false)) { if (player.setTracks(false)) {
//gather track info //gather track info
@ -1004,13 +1070,21 @@ function mistPlay(streamName,options) {
catch (e) { catch (e) {
//show the next player/reload buttons if there is an error in the player build code //show the next player/reload buttons if there is an error in the player build code
options.target.appendChild(player.element); options.target.appendChild(player.element);
player.askNextCombo(); player.askNextCombo('Error while building player');
throw e; throw e;
player.report({
type: 'init',
error: 'Error while building player'
});
return; return;
} }
options.target.appendChild(element); options.target.appendChild(element);
element.setAttribute('data-player',mistPlayer); element.setAttribute('data-player',mistPlayer);
element.setAttribute('data-mime',source.type); element.setAttribute('data-mime',source.type);
player.report({
type: 'init',
info: 'Player built'
});
if (player.setTracks(false)) { if (player.setTracks(false)) {
player.onready(function(){ player.onready(function(){
@ -1019,24 +1093,48 @@ function mistPlay(streamName,options) {
} }
//monitor for errors //monitor for errors
player.checkStalledTimeout = false; element.checkStalledTimeout = false;
player.checkProgressTimeout = false; element.checkProgressTimeout = false;
element.addEventListener('error',function(e){ element.addEventListener('error',function(e){
player.askNextCombo(); player.askNextCombo('The player has thrown an error');
},true); var r = {
type: 'playback',
error: 'The player has thrown an error',
origin: e.target.outerHTML.slice(0,e.target.outerHTML.indexOf('>')+1),
};
if ('readyState' in player.element) {
r.readyState = player.element.readyState;
}
if ('networkState' in player.element) {
r.networkState = player.element.networkState;
}
if (('error' in player.element) && ('code' in player.element.error)) {
r.code = player.element.error.code;
}
player.report(r);
});
var stalled = function(e){ var stalled = function(e){
if (player.checkStalledTimeout) { return; } if (element.checkStalledTimeout) { return; }
player.checkStalledTimeout = setTimeout(function(){ element.checkStalledTimeout = setTimeout(function(){
if (player.paused) { return; } if (player.paused) { return; }
player.askNextCombo(); player.askNextCombo('Playback has stalled');
},5e3); player.report({
'type': 'playback',
'warn': 'Playback was stalled for > 10 sec'
});
},10e3);
}; };
element.addEventListener('stalled',stalled,true); element.addEventListener('stalled',stalled,true);
element.addEventListener('waiting',stalled,true); element.addEventListener('waiting',stalled,true);
var progress = function(e){ var progress = function(e){
if (player.checkStalledTimeout) { if (element.checkStalledTimeout) {
clearTimeout(player.checkStalledTimeout); clearTimeout(element.checkStalledTimeout);
player.checkStalledTimeout = false; element.checkStalledTimeout = false;
player.cancelAskNextCombo();
}
if (element.checkStalledTimeout) {
clearTimeout(element.checkStalledTimeout);
element.checkStalledTimeout = false;
player.cancelAskNextCombo(); player.cancelAskNextCombo();
} }
}; };
@ -1044,29 +1142,53 @@ function mistPlay(streamName,options) {
element.addEventListener('playing',progress,true); element.addEventListener('playing',progress,true);
element.addEventListener('play',function(){ element.addEventListener('play',function(){
player.paused = false; player.paused = false;
if ((!player.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) { if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) {
//check if the progress made is equal to the time spent //check if the progress made is equal to the time spent
var lasttime = player.element.currentTime; var lasttime = player.element.currentTime;
player.checkProgressTimeout = setInterval(function(){ element.checkProgressTimeout = setInterval(function(){
var newtime = player.element.currentTime; var newtime = player.element.currentTime;
if (newtime == 0) { return; }
var progress = newtime - lasttime; var progress = newtime - lasttime;
lasttime = newtime; lasttime = newtime;
if (progress == 0) { if (progress == 0) {
player.addlog('There should be playback but nothing was played'); var msg = 'There should be playback but nothing was played';
player.askNextCombo(); var r = {
type: 'playback',
warning: msg
};
player.addlog(msg);
if ('readyState' in player.element) {
r.readyState = player.element.readyState;
}
if ('networkState' in player.element) {
r.networkState = player.element.networkState;
}
if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) {
r.code = player.element.error.code;
}
player.report(r);
player.askNextCombo('No playback');
if ('load' in player.element) { player.element.load(); }
return; return;
} }
if (progress < 4.9) { player.cancelAskNextCombo();
player.addlog('It seems playback is \lagging ('+Math.round(100 - progress/0.5)/10+'%)'); if (progress < 5) {
var msg = 'It seems playback is lagging (progressed '+Math.round(progress*100)/100+'/10s)'
player.addlog(msg);
player.report({
'type': 'playback',
'warning': msg
});
return;
} }
},5e3); },10e3);
} }
},true); },true);
element.addEventListener('pause',function(){ element.addEventListener('pause',function(){
player.paused = true; player.paused = true;
if (player.checkProgressTimeout) { if (element.checkProgressTimeout) {
clearInterval(player.checkStalledTimeout); clearInterval(element.checkProgressTimeout);
player.checkStalledTimeout = false; element.checkProgressTimeout = false;
} }
},true); },true);
@ -1096,6 +1218,10 @@ function mistPlay(streamName,options) {
else { else {
var str = 'Stream not found.'; var str = 'Stream not found.';
} }
protoplay.report({
type: 'init',
error: str
});
mistError(str); mistError(str);
} }
} }

View file

@ -107,6 +107,7 @@
.mistplayer .controls .button { .mistplayer .controls .button {
cursor: pointer; cursor: pointer;
width: 45px; width: 45px;
margin: 5px;
line-height: 45px; line-height: 45px;
font-size: 16px; font-size: 16px;
position: relative; position: relative;
@ -115,6 +116,7 @@
} }
.mistplayer .controls.smaller .button { .mistplayer .controls.smaller .button {
width: 15px; width: 15px;
margin: 0;
line-height: 15px; line-height: 15px;
font-size: 8px; font-size: 8px;
} }
@ -299,7 +301,7 @@
background-color: black; background-color: black;
padding: 5px 10px; padding: 5px 10px;
right: -1000px; right: -1000px;
bottom: 27px; bottom: 23px;
} }
.mistplayer .controls.smaller .tracks .settings { .mistplayer .controls.smaller .tracks .settings {
padding: 2px 3px; padding: 2px 3px;

View file

@ -16,6 +16,7 @@
// global options can be set here // global options can be set here
var mistoptions = { var mistoptions = {
host: 'http://cat.mistserver.org:8080' host: 'http://cat.mistserver.org:8080'
//host: 'http://thulmk3:8080'
//host: 'https://cat.mistserver.org:4433' //host: 'https://cat.mistserver.org:4433'
//host: 'http://localhost:8080' //host: 'http://localhost:8080'
}; };
@ -65,18 +66,19 @@
div.appendChild(msg); div.appendChild(msg);
div.style.color = 'red'; div.style.color = 'red';
logele.appendChild(div); logele.appendChild(div);
},true); });
document.addEventListener('log',function(e){ document.addEventListener('log',function(e){
console.log('[log] '+e.message) console.log('[log] '+e.message);
return;
var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message); var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message);
var div = document.createElement('div'); var div = document.createElement('div');
div.appendChild(msg); div.appendChild(msg);
logele.appendChild(div); logele.appendChild(div);
},true); });
//tryplayers = Object.keys(mistplayers); //tryplayers = Object.keys(mistplayers);
tryplayers = []; tryplayers = [];
tryplayers.push('derp'); tryplayers.push('automatic');
//tryplayers.push('html5'); //tryplayers.push('html5');
//tryplayers.push('dashjs'); //tryplayers.push('dashjs');
//tryplayers.push('videojs'); //tryplayers.push('videojs');
@ -84,6 +86,7 @@
//tryplayers.push('silverlight'); //tryplayers.push('silverlight');
streams = []; streams = [];
streams.push('live'); streams.push('live');
//streams.push('golive+ThePaddedRoom');
//streams.push('subtel'); //streams.push('subtel');
//streams.push('ogg'); //streams.push('ogg');
//streams.push('vids+mist.mp4'); //streams.push('vids+mist.mp4');

View file

@ -18,7 +18,7 @@ p.prototype.build = function (options,callback) {
cont.className = 'mistplayer'; cont.className = 'mistplayer';
var me = this; var me = this;
var ele = this.element('video'); var ele = this.getElement('video');
ele.className = ''; ele.className = '';
cont.appendChild(ele); cont.appendChild(ele);
ele.width = options.width; ele.width = options.width;

View file

@ -36,7 +36,7 @@ p.prototype.build = function (options) {
} }
var ele = this.element('object'); var ele = this.getElement('object');
ele.setAttribute('width',options.width); ele.setAttribute('width',options.width);
ele.setAttribute('height',options.height); ele.setAttribute('height',options.height);

View file

@ -39,7 +39,7 @@ p.prototype.build = function (options) {
var shortmime = options.source.type.split('/'); var shortmime = options.source.type.split('/');
shortmime.shift(); shortmime.shift();
var ele = this.element((shortmime[0] == 'audio' ? 'audio' : 'video')); var ele = this.getElement((shortmime[0] == 'audio' ? 'audio' : 'video'));
ele.className = ''; ele.className = '';
cont.appendChild(ele); cont.appendChild(ele);
ele.crossOrigin = 'anonymous'; //required for subtitles ele.crossOrigin = 'anonymous'; //required for subtitles
@ -98,9 +98,15 @@ p.prototype.build = function (options) {
if (options.live) { if (options.live) {
ele.addEventListener('error',function(e){ ele.addEventListener('error',function(e){
if ((ele.error) && (ele.error.code == 3)) { if ((ele.error) && (ele.error.code == 3)) {
e.stopPropagation();
ele.load(); ele.load();
me.cancelAskNextCombo(); me.cancelAskNextCombo();
e.message = 'Handled decoding error';
me.addlog('Decoding error: reloading..'); me.addlog('Decoding error: reloading..');
me.report({
type: 'playback',
warning: 'A decoding error was encountered, but handled'
});
} }
},true); },true);
} }
@ -150,7 +156,7 @@ p.prototype.build = function (options) {
} }
me.adderror(msg); me.adderror(msg);
},true); },true);
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting']; 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) { for (var i in events) {
ele.addEventListener(events[i],function(e){ ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type); me.addlog('Player event fired: '+e.type);

View file

@ -17,7 +17,7 @@ mistplayers.jwplayer = {
var p = mistplayers.jwplayer.player; var p = mistplayers.jwplayer.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options) { p.prototype.build = function (options) {
var ele = this.element('div'); var ele = this.getElement('div');
this.jw = jwplayer(ele).setup({ this.jw = jwplayer(ele).setup({
file: options.src, file: options.src,

View file

@ -32,7 +32,7 @@ p.prototype.build = function (options) {
return p; return p;
} }
var ele = this.element('object'); var ele = this.getElement('object');
ele.setAttribute('data','data:application/x-silverlight,'); ele.setAttribute('data','data:application/x-silverlight,');
ele.setAttribute('type','application/x-silverlight'); ele.setAttribute('type','application/x-silverlight');
ele.setAttribute('width',options.width); ele.setAttribute('width',options.width);

View file

@ -17,7 +17,7 @@ mistplayers.theoplayer = {
var p = mistplayers.theoplayer.player; var p = mistplayers.theoplayer.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options) { p.prototype.build = function (options) {
var ele = this.element('video'); var ele = this.getElement('video');
ele.src = options.src; ele.src = options.src;
ele.width = options.width; ele.width = options.width;

View file

@ -28,7 +28,7 @@ p.prototype.build = function (options) {
this.addlog('Building VideoJS player..'); this.addlog('Building VideoJS player..');
var ele = this.element('video'); var ele = this.getElement('video');
cont.appendChild(ele); cont.appendChild(ele);
ele.className = ''; ele.className = '';
ele.crossOrigin = 'anonymous'; //required for subtitles ele.crossOrigin = 'anonymous'; //required for subtitles
@ -119,7 +119,7 @@ p.prototype.build = function (options) {
} }
me.adderror(msg); me.adderror(msg);
},true); },true);
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting']; 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) { for (var i in events) {
ele.addEventListener(events[i],function(e){ ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type); me.addlog('Player event fired: '+e.type);
@ -155,6 +155,10 @@ if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.m
}; };
} }
p.prototype.updateSrc = function(src){ p.prototype.updateSrc = function(src){
if (src == '') {
this.videojs.dispose();
return;
}
this.videojs.src({ this.videojs.src({
src: src, src: src,
type: this.source.type type: this.source.type