Grabación de video del usuario

Mat Scales

Ahora muchos navegadores tienen la habilidad de acceder a la entrada de audio y video desde el usuario. Sin embargo, según el navegador puede ser una experiencia en línea y totalmente dinámica o puede ser delegada a otra app en el dispositivo del usuario.

Comienza de forma simple y progresiva

Lo más fácil es simplemente pedirle al usuario un archivo grabado previamente. Para ello, crea un elemento simple de entrada de archivo y agrega un filtro accept que indique que solo podemos aceptar archivos de video y un atributo capture que indique que queremos obtenerlo directamente desde la cámara.

<input type="file" accept="video/*" capture />

Este método funciona en todas las plataformas. En el escritorio, se le solicitará al usuario que suba un archivo desde el sistema de archivos (ignorando el atributo capture). En Safari para iOS, se abrirá la app de la cámara, lo que te permitirá grabar un video y, luego, enviarlo a la página web. En Android, se le dará al usuario la opción de elegir qué app quiere usar para grabar el video antes de enviarlo a la página web.

Muchos dispositivos móviles tienen más de una cámara. Si tienes una preferencia, puedes establecer el atributo capture en user si quieres que la cámara esté orientada hacia el usuario o en environment si quieres que esté orientada hacia afuera.

<input type="file" accept="video/*" capture="user" />
<input type="file" accept="video/*" capture="environment" />

Ten en cuenta que esta es solo una sugerencia. Si el navegador no admite la opción o el tipo de cámara que solicitas no está disponible, es posible que el navegador elija otra cámara.

Una vez que el usuario haya terminado la grabación y esté de vuelta en el sitio web, tienes que adquirir los datos del archivo de algún modo. Para obtener acceso rápido, adjunta un evento onchange al elemento de entrada y, luego, lee la propiedad files del objeto de evento.

<input type="file" accept="video/*" capture="camera" id="recorder" />
<video id="player" controls></video>
<script>
  var recorder = document.getElementById('recorder');
  var player = document.getElementById('player');

  recorder.addEventListener('change', function (e) {
    var file = e.target.files[0];
    // Do something with the video file.
    player.src = URL.createObjectURL(file);
  });
</script>

Una vez que obtienes acceso al archivo, puedes hacer lo que quieras con él. Por ejemplo, puedes hacer lo siguiente:

  • Adjuntarlo directamente a un elemento <video> para que puedas reproducirlo
  • Descárgala en el dispositivo del usuario
  • Sube el archivo a un servidor adjuntándolo a un XMLHttpRequest
  • Dibuja los marcos en un lienzo y aplícale filtros

Si bien el uso del método de elemento de entrada para la obtención de acceso a los datos de video es ubicuo, es la opción menos atractiva. Realmente queremos obtener acceso a la cámara y proporcionar una experiencia agradable directamente en la página.

Accede a la cámara de forma interactiva

Los navegadores modernos pueden tener acceso directo a la cámara, lo cual nos permite compilar experiencias que estén totalmente integradas con la página web, de modo que el usuario nunca tenga que abandonar el navegador.

Adquirir acceso a la cámara

Podemos acceder de forma directa a la cámara usando una API en la especificación WebRTC llamada getUserMedia(). getUserMedia() le solicitará al usuario acceso a sus micrófonos y cámaras conectados.

Si tiene éxito, la API muestra un Stream que contiene datos de la cámara o el micrófono, y luego podemos adjuntarlo a un elemento <video>, a una transmisión de WebRTC o guardarlo con la API de MediaRecorder.

Para obtener datos desde la cámara, simplemente configuramos video: true en el objeto de restricciones que se pasa a la API de getUserMedia().

<video id="player" controls></video>
<script>
  var player = document.getElementById('player');

  var handleSuccess = function (stream) {
    player.srcObject = stream;
  };

  navigator.mediaDevices
    .getUserMedia({audio: true, video: true})
    .then(handleSuccess);
</script>

Si quieres elegir una cámara en particular, primero puedes enumerar las cámaras disponibles.

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'videoinput');
});

Luego, puedes pasar el deviceId que deseas usar cuando llames a getUserMedia.

navigator.mediaDevices.getUserMedia({
  audio: true,
  video: {
    deviceId: devices[0].deviceId,
  },
});

Por sí solo, no es tan útil. Todo lo que podemos hacer es tomar los datos del video y reproducirlo.

Accede a los datos sin procesar desde la cámara

Para acceder a los datos de video sin procesar de la cámara, puedes dibujar cada fotograma en un <canvas> y manipular los píxeles directamente.

Para un lienzo 2D, puedes usar el método drawImage del contexto para dibujar el marco actual de un elemento <video> en el lienzo.

context.drawImage(myVideoElement, 0, 0);

Con un lienzo WebGL, puedes usar un elemento <video> como fuente de una textura.

gl.texImage2D(
  gl.TEXTURE_2D,
  0,
  gl.RGBA,
  gl.RGBA,
  gl.UNSIGNED_BYTE,
  myVideoElement,
);

Ten en cuenta que, en cualquier caso, se usará el fotograma actual de un video que se esté reproduciendo. Para procesar varios fotogramas, debes volver a dibujar el video en el lienzo cada vez.

Puedes obtener más información sobre este tema en nuestro artículo sobre cómo aplicar efectos en tiempo real a imágenes y videos.

Guarda los datos de la cámara

La forma más sencilla de guardar los datos de la cámara es usar la API de MediaRecorder.

La API de MediaRecorder tomará la transmisión creada por getUserMedia y, luego, guardará de forma progresiva los datos de la transmisión en tu destino preferido.

<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
  let shouldStop = false;
  let stopped = false;
  const downloadLink = document.getElementById('download');
  const stopButton = document.getElementById('stop');

  stopButton.addEventListener('click', function() {
    shouldStop = true;
  })

  var handleSuccess = function(stream) {
    const options = {mimeType: 'video/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);

    mediaRecorder.addEventListener('dataavailable', function(e) {
      if (e.data.size > 0) {
        recordedChunks.push(e.data);
      }

      if(shouldStop === true && stopped === false) {
        mediaRecorder.stop();
        stopped = true;
      }
    });

    mediaRecorder.addEventListener('stop', function() {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'acetest.webm';
    });

    mediaRecorder.start();
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: true })
      .then(handleSuccess);
</script>

En nuestro caso, estamos guardando los datos directamente en un array que luego podemos convertir en un Blob y que podemos usar para guardar en nuestro servidor web o directamente en el almacenamiento del dispositivo del usuario.

Pide permiso para usar la cámara de manera responsable

Si el usuario no le otorgó a tu sitio acceso a la cámara con anterioridad, en el instante en que llames a getUserMedia, el navegador le pedirá al usuario que le otorgue permiso a tu sitio para acceder a la cámara.

A los usuarios no les gusta que se les solicite acceso a los dispositivos potentes de su máquina y con frecuencia bloquean la solicitud o la ignoran si no comprenden el contexto del que se creó la instrucción. Es mejor solo pedir acceso a la cámara la primera vez que se necesita. Una vez que el usuario otorgue el acceso, no se le volverá a preguntar. Sin embargo, si lo rechaza, no podrás volver a obtener acceso para pedirle permiso.

Usa la API de permisos para comprobar si ya tienes acceso

La API de getUserMedia no te permite saber si ya tienes acceso a la cámara. Esto te presenta un problema, para proporcionar una buena IU para hacer que el usuario te otorgue acceso a la cámara, tienes que pedir acceso a la cámara.

Esto se puede resolver en algunos navegadores usando la API de Permission. La API de navigator.permission te permite consultar el estado de la capacidad de acceder a APIs específicas sin tener que volver a preguntar.

Para consultar si tienes acceso a la cámara del usuario, puedes pasar {name: 'camera'} al método de consulta y mostrará lo siguiente:

  • granted: El usuario te otorgó acceso a la cámara anteriormente.
  • prompt: El usuario no te otorgó acceso y se lo solicitarán cuando llames a getUserMedia.
  • denied: El sistema o el usuario bloqueó explícitamente el acceso a la cámara y no podrás obtener acceso a ella.

Ahora puedes comprobar rápidamente si necesitas modificar tu interfaz de usuario para que se adapte a las acciones que el usuario debe realizar.

navigator.permissions.query({name: 'camera'}).then(function (result) {
  if (result.state == 'granted') {
  } else if (result.state == 'prompt') {
  } else if (result.state == 'denied') {
  }
  result.onchange = function () {};
});

Comentarios