From 74eae46ac3c29a7940a771e473a124abbe781bcb Mon Sep 17 00:00:00 2001 From: Vishal Date: Sat, 6 Jan 2024 16:23:24 +0530 Subject: [PATCH 1/5] Feat: Support mirrored video for createCapture --- src/dom/dom.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index b12a2750c8..a0f245637d 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2212,7 +2212,7 @@ if (navigator.mediaDevices.getUserMedia === undefined) { p5.prototype.createCapture = function(...args) { p5._validateParameters('createCapture', args); - // return if getUserMedia is not supported by browser + // return if getUserMedia is not supported by the browser if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) { throw new DOMException('getUserMedia not supported in this browser'); } @@ -2221,12 +2221,23 @@ p5.prototype.createCapture = function(...args) { let useAudio = true; let constraints; let callback; + let flipped = false; + for (const arg of args) { if (arg === p5.prototype.VIDEO) useAudio = false; else if (arg === p5.prototype.AUDIO) useVideo = false; - else if (typeof arg === 'object') constraints = arg; - else if (typeof arg === 'function') callback = arg; + else if (typeof arg === 'object') { + constraints = arg; + flipped = constraints.flipped || false; + } + else if (typeof arg === 'boolean') { + flipped = arg; + } + else if (typeof arg === 'function') { + callback = arg; + } } + if (!constraints) constraints = { video: useVideo, audio: useAudio }; const domElement = document.createElement('video'); @@ -2253,6 +2264,9 @@ p5.prototype.createCapture = function(...args) { if (domElement.width) { videoEl.width = domElement.width; videoEl.height = domElement.height; + if (flipped) { + videoEl.elt.style.transform = 'scaleX(-1)'; + } } else { videoEl.width = videoEl.elt.width = domElement.videoWidth; videoEl.height = videoEl.elt.height = domElement.videoHeight; @@ -2261,9 +2275,11 @@ p5.prototype.createCapture = function(...args) { if (callback) callback(domElement.srcObject); }); + return videoEl; }; + /** * Creates a new p5.Element object. * From cc1c63481c23fce62a3b901177f99032a382b621 Mon Sep 17 00:00:00 2001 From: Vishal Date: Wed, 10 Jan 2024 23:57:53 +0530 Subject: [PATCH 2/5] feat:Mirrored video feature is updated --- src/dom/dom.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index a0f245637d..b29be50b4b 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2275,7 +2275,7 @@ p5.prototype.createCapture = function(...args) { if (callback) callback(domElement.srcObject); }); - + videoEl.flipped=flipped; return videoEl; }; @@ -4393,7 +4393,6 @@ class MediaElement extends p5.Element { duration() { return this.elt.duration; } - _ensureCanvas() { if (!this.canvas) { this.canvas = document.createElement('canvas'); @@ -4414,11 +4413,14 @@ class MediaElement extends p5.Element { } this.drawingContext.clearRect( - 0, - 0, - this.canvas.width, - this.canvas.height - ); + 0, 0, this.canvas.width, this.canvas.height); + + if (this.flipped === true) { + this.drawingContext.save(); + this.drawingContext.scale(-1, 1); + this.drawingContext.translate(-this.canvas.width, 0); + } + this.drawingContext.drawImage( this.elt, 0, @@ -4426,6 +4428,11 @@ class MediaElement extends p5.Element { this.canvas.width, this.canvas.height ); + + if (this.flipped === true) { + this.drawingContext.restore(); + } + this.setModified(true); this._frameOnCanvas = this._pInst.frameCount; } From 3e47842c1f57135e643967f202efb6e16bc76ebd Mon Sep 17 00:00:00 2001 From: Vishal Date: Tue, 16 Jan 2024 00:33:00 +0530 Subject: [PATCH 3/5] Updated the doc with example and also worked on the suggestion --- src/dom/dom.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index b29be50b4b..1de0a1c95a 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2135,7 +2135,11 @@ if (navigator.mediaDevices.getUserMedia === undefined) { * W3C documentation for possible properties. Different browsers support different * properties. * - * The second parameter, `callback`, is optional. It's a function to call once + * The 'flipped' property is an optional property which can be set to `{flipped:true}` + * to mirror the video output.If it is true then it means that video will be mirrored + * or flipped and if nothing is mentioned then by default it will be `false`. + * + * The second parameter,`callback`, is optional. It's a function to call once * the capture is ready for use. The callback function should have one * parameter, `stream`, that's a * MediaStream object. @@ -2148,6 +2152,8 @@ if (navigator.mediaDevices.getUserMedia === undefined) { * @param {String|Constant|Object} [type] type of capture, either AUDIO or VIDEO, * or a constraints object. Both video and audio * audio streams are captured by default. + * @param {Object} [flipped] flip the capturing video and mirror the output with `{flipped:true}`. By + * default it is false. * @param {Function} [callback] function to call once the stream * has loaded. * @return {p5.Element} new p5.Element object. @@ -2183,6 +2189,19 @@ if (navigator.mediaDevices.getUserMedia === undefined) { * } * * + *
+ * + * let capture; + * + * function setup() { + * // Create the video capture with mirrored output. + * capture = createCapture(VIDEO,{ flipped:true }); + * capture.size(100,100); + * describe('A video stream from the webcam with flipped or mirrored output.'); + * } + * + * + *
* *
* @@ -2227,11 +2246,12 @@ p5.prototype.createCapture = function(...args) { if (arg === p5.prototype.VIDEO) useAudio = false; else if (arg === p5.prototype.AUDIO) useVideo = false; else if (typeof arg === 'object') { - constraints = arg; - flipped = constraints.flipped || false; - } - else if (typeof arg === 'boolean') { - flipped = arg; + // Check if the argument is an object with a 'flipped' property + if (arg.flipped !== undefined) { + flipped = arg.flipped; + } else { + constraints = arg; + } } else if (typeof arg === 'function') { callback = arg; From 62fae2dc4cd9b25542463ebe2972db079e027d8d Mon Sep 17 00:00:00 2001 From: Vishal Date: Sun, 21 Jan 2024 16:27:52 +0530 Subject: [PATCH 4/5] Enhancment in the Flipped Feature --- src/dom/dom.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 1de0a1c95a..bf8be3a5f4 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2246,24 +2246,22 @@ p5.prototype.createCapture = function(...args) { if (arg === p5.prototype.VIDEO) useAudio = false; else if (arg === p5.prototype.AUDIO) useVideo = false; else if (typeof arg === 'object') { - // Check if the argument is an object with a 'flipped' property if (arg.flipped !== undefined) { flipped = arg.flipped; - } else { - constraints = arg; + delete arg.flipped; } + constraints = Object.assign({}, constraints, arg); } else if (typeof arg === 'function') { callback = arg; } } - if (!constraints) constraints = { video: useVideo, audio: useAudio }; - + const videoConstraints = { video: useVideo, audio: useAudio }; + constraints = Object.assign({}, videoConstraints, constraints); const domElement = document.createElement('video'); // required to work in iOS 11 & up: domElement.setAttribute('playsinline', ''); - navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { try { if ('srcObject' in domElement) { @@ -2271,10 +2269,11 @@ p5.prototype.createCapture = function(...args) { } else { domElement.src = window.URL.createObjectURL(stream); } - } catch (err) { + } + catch(err) { domElement.src = stream; } - }, console.log); + }, console.err); const videoEl = addElement(domElement, this, true); videoEl.loadedmetadata = false; From a300d259dcbcaa8e3e604d05170985e3e8f3c313 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Tue, 27 Feb 2024 11:42:47 -0500 Subject: [PATCH 5/5] Update src/dom/dom.js --- src/dom/dom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index bf8be3a5f4..04f8b33ba7 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -2273,7 +2273,7 @@ p5.prototype.createCapture = function(...args) { catch(err) { domElement.src = stream; } - }, console.err); + }, console.error); const videoEl = addElement(domElement, this, true); videoEl.loadedmetadata = false;