Como gravar um vídeo do usuário

Balanças esteira

Agora, muitos navegadores podem acessar entradas de vídeo e áudio do usuário. No entanto, dependendo do navegador, essa pode ser uma experiência dinâmica e in-line completa ou delegada a outro app no dispositivo do usuário.

Comece de forma simples e progressiva

A coisa mais fácil a fazer é simplesmente pedir 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 recebê-los diretamente da câmera.

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

Esse método funciona em todas as plataformas. No computador, o usuário será solicitado a fazer upload de um arquivo do sistema de arquivos (ignorando o atributo capture). No Safari para iOS, o app da câmera será aberto, permitindo que você grave vídeos e envie-os de volta para a página da Web. No Android, isso dará ao usuário a escolha de qual app usar para gravar o vídeo antes de enviá-lo de volta à 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 que a câmera esteja 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" />

Essa é apenas uma dica. Se o navegador não for compatível com essa opção ou o tipo de câmera solicitado não estiver disponível, o navegador poderá escolher outra câmera.

Depois que o usuário terminar a gravação e voltar ao site, você vai precisar conseguir os dados do arquivo. Para ter acesso rápido, anexe um evento onchange ao elemento de entrada e leia a propriedade files do 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>

Depois de ter acesso ao arquivo, você poderá fazer o que quiser com ele. Por exemplo, você pode:

  • Anexar diretamente a um elemento <video> para poder tocar
  • Fazer o download no dispositivo do usuário
  • Faça upload do arquivo para um servidor anexando-o a um XMLHttpRequest
  • Desenhe os frames em uma tela e aplique filtros a ela

Embora o uso do método do elemento de entrada para acessar dados de vídeo seja onipresente, é a opção menos atraente. Queremos ter acesso à câmera e oferecer uma experiência boa diretamente na página.

Acessar a câmera de forma interativa

Os navegadores mais recentes podem ter uma linha direta com a câmera, o que nos permite criar experiências totalmente integradas à página da Web, sem que o usuário nunca saia do navegador.

Conseguir acesso à câmera

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

Se bem-sucedida, a API vai retornar um Stream que vai conter os dados da câmera ou do microfone. Em seguida, é possível anexá-lo a um elemento <video>, anexá-lo a um stream WebRTC ou salvá-lo usando a API MediaRecorder.

Para extrair dados da câmera, basta definir video: true no objeto de restrições que é transmitido à 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 quiser escolher uma câmera específica, primeiro enumera as câmeras disponíveis.

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

Você pode transmitir o deviceId que quer usar ao chamar getUserMedia.

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

Sozinha, isso não é tão útil. Tudo o que podemos fazer é pegar os dados do vídeo e reproduzi-lo.

Acessar os dados brutos da câmera

Para acessar os dados de vídeo brutos da câmera, desenhe cada frame em um <canvas> e manipule os pixels diretamente.

Para uma tela 2D, use 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, você pode 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 todas as vezes.

Saiba mais sobre isso no 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 usa o stream criado por getUserMedia e salva progressivamente os dados do fluxo para o 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>

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

Peça permissão para usar a câmera com responsabilidade

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

Os usuários não gostam de receber solicitações de acesso a dispositivos avançados na máquina e, com frequência, bloqueiam a solicitação ou a ignoram se não entendem o contexto para o qual a solicitação foi criada. A prática recomendada é só pedir acesso à câmera na primeira vez em que ela for necessária. Depois que o usuário concede acesso, ele não precisa fazer isso novamente. No entanto, se ele recusar, não será possível solicitar a permissão novamente.

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 para que o usuário conceda acesso à câmera, você precisa pedir acesso à câmera.

Isso pode ser resolvido em alguns navegadores usando a API Permission. A API navigator.permission permite que você consulte o estado da capacidade de acessar APIs específicas sem ter que solicitar novamente.

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

  • granted: o usuário já deu a você acesso à câmera;
  • prompt: o usuário não deu acesso e será avisado 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 acomodar as ações que o usuário precisa 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 () {};
});

Feedback