Muitos navegadores agora têm a capacidade de acessar a entrada de vídeo e áudio do usuário. No entanto, dependendo do navegador, pode ser uma experiência dinâmica e inline completa ou pode ser delegada a outro app no dispositivo do usuário.
Comece de forma simples e progressiva
A maneira mais fácil de fazer isso é pedir ao usuário um arquivo pré-gravado. Para fazer 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. No computador, o usuário vai ser solicitado a fazer upload de um arquivo do
sistema de arquivos (ignorando o atributo capture
). No Safari
no iOS, ele vai abrir o app Câmera, permitindo que você grave um vídeo e
o envie de volta para a página da Web. No Android, o usuário
escolhe em qual app gravar o vídeo antes de enviar 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" />
Essa é apenas uma dica. Se o navegador não oferecer suporte à opção ou se 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ê
precisa de alguma forma acessar os dados do arquivo. É possível ter acesso rápido
anexando um evento onchange
ao elemento de entrada e lendo
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ê pode fazer o que quiser com ele. Por exemplo, você pode:
- Anexá-lo diretamente a um elemento
<video>
para que ele possa ser reproduzido - Fazer o download no dispositivo do usuário
- Fazer upload para um servidor anexando a um
XMLHttpRequest
- Desenhar os frames em uma tela e aplicar filtros a ela
Embora o uso do método de elemento de entrada para acessar dados de vídeo seja onibpresente, é a opção menos atraente. Queremos ter acesso à câmera e oferecer uma boa experiência diretamente na página.
Acessar a câmera de forma interativa
Os navegadores modernos podem ter uma linha direta para a câmera, permitindo que criemos experiências totalmente integradas à página da Web, e o usuário nunca sai do navegador.
Adquirir acesso à câmera
É possível acessar a câmera diretamente usando uma API na especificação
do WebRTC chamada getUserMedia()
. O getUserMedia()
vai solicitar ao usuário
acesso aos microfones e câmeras conectados.
Se bem-sucedida, a API vai retornar um Stream
que conterá os dados da câmera ou
do microfone. Em seguida, podemos anexar o elemento a um <video>
, a um stream
do WebRTC ou salvar usando a API MediaRecorder
.
Para receber dados da câmera, definimos video: true
no objeto de restrições
que é 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 é muito útil. Tudo o 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, 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, é 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 de sua preferência.
<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 pode ser transformada
em um Blob
, que pode ser usado para salvar no servidor da Web ou diretamente no
armazenamento do dispositivo do usuário.
Pedir permissão para usar a câmera de forma responsável
Se o usuário não tiver concedido acesso à câmera ao seu site,
assim que você chamar getUserMedia
, o navegador vai pedir ao usuário para
conceder permissão ao site para usar a câmera.
Os usuários odeiam receber solicitações de acesso a dispositivos poderosos na máquina e frequentemente bloqueiam a solicitação ou a ignoram se não entenderam o contexto em que ela foi criada. A prática recomendada é pedir acesso à câmera apenas quando for necessário. Depois que o usuário conceder o acesso, ele não será solicitado novamente. No entanto, se ele rejeitar o acesso, não será possível pedir permissão para o usuário novamente.
Usar 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.
Isso pode ser resolvido em alguns navegadores usando a API Permission. A
API navigator.permission
permite consultar o estado da capacidade de
acessar APIs específicas sem precisar solicitar novamente.
Para consultar se você tem acesso à câmera do usuário, transmita
{name: 'camera'}
ao método de consulta, que vai retornar:
granted
: o usuário já concedeu acesso à câmera;prompt
: o usuário não concedeu acesso a você e será solicitado quando você chamargetUserMedia
.denied
: o sistema ou o usuário bloqueou explicitamente o acesso à câmera, e você não poderá acessá-la.
Agora você pode conferir rapidamente se precisa alterar a 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 () {};
});