Cet article est en cours de traduction. :)
En combinant les possibilités de l'élément video
avec celles de l'élément canvas
, vous pouvez manipuler les données vidéos en temps réel, et y incorporer une variété d'effet visuels. Ce tutoriel, adapté de ce blog par Paul Rouget, explique comment réaliser un travail d'incrustation chroma-keying (fond vert) en utilisant JavaScript.
Le document de travail
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="https://www.w3.org/1999/xhtml"> <head> <style> body { background: black; color:#CCCCCC; } #c2 { background-image: url(foo.png); background-repeat: no-repeat; } div { float: left; border :1px solid #444444; padding:10px; margin: 10px; background:#3B3B3B; } </style> <script type="text/javascript;version=1.8" src="main.js"></script> </head> <body onload="processor.doLoad()"> <div> <video id="video" src="video.ogv" controls="true"/> </div> <div> <canvas id="c1" width="160" height="96"/> <canvas id="c2" width="160" height="96"/> </div> </body> </html>
Les éléments clés à retenir sont :
- Ce document dispose de deux balises
canvas
, avec les IDsc1
etc2
. L'élémentc1
est utilisé pour afficher l'image courante de la vidéo originale, pendant quec2
est utilisé pour afficher la vidéo après application de l'effet d'incrustation ;c2
est préchargé avec la même image que celle qui sera utilisée pour le remplacement du fond vert. - Le code JavaScript est importé dans le script nommé
main.js
; Ce script utilises les fonctionnalités propres à la version 1.8, aussi cette version est précisé, à la ligne 22, quand le script est importé. - Quand le document se charge, la méthode
processor.doLoad()
, dans le scriptmain.js
, est appelée.
Le code JavaScript
Le code JavaScript main.js
est composé de trois méthodes.
Initialisation du lecteur avec effet d'incrustation (chroma-key)
La métode doLoad()
est appelée quand le document XHTML se charge. Cette méthode sert à initialiser chaque variable nécessaire au code traitant l'incrustation (chroma-key), ainsi qu'à associer un écouteur d'évènement qui détectera le moment où l'utilisateur lancera la vidéo.
doLoad: function() { this.video = document.getElementById("video"); this.c1 = document.getElementById("c1"); this.ctx1 = this.c1.getContext("2d"); this.c2 = document.getElementById("c2"); this.ctx2 = this.c2.getContext("2d"); let self = this; this.video.addEventListener("play", function() { self.width = self.video.videoWidth / 2; self.height = self.video.videoHeight / 2; self.timerCallback(); }, false); },
Le code récupère les références aux élément XHTML qui nous intéressent, à savoir l'élément video
et les deux éléments canvas
. Il définit également les contextes graphique de chacun des éléments canvas
. Ce sera utile pour la suite, lorsque nous créérons l'effet d'incrustation.
Ensuite, l'écouteur d'évènement addEventListener()
est appelé sur l'élément video
pour détecter le moment où l'utilisateur va cliquer sur le bouton de lecture. Dès lors, le code récupère la hauteur et la largeur de la vidéo, que l'on divise par deux (nécessaire pour plus tard effectuer l'effet d'incrustation), puis on appelle la méthode timerCallback()
pour surveiller l'avancement de la vidéo et appliquer l'effet visuel.
The timer callback
The timer callback is called initially when the video starts playing (when the "play" event occurs), then takes responsibility for establishing itself to be called periodically in order to launch the keying effect for each frame.
timerCallback: function() { if (this.video.paused || this.video.ended) { return; } this.computeFrame(); let self = this; setTimeout(function () { self.timerCallback(); }, 0); },
The first thing the callback does is check to see if the video is even playing; if it's not, the callback returns immediately without doing anything.
Then it calls the computeFrame()
method, which performs the chroma-keying effect on the current video frame.
The last thing the callback does is call setTimeout()
to schedule itself to be called again as soon as possible. In the real world, you would probably schedule this to be done based on knowledge of the video's frame rate.
Manipulation des données vidéo (frame)
La méthode computeFrame()
, présentée ci-dessous, est en charge de récupérer les données de chaque image et d'y appliquer l'effet d'incrustation.
computeFrame: function() { this.ctx1.drawImage(this.video, 0, 0, this.width, this.height); let frame = this.ctx1.getImageData(0, 0, this.width, this.height); let l = frame.data.length / 4; for (let i = 0; i < l; i++) { let r = frame.data[i * 4 + 0]; let g = frame.data[i * 4 + 1]; let b = frame.data[i * 4 + 2]; if (g > 100 && r > 100 && b < 43) frame.data[i * 4 + 3] = 0; } this.ctx2.putImageData(frame, 0, 0); return; }
Quand la routine est appelée, l'élément vidéo affiche l'image (frame) la plus récente des données vidéo, ce qui ressemble à :
A la seconde ligne, cette image est copiée dans le contexte graphique ctx1
du premier élément canvas
, en spécifiant sa hauteur et largeur, définie plus tôt (soit réduite de moitié). Notez que c'est très simplement que vous passez les données de l'élément vidéo à afficher sur le contexte graphique dans la méthode drawImage()
. Voici ce que cela donne :
Line 3 fetches a copy of the raw graphics data for the current frame of video by calling the getImageData()
method on the first context. This provides raw 32-bit pixel image data we can then manipulate. Line 4 computes the number of pixels in the image by dividing the total size of the frame's image data by four.
The for
loop that begins on line 6 scans through the frame's pixels, pulling out the red, green, and blue values for each pixel, and compares the values against predetermined numbers that are used to detect the green screen that will be replaced with the still background image imported from foo.png
.
Every pixel in the frame's image data that is found that is within the parameters that are considered to be part of the green screen has its alpha value replaced with a zero, indicating that the pixel is entirely transparent. As a result, the final image has the entire green screen area 100% transparent, so that when it's drawn into the destination context in line 13, the result is an overlay onto the static backdrop.
The resulting image looks like this:
This is done repeatedly as the video plays, so that frame after frame is processed and displayed with the chroma-key effect.