Merge branch 'development' into LTS_development
This commit is contained in:
commit
e52bbb4af6
8 changed files with 373 additions and 77 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
265
embed/player.js
265
embed/player.js
|
@ -28,7 +28,7 @@ function MistVideo(streamName,options) {
|
|||
reloadDelay: false, //don't override default reload delay
|
||||
urlappend: false, //don't add this to urls
|
||||
setTracks: false, //don't set tracks
|
||||
fillSpace: false, //don't fill parent container
|
||||
fillSpace: false, //don't fill parent container
|
||||
width: false, //no set width
|
||||
height: false, //no set height
|
||||
maxwidth: false, //no max width (apart from targets dimensions)
|
||||
|
@ -62,6 +62,7 @@ function MistVideo(streamName,options) {
|
|||
return event;
|
||||
};
|
||||
this.log("Initializing..");
|
||||
this.bootMs = new Date().getTime();
|
||||
|
||||
this.timers = {
|
||||
list: {}, //will contain the timeouts, format timeOutIndex: endTime
|
||||
|
@ -110,14 +111,7 @@ function MistVideo(streamName,options) {
|
|||
|
||||
var source = false;
|
||||
var mistPlayer = false;
|
||||
|
||||
if (options.startCombo) {
|
||||
options.startCombo.started = {
|
||||
player: false,
|
||||
source: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//retrieve the sources we can loop over
|
||||
var sources;
|
||||
if (options.forceSource) {
|
||||
|
@ -177,7 +171,7 @@ function MistVideo(streamName,options) {
|
|||
MistUtil.array.multiSort(players,sortoptions.player);
|
||||
}
|
||||
if ("first" in options.forcePriority) {
|
||||
sortopions.first = options.forcePriority.first; //overwrite
|
||||
sortoptions.first = options.forcePriority.first; //overwrite
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,6 +192,20 @@ function MistVideo(streamName,options) {
|
|||
current: false
|
||||
}
|
||||
};
|
||||
|
||||
if (options.startCombo) {
|
||||
options.startCombo.started = {
|
||||
player: false,
|
||||
source: false
|
||||
};
|
||||
for (var i = 0; i < players.length; i++) {
|
||||
if (players[i].shortname == options.startCombo.player) {
|
||||
options.startCombo.player = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkStartCombo(which) {
|
||||
if ((options.startCombo) && (!options.startCombo.started[which])) {
|
||||
|
@ -401,7 +409,16 @@ function MistVideo(streamName,options) {
|
|||
|
||||
|
||||
if (MistVideo.choosePlayer()) {
|
||||
|
||||
|
||||
if (MistVideo.reporting) {
|
||||
MistVideo.reporting.report({
|
||||
player: MistVideo.playerName,
|
||||
sourceType: MistVideo.source.type,
|
||||
sourceUrl: MistVideo.source.url,
|
||||
pageUrl: location.href
|
||||
});
|
||||
}
|
||||
|
||||
//build player
|
||||
MistVideo.player = new mistplayers[MistVideo.playerName].player();
|
||||
|
||||
|
@ -416,6 +433,10 @@ function MistVideo(streamName,options) {
|
|||
|
||||
MistVideo.container.removeAttribute("data-loading");
|
||||
MistVideo.video = video;
|
||||
|
||||
if (MistVideo.reporting) {
|
||||
MistVideo.reporting.init();
|
||||
}
|
||||
|
||||
if ("api" in MistVideo.player) {
|
||||
|
||||
|
@ -495,6 +516,11 @@ function MistVideo(streamName,options) {
|
|||
score = Math.max(score,list[list.length-1].score);
|
||||
|
||||
this.vars.score = score;
|
||||
|
||||
if (MistVideo.reporting) {
|
||||
MistVideo.reporting.stats.set("playbackScore",Math.round(score*10)/10);
|
||||
}
|
||||
|
||||
return score;
|
||||
},
|
||||
valueToScore: function(a,b){ //calculate the moving average
|
||||
|
@ -567,8 +593,7 @@ function MistVideo(streamName,options) {
|
|||
MistUtil.event.addListener(MistVideo.video,events[i],function(){
|
||||
if (MistVideo.monitor) { MistVideo.monitor.reset(); }
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//remove placeholder and add UI structure
|
||||
|
@ -874,6 +899,7 @@ function MistVideo(streamName,options) {
|
|||
|
||||
MistUtil.event.send("initialized",null,options.target);
|
||||
MistVideo.log("Initialized");
|
||||
|
||||
if (MistVideo.options.callback) { options.callback(MistVideo); }
|
||||
|
||||
});
|
||||
|
@ -881,8 +907,8 @@ function MistVideo(streamName,options) {
|
|||
else if (MistVideo.options.startCombo) {
|
||||
//try again without a startCombo
|
||||
delete MistVideo.options.startCombo;
|
||||
MistVideo.unload();
|
||||
MistVideo = mistPlay(MistVideo.stream,MistVideo.options);
|
||||
MistVideo.unload("No compatible players found - retrying without startCombo.");
|
||||
mistPlay(MistVideo.stream,MistVideo.options);
|
||||
}
|
||||
else {
|
||||
MistVideo.showError("No compatible player/source combo found.",{reload:true});
|
||||
|
@ -933,10 +959,188 @@ function MistVideo(streamName,options) {
|
|||
socket.die = false;
|
||||
socket.destroy = function(){
|
||||
this.die = true;
|
||||
this.onclose = function(){};
|
||||
this.close();
|
||||
};
|
||||
socket.onopen = function(e){
|
||||
this.wasConnected = true;
|
||||
|
||||
//report player status to MistServer
|
||||
if (!MistVideo.reporting) {
|
||||
MistVideo.reporting = {
|
||||
stats: {
|
||||
set: function(key,value){
|
||||
this.d[key] = value;
|
||||
},
|
||||
add: function(key,add){
|
||||
if (typeof add == "undefined") { add = 1; }
|
||||
this.d[key] += add;
|
||||
},
|
||||
d: {
|
||||
nWaiting: 0,
|
||||
timeWaiting: 0,
|
||||
nStalled: 0,
|
||||
timeStalled: 0,
|
||||
timeUnpaused: 0,
|
||||
nError: 0,
|
||||
nLog: 0,
|
||||
videoHeight: null,
|
||||
videoWidth: null,
|
||||
playerHeight: null,
|
||||
playerWidth: null
|
||||
},
|
||||
last: {
|
||||
firstPlayback: null,
|
||||
nWaiting: 0,
|
||||
timeWaiting: 0,
|
||||
nStalled: 0,
|
||||
timeStalled: 0,
|
||||
timeUnpaused: 0,
|
||||
nError: 0,
|
||||
lastError: null,
|
||||
playbackScore: 1,
|
||||
nLog: 0,
|
||||
autoplay: null,
|
||||
videoHeight: null,
|
||||
videoWidth: null,
|
||||
playerHeight: null,
|
||||
playerWidth: null
|
||||
}
|
||||
},
|
||||
report: function(d){
|
||||
MistVideo.socket.send(JSON.stringify(d));
|
||||
},
|
||||
reportStats: function(){
|
||||
var d = {};
|
||||
var report = false;
|
||||
var newlogs = MistVideo.logs.slice(this.stats.last.nLog);
|
||||
for (var i in this.stats.d) {
|
||||
if (this.stats.d[i] != this.stats.last[i]) {
|
||||
d[i] = this.stats.d[i];
|
||||
this.stats.last[i] = d[i];
|
||||
report = true;
|
||||
}
|
||||
}
|
||||
if (report) {
|
||||
if (newlogs.length) {
|
||||
d.logs = [];
|
||||
for (var i in newlogs) {
|
||||
d.logs.push(newlogs[i].message);
|
||||
}
|
||||
}
|
||||
this.report(d);
|
||||
}
|
||||
MistVideo.timers.start(function(){
|
||||
MistVideo.reporting.reportStats();
|
||||
},5e3);
|
||||
},
|
||||
init: function(){
|
||||
var video = MistVideo.video;
|
||||
|
||||
var firstPlay = MistUtil.event.addListener(video,"playing",function(){
|
||||
MistVideo.reporting.stats.set("firstPlayback",new Date().getTime() - MistVideo.bootMs);
|
||||
MistUtil.event.removeListener(firstPlay);
|
||||
});
|
||||
|
||||
//set listeners for player reporting
|
||||
MistUtil.event.addListener(video,"waiting",function(){
|
||||
MistVideo.reporting.stats.add("nWaiting");
|
||||
});
|
||||
MistUtil.event.addListener(video,"stalled",function(){
|
||||
MistVideo.reporting.stats.add("nStalled");
|
||||
});
|
||||
MistUtil.event.addListener(MistVideo.options.target,"error",function(e){
|
||||
MistVideo.reporting.stats.add("nError");
|
||||
MistVideo.reporting.stats.set("lastError",e.message);
|
||||
});
|
||||
|
||||
if (Object && Object.defineProperty) {
|
||||
var timeWaiting = 0;
|
||||
var waitingSince = false;
|
||||
var timeStalled = 0;
|
||||
var stalledSince = false;
|
||||
var timeUnpaused = 0;
|
||||
var unpausedSince = false;
|
||||
var d = MistVideo.reporting.stats.d;
|
||||
Object.defineProperty(d,"timeWaiting",{
|
||||
get: function(){
|
||||
return timeWaiting + (waitingSince ? (new Date()).getTime() - waitingSince : 0);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"timeStalled",{
|
||||
get: function(){
|
||||
return timeStalled + (stalledSince ? (new Date()).getTime() - stalledSince : 0);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"timeUnpaused",{
|
||||
get: function(){
|
||||
return timeUnpaused + (unpausedSince ? (new Date()).getTime() - unpausedSince : 0);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"nLog",{
|
||||
get: function(){
|
||||
return MistVideo.logs.length;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"videoHeight",{
|
||||
get: function(){
|
||||
return MistVideo.video.videoHeight;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"videoWidth",{
|
||||
get: function(){
|
||||
return MistVideo.video.videoWidth;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"playerHeight",{
|
||||
get: function(){
|
||||
return MistVideo.video.clientHeight;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"playerWidth",{
|
||||
get: function(){
|
||||
return MistVideo.video.clientWidth;
|
||||
}
|
||||
});
|
||||
|
||||
MistUtil.event.addListener(video,"waiting",function(){
|
||||
timeWaiting = d.timeWaiting; //in case we get waiting several times in a row
|
||||
waitingSince = (new Date()).getTime();
|
||||
});
|
||||
MistUtil.event.addListener(video,"stalled",function(){
|
||||
timeStalled = d.timeStalled; //in case we get stalled several times in a row
|
||||
stalledSince = (new Date()).getTime();
|
||||
});
|
||||
var events = ["playing","pause"];
|
||||
for (var i in events) {
|
||||
MistUtil.event.addListener(video,events[i],function(){
|
||||
timeWaiting = d.timeWaiting;
|
||||
timeStalled = d.timeStalled;
|
||||
waitingSince = false;
|
||||
stalledSince = false;
|
||||
});
|
||||
}
|
||||
MistUtil.event.addListener(video,"playing",function(){
|
||||
timeUnpaused = d.timeUnpaused; //in case we get playing several times in a row
|
||||
unpausedSince = (new Date()).getTime();
|
||||
});
|
||||
MistUtil.event.addListener(video,"pause",function(){
|
||||
timeUnpaused = d.timeUnpaused;
|
||||
unpausedSince = false;
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//periodically send the gathered stats
|
||||
this.reportStats();
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
socket.onclose = function(e){
|
||||
if (this.die) {
|
||||
|
@ -1063,7 +1267,7 @@ function MistVideo(streamName,options) {
|
|||
|
||||
if ("source" in diff) {
|
||||
if ("error" in MistVideo.info) {
|
||||
MistVideo.reload();
|
||||
MistVideo.reload("Reloading, stream info has error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1090,11 +1294,11 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "width":
|
||||
case "height": {
|
||||
resized = true;
|
||||
break;
|
||||
}
|
||||
case "width":
|
||||
case "height": {
|
||||
resized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1111,6 +1315,8 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
openSocket();
|
||||
}
|
||||
|
@ -1118,7 +1324,7 @@ function MistVideo(streamName,options) {
|
|||
openWithGet();
|
||||
}
|
||||
|
||||
this.unload = function(){
|
||||
this.unload = function(reason){
|
||||
if (this.destroyed) { return; }
|
||||
|
||||
this.log("Unloading..");
|
||||
|
@ -1138,6 +1344,9 @@ function MistVideo(streamName,options) {
|
|||
MistVideo.monitor.destroy();
|
||||
}
|
||||
if (this.socket) {
|
||||
if (this.reporting) {
|
||||
this.reporting.report({unload:reason ? reason : null});
|
||||
}
|
||||
this.socket.destroy();
|
||||
}
|
||||
if ((this.player) && (this.player.api)) {
|
||||
|
@ -1173,17 +1382,17 @@ function MistVideo(streamName,options) {
|
|||
delete this.video;
|
||||
|
||||
};
|
||||
this.reload = function(){
|
||||
this.reload = function(reason){
|
||||
var time = ("player" in this && "api" in this.player ? this.player.api.currentTime : false);
|
||||
|
||||
this.unload();
|
||||
MistVideo = mistPlay(this.stream,this.options);
|
||||
this.unload(reason);
|
||||
var NewMistVideo = mistPlay(this.stream,this.options);
|
||||
|
||||
if ((time) && (this.info.type != "live")) {
|
||||
//after load, try to restore the video position
|
||||
var f = function(){
|
||||
if (MistVideo.player && MistVideo.player.api) {
|
||||
MistVideo.player.api.currentTime = time;
|
||||
if (NewMistVideo.player && NewMistVideo.player.api) {
|
||||
NewMistVideo.player.api.currentTime = time;
|
||||
}
|
||||
this.removeEventListener("initialized",f);
|
||||
};
|
||||
|
@ -1212,7 +1421,7 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
}
|
||||
|
||||
this.unload();
|
||||
this.unload("nextCombo");
|
||||
var opts = this.options;
|
||||
opts.startCombo = startCombo;
|
||||
MistVideo = mistPlay(this.stream,opts);
|
||||
|
|
|
@ -357,9 +357,12 @@ MistSkins["default"] = {
|
|||
|
||||
var promise = MistVideo.player.api.play();
|
||||
if (promise) {
|
||||
promise.catch(function(){
|
||||
promise.then(function(){
|
||||
if (MistVideo.reporting) { MistVideo.reporting.stats.d.autoplay = "success"; }
|
||||
}).catch(function(){
|
||||
if (MistVideo.destroyed) { return; }
|
||||
MistVideo.log("Autoplay failed even with muted video. Unmuting and showing play button.");
|
||||
if (MistVideo.reporting) { MistVideo.reporting.stats.d.autoplay = "failed"; }
|
||||
MistVideo.player.api.muted = false;
|
||||
|
||||
//play has failed
|
||||
|
@ -388,6 +391,8 @@ MistSkins["default"] = {
|
|||
|
||||
MistVideo.log("Autoplay worked! Video will be unmuted on mouseover if the page has been interacted with.");
|
||||
|
||||
if (MistVideo.reporting) { MistVideo.reporting.stats.d.autoplay = "muted"; }
|
||||
|
||||
//show large "muted" icon
|
||||
var largeMutedButton = MistVideo.skin.icons.build("muted",100);
|
||||
MistUtil.class.add(largeMutedButton,"mistvideo-pointer");
|
||||
|
@ -432,9 +437,11 @@ MistSkins["default"] = {
|
|||
},function(){});
|
||||
}
|
||||
}
|
||||
else if (MistVideo.reporting) { MistVideo.reporting.stats.d.autoplay = "failed"; }
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (MistVideo.reporting) { MistVideo.reporting.stats.d.autoplay = "success"; }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1046,7 +1053,7 @@ MistSkins["default"] = {
|
|||
},button);
|
||||
|
||||
//apply initial video state
|
||||
var initevent = MistUtil.event.addListener(video,"loadstart",function(){
|
||||
var initevent = MistUtil.event.addListener(video,"loadedmetadata",function(){
|
||||
if (('localStorage' in window) && (localStorage != null) && ('mistVolume' in localStorage)) {
|
||||
MistVideo.player.api.volume = localStorage['mistVolume'];
|
||||
}
|
||||
|
@ -1953,7 +1960,7 @@ MistSkins["default"] = {
|
|||
type: "button",
|
||||
label: "Reload player",
|
||||
onclick: function(){
|
||||
MistVideo.reload();
|
||||
MistVideo.reload("Reloading because reload button was clicked.");
|
||||
}
|
||||
};
|
||||
if (!isNaN(options.reload+"")) { obj.delay = options.reload; }
|
||||
|
@ -2470,7 +2477,7 @@ MistSkins.dev = {
|
|||
MistUtil.event.addListener(select,"change",function(){
|
||||
MistVideo.options.forcePlayer = (this.value == "" ? false : this.value);
|
||||
if (MistVideo.options.forcePlayer != MistVideo.playerName) { //only reload if there is a change
|
||||
MistVideo.reload();
|
||||
MistVideo.reload("Reloading to force player.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2512,7 +2519,7 @@ MistSkins.dev = {
|
|||
MistUtil.event.addListener(select,"change",function(){
|
||||
MistVideo.options.forceType = (this.value == "" ? false : this.value);
|
||||
if ((!MistVideo.source) || (MistVideo.options.forceType != MistVideo.source.type)) { //only reload if there is a change
|
||||
MistVideo.reload();
|
||||
MistVideo.reload("Reloading to force new type.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2546,7 +2553,7 @@ MistSkins.dev = {
|
|||
MistUtil.event.addListener(select,"change",function(){
|
||||
MistVideo.options.forceSource = (this.value == "" ? false : this.value);
|
||||
if (MistVideo.options.forceSource != MistVideo.source.index) { //only reload if there is a change
|
||||
MistVideo.reload();
|
||||
MistVideo.reload("Reloading to force new source.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2601,7 +2608,7 @@ MistSkins.dev.structure.submenu.children.unshift({
|
|||
title: "Build MistVideo again",
|
||||
label: "MistVideo.reload();",
|
||||
onclick: function(){
|
||||
this.reload();
|
||||
this.reload("Dev-reload button clicked.");
|
||||
}
|
||||
},{
|
||||
type: "button",
|
||||
|
|
|
@ -37,7 +37,7 @@ var MistUtil = {
|
|||
return string.charAt(0).toUpperCase()+string.slice(1);
|
||||
},
|
||||
number: function(num) {
|
||||
if ((isNaN(Number(num))) || (num == 0)) { return num; }
|
||||
if ((isNaN(Number(num))) || (Number(num) == 0)) { return num; }
|
||||
|
||||
//rounding
|
||||
//use a significance of three, but don't round "visible" digits
|
||||
|
@ -58,10 +58,10 @@ var MistUtil = {
|
|||
|
||||
return num;
|
||||
},
|
||||
bytes: function(val){
|
||||
bytes: function(val,bits){
|
||||
if (isNaN(Number(val))) { return val; }
|
||||
|
||||
var suffix = ["bytes","KB","MB","GB","TB","PB"];
|
||||
var suffix = bits ? ["bits","Kb","Mb","Gb","Tb","Pb"] : ["bytes","KB","MB","GB","TB","PB"];
|
||||
if (val == 0) {
|
||||
unit = suffix[0];
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ var MistUtil = {
|
|||
}
|
||||
return this.number(val)+unit;
|
||||
},
|
||||
bits: function(val) { return this.bytes(val,true); },
|
||||
mime2human: function(mime){
|
||||
switch (mime) {
|
||||
case "html5/video/webm": {
|
||||
|
|
|
@ -333,14 +333,45 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
this.ws = new WebSocket(MistVideo.source.url);
|
||||
this.ws.binaryType = "arraybuffer";
|
||||
|
||||
this.ws.s = this.ws.send;
|
||||
this.ws.send = function(){
|
||||
if (this.readyState == 1) {
|
||||
return this.s.apply(this,arguments);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
this.ws.onopen = function(){
|
||||
this.wasConnected = true;
|
||||
resolve();
|
||||
};
|
||||
this.ws.onerror = function(e){
|
||||
MistVideo.showError("MP4 over WS: websocket error");
|
||||
}
|
||||
};
|
||||
this.ws.onclose = function(e){
|
||||
MistVideo.log("MP4 over WS: websocket closed");
|
||||
if (this.wasConnected && (!MistVideo.destroyed)) {
|
||||
MistVideo.log("MP4 over WS: reopening websocket");
|
||||
player.wsconnect().then(function(){
|
||||
if (!player.sb) {
|
||||
//retrieve codec info
|
||||
var f = function(msg){
|
||||
//got codec data, set up source buffer
|
||||
|
||||
if (!player.sb) { player.sbinit(msg.data.codecs); }
|
||||
else { player.api.play(); }
|
||||
|
||||
player.ws.removeListener("codec_data",f);
|
||||
};
|
||||
player.ws.addListener("codec_data",f);
|
||||
send({type:"request_codec_data",supported_codecs:MistVideo.source.supportedCodecs});
|
||||
}
|
||||
else {
|
||||
player.api.play();
|
||||
}
|
||||
},function(){
|
||||
Mistvideo.error("Lost connection to the Media Server");
|
||||
});
|
||||
}
|
||||
};
|
||||
this.ws.listeners = {}; //kind of event listener list for websocket messages
|
||||
this.ws.addListener = function(type,f){
|
||||
|
@ -367,6 +398,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
//the last fragment has been added to the buffer
|
||||
var eObj;
|
||||
eObj = MistUtil.event.addListener(video,"waiting",function(e){
|
||||
player.sb.paused = true;
|
||||
MistUtil.event.send("ended",null,video);
|
||||
MistUtil.event.removeListener(eObj);
|
||||
});
|
||||
|
@ -378,7 +410,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
var serverDelay = player.ws.serverDelay.get();
|
||||
var desiredBuffer = Math.max(500+serverDelay,serverDelay*2);
|
||||
if (MistVideo.info.type != "live") { desiredBuffer += 2000; } //if VoD, keep an extra 2 seconds of buffer
|
||||
if (player.debugging) console.log("on_time received",msg.data.current/1e3,"currtime",video.currentTime,requested_rate+"x","buffer",Math.round(buffer),"/",Math.round(desiredBuffer),(MistVideo.info.type == "live" ? "latency:"+Math.round(msg.data.end-video.currentTime*1e3)+"ms" : ""),"listeners",player.ws.listeners && player.ws.listeners.on_time ? player.ws.listeners.on_time : 0,"msgqueue",player.msgqueue ? player.msgqueue.length : 0,msg.data);
|
||||
if (player.debugging) console.log("on_time received",msg.data.current/1e3,"currtime",video.currentTime,requested_rate+"x","buffer",Math.round(buffer),"/",Math.round(desiredBuffer),(MistVideo.info.type == "live" ? "latency:"+Math.round(msg.data.end-video.currentTime*1e3)+"ms" : ""),"bitrate:"+MistUtil.format.bits(player.monitor.currentBps)+"/s","listeners",player.ws.listeners && player.ws.listeners.on_time ? player.ws.listeners.on_time : 0,"msgqueue",player.msgqueue ? player.msgqueue.length : 0,msg.data);
|
||||
|
||||
if (!player.sb) {
|
||||
MistVideo.log("Received on_time, but the source buffer is being cleared right now. Ignoring.");
|
||||
|
@ -466,6 +498,11 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MistVideo.reporting && msg.data.tracks) {
|
||||
MistVideo.reporting.stats.d.tracks = msg.data.tracks.join(",");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "tracks": {
|
||||
|
@ -605,6 +642,9 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
var data = new Uint8Array(e.data);
|
||||
if (data) {
|
||||
for (var i in player.monitor.bitCounter) {
|
||||
player.monitor.bitCounter[i] += e.data.byteLength*8;
|
||||
}
|
||||
if ((player.sb) && (!player.msgqueue)) {
|
||||
if (player.sb.updating || player.sb.queue.length || player.sb._busy) {
|
||||
player.sb.queue.push(data);
|
||||
|
@ -755,6 +795,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
else if (e.data.current > video.currentTime) {
|
||||
player.sb.paused = false;
|
||||
video.currentTime = e.data.current*1e-3;
|
||||
video.play().then(resolve).catch(reject);
|
||||
player.ws.removeListener("on_time",f);
|
||||
}
|
||||
|
@ -768,15 +809,13 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
},
|
||||
pause: function(){
|
||||
video.pause();
|
||||
send({type: "hold",});
|
||||
send({type: "hold"});
|
||||
if (player.sb) { player.sb.paused = true; }
|
||||
},
|
||||
setTracks: function(obj){
|
||||
obj.type = "tracks";
|
||||
obj = MistUtil.object.extend({
|
||||
type: "tracks",
|
||||
audio: null,
|
||||
video: null,
|
||||
seek_time: Math.max(0,video.currentTime*1e3-(500+player.ws.serverDelay.get()))
|
||||
},obj);
|
||||
send(obj);
|
||||
|
@ -791,9 +830,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
//it's okay if it fails
|
||||
} catch (e) { }
|
||||
});
|
||||
player.ws.close();
|
||||
delete window.mistMewsOnVisibilityChange;
|
||||
document.removeEventListener("visibilitychange",onVisibilityChange);
|
||||
player.ws.close();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -881,32 +918,6 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
});
|
||||
}
|
||||
});
|
||||
//pause if tab is hidden to prevent buildup of frames
|
||||
var autopaused = false;
|
||||
//only add this once!
|
||||
function onVisibilityChange() {
|
||||
if (document.hidden) {
|
||||
//check if we are playing (not video.paused! that already returns true)
|
||||
if (!player.sb.paused) {
|
||||
player.api.pause();
|
||||
autopaused = true;
|
||||
if (MistVideo.info.type == "live") {
|
||||
autopaused = "live"; //go to live point
|
||||
//NB: even if the player wasn't near the live point when it was paused, we've likely exited the buffer while we were paused, so the current position probably won't exist anymore. Just skip to live.
|
||||
}
|
||||
MistVideo.log("Pausing the player as the tab is inactive.");
|
||||
}
|
||||
}
|
||||
else if (autopaused) {
|
||||
player.api.play(autopaused == "live");
|
||||
autopaused = false;
|
||||
MistVideo.log("Restarting the player as the tab is now active again.");
|
||||
}
|
||||
}
|
||||
if (!window.mistMewsOnVisibilityChange) {
|
||||
window.mistMewsOnVisibilityChange = true;
|
||||
document.addEventListener("visibilitychange",onVisibilityChange);
|
||||
}
|
||||
|
||||
var seeking = false;
|
||||
MistUtil.event.addListener(video,"seeking",function(){
|
||||
|
@ -927,6 +938,19 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
}
|
||||
});
|
||||
MistUtil.event.addListener(video,"pause",function(){
|
||||
if (player.sb && !player.sb.paused) {
|
||||
MistVideo.log("The browser paused the vid - probably because it has no audio and the tab is no longer visible. Pausing download.");
|
||||
send({type:"hold"});
|
||||
player.sb.paused = true;
|
||||
var p = MistUtil.event.addListener(video,"play",function(){
|
||||
if (player.sb && player.sb.paused) {
|
||||
send({type:"play"});
|
||||
}
|
||||
MistUtil.event.removeListener(p);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (player.debugging) {
|
||||
MistUtil.event.addListener(video,"waiting",function(){
|
||||
|
@ -949,4 +973,54 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
|
||||
});
|
||||
}
|
||||
|
||||
//ABR: monitor playback issues and switch to lower bitrate track if available
|
||||
this.monitor = {
|
||||
bitCounter: [],
|
||||
bitsSince: [],
|
||||
currentBps: null,
|
||||
nWaiting: 0,
|
||||
nWaitingThreshold: 3,
|
||||
listener: MistUtil.event.addListener(video,"waiting",function(){
|
||||
player.monitor.nWaiting++;
|
||||
|
||||
if (player.monitor.nWaiting >= player.monitor.nWaitingThreshold) {
|
||||
player.monitor.nWaiting = 0;
|
||||
MistVideo.log("ABR threshold triggered, requesting lower quality");
|
||||
player.monitor.action();
|
||||
}
|
||||
}),
|
||||
getBitRate: function(){
|
||||
if (player.sb && !player.sb.paused) {
|
||||
|
||||
this.bitCounter.push(0);
|
||||
this.bitsSince.push(new Date().getTime());
|
||||
|
||||
//calculate current bitrate
|
||||
var bits, since;
|
||||
if (this.bitCounter.length > 5) {
|
||||
bits = player.monitor.bitCounter.shift();
|
||||
since = this.bitsSince.shift();
|
||||
}
|
||||
else {
|
||||
bits = player.monitor.bitCounter[0];
|
||||
since = this.bitsSince[0];
|
||||
}
|
||||
var dt = new Date().getTime() - since;
|
||||
this.currentBps = bits / (dt*1e-3);
|
||||
|
||||
//console.log(MistUtil.format.bytes(this.currentBps)+"its/s");
|
||||
|
||||
}
|
||||
|
||||
MistVideo.timers.start(function(){
|
||||
player.monitor.getBitRate();
|
||||
},500);
|
||||
},
|
||||
action: function(){
|
||||
player.api.setTracks({video:"max<"+Math.round(this.currentBps)+"bps"});
|
||||
}
|
||||
};
|
||||
|
||||
this.monitor.getBitRate();
|
||||
};
|
||||
|
|
|
@ -157,6 +157,10 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
|
||||
currenttracks = ev.tracks;
|
||||
}
|
||||
|
||||
if (MistVideo.reporting && ev.tracks) {
|
||||
MistVideo.reporting.stats.d.tracks = ev.tracks.join(",");
|
||||
}
|
||||
},
|
||||
on_seek: function(e){
|
||||
var thisPlayer = this;
|
||||
|
@ -276,7 +280,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
if (callback) { callback(); }
|
||||
};
|
||||
thisWebRTCPlayer.peerConn.onconnectionstatechange = function(e){
|
||||
if (this.destroyed) { return; } //the player doesn't exist any more
|
||||
if (MistVideo.destroyed) { return; } //the player doesn't exist any more
|
||||
switch (this.connectionState) {
|
||||
case "failed": {
|
||||
//WebRTC will never work (firewall maybe?)
|
||||
|
@ -296,7 +300,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
};
|
||||
thisWebRTCPlayer.peerConn.oniceconnectionstatechange = function(e){
|
||||
if (this.destroyed) { return; } //the player doesn't exist any more
|
||||
if (MistVideo.destroyed) { return; } //the player doesn't exist any more
|
||||
switch (this.iceConnectionState) {
|
||||
case "failed": {
|
||||
MistVideo.showError("ICE connection "+this.iceConnectionState);
|
||||
|
@ -722,6 +726,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
try {
|
||||
me.webrtc.stop();
|
||||
me.webrtc.signaling.ws.close();
|
||||
me.webrtc.peerConn.close();
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue