diff --git a/extension/carbon-for-f1tv.user.js b/extension/carbon-for-f1tv.user.js
index b90791a..0026933 100644
--- a/extension/carbon-for-f1tv.user.js
+++ b/extension/carbon-for-f1tv.user.js
@@ -10,6 +10,8 @@
var DEFAULT_THEATERMODE = true;
+var DEFAULT_LATENCY = 30;
+
var theatermode_btn_image = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPScxLjEnIGlkPSdMYXllcl8xJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4PScwcHgnIHk9JzBweCcgdmlld0JveD0nMCAwIDI4My41IDQyNS4yJyBzdHlsZT0nZW5hYmxlLWJhY2tncm91bm" +
@@ -188,7 +190,7 @@ if (carbon_mode == "popout") {
let diffToTargetAbs = Math.abs(diffToTarget);
if (diffToTargetAbs > 15) {
- videoJumpToProgress(targetProgress);
+ videoSyncJumpToProgress(targetProgress);
} else if (diffToTargetAbs > 10) {
if (diffToTarget > 0) { videoSpeed(3); } else { videoSpeed(0.333); }
} else if (diffToTargetAbs > 5) {
@@ -202,11 +204,94 @@ if (carbon_mode == "popout") {
} else {
videoSpeed(1);
}
+
+ if (diffToTargetAbs > 0.2) {
+ $(".sync-toppopup").show();
+ $(".sync-toppopup-diff").text(diffToTarget.toFixed(2) + "s");
+ $(".sync-toppopup-speed").text(thisPlaybackrate.toFixed(2) + "x");
+ } else {
+ $(".sync-toppopup").hide();
+ }
}
}, 500);
+} else {
+
+ var latencyModeData = setInterval(function () {
+ if ($(".player-container.shown .bitmovinplayer-container.carbon-player").length > 0) {
+ let thisProgress = $(".carbon-player video")[0].currentTime;
+ $("#this-progress").text(thisProgress);
+
+ let thisPlaybackrate = $(".carbon-player video")[0].playbackRate;
+ $("#this-playbackrate").text(thisPlaybackrate);
+
+ let latencyTarget = parseInt(document.getElementById("latencymode-latency").value) / 1000 || DEFAULT_LATENCY;
+ let targetProgress = (Date.now() / 1000) - latencyTarget;
+ $("#this-targetprogress").text(targetProgress);
+
+ let diffToTarget = targetProgress - thisProgress;
+ let diffToTargetAbs = Math.abs(diffToTarget);
+ $("#diff-to-target").text(diffToTarget);
+
+ let state;
+ if ($(".carbon-player video")[0].paused) {
+ state = "paused";
+ } else {
+ state = "playing";
+ }
+
+ $("#this-state").text(state);
+
+
+
+ //let diffToTarget = targetProgress - thisProgressRealtime;
+ //$("#diff-to-target").text(diffToTarget);
+ } else {
+ log("can't sync to latency, player not loaded");
+ }
+ }, 500);
+
+
+ let syncNow = setInterval(function () {
+ let latencyMode = parseInt(document.getElementById("latency-mode").value);
+ if (latencyMode == 1) {
+ let thisProgress = parseFloat($("#this-progress").text());
+ let targetProgress = parseFloat($("#this-targetprogress").text());
+ let thisPlaybackrate = parseFloat($("#this-playbackrate").text());
+ let diffToTarget = targetProgress - thisProgress;
+ let diffToTargetAbs = Math.abs(diffToTarget);
+
+ if (diffToTargetAbs > 15) {
+ videoLatencyJumpToProgress(targetProgress);
+ } else if (diffToTargetAbs > 10) {
+ if (diffToTarget > 0) { videoSpeed(3); } else { videoSpeed(0.333); }
+ } else if (diffToTargetAbs > 5) {
+ if (diffToTarget > 0) { videoSpeed(2); } else { videoSpeed(0.5); }
+ } else if (diffToTargetAbs > 1) {
+ if (diffToTarget > 0) { videoSpeed(1.5); } else { videoSpeed(0.666); }
+ } else if (diffToTargetAbs > 0.5) {
+ if (diffToTarget > 0) { videoSpeed(1.25); } else { videoSpeed(0.8); }
+ } else if (diffToTargetAbs > 0.2) {
+ if (diffToTarget > 0) { videoSpeed(1.11); } else { videoSpeed(0.9); }
+ } else {
+ videoSpeed(1);
+ }
+
+ if (diffToTargetAbs > 0.2) {
+ $(".sync-toppopup").show();
+ $(".sync-toppopup-diff").text(diffToTarget.toFixed(2) + "s");
+ $(".sync-toppopup-speed").text(thisPlaybackrate.toFixed(2) + "x");
+ } else {
+ $(".sync-toppopup").hide();
+ }
+ }
+ }, 500);
+
+
}
+
+
function videoSpeed(speed) {
if ($(".player-container.shown .bitmovinplayer-container.carbon-player").length > 0) {
$(".carbon-player video")[0].playbackRate = speed;
@@ -216,7 +301,7 @@ function videoSpeed(speed) {
}
-function videoJumpToProgress(progress) {
+function videoSyncJumpToProgress(progress) {
let oldTime = $(".carbon-player video")[0].currentTime;
let newTime = progress + 10;
$(".carbon-player video")[0].currentTime = newTime;
@@ -231,7 +316,7 @@ function videoJumpToProgress(progress) {
// for some reason, on live streams we can't jump forward by much,
// this simulates clicking "live" button and enables sync again to jump backward
$(".bmpui-ui-playbacktimelabel-live")[0].click();
- setTimeout(function() {
+ setTimeout(function () {
toggleSyncMode(1);
}, 1000);
}
@@ -244,6 +329,38 @@ function videoJumpToProgress(progress) {
}
+function videoLatencyJumpToProgress(progress) {
+ let oldTime = $(".carbon-player video")[0].currentTime;
+ if (oldTime < 1700000000) {
+ log("not live, or something is wrong. Disabling latency mode");
+ toggleLatencyMode(0);
+ }
+ let newTime = progress + 10;
+ $(".carbon-player video")[0].currentTime = newTime;
+ let checkTime = $(".carbon-player video")[0].currentTime;
+ log("Jumped from: " + oldTime + ", to: " + newTime + ". Current time:" + checkTime);
+ if (Math.abs(checkTime - newTime) > 15) {
+ log("problem with sync, disabling");
+ toggleLatencyMode(0);
+ $(".carbon-player video")[0].pause();
+ if ($(".bmpui-ui-playbacktimelabel-live").length > 0) {
+ log("attempting to fix latency mode sync on live stream");
+ // for some reason, on live streams we can't jump forward by much,
+ // this simulates clicking "live" button and enables sync again to jump backward
+ $(".bmpui-ui-playbacktimelabel-live")[0].click();
+ setTimeout(function () {
+ toggleLatencyMode(1);
+ }, 1000);
+ }
+ } else {
+ // Simulates click on seek-backward button.
+ // This fixes Bitmovin player sometimes not buffering video after setting it's currentTime value.
+ $(".carbon-player video")[0].currentTime = newTime;
+ $(".carbon-player .bmpui-ui-rewindbutton")[0].click();
+ }
+}
+
+
function injectPlayerFeatures() {
log("Injecting player features");
@@ -311,6 +428,74 @@ function injectPlayerFeatures() {
})
}
+ if ((carbon_mode !== "popout") && (is_live == true)) {
+
+
+ let latencyDebugToggleHtml = "
";
+ $(".bmpui-ui-settings-panel-page .bmpui-container-wrapper")[0].insertAdjacentHTML("afterbegin", latencyDebugToggleHtml);
+ $(".carbon-latency-debug-toggle")[0].addEventListener("click", function () {
+ toggleLatencyModeDebug();
+ })
+
+
+ $(".carbon-player .bmpui-seekbar")[0].addEventListener("click", function () {
+ toggleLatencyMode(0);
+ });
+ $(".carbon-player .bmpui-ui-rewindbutton")[0].addEventListener("mouseup", function () { //can't be listening to "click" event on rewind button, as we're simulating it elsewhere to fix sync mode
+ toggleLatencyMode(0);
+ });
+ $(".carbon-player .bmpui-ui-forwardbutton")[0].addEventListener("mouseup", function () {
+ toggleLatencyMode(0);
+ });
+ $(".carbon-player .bmpui-ui-playbacktogglebutton")[0].addEventListener("mouseup", function () {
+ toggleLatencyMode(0);
+ });
+ $(".carbon-player .bmpui-ui-hugeplaybacktogglebutton")[0].addEventListener("mouseup", function () {
+ toggleLatencyMode(0);
+ });
+
+
+
+ function setLatency(latency) {
+ let oldLatency = parseInt($("#latencymode-latency")[0].value) || DEFAULT_LATENCY * 1000;
+ let newLatency = oldLatency + latency;
+ $("#latencymode-latency")[0].value = newLatency;
+ $(".carbon-targetlatency-view").text((newLatency / 1000).toFixed(1));
+ log("Setting live latency by: " + latency + ". New latency: " + newLatency);
+ }
+
+
+ let targetLatencyMenuHtml = "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+
+ $(".carbon-player .bmpui-container-wrapper .bmpui-ui-volumeslider")[0].insertAdjacentHTML("afterEnd", targetLatencyMenuHtml);
+
+ $(".carbon-player .carbon-btn-latency-back-10").on("click", function () {
+ setLatency(-1000);
+ });
+ $(".carbon-player .carbon-btn-latency-back-01").on("click", function () {
+ setLatency(-100);
+ });
+ $(".carbon-player .carbon-btn-latency-forward-01").on("click", function () {
+ setLatency(+100);
+ });
+ $(".carbon-player .carbon-btn-latency-forward-10").on("click", function () {
+ setLatency(+1000);
+ });
+ $(".carbon-player .carbon-targetlatency-menu-disable").on("click", function () {
+ toggleLatencyMode(0);
+ });
+ $(".carbon-player .carbon-targetlatency-menu-enable").on("click", function () {
+ toggleLatencyMode(1);
+ });
+ }
+
if (carbon_mode == "popout") {
$(".carbon-player .bmpui-ui-casttogglebutton").addClass("bmpui-hidden");
//$(".carbon-player .bmpui-ui-forwardbutton").addClass("bmpui-hidden");
@@ -327,22 +512,22 @@ function injectPlayerFeatures() {
});
$(".carbon-player .bmpui-ui-rewindbutton")[0].addEventListener("mouseup", function () { //can't be listening to "click" event on rewind button, as we're simulating it elsewhere to fix sync mode
toggleSyncMode(0);
- })
+ });
$(".carbon-player .bmpui-ui-forwardbutton")[0].addEventListener("mouseup", function () {
toggleSyncMode(0);
- })
+ });
$(".carbon-player .bmpui-ui-playbacktogglebutton")[0].addEventListener("mouseup", function () {
toggleSyncMode(0);
- })
+ });
$(".carbon-player .bmpui-ui-hugeplaybacktogglebutton")[0].addEventListener("mouseup", function () {
toggleSyncMode(0);
- })
+ });
let syncDebugToggleHtml = "";
$(".bmpui-ui-settings-panel-page .bmpui-container-wrapper")[0].insertAdjacentHTML("afterbegin", syncDebugToggleHtml);
$(".carbon-sync-debug-toggle")[0].addEventListener("click", function () {
toggleSyncDebug();
- })
+ });
let syncOffsetSwitcherHtml = "" +
"" +
@@ -352,14 +537,16 @@ function injectPlayerFeatures() {
updateSyncOffsetView();
$(".carbon-player .carbon-btn-syncoffset-back").on("click", function () {
setSyncOffset(-200);
- })
+ });
$(".carbon-player .carbon-btn-syncoffset-forward").on("click", function () {
setSyncOffset(+200);
- })
+ });
}
- let donateHtml = "";
+
+
+ let donateHtml = "";
$(".bmpui-ui-settings-panel-page .bmpui-container-wrapper")[0].insertAdjacentHTML("beforeEnd", donateHtml);
@@ -390,14 +577,18 @@ function toggleSyncDebug() {
function toggleSyncMode(mode) {
let syncMode = parseInt(document.getElementById("sync-mode").value);
+ if (syncMode == mode) {
+ return;
+ }
if (syncMode == 1 || mode == 0) {
log("sync mode disabled");
document.getElementById("sync-mode").value = 0;
$(".carbon-btn-synctoggle-label").text("SYNC OFF");
$(".carbon-btn-synctoggle-dot").css("background-color", "#999");
$(".carbon-syncoffset-menu").hide();
- setTimeout(function() {
- videoSpeed(1);
+ $(".sync-toppopup").hide();
+ setTimeout(function () {
+ videoSpeed(1);
}, 1000);
} else {
log("sync mode enabled");
@@ -411,6 +602,41 @@ function toggleSyncMode(mode) {
}
}
+function toggleLatencyModeDebug() {
+ if ($("#carbon-helper .latencymode-data").is(":visible")) {
+ $("#carbon-helper .latencymode-data").hide();
+ } else {
+ $("#carbon-helper .latencymode-data").show();
+ }
+}
+
+function toggleLatencyMode(mode) {
+ let latencyMode = parseInt(document.getElementById("latency-mode").value);
+ if (latencyMode == mode) {
+ return;
+ }
+ if (latencyMode == 1 || mode == 0) {
+ log("latency mode disabled");
+ document.getElementById("latency-mode").value = 0;
+ $(".carbon-targetlatency-menu").hide();
+ $(".carbon-targetlatency-menu-enable").show();
+ $(".sync-toppopup").hide();
+ setTimeout(function () {
+ videoSpeed(1);
+ }, 1000);
+ } else if (parseInt($("#this-progress").text) < 1700000000) {
+ log("can't enable latency mode. video isn't live?");
+ } else {
+ log("latency mode enabled");
+ document.getElementById("latency-mode").value = 1;
+ $(".carbon-targetlatency-menu").show();
+ $(".carbon-targetlatency-menu-enable").hide();
+ if ($(".carbon-player video")[0].paused) {
+ $(".carbon-player video")[0].play();
+ }
+ }
+}
+
function waitForPlayer() {
if ($(".player-container.shown .bitmovinplayer-container").length > 0) {
if ($(".player-container.shown .bitmovinplayer-container.carbon-player").length > 0) {
@@ -455,7 +681,29 @@ function waitForPageLoad() {
".sync-data span.thiswindow {font-weight: bold; color: #ffff00;}" +
"";
$("#carbon-helper")[0].insertAdjacentHTML("beforeEnd", syncDataHtml);
+ } else {
+ let latencyModeDataHtml = "" +
+ "
video progress:
" +
+ "
latency: , target progress:
" +
+ "
diff to target:
" +
+ "
state:
" +
+
+ "
latency mode:
" +
+ "
playback rate:
" +
+ "
" +
+ "";
+ $("#carbon-helper")[0].insertAdjacentHTML("beforeEnd", latencyModeDataHtml);
}
+ let syncTopPopupHtml = "";
+ $("#carbon-helper")[0].insertAdjacentHTML("beforeEnd", syncTopPopupHtml);
+
}
}