Como gravar um vídeo do usuário

Mat Scales

Atualmente, muitos navegadores têm o recurso de acessar inserções de dados de áudio e vídeo do usuário. No entanto, dependendo do navegador, essa pode ser uma experiência dinâmica e integrada ou delegada a outro aplicativo no dispositivo do usuário.

A coisa mais fácil a se fazer é simplesmente solicitar ao usuário um arquivo pré-gravado. Para isso, crie um elemento de entrada de arquivo simples e adicione um filtro accept que indique que só podemos aceitar arquivos de vídeo e um atributo capture que indique que queremos receber o arquivo diretamente da câmera.

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

Esse método funciona em todas as plataformas. Em computadores, o usuário poderá fazer upload de um arquivo do sistema de arquivos (ignorando o atributo capture). No Safari para iOS, ele vai abrir o app da câmera, permitindo gravar um vídeo e enviar de volta para a página da Web. No Android, ele vai dar ao usuário a opção de escolher qual app usar para gravar o vídeo antes de enviá-lo de volta para a página da Web.

Muitos dispositivos móveis têm mais de uma câmera. Se você tiver uma preferência, defina o atributo capture como user, se quiser a câmera voltada para o usuário, ou environment, se quiser a câmera voltada para fora.

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

Isso é apenas uma dica: se o navegador não oferecer essa opção ou o tipo de câmera solicitado não estiver disponível, o navegador poderá escolher outra câmera.

Quando o usuário terminar a gravação e for redirecionado ao site, você precisa acessar os dados do arquivo de alguma forma. É possível ter acesso rápido anexando um evento onchange ao elemento de entrada e lendo a propriedade files do objeto do 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>

Quando tiver acesso ao arquivo, você poderá fazer o que quiser com ele. Por exemplo, você pode:

  • Anexá-lo diretamente a um elemento <video> para poder reproduzir
  • Fazer o download para o dispositivo do usuário
  • Faça upload dele em um servidor anexando-o a um XMLHttpRequest
  • Desenhar os frames em uma tela e aplicar filtros a eles

Embora usar o método do elemento "input" para acessar dados de vídeo seja universal, é a opção menos vantajosa. Queremos ter acesso à câmera e oferecer uma experiência bacana diretamente na página.

Acessar a câmera de forma interativa

Os navegadores modernos podem ter ligação direta com a câmera, permitindo criar experiências totalmente integradas com a página da Web, sem que o usuário precise sair do navegador.

Obter acesso à câmera

Podemos acessar a câmera diretamente usando uma API na especificação WebRTC chamada getUserMedia(). getUserMedia() vai solicitar ao usuário acesso às câmeras e microfones conectados.

Se receber a autorização, a API retornará um Stream que conterá os dados da câmera ou do microfone. Em seguida, poderemos anexá-los a um elemento <video>, a um stream do WebRTC ou salvá-los usando a API MediaRecorder.

Para acessar dados da câmera, basta definir video: true no objeto de restrições transmitido para a API 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>

Se você quiser escolher uma câmera específica, primeiro enumere as câmeras disponíveis.

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

Em seguida, transmita o deviceId que você quer usar ao chamar getUserMedia.

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

Por si só, isso não é tão útil. Tudo que podemos fazer é pegar os dados do vídeo e reproduzir.

Acessar os dados brutos da câmera

Para acessar os dados brutos de vídeo da câmera, você pode desenhar cada frame em uma <canvas> e manipular os pixels diretamente.

Para uma tela 2D, você pode usar o método drawImage do contexto para desenhar o frame atual de um elemento <video> na tela.

context.drawImage(myVideoElement, 0, 0);

Com uma tela WebGL, é possível usar um elemento <video> como a origem de uma textura.

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

Em ambos os casos, o frame atual de um vídeo em reprodução será usado. Para processar vários frames, você precisa redesenhar o vídeo na tela toda vez.

Para saber mais, consulte nosso artigo sobre como aplicar efeitos em tempo real a imagens e vídeos.

Salvar os dados da câmera

A maneira mais fácil de salvar os dados da câmera é usar a API MediaRecorder.

A API MediaRecorder vai usar o stream criado por getUserMedia e, em seguida, salvar progressivamente os dados do stream no destino escolhido.

<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>

No nosso caso, estamos salvando os dados diretamente em uma matriz que depois podemos transformar em uma Blob que pode ser usada para salvar no nosso servidor da Web ou diretamente no armazenamento no dispositivo do usuário.

Pedir permissão para usar a câmera com responsabilidade

Se o usuário ainda não tiver concedido acesso à câmera ao site, no momento em que você chamar getUserMedia, o navegador vai solicitar que o usuário conceda permissão ao site para usar a câmera.

Os usuários odeiam receber solicitações de acesso a dispositivos avançados na máquina deles e, com frequência, bloqueiam a solicitação ou a ignoram se não entendem o contexto da solicitação. A prática recomendada é só pedir acesso à câmera na primeira vez em que ela for necessária. Depois que o usuário conceder acesso, ele não vai receber mais solicitações de permissão de acesso. No entanto, se ele recusar o acesso, você não poderá solicitar a permissão do usuário de novo.

Use a API de permissões para verificar se você já tem acesso

A API getUserMedia não informa se você já tem acesso à câmera. Isso apresenta um problema. Para fornecer uma boa interface e fazer com que o usuário conceda acesso à câmera, você precisa pedir acesso à câmera.

Esse problema pode ser resolvido em alguns navegadores por meio do uso da Permission API. A API navigator.permission permite consultar o estado da capacidade de acessar APIs específicas sem precisar solicitar novamente.

Para verificar se você tem acesso à câmera do usuário, transmita {name: 'camera'} no método de consulta. Ele vai retornar:

  • granted: o usuário já deu acesso à câmera;
  • prompt: o usuário não deu acesso a você e vai receber uma solicitação quando você chamar getUserMedia.
  • denied: o sistema ou o usuário bloqueou explicitamente o acesso à câmera, e você não poderá ter acesso a ela.

Agora você pode verificar rapidamente se precisa alterar sua interface do usuário para oferecer as ações que o usuário precisa tomar.

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 () {};
});

Feedback