Mostrando múltiples instancias de nuestra webcam

Hace unas semanas, a raiz del artículo "Controlando nuestra WebCam con HTML5", me comentaron si sería posible mostrar más de una vista de nuestra camara web en la misma página. Pues bueno, más vale tarde quen nunca... ¡aquí va un ejemplo!

Antes de nada, debemos saber que actualmente los navegadores no permiten mostrar más de una instancia de nuestra cámara por medio del elemento video. Sin embargo podemos recurrir a la librería canvas para lograr el efecto.

He actualizado el código del ejemplo que preparé en su momento de manera que podéis encotrarlo en el mismo repositorio de GitHub (para acceder al código inicial hay que hacer un checkout al tag "1.0.0").

En este caso muestro cuatro "instancias" de nuestra cámara web con la finalidad de crear un efecto "warholiano". En ese aspecto este ejemplo no es del todo nexacto (el código necesario se salía de la finalidad de este artículo) pero podéis encontrar un proyecto perfecto en este enlace.

Voy a entrar en detalle sobre las modificaciones que he realizado desde la última vez, por lo que para entender mejor el código podéis empezar por el artículo anterior.

La estructura del proyecto se mantiene a excepción de la carpeta css, la cual he añadido para alojar los estilos. Como siempre, comenzaré por el fichero index.html:

<!-- index.html -->

<!DOCTYPE html>
<html lang="es">
    <head>
        <meta charset="UTF-8">
        <title>Multiple Webcam Example App</title>
        <link rel="

nstylesheet" href="css/main.css">
        <script src="js/jquery.js"></script>
        <script src="js/main.js"></script>
    </head>
    <body>
        <video id="video" width="450" height="368" autoplay="autoplay"></video>
        <table>
            <tr>
                <td><canvas id="canvas_1" width="450" height="368"></canvas></td>
                <td><canvas id="canvas_2" width="450" height="368"></canvas></td>
            </tr>
            <tr>
                <td><canvas id="canvas_3" width="450" height="368"></canvas></td>
                <td><canvas id="canvas_4" width="450" height="368"></canvas></td>
            </tr>
        </table>
        <button id="photo">Take a Photo!</button>
    </body>
</html>

Como veréis no hay r mucho que decir aquí. En las cabeceras llamo a los ficheros de javascript (jquery) y css. A continuación coloco el elemento video que será quien maneje la vista de nuestra cámara y una tabla que contiene los cuatro lienzos canvas sobre los que proyectaremos la imagen del vídeo. Al final he colocado un botón que congelará/descongelará las imagenes.

En la hoja de estilos main.css oculto el elemento video de manera que solo serán visibles los lienzos canvas.

Y ahora, muy brevemente, el fichero main.js:

// js/main.js

$(document).ready
(
    function()
    {

        var is_playing = true,
            canvas_1 = $('#canvas_1'),
            canvas_2 = $('#canvas_2'),
            canvas_3 = $('#canvas_3'),
            canvas_4 = $('#canvas_4'),
            cxt_1 = canvas_1[0].getContext('2d'),
            cxt_2 = canvas_2[0].getContext('2d'),
            cxt_3 = canvas_3[0].getContext('2d'),
    
   
     cxt_4 = canvas_4[0].getContext('2d'),
            video = $('#video'),
            video = video[0],
            red   = 1,
            green = 2,
            blue  = 3;

        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { 'video': true },
                function(stream)
                {
                    video.src = stream;
                    video.play();
                }
            );
        } else if (navigator.webkitGetUserMedia) {
            navigator.webkitGetUserMedia
            (
                { 'video': true },
                function(stream)
                {
                    video.src = window.webkitURL.createObjectURL(stream);
                    video.play();
                }
            );
        } else if (navigator.mozGetUserMedia) {
            navigator.mozGetUserMedia
            (
                { 'video': true },
      
       
   function(stream)
                {
                    video.mozSrcObject = stream;
                    video.play();
                },
                function(err)
                {
                    alert('An error occured! '+err);
                }
            );
        }
        $('#photo').click
        (
            function()
            {
                is_playing = !is_playing;
            }
        );
        function setTone(context, canvas, color_unchange)
        {
            var imgData = context.getImageData(0, 0, canvas.width, canvas.height),
                pixels  = imgData.data,
                i = 0,
                grayscale;
            for (i, n = pixels.length; i < n; i += 4) {
                grayscale = pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
                switch (color_unchange) {
                    case red:
                        pixels[i+1] = 
grayscale;
    
                    pixels[i+2] = grayscale;
                        break;
                    case green:
                        pixels[i  ] = grayscale;
                        pixels[i+2] = grayscale;
                        break;
                    case blue:
                        pixels[i  ] = grayscale;
                        pixels[i+1] = grayscale;
                        break;
                    default:
                        pixels[i  ] = grayscale;
                        pixels[i+1] = grayscale;
                        pixels[i+2] = grayscale;
                        break;
                
                }
            }
            context.putImageData(imgData, 0, 0);
        }
        window.setInterval
        (
            function()
            {
                if (is_playing) {
                    cxt_1.drawImage(video, 0, 0, 450, 368);
                    cxt_2.drawImage(video, 
0, 0, 450, 368);
   
                 cxt_3.drawImage(video, 0, 0, 450, 368);
                    cxt_4.drawImage(video, 0, 0, 450, 368);
                    setTone(cxt_1, canvas_1[0]);
                    setTone(cxt_2, canvas_2[0], red);
                    setTone(cxt_3, canvas_3[0], green);
                    setTone(cxt_4, canvas_4[0], blue);
                }
            },
            100
        );

    }
);

El truco de todo esta en el método setInterval del objecto window de manera que cada 100 milisegundos pinto (método drawImage de la librería canvas) en los cuatro lienzos el contenido del elemento video:

window.setInterval
(
    function()
    {
        if (is_playing) {
            cxt_1.drawImage(video, 0, 0, 450, 368);
            cxt_2.drawImage(video, 0, 0, 450, 368);
            cxt_3.drawImage(video, 0, 
0, 450, 368);
           
 cxt_4.drawImage(video, 0, 0, 450, 368);
            setTone(cxt_1, canvas_1[0]);
            setTone(cxt_2, canvas_2[0], red);
            setTone(cxt_3, canvas_3[0], green);
            setTone(cxt_4, canvas_4[0], blue);
        }
    },
    100
);

Además llamo a la función setTone que altera los pixeles de esto cuatro lienzos con el objetivo de mostrar una tonalidad diferente en cada uno de ellos. Esta función se deriva del código que encontré aquí.

Me he servido de la variable booleana is_playing, de forma que al pinchar en el botón altero su valor, pudiendo de esta manera parar/reanudar la grabación del video (obviamente la cámara nunca deja de grabar):

$('#photo').click
(
    function()
    {
        is_playing = !is_
playing;
    }
);
r

Ahora que vuelvo a echar un ojo a caniuse, la verdad es que el soporte para la API getUserMedia/Stream sigue sin estar muy extendido (el único navegador móvil que lo soporta es el de Opera). ¿Tal vez despegue ahora que los websockets empiezan a extenderse?

Comments

You must sing in to post a comment.

There are no comments for this article.