From 3eec64290be68299aeca645cd5f9daf8fb75d7f7 Mon Sep 17 00:00:00 2001 From: David Buezas Date: Thu, 13 Jul 2023 15:21:33 +0200 Subject: [PATCH 1/2] Add video wrapper for ptz transforms --- custom_components/webrtc/www/digital-ptz.js | 35 +++++++++++++++---- custom_components/webrtc/www/webrtc-camera.js | 15 ++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/custom_components/webrtc/www/digital-ptz.js b/custom_components/webrtc/www/digital-ptz.js index a47b5ea..c84c4cf 100644 --- a/custom_components/webrtc/www/digital-ptz.js +++ b/custom_components/webrtc/www/digital-ptz.js @@ -13,7 +13,7 @@ const DEFAULT_OPTIONS = { persist: true, }; export class DigitalPTZ { - constructor(containerEl, videoEl, options) { + constructor(containerEl, transformEl, videoEl, options) { this.offHandles = []; this.recomputeRects = () => { this.transform.updateRects(this.videoEl, this.containerEl); @@ -23,14 +23,15 @@ export class DigitalPTZ { this.render = (transition = false) => { if (transition) { // transition is used to animate dbl click zoom - this.videoEl.style.transition = "transform 200ms"; + this.transformEl.style.transition = "transform 200ms"; setTimeout(() => { - this.videoEl.style.transition = ""; + this.transformEl.style.transition = ""; }, 200); } - this.videoEl.style.transform = this.transform.render(); + this.transformEl.style.transform = this.transform.render(); }; this.containerEl = containerEl; + this.transformEl = transformEl; this.videoEl = videoEl; this.options = Object.assign({}, DEFAULT_OPTIONS, options); this.transform = new Transform({ @@ -205,6 +206,27 @@ function startMouseDragPan(params) { /** Transform */ const PERSIST_KEY_PREFIX = "webrtc-digital-ptc:"; const clamp = (value, min, max) => Math.min(Math.max(value, min), max); +function getTransformedDimensions(video) { + const { videoWidth, videoHeight } = video; + if (!videoHeight || !videoWidth) return undefined; + var transform = window.getComputedStyle(video).getPropertyValue("transform"); + const match = transform.match(/matrix\((.+)\)/); + if (!(match === null || match === void 0 ? void 0 : match[1])) + return { videoWidth, videoHeight }; // the video isn't transformed + const matrixValues = match[1].split(", ").map(Number); + const matrix = new DOMMatrix(matrixValues); + const points = [ + new DOMPoint(0, 0), + new DOMPoint(videoWidth, 0), + new DOMPoint(0, videoHeight), + new DOMPoint(videoWidth, videoHeight), + ].map((point) => point.matrixTransform(matrix)); + const minX = Math.min(...points.map((point) => point.x)); + const maxX = Math.max(...points.map((point) => point.x)); + const minY = Math.min(...points.map((point) => point.y)); + const maxY = Math.max(...points.map((point) => point.y)); + return { videoWidth: maxX - minX, videoHeight: maxY - minY }; +} class Transform { constructor(settings) { this.scale = 1; @@ -252,7 +274,8 @@ class Transform { return; } this.containerRect = containerRect; - if (!videoEl.videoWidth) { + const transformed = getTransformedDimensions(videoEl); + if (!transformed) { // The video hasn't loaded yet. // Once it loads, the videometadata listener will call this function again. return; @@ -262,7 +285,7 @@ class Transform { // This needs to be accounted for when panning, the code below keeps track of that. const screenAspectRatio = this.containerRect.width / this.containerRect.height; - const videoAspectRatio = videoEl.videoWidth / videoEl.videoHeight; + const videoAspectRatio = transformed.videoWidth / transformed.videoHeight; if (videoAspectRatio > screenAspectRatio) { // Black bars on the top and bottom const videoHeight = this.containerRect.width / videoAspectRatio; diff --git a/custom_components/webrtc/www/webrtc-camera.js b/custom_components/webrtc/www/webrtc-camera.js index 3f4a689..2861f55 100644 --- a/custom_components/webrtc/www/webrtc-camera.js +++ b/custom_components/webrtc/www/webrtc-camera.js @@ -160,13 +160,12 @@ class WebRTCCamera extends VideoRTC { background-color: black; height: 100%; position: relative; /* important for Safari */ - overflow: hidden; /* important for zoom-controller */ } .player:active { cursor: move; /* important for zoom-controller */ } - video { - transform-origin: 50% 50%; /* important for zoom-controller */ + .player .ptz-transform { + height: 100%; } .header { position: absolute; @@ -183,7 +182,9 @@ class WebRTCCamera extends VideoRTC { } -
+
+
+
@@ -192,8 +193,7 @@ class WebRTCCamera extends VideoRTC { `; this.querySelector = selectors => this.shadowRoot.querySelector(selectors); - - this.querySelector('.player').appendChild(this.video); + this.querySelector('.ptz-transform').appendChild(this.video); if (this.config.muted) this.video.muted = true; if (this.config.poster) this.video.poster = this.config.poster; @@ -203,7 +203,8 @@ class WebRTCCamera extends VideoRTC { if (this.config.digital_ptz === false) return; new DigitalPTZ( this.querySelector('.player'), - this.querySelector('.player video'), + this.querySelector('.player .ptz-transform'), + this.video, Object.assign({}, this.config.digital_ptz, {persist_key: this.config.url}) ); } From ed3dd665abcec145d708b4547ba3a5d873091f00 Mon Sep 17 00:00:00 2001 From: David Buezas Date: Sun, 16 Jul 2023 16:08:38 +0200 Subject: [PATCH 2/2] Replace optional chaining as it transpiles to hard to read code --- custom_components/webrtc/www/digital-ptz.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/custom_components/webrtc/www/digital-ptz.js b/custom_components/webrtc/www/digital-ptz.js index c84c4cf..b6a3396 100644 --- a/custom_components/webrtc/www/digital-ptz.js +++ b/custom_components/webrtc/www/digital-ptz.js @@ -211,10 +211,8 @@ function getTransformedDimensions(video) { if (!videoHeight || !videoWidth) return undefined; var transform = window.getComputedStyle(video).getPropertyValue("transform"); const match = transform.match(/matrix\((.+)\)/); - if (!(match === null || match === void 0 ? void 0 : match[1])) - return { videoWidth, videoHeight }; // the video isn't transformed - const matrixValues = match[1].split(", ").map(Number); - const matrix = new DOMMatrix(matrixValues); + if (!match || !match[1]) return { videoWidth, videoHeight }; // the video isn't transformed + const matrix = new DOMMatrix(match[1].split(", ").map(Number)); const points = [ new DOMPoint(0, 0), new DOMPoint(videoWidth, 0),