Embed: more fixes to do with showing the correct seek window in the progress bar

Embed: display more consistent timestamps across protocols and correctly show seekable range
Embed: fix: don't force nonexistent forcePlayer value
Embed: when info.unixoffset is known, display clock time based timestamps
This commit is contained in:
Cat 2021-10-20 13:46:10 +02:00 committed by Thulinma
parent c542155e10
commit 67d992e352
12 changed files with 178 additions and 22 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -35,6 +35,7 @@ function MistVideo(streamName,options) {
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
useDateTime: true, //when the unix timestamp of the stream is known, display the date/time
MistVideoObject: false//no reference object is passed
},options);
if (options.host) { options.host = MistUtil.http.url.sanitizeHost(options.host); }
@ -144,7 +145,7 @@ function MistVideo(streamName,options) {
for (var i in mistplayers) {
mistplayers[i].shortname = i;
}
if (options.forcePlayer) {
if (options.forcePlayer && mistplayers[options.forcePlayer]) {
players = [mistplayers[options.forcePlayer]];
MistVideo.log("Forcing player "+options.forcePlayer);
}

View file

@ -770,6 +770,38 @@ MistSkins["default"] = {
var video = this.video;
var MistVideo = this;
var first = Infinity;
if (MistVideo.info && MistVideo.info.meta && MistVideo.info.meta.tracks) {
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].firstms*1e-3 < first) {
first = MistVideo.info.meta.tracks[i].firstms*1e-3;
}
}
}
if (first == Infinity) {
first = 0;
}
function firsts() {
if (MistVideo.player.api.duration < first) { return 0; }
return first;
}
function getBufferWindow() {
var buffer_window = MistVideo.info.meta.buffer_window;
if (typeof buffer_window == "undefined") {
//for some reason, buffer_window is not defined (liveDVR?)
//just assume we can seek in our own buffer
if (MistVideo.player && MistVideo.player.api && MistVideo.player.api.buffered && MistVideo.player.api.buffered.length) {
buffer_window = (MistVideo.player.api.duration - MistVideo.player.api.buffered.start(0))*1e3;
}
else {
//no buffer of our own either? we'll just assume we have a minute
buffer_window = 60e3;
}
}
return buffer_window *= 1e-3;
}
//these functions update the states
container.updateBar = function(currentTime){
@ -785,11 +817,11 @@ MistSkins["default"] = {
if (!isFinite(MistVideo.player.api.duration)) { return 0; }
var result = 0;
if (MistVideo.info.type == "live") {
var buffer_window = MistVideo.info.meta.buffer_window * 1e-3;
var buffer_window = getBufferWindow();
result = (time - MistVideo.player.api.duration + buffer_window) / buffer_window;
}
else {
result = time / MistVideo.player.api.duration;
result = (time - firsts()) / (MistVideo.player.api.duration - firsts());
}
return Math.min(1,Math.max(0,result));
}
@ -865,14 +897,14 @@ MistSkins["default"] = {
var perc = MistUtil.getPos(this,e);
if (MistVideo.info.type == "live") {
//live mode: seek in DVR window
var bufferWindow = MistVideo.info.meta.buffer_window * 1e-3; //buffer window in seconds
var bufferWindow = getBufferWindow();
//assuming the "right" part or the progressbar is at true live
return (perc-1) * bufferWindow + MistVideo.player.api.duration;
}
else {
//VOD mode
if (!isFinite(MistVideo.player.api.duration)) { return false; }
return perc * MistVideo.player.api.duration;
return perc * (MistVideo.player.api.duration - firsts()) + firsts();
}
};
//seeking
@ -887,6 +919,7 @@ MistSkins["default"] = {
//hovering
var tooltip = MistVideo.UI.buildStructure({type:"tooltip"});
tooltip.style.opacity = 0;
container.appendChild(tooltip);
MistUtil.event.addListener(margincontainer,"mouseout",function(){
@ -899,8 +932,15 @@ MistSkins["default"] = {
tooltip.style.opacity = 0;
return;
}
tooltip.setText(MistUtil.format.time(secs));
if (MistVideo.options.useDateTime && MistVideo.info && MistVideo.info.unixoffset) {
tooltip.setText(MistUtil.format.ago(new Date(MistVideo.info.unixoffset + secs*1e3)));
}
else {
tooltip.setText(MistUtil.format.time(secs));
}
tooltip.style.opacity = 1;
var perc = MistUtil.getPos(this,e);// e.clientX - this.getBoundingClientRect().left;
var pos = {bottom:20};
@ -1193,8 +1233,18 @@ MistSkins["default"] = {
var formatTime = MistUtil.format.time;
container.set = function(){
var v = MistVideo.player.api.currentTime;
text.nodeValue = formatTime(v);
container.setAttribute("title",text.nodeValue);
var t;
if (MistVideo.options.useDateTime && MistVideo.info && MistVideo.info.unixoffset) {
var d = new Date(MistVideo.info.unixoffset + v*1e3);
t = MistUtil.format.ago(d);
container.setAttribute("title",MistUtil.format.ago(d,34560e6));
}
else {
t = formatTime(v);
container.setAttribute("title",t);
}
text.nodeValue = t;
};
container.set();
@ -1226,7 +1276,16 @@ MistSkins["default"] = {
return;
}
this.style.display = "";
text.nodeValue = MistUtil.format.time(duration);
if (MistVideo.options.useDateTime && MistVideo.info && MistVideo.info.unixoffset) {
var t = new Date(duration*1e3 + MistVideo.info.unixoffset)
text.nodeValue = MistUtil.format.ago(t);
container.setAttribute("title",MistUtil.format.ago(t,34560e6)); //format as if more than a year ago
}
else {
text.nodeValue = MistUtil.format.time(duration);
container.setAttribute("title",text.nodeValue);
}
};
MistUtil.event.addListener(MistVideo.video,"durationchange",function(){

View file

@ -33,6 +33,73 @@ var MistUtil = {
return (days ? days : "")+str.join(":")+ago;
},
ago: function(date,range){
//format a date nicely depending on how long ago it was
//if the range param [ms] is specified, use that to choose how to format the date string
var ago = range ? range : new Date().getTime() - date.getTime();
var out = "";
var negative = (ago < 0);
if (negative) { ago *= -1; }
if (ago < 1000) {
//less than a second ago
out = "live";
}
else if (ago < 60e3) {
//less than a minute ago
out = Math.round(ago/1e3)+" sec";
if (negative) {
out = "in "+out;
}
else {
out += " ago";
}
}
else if ((!range && (new Date().toLocaleDateString() == date.toLocaleDateString())) || (range < 86400e3)) {
//today
out = date.toLocaleTimeString(undefined,{
hour: "numeric",
minute: "2-digit",
second: "2-digit"
});
}
else if (ago < 518400e3) {
//less than 6 days ago
out = date.toLocaleString(undefined,{
weekday: "short",
hour: "numeric",
minute: "2-digit",
second: "2-digit"
});
}
else if ((!range && (new Date().getFullYear() == date.getFullYear())) || (range < 31622400e3)) {
//this year
out = date.toLocaleString(undefined,{
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "2-digit",
second: "2-digit"
});
}
else {
//before this year
out = date.toLocaleString(undefined,{
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit"
});
}
return out;
},
ucFirst: function(string){
return string.charAt(0).toUpperCase()+string.slice(1);
},

View file

@ -195,6 +195,7 @@ p.prototype.build = function (MistVideo,callback) {
MistVideo.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,params));
}
MistUtil.event.addListener(video,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
@ -217,9 +218,21 @@ p.prototype.build = function (MistVideo,callback) {
};
if (MistVideo.source.type == "html5/video/mp4") {
var otherdurationoverride = overrides.get.duration;
overrides.get.duration = function(){
return otherdurationoverride.apply(this,arguments) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
}
overrides.get.currentTime = function(){
return this.currentTime - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
}
overrides.get.buffered = function(){
var video = this;
return {
length: video.buffered.length,
start: function(i) { return video.buffered.start(i) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3; },
end: function(i) { return video.buffered.end(i) - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3; }
}
};
}
}
else {

View file

@ -1067,7 +1067,7 @@ p.prototype.build = function (MistVideo,callback) {
}
});
//override duration
var lastduration = 1;
var lastduration = Infinity;
Object.defineProperty(this.api,"duration",{
get: function(){
return lastduration;
@ -1079,7 +1079,7 @@ p.prototype.build = function (MistVideo,callback) {
},
set: function(value){
var f = function(msg){
video.playbackRate = msg.data.play_rate;
video.playbackRate = msg.data.play_rate_curr;
};
player.ws.addListener("set_speed",f);
send({type: "set_speed", play_rate: (value == 1 ? "auto" : value)});

View file

@ -100,6 +100,14 @@ p.prototype.build = function (MistVideo,callback) {
MistVideo.log("Building videojs");
me.videojs = videojs(ele,vjsopts,function(){
MistVideo.log("Videojs initialized");
if (MistVideo.info.type == "live") {
//overwrite the stream info's buffer window to the seekable range as indicated by the m3u8
MistUtil.event.addListener(ele,"progress",function(e){
var i = MistVideo.player.videojs.seekable().length-1;
MistVideo.info.meta.buffer_window = (Math.max(MistVideo.player.videojs.seekable().end(i),ele.duration) - MistVideo.player.videojs.seekable().start(i))*1e3;
});
}
});
MistUtil.event.addListener(ele,"error",function(e){
@ -160,6 +168,7 @@ p.prototype.build = function (MistVideo,callback) {
};
if (MistVideo.info.type == "live") {
function getLastBuffer(video) {
var buffer_end = 0;
if (video.buffered.length) {
@ -171,8 +180,7 @@ p.prototype.build = function (MistVideo,callback) {
overrides.get.duration = function(){
if (MistVideo.info) {
var duration = (MistVideo.info.lastms + (new Date()).getTime() - MistVideo.info.updated.getTime())*1e-3;
//if (isNaN(duration)) { return 1e9; }
var duration = ele.duration;
return duration;
}
return 0;
@ -186,19 +194,25 @@ p.prototype.build = function (MistVideo,callback) {
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;
MistVideo.player.videojs.currentTime(MistVideo.video.currentTime - diff); //seeking backwards does not work if we set it on the video directly
MistVideo.player.videojs.currentTime(MistVideo.video.currentTime - diff);
}
var lastms = 0;
overrides.get.currentTime = function(){
if (MistVideo.info) { lastms = MistVideo.info.lastms*1e-3; }
var time = this.currentTime + lastms - MistVideo.player.api.liveOffset - HLSlatency;
var time = MistVideo.player.videojs ? MistVideo.player.videojs.currentTime() : ele.currentTime;
if (isNaN(time)) { return 0; }
return time;
}
overrides.get.buffered = function(){
var buffered = MistVideo.player.videojs ? MistVideo.player.videojs.buffered() : ele.buffered;
return {
length: buffered.length,
start: function(i) { return buffered.start(i); },
end: function(i) { return buffered.end(i); i}
}
};
}
}
else {

View file

@ -129,6 +129,8 @@ p.prototype.build = function (MistVideo,callback) {
duration = d;
MistUtil.event.send("durationchange",d,video);
}
MistVideo.info.meta.buffer_window = ev.end - ev.begin;
if ((ev.tracks) && (currenttracks != ev.tracks)) {
var tracks = MistVideo.info ? MistUtil.tracks.parse(MistVideo.info.meta.tracks) : [];