Merge branch 'development' into LTS_development
This commit is contained in:
commit
e9338032fd
9 changed files with 378 additions and 107 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -52,11 +52,13 @@ mist
|
|||
*.cmake
|
||||
Doxyfile
|
||||
CMakeCache.txt
|
||||
CMakeDoxyfile.in
|
||||
DartConfiguration.tcl
|
||||
build.ninja
|
||||
rules.ninja
|
||||
.ninja_log
|
||||
.ninja_deps
|
||||
.cache
|
||||
aes_ctr128
|
||||
/embed/testing
|
||||
*test
|
||||
|
|
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
|
@ -60,3 +60,10 @@ echo " Minimizing CSS..";
|
|||
#fi
|
||||
echo " Done.";
|
||||
echo "Done.";
|
||||
git add min
|
||||
echo "Staged."
|
||||
CONFLICTS=`git diff --name-only --diff-filter=U`
|
||||
if [ -z "$CONFLICTS" ] ; then
|
||||
git status | grep "rebase in progress" > /dev/null && echo "No more conflicts; continuing rebase!" && git rebase --continue
|
||||
fi
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ function MistVideo(streamName,options) {
|
|||
height: false, //no set height
|
||||
maxwidth: false, //no max width (apart from targets dimensions)
|
||||
maxheight: false, //no max height (apart from targets dimensions)
|
||||
ABR_resize: true, //for supporting wrappers: when the player resizes, request a video track that matches the resolution best
|
||||
ABR_bitrate: true, //for supporting wrappers: when there are playback issues, request a lower bitrate video track
|
||||
MistVideoObject: false//no reference object is passed
|
||||
},options);
|
||||
if (options.host) { options.host = MistUtil.http.url.sanitizeHost(options.host); }
|
||||
|
@ -72,6 +74,7 @@ function MistVideo(streamName,options) {
|
|||
start: function(callback,delay){
|
||||
var i = setTimeout(function(){
|
||||
delete MistVideo.timers.list[i];
|
||||
if (MistVideo.destroyed) return;
|
||||
callback();
|
||||
},delay);
|
||||
this.list[i] = new Date(new Date().getTime() + delay);
|
||||
|
@ -104,6 +107,12 @@ function MistVideo(streamName,options) {
|
|||
return url;
|
||||
}
|
||||
|
||||
if (options.reloadDelay && (options.reloadDelay > 3600)) {
|
||||
options.reloadDelay /= 1000;
|
||||
this.log("A reloadDelay of more than an hour was set: assuming milliseconds were intended. ReloadDelay is now "+options.reloadDelay+"s");
|
||||
}
|
||||
|
||||
|
||||
new MistSkin(this);
|
||||
|
||||
this.checkCombo = function(options,quiet) {
|
||||
|
@ -410,6 +419,11 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
}
|
||||
|
||||
//check MistServer version if combined track selection is supported
|
||||
if (MistVideo.options.ABR_bitrate && MistVideo.options.ABR_resize && (MistVideo.info && !MistVideo.info.selver)) {
|
||||
//having both won't work, disable bitrate based ABR
|
||||
MistVideo.options.ABR_bitrate = false;
|
||||
}
|
||||
|
||||
if (MistVideo.choosePlayer()) {
|
||||
|
||||
|
@ -713,8 +727,14 @@ function MistVideo(streamName,options) {
|
|||
};
|
||||
|
||||
}
|
||||
MistVideo.player.resize = function(options){
|
||||
MistVideo.player.resize = function(options,oldsize){
|
||||
var container = MistVideo.video.currentTarget.querySelector(".mistvideo");
|
||||
if (!oldsize) {
|
||||
oldsize = {
|
||||
width: MistVideo.video.clientWidth,
|
||||
height: MistVideo.video.clientHeight
|
||||
};
|
||||
}
|
||||
if (!container.hasAttribute("data-fullscreen")) {
|
||||
//if ((!document.fullscreenElement) || (document.fullscreenElement.parentElement != MistVideo.video.currentTarget)) {
|
||||
//first, base the size on the video dimensions
|
||||
|
@ -730,7 +750,7 @@ function MistVideo(streamName,options) {
|
|||
width:window.innerWidth,
|
||||
height: false,
|
||||
reiterating: true
|
||||
});
|
||||
},oldsize);
|
||||
}
|
||||
|
||||
//check if the container is smaller than the video, if so, set the max size to the current container dimensions and reiterate
|
||||
|
@ -740,7 +760,7 @@ function MistVideo(streamName,options) {
|
|||
width: false,
|
||||
height: MistVideo.video.currentTarget.clientHeight,
|
||||
reiterating: true
|
||||
});
|
||||
},oldsize);
|
||||
}
|
||||
if ((MistVideo.video.currentTarget.clientWidth) && (MistVideo.video.currentTarget.clientWidth < size.width)) {
|
||||
//console.log("current w:",size.width,"target w:",MistVideo.video.currentTarget.clientWidth);
|
||||
|
@ -748,20 +768,23 @@ function MistVideo(streamName,options) {
|
|||
width: MistVideo.video.currentTarget.clientWidth,
|
||||
height: false,
|
||||
reiterating: true
|
||||
});
|
||||
},oldsize);
|
||||
}
|
||||
MistVideo.log("Player size calculated: "+size.width+" x "+size.height+" px");
|
||||
return true;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
//this is the video that is in the main container, and resize this one to the screen dimensions
|
||||
this.setSize({
|
||||
height: window.innerHeight,
|
||||
width: window.innerWidth
|
||||
});
|
||||
return true;
|
||||
size = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
}
|
||||
this.setSize(size);
|
||||
}
|
||||
if ((size.width != oldsize.width) || (size.height != oldsize.height)) {
|
||||
MistVideo.log("Player size calculated: "+size.width+" x "+size.height+" px");
|
||||
MistUtil.event.send("player_resize",size,MistVideo.video);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
//if this is the main video
|
||||
|
@ -894,6 +917,43 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
}
|
||||
|
||||
if (MistVideo.player.api.ABR_resize && MistVideo.options.ABR_resize) {
|
||||
var resizeratelimiter = false;
|
||||
var newsize = false;
|
||||
MistUtil.event.addListener(MistVideo.video,"player_resize",function(e){
|
||||
if (MistVideo.options.setTracks && MistVideo.options.setTracks.video) {
|
||||
//trackselection is not set to 'automatic'
|
||||
return;
|
||||
}
|
||||
|
||||
//Whenever the player resizes, start a timer. When the timer ends, request the correct video track. When the player resizes before the timer ends, stop it: track request is sent 1s after the player has the new size
|
||||
|
||||
if (resizeratelimiter) {
|
||||
MistVideo.timers.stop(resizeratelimiter);
|
||||
}
|
||||
resizeratelimiter = MistVideo.timers.start(function(){
|
||||
MistVideo.player.api.ABR_resize(e.message);
|
||||
resizeratelimiter = false;
|
||||
},1e3);
|
||||
|
||||
});
|
||||
|
||||
MistUtil.event.addListener(MistVideo.video,"trackSetToAuto",function(e){
|
||||
//the user selected automatic track selection, update the track resolution
|
||||
if (e.message == "video") {
|
||||
MistVideo.player.api.ABR_resize({
|
||||
width: MistVideo.video.clientWidth,
|
||||
height: MistVideo.video.clientHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
//initialize
|
||||
MistVideo.player.api.ABR_resize({
|
||||
width: MistVideo.video.clientWidth,
|
||||
height: MistVideo.video.clientHeight
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in MistVideo.player.onreadylist) {
|
||||
|
@ -1011,7 +1071,9 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
},
|
||||
report: function(d){
|
||||
if (MistVideo.socket.readyState == 1) {
|
||||
MistVideo.socket.send(JSON.stringify(d));
|
||||
}
|
||||
},
|
||||
reportStats: function(){
|
||||
var d = {};
|
||||
|
@ -1055,7 +1117,7 @@ function MistVideo(streamName,options) {
|
|||
MistUtil.event.addListener(MistVideo.options.target,"error",function(e){
|
||||
MistVideo.reporting.stats.add("nError");
|
||||
MistVideo.reporting.stats.set("lastError",e.message);
|
||||
});
|
||||
},video); //remove event listener when the player is removed
|
||||
|
||||
if (Object && Object.defineProperty) {
|
||||
var timeWaiting = 0;
|
||||
|
@ -1087,22 +1149,22 @@ function MistVideo(streamName,options) {
|
|||
});
|
||||
Object.defineProperty(d,"videoHeight",{
|
||||
get: function(){
|
||||
return MistVideo.video.videoHeight;
|
||||
return MistVideo.video ? MistVideo.video.videoHeight : null;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"videoWidth",{
|
||||
get: function(){
|
||||
return MistVideo.video.videoWidth;
|
||||
return MistVideo.video ? MistVideo.video.videoWidth : null;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"playerHeight",{
|
||||
get: function(){
|
||||
return MistVideo.video.clientHeight;
|
||||
return MistVideo.video ? MistVideo.video.clientHeight : null;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(d,"playerWidth",{
|
||||
get: function(){
|
||||
return MistVideo.video.clientWidth;
|
||||
return MistVideo.video ? MistVideo.video.clientWidth : null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1348,6 +1410,7 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
if (this.socket) {
|
||||
if (this.reporting) {
|
||||
this.reporting.reportStats();
|
||||
this.reporting.report({unload:reason ? reason : null});
|
||||
}
|
||||
this.socket.destroy();
|
||||
|
|
|
@ -1383,7 +1383,11 @@ MistSkins["default"] = {
|
|||
var checkboxes = {};
|
||||
|
||||
function changeToTracks(type,value){
|
||||
MistVideo.log("User selected "+type+" track with id "+value);
|
||||
if (value) { MistVideo.log("User selected "+type+" track with id "+value); }
|
||||
else {
|
||||
MistVideo.log("User selected automatic track selection for "+type);
|
||||
MistUtil.event.send("trackSetToAuto",type,MistVideo.video);
|
||||
}
|
||||
|
||||
if (!MistVideo.options.setTracks) { MistVideo.options.setTracks = {}; }
|
||||
MistVideo.options.setTracks[type] = value;
|
||||
|
@ -2135,7 +2139,11 @@ MistSkins["default"] = {
|
|||
MistSkins.dev = {
|
||||
structure: MistUtil.object.extend({},MistSkins["default"].structure,true),
|
||||
blueprints: {
|
||||
timeout: function(){ //don't use countdowns on buttons
|
||||
timeout: function(){
|
||||
//don't use countdowns on buttons unless MistVideo.options.reloadDelay is set
|
||||
if (this.options.reloadDelay !== false) {
|
||||
return MistSkins.default.blueprints.timeout.apply(this,arguments);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
log: function(){
|
||||
|
@ -2426,6 +2434,12 @@ MistSkins.dev = {
|
|||
}
|
||||
return new Promise(function(resolve,reject){resolve();},function(){});
|
||||
}
|
||||
},
|
||||
"Current bitrate": function(){
|
||||
if (MistVideo.player.monitor && ("currentBps" in MistVideo.player.monitor)) {
|
||||
var out = MistUtil.format.bits(MistVideo.player.monitor.currentBps);
|
||||
return out ? out+"ps" : out;
|
||||
}
|
||||
}
|
||||
};
|
||||
var updates = [];
|
||||
|
|
|
@ -102,6 +102,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
|
||||
var player = this;
|
||||
//player.debugging = true;
|
||||
//player.debugging = "dl"; //download appended data on ms close
|
||||
|
||||
//this function is called both when the websocket is ready and the media source is ready - both should be open to proceed
|
||||
function checkReady() {
|
||||
|
@ -123,14 +124,13 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
resolve();
|
||||
};
|
||||
player.ms.onsourceclose = function(e){
|
||||
console.error("ms close",e);
|
||||
if (player.debugging) console.error("ms close",e);
|
||||
send({type:"stop"}); //stop sending data please something went wrong
|
||||
};
|
||||
player.ms.onsourceended = function(e){
|
||||
console.error("ms ended",e);
|
||||
|
||||
//for debugging
|
||||
if (player.debugging) console.error("ms ended",e);
|
||||
|
||||
if (player.debugging == "dl") {
|
||||
function downloadBlob (data, fileName, mimeType) {
|
||||
var blob, url;
|
||||
blob = new Blob([data], {
|
||||
|
@ -154,7 +154,6 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
a.remove();
|
||||
};
|
||||
|
||||
if (player.debugging) {
|
||||
var l = 0;
|
||||
for (var i = 0; i < player.sb.appended.length; i++) {
|
||||
l += player.sb.appended[i].length;
|
||||
|
@ -191,7 +190,6 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
//save the current source buffer codecs
|
||||
player.sb._codecs = codecs;
|
||||
|
||||
player.sb._duration = 1;
|
||||
player.sb._size = 0;
|
||||
player.sb.queue = [];
|
||||
var do_on_updateend = [];
|
||||
|
@ -278,8 +276,43 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
player.sb._busy = true;
|
||||
//console.log("appendBuffer");
|
||||
try {
|
||||
player.sb.appendBuffer(data);
|
||||
}
|
||||
catch(e){
|
||||
switch (e.name) {
|
||||
case "QuotaExceededError": {
|
||||
if (video.buffered.length) {
|
||||
if (video.currentTime - video.buffered.start(0) > 1) {
|
||||
//clear as much from the buffer as we can
|
||||
MistVideo.log("Triggered QuotaExceededError: cleaning up "+(Math.round((video.currentTime - video.buffered.start(0) - 1)*10)/10)+"s");
|
||||
player.sb._clean(1);
|
||||
}
|
||||
else {
|
||||
var bufferEnd = video.buffered.end(video.buffered.length-1);
|
||||
MistVideo.log("Triggered QuotaExceededError but there is nothing to clean: skipping ahead "+(Math.round((bufferEnd - video.currentTime)*10)/10)+"s");
|
||||
video.currentTime = bufferEnd;
|
||||
}
|
||||
player.sb._busy = false;
|
||||
player.sb._append(data); //now try again
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "InvalidStateError": {
|
||||
player.api.pause(); //playback is borked, so stop downloading more data
|
||||
if (MistVideo.video.error) {
|
||||
//Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null
|
||||
|
||||
//the video element error is already triggering the showError()
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
MistVideo.showError(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
//we're initing the source buffer and there is a msg queue of data built up before the buffer was ready. Start by adding these data fragments to the source buffer
|
||||
if (player.msgqueue) {
|
||||
|
@ -349,7 +382,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
};
|
||||
this.ws.onclose = function(e){
|
||||
MistVideo.log("MP4 over WS: websocket closed");
|
||||
if (this.wasConnected && (!MistVideo.destroyed)) {
|
||||
if (this.wasConnected && (!MistVideo.destroyed) && (MistVideo.state == "Stream is online") && (!MistVideo.video.error)) {
|
||||
MistVideo.log("MP4 over WS: reopening websocket");
|
||||
player.wsconnect().then(function(){
|
||||
if (!player.sb) {
|
||||
|
@ -388,6 +421,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
player.msgqueue = false;
|
||||
var requested_rate = 1;
|
||||
var serverdelay = [];
|
||||
var currenttracks = [];
|
||||
this.ws.onmessage = function(e){
|
||||
if (!e.data) { throw "Received invalid data"; }
|
||||
if (typeof e.data == "string") {
|
||||
|
@ -402,23 +436,39 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
MistUtil.event.send("ended",null,video);
|
||||
MistUtil.event.removeListener(eObj);
|
||||
});
|
||||
|
||||
player.ws.onclose = function(){}; //don't reopen websocket, just close, it's okay, rly
|
||||
break;
|
||||
}
|
||||
case "on_time": {
|
||||
var buffer = msg.data.current - video.currentTime*1e3;
|
||||
var serverDelay = player.ws.serverDelay.get();
|
||||
var desiredBuffer = Math.max(500+serverDelay,serverDelay*2);
|
||||
var desiredBuffer = Math.max(100+serverDelay,serverDelay*2);
|
||||
var desiredBufferwithJitter = desiredBuffer+(msg.data.jitter ? msg.data.jitter : 0);
|
||||
|
||||
|
||||
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" : ""),"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.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" : ""),
|
||||
(player.monitor ? "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,
|
||||
"readyState",MistVideo.video.readyState,msg.data
|
||||
);
|
||||
}
|
||||
|
||||
if (!player.sb) {
|
||||
MistVideo.log("Received on_time, but the source buffer is being cleared right now. Ignoring.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (player.sb._duration != msg.data.end*1e-3) {
|
||||
player.sb._duration = msg.data.end*1e-3;
|
||||
if (lastduration != msg.data.end*1e-3) {
|
||||
lastduration = msg.data.end*1e-3;
|
||||
MistUtil.event.send("durationchange",null,MistVideo.video);
|
||||
}
|
||||
MistVideo.info.meta.buffer_window = msg.data.end - msg.data.begin;
|
||||
|
@ -429,32 +479,37 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
if (msg.data.play_rate_curr == "auto") {
|
||||
if (video.currentTime > 0) { //give it some time to seek to live first when starting up
|
||||
//assume we want to be as live as possible
|
||||
if (buffer - desiredBuffer > desiredBuffer) {
|
||||
requested_rate = 1.1 + Math.min(1,((buffer-desiredBuffer)/desiredBuffer))*0.15;
|
||||
if (buffer > desiredBufferwithJitter*2) {
|
||||
requested_rate = 1 + Math.min(1,((buffer-desiredBufferwithJitter)/desiredBufferwithJitter))*0.08;
|
||||
video.playbackRate *= requested_rate;
|
||||
MistVideo.log("Our buffer is big, so increase the playback speed to catch up.");
|
||||
MistVideo.log("Our buffer ("+Math.round(buffer)+"ms) is big (>"+Math.round(desiredBufferwithJitter*2)+"ms), so increase the playback speed to "+(Math.round(requested_rate*100)/100)+" to catch up.");
|
||||
}
|
||||
else if (buffer < 0) {
|
||||
requested_rate = 0.8;
|
||||
video.playbackRate *= requested_rate;
|
||||
MistVideo.log("Our buffer ("+Math.round(buffer)+"ms) is negative so decrease the playback speed to "+(Math.round(requested_rate*100)/100)+" to let it catch up.");
|
||||
}
|
||||
else if (buffer < desiredBuffer/2) {
|
||||
requested_rate = 0.9;
|
||||
requested_rate = 1 + Math.min(1,((buffer-desiredBuffer)/desiredBuffer))*0.08;
|
||||
video.playbackRate *= requested_rate;
|
||||
MistVideo.log("Our buffer is small, so decrease the playback speed to catch up.");
|
||||
MistVideo.log("Our buffer ("+Math.round(buffer)+"ms) is small (<"+Math.round(desiredBuffer/2)+"ms), so decrease the playback speed to "+(Math.round(requested_rate*100)/100)+" to catch up.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (requested_rate > 1) {
|
||||
if (buffer < desiredBuffer) {
|
||||
if (buffer < desiredBufferwithJitter) {
|
||||
video.playbackRate /= requested_rate;
|
||||
requested_rate = 1;
|
||||
MistVideo.log("Our buffer is small enough, so return to real time playback.");
|
||||
MistVideo.log("Our buffer ("+Math.round(buffer)+"ms) is small enough (<"+Math.round(desiredBufferwithJitter)+"ms), so return to real time playback.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
//requested rate < 1
|
||||
if (buffer > desiredBuffer) {
|
||||
if (buffer > desiredBufferwithJitter) {
|
||||
video.playbackRate /= requested_rate;
|
||||
requested_rate = 1;
|
||||
MistVideo.log("Our buffer is big enough, so return to real time playback.");
|
||||
MistVideo.log("Our buffer ("+Math.round(buffer)+"ms) is big enough (>"+Math.round(desiredBufferwithJitter)+"ms), so return to real time playback.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -465,7 +520,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
if (buffer < desiredBuffer/2) {
|
||||
if (buffer < -10e3) {
|
||||
//seek to play point
|
||||
send({type: "seek", seek_time: video.currentTime*1e3});
|
||||
send({type: "seek", seek_time: Math.round(video.currentTime*1e3)});
|
||||
}
|
||||
else {
|
||||
//negative buffer? ask for faster delivery
|
||||
|
@ -503,6 +558,35 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
MistVideo.reporting.stats.d.tracks = msg.data.tracks.join(",");
|
||||
}
|
||||
|
||||
//check if the tracks are different than before, and if so, signal the skin to display the playing tracks
|
||||
if ((msg.data.tracks) && (currenttracks != msg.data.tracks)) {
|
||||
var tracks = MistVideo.info ? MistUtil.tracks.parse(MistVideo.info.meta.tracks) : [];
|
||||
for (var i in msg.data.tracks) {
|
||||
if (currenttracks.indexOf(msg.data.tracks[i]) < 0) {
|
||||
//find track type
|
||||
var type;
|
||||
for (var j in tracks) {
|
||||
if (msg.data.tracks[i] in tracks[j]) {
|
||||
type = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!type) {
|
||||
//track type not found, this should not happen
|
||||
continue;
|
||||
}
|
||||
|
||||
//create an event to pass this to the skin
|
||||
MistUtil.event.send("playerUpdate_trackChanged",{
|
||||
type: type,
|
||||
trackid: msg.data.tracks[i]
|
||||
},MistVideo.video);
|
||||
}
|
||||
}
|
||||
|
||||
currenttracks = msg.data.tracks;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "tracks": {
|
||||
|
@ -520,21 +604,15 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
|
||||
|
||||
if (checkEqual(player.last_codecs ? player.last_codecs : player.sb._codecs,msg.data.codecs)) {
|
||||
if (player.debugging) console.log("reached switching point");
|
||||
if (msg.data.current > 0) {
|
||||
if (player.sb) { //if sb is being cleared at the moment, don't bother
|
||||
player.sb._do(function(){ //once the source buffer is done updating the current segment, clear the specified interval from the buffer
|
||||
player.sb.remove(0,msg.data.current*1e-3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MistVideo.log("Player switched tracks, keeping source buffer as codecs are the same as before.");
|
||||
if ((video.currentTime == 0) && (msg.data.current != 0)) {
|
||||
video.currentTime = msg.data.current;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (player.debugging) {
|
||||
console.warn("Different codecs!");
|
||||
console.warn("video time",video.currentTime,"waiting until",msg.data.current*1e-3);
|
||||
console.warn("video time",video.currentTime,"switch startpoint",msg.data.current*1e-3);
|
||||
}
|
||||
player.last_codecs = msg.data.codecs;
|
||||
//start gathering messages in a new msg queue. They won't be appended to the current source buffer
|
||||
|
@ -547,6 +625,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
//play out buffer, then when we reach the starting timestamp of the new data, reset the source buffers
|
||||
var clear = function(){
|
||||
//once the source buffer is done updating the current segment, clear the specified interval from the buffer
|
||||
currPos = video.currentTime.toFixed(3);
|
||||
if (player && player.sb) {
|
||||
player.sb._do(function(remaining_do_on_updateend){
|
||||
if (!player.sb.updating) {
|
||||
|
@ -560,7 +639,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
player.ms.onsourceended = null;
|
||||
//console.log("sb murdered");
|
||||
if (player.debugging && remaining_do_on_updateend && remaining_do_on_updateend.length) {
|
||||
console.warn("There are do_on_updateend functions queued, which I *should* re-apply after clearing the sb.");
|
||||
console.warn("There are do_on_updateend functions queued, which I will re-apply after clearing the sb.");
|
||||
}
|
||||
|
||||
player.msinit().then(function(){
|
||||
|
@ -568,11 +647,20 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
player.sb.do_on_updateend = remaining_do_on_updateend;
|
||||
|
||||
var e = MistUtil.event.addListener(video,"loadedmetadata",function(){
|
||||
MistVideo.log("Buffer cleared, setting playback position to "+MistUtil.format.time(t,{ms:true}));
|
||||
MistVideo.log("Buffer cleared");
|
||||
|
||||
var f = function() {
|
||||
if (currPos > t) {
|
||||
t = currPos;
|
||||
}
|
||||
if (!video.buffered.length || (video.buffered.end(video.buffered.length-1) < t)) {
|
||||
if (player.debugging) { console.log("Desired seeking position ("+MistUtil.format.time(t,{ms:true})+") not yet in buffer ("+(video.buffered.length ? MistUtil.format.time(video.buffered.end(video.buffered.length-1),{ms:true}) : "null")+")"); }
|
||||
player.sb._doNext(f);
|
||||
return;
|
||||
}
|
||||
video.currentTime = t;
|
||||
if (video.currentTime < t) {
|
||||
MistVideo.log("Setting playback position to "+MistUtil.format.time(t,{ms:true}));
|
||||
if (video.currentTime.toFixed(3) < t) {
|
||||
player.sb._doNext(f);
|
||||
if (player.debugging) { console.log("Could not set playback position"); }
|
||||
}
|
||||
|
@ -581,9 +669,7 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
var p = function(){
|
||||
player.sb._doNext(function(){
|
||||
if (video.buffered.length) {
|
||||
if (player.debugging) {
|
||||
console.log(video.buffered.start(0),video.buffered.end(0),video.currentTime);
|
||||
}
|
||||
//if (player.debugging) { console.log(video.buffered.start(0),video.buffered.end(0),video.currentTime); }
|
||||
if (video.buffered.start(0) > video.currentTime) {
|
||||
var b = video.buffered.start(0);
|
||||
video.currentTime = b;
|
||||
|
@ -624,12 +710,49 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
clear();
|
||||
break;
|
||||
}
|
||||
|
||||
function reachedSwitchingPoint(msg) {
|
||||
if (player.debugging) {
|
||||
console.warn("reached switching point",msg.data.current*1e-3,MistUtil.format.time(msg.data.current*1e-3));
|
||||
}
|
||||
MistVideo.log("Track switch: reached switching point");
|
||||
clear();
|
||||
}
|
||||
if (video.currentTime == 0) {
|
||||
reachedSwitchingPoint(msg);
|
||||
}
|
||||
else {
|
||||
if (msg.data.current >= video.currentTime*1e3) {
|
||||
MistVideo.log("Track switch: waiting for playback to reach the switching point ("+MistUtil.format.time(msg.data.current*1e-3,{ms:true})+")");
|
||||
|
||||
//wait untill the video has reached the time of the newly received track or the end of our buffer
|
||||
var ontime = MistUtil.event.addListener(video,"timeupdate",function(){
|
||||
if (msg.data.current < video.currentTime * 1e3) {
|
||||
if (player.debugging) { console.log("Track switch: video.currentTime has reached switching point."); }
|
||||
reachedSwitchingPoint(msg);
|
||||
MistUtil.event.removeListener(ontime);
|
||||
MistUtil.event.removeListener(onwait);
|
||||
}
|
||||
});
|
||||
var onwait = MistUtil.event.addListener(video,"waiting",function(){
|
||||
if (player.debugging) { console.log("Track switch: video has reached end of buffer.","Gap:",Math.round(msg.data.current - video.currentTime * 1e3),"ms"); }
|
||||
reachedSwitchingPoint(msg);
|
||||
MistUtil.event.removeListener(ontime);
|
||||
MistUtil.event.removeListener(onwait);
|
||||
});
|
||||
}
|
||||
else {
|
||||
//subscribe to on_time, wait until we've received current playback point
|
||||
//if we don't wait, the screen will go black until the buffer is full enough
|
||||
MistVideo.log("Track switch: waiting for the received data to reach the current playback point");
|
||||
var ontime = function(newmsg){
|
||||
if (newmsg.data.current >= video.currentTime*1e3) {
|
||||
reachedSwitchingPoint(newmsg);
|
||||
player.ws.removeListener("on_time",ontime);
|
||||
}
|
||||
}
|
||||
player.ws.addListener("on_time",ontime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -642,9 +765,11 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
var data = new Uint8Array(e.data);
|
||||
if (data) {
|
||||
if (player.monitor && player.monitor.bitCounter) {
|
||||
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);
|
||||
|
@ -813,10 +938,11 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
if (player.sb) { player.sb.paused = true; }
|
||||
},
|
||||
setTracks: function(obj){
|
||||
if (!MistUtil.object.keys(obj).length) { return; }
|
||||
obj.type = "tracks";
|
||||
obj = MistUtil.object.extend({
|
||||
type: "tracks",
|
||||
seek_time: Math.max(0,video.currentTime*1e3-(500+player.ws.serverDelay.get()))
|
||||
//seek_time: Math.round(Math.max(0,video.currentTime*1e3-(500+player.ws.serverDelay.get())))
|
||||
},obj);
|
||||
send(obj);
|
||||
},
|
||||
|
@ -840,22 +966,33 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
return video.currentTime;
|
||||
},
|
||||
set: function(value){
|
||||
//console.warn("seek to",value);
|
||||
if (isNaN(value) || (value < 0)) {
|
||||
MistVideo.log("Ignoring seek to "+value+" because ewww.");
|
||||
return;
|
||||
}
|
||||
MistUtil.event.send("seeking",value,video);
|
||||
send({type: "seek", seek_time: Math.max(0,value*1e3-(250+player.ws.serverDelay.get()))}); //safety margin for server latency
|
||||
send({type: "seek", seek_time: Math.round(Math.max(0,value*1e3-(250+player.ws.serverDelay.get())))}); //safety margin for server latency
|
||||
//set listener "seek"
|
||||
var onseek = function(e){
|
||||
player.ws.removeListener("seek",onseek);
|
||||
var ontime = function(e){
|
||||
player.ws.removeListener("on_time",ontime);
|
||||
//in the first on_time, assume that the data were getting is where we want to be
|
||||
value = (e.data.current*1e-3).toFixed(3);
|
||||
value = e.data.current * 1e-3;
|
||||
value = value.toFixed(3);
|
||||
//retry a max of 10 times
|
||||
var retries = 10;
|
||||
var f = function() {
|
||||
video.currentTime = value;
|
||||
if (video.currentTime != value) {
|
||||
if (player.debugging) console.log("Failed to set video.currentTime, wanted:",value,"got:",video.currentTime);
|
||||
if (video.currentTime.toFixed(3) < value) {
|
||||
MistVideo.log("Failed to seek, wanted: "+value+" got: "+video.currentTime.toFixed(3));
|
||||
if (retries >= 0) {
|
||||
retries--;
|
||||
player.sb._doNext(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
f();
|
||||
};
|
||||
player.ws.addListener("on_time",ontime);
|
||||
|
@ -866,9 +1003,10 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
});
|
||||
//override duration
|
||||
var lastduration = 1;
|
||||
Object.defineProperty(this.api,"duration",{
|
||||
get: function(){
|
||||
return player.sb ? player.sb._duration : 1;
|
||||
return lastduration;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this.api,"playbackRate",{
|
||||
|
@ -968,28 +1106,68 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
}
|
||||
console.log("waiting","currentTime",video.currentTime,"buffers",buffers,contained ? "contained" : "outside of buffer","readystate",video.readyState,"networkstate",video.networkState);
|
||||
if ((video.readyState >= 2) && (video.networkState >= 2)) {
|
||||
console.error("Why am I waiting?!");
|
||||
console.error("Why am I waiting?!",video.currentTime);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
this.ABR = {
|
||||
size: null,
|
||||
bitrate: null,
|
||||
generateString: function(type,raw){
|
||||
switch (type) {
|
||||
case "size": {
|
||||
return "~"+[raw.width,raw.height].join("x");
|
||||
}
|
||||
case "bitrate": {
|
||||
return "<"+Math.round(raw)+"bps,minbps";
|
||||
}
|
||||
default: {
|
||||
throw "Unknown ABR type";
|
||||
}
|
||||
}
|
||||
},
|
||||
request: function(type,value){
|
||||
this[type] = value;
|
||||
|
||||
var request = [];
|
||||
if (this.bitrate !== null) {
|
||||
request.push(this.generateString("bitrate",this.bitrate));
|
||||
}
|
||||
if (this.size !== null) {
|
||||
request.push(this.generateString("size",this.size));
|
||||
}
|
||||
else {
|
||||
request.push("maxbps");
|
||||
}
|
||||
|
||||
return player.api.setTracks({
|
||||
video: request.join(",|")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.api.ABR_resize = function(size){
|
||||
MistVideo.log("Requesting the video track with the resolution that best matches the player size");
|
||||
player.ABR.request("size",size);
|
||||
};
|
||||
//ABR: monitor playback issues and switch to lower bitrate track if available
|
||||
//NB: this ABR requests a lower bitrate if needed, but it can never go back up
|
||||
this.monitor = {
|
||||
bitCounter: [],
|
||||
bitsSince: [],
|
||||
currentBps: null,
|
||||
nWaiting: 0,
|
||||
nWaitingThreshold: 3,
|
||||
listener: MistUtil.event.addListener(video,"waiting",function(){
|
||||
listener: MistVideo.options.ABR_bitrate ? 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();
|
||||
}
|
||||
}),
|
||||
}) : null,
|
||||
getBitRate: function(){
|
||||
if (player.sb && !player.sb.paused) {
|
||||
|
||||
|
@ -1009,18 +1187,20 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
var dt = new Date().getTime() - since;
|
||||
this.currentBps = bits / (dt*1e-3);
|
||||
|
||||
//console.log(MistUtil.format.bytes(this.currentBps)+"its/s");
|
||||
|
||||
//console.log(MistUtil.format.bits(this.currentBps)+"its/s");
|
||||
}
|
||||
|
||||
MistVideo.timers.start(function(){
|
||||
player.monitor.getBitRate();
|
||||
},500);
|
||||
},
|
||||
action: function(){
|
||||
player.api.setTracks({video:"max<"+Math.round(this.currentBps)+"bps"});
|
||||
if (MistVideo.options.setTracks && MistVideo.options.setTracks.video) {
|
||||
//a video track was selected by the user, do not change it
|
||||
return;
|
||||
}
|
||||
MistVideo.log("ABR threshold triggered, requesting lower quality");
|
||||
player.ABR.request("bitrate",this.currentBps);
|
||||
}
|
||||
};
|
||||
|
||||
this.monitor.getBitRate();
|
||||
};
|
||||
|
|
|
@ -725,6 +725,11 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
f();
|
||||
}
|
||||
|
||||
me.api.ABR_resize = function(size){
|
||||
MistVideo.log("Requesting the video track with the resolution that best matches the player size");
|
||||
me.api.setTracks({video:"~"+[size.width,size.height].join("x")});
|
||||
};
|
||||
|
||||
me.api.unload = function(){
|
||||
try {
|
||||
me.webrtc.stop();
|
||||
|
|
Loading…
Add table
Reference in a new issue