Hoje em dia todo computador, notebook ou smartphone tem uma (ou mais) câmera(s).
Antigamente, a única maneira de aproveitar essas câmeras em uma aplicação Web era usando Flash.
Mas, hoje em dia, podemos tirar fotos com JS. Para isso, vamos utilizar o WebRTC.
WebRTC é um padrão Web que define protocolos e APIs em JS para enviar vídeo, áudio e dados entre navegadores de maneira peer-to-peer e em tempo real. Está disponível no Chrome para Desktop e Android, Firefox, Opera e Edge.
Pra começar, um <video> e um <canvas>
Precisamos de uma estrutura mínima no nosso HTML.
A tag <video> com a propriedade autoplay, ficará responsável por mostrar para o usuário as imagens obtidas da câmera.
Para guardar a imagem da foto, vamos usar um <canvas>, que começara invisível.
Finalmente, precisaremos de um <button> para que o usuário informe o momento em que deve ser tirada a foto a partir do vídeo.
Os três elementos mencionados ficarão dentro de uma <div>.
<div class="camera"> <video id="video" class="foto" autoplay>Vídeo não disponível.</video> <canvas id="canvas" class="foto" style="display: none;"></canvas> <button id="tira-foto">Tirar foto</button> </div>
Um pouquinho de css
Para posicionar os elementos e definir uma borda, teremos um pequeno trecho de css:
.camera {
width: 340px;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.5em;
}
.foto {
width: 320px;
height: 240px;
border: 1px solid black;
margin: 1em;
}
Mude o CSS à vontade, para deixar mais estiloso!
Mostrando o vídeo
No nosso código JS, obteremos o elemento cujo id é video:
var video = document.querySelector('#video');
Para obter as imagens da câmera, precisamos usar o método getUserMedia do navigator.
Uma coisa chata é que a API ainda não está consolidada. Por isso, o getUserMedia tem prefixos diferentes para cada navegador:
navigator.webkitGetUserMedia– para o Chrome e Operanavigator.mozGetUserMedia– para o Firefoxnavigator.msGetUserMedia– para o Edge
Testaremos o getUserMedia correto, armazendo-o em uma variável e ajustando o this através de um bind com o objeto navigator:
var getUserMedia = (navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia).bind(navigator);
Ao chamar o getUserMedia, devemos passar 3 parâmetros:
- um objeto de configuração que diz se deve ser obtido vídeo ou áudio da câmera, entre outros detalhes
- uma função chamada quando houve sucesso ao obter mídia da câmera. Nessa função, obtemos um
MediaStream, que devemos passar para a propriedadesrcObjectdo nosso elementovideo. Guardaremos oMediaStreamem uma variável, para uso posterior. - uma função chamada em caso de erro
var configuracaoMedia = {video: true, audio: false};
var mediaStream;
function iniciaVideo (stream) {
video.srcObject = stream;
mediaStream = stream;
}
function trataErroMedia (erro) {
console.error('Erro: ' + erro);
}
getUserMedia(configuracaoMedia, iniciaVideo, trataErroMedia);
O resultado até aqui pode ser encontrado em:
https://jsfiddle.net/alexandreaquiles/xqhLL2wm/1/
Obtendo uma foto
Devemos obter o canvas e o botão a partir de seus ids:
var canvas = document.querySelector('#canvas');
var botaoTiraFoto = document.querySelector('#tira-foto');
Precisamos registrar um tratador do evento de clique no botão.
botaoTiraFoto.addEventListener('click', function (e) {
//tratamento aqui...
});
Dentro da função de tratamento do clique, devemos obter o contexto 2D a partir do canvas e utilizá-lo para desenhar uma imagem a partir do vídeo. Nesse momento, a foto é tirada.
Podemos obter os dados através do método toDataURL do canvas.
botaoTiraFoto.addEventListener('click', function (e) {
canvas.getContext('2d').drawImage(video, 0, 0, 320, 240);
var dados = canvas.toDataURL('image/png');
//fazer algo com os dados...
});
O método toDataURL retorna uma Data URI, uma maneira de representar imagens e outros tipos de dados como um texto. Por exemplo, um .png com um pontinho preto pode ser representado como: data:image/png;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=
Podemos enviar essa Data URI para o servidor com uma chamada AJAX.
Do lado do servidor, você pode gravar a Data URI no BD para uso posterior como src de um <img> ou ainda transformá-lo em um arquivo.
Em PHP, usaríamos
str_replace,base64_decodeefile_put_contentspara salvar a imagem em um arquivo no servidor.
Você pode testar o código até essa parte em:
https://jsfiddle.net/alexandreaquiles/xqhLL2wm/1/
Mostrando a foto e escondendo o vídeo
Quando o usuário clicar o botão, é interessante mostrarmos o <canvas> que tem a imagem da foto tirada e, em seguida, escondermos o vídeo da câmera.
botaoTiraFoto.addEventListener('click', function (e) {
//código omitido...
video.style.display = 'none';
canvas.style.display = '';
});
Ao exibirmos a imagem, percebemos que há uma distorção. Para resolver, é importante fixar a altura e largura do <canvas>:
canvas.width = 320; canvas.height = 240;
A câmera continuará filmando, mas a imagem ficará parada, sendo alterada só quando o usuário clicar no botão.
Você pode acompanhar o resultado em:
https://jsfiddle.net/alexandreaquiles/xqhLL2wm/2/
Parando a câmera ao obter foto
Para suspender a filmagem logo depois do clique no botão, podemos usar o seguinte código:
mediaStream.getVideoTracks().forEach(function (media) {
media.stop();
});
É interessante alternar entre filmagem com a câmera e a exibição da foto tirada. Para isso, alternamos entre exibição do <video> e do <canvas>. Além disso, alternamos entre parar e voltar com o funcionamento da câmera.
botaoTiraFoto.addEventListener('click', function (e) {
if (mediaStream) {
//código omitido...
mediaStream.getVideoTracks().forEach(function (media) {
media.stop();
});
video.style.display = 'none';
canvas.style.display = '';
mediaStream = null;
} else {
getUserMedia(configuracaoMedia, iniciaVideo, trataErroMedia);
video.style.display = '';
canvas.style.display = 'none';
}
O código até aqui está em:
https://jsfiddle.net/alexandreaquiles/xqhLL2wm/3/
Para funcionar no Chrome do Android
Com o código anterior, ocorre um bug quando acessado pelo Chrome do Android: é mostrada uma imagem preta no vídeo.
Para corrigí-lo, é necessário fixar as propriedades opcionais maxWidth e minWidth na configuração de getUserMedia:
var configuracaoMedia = {
video: {
optional: [
{ maxWidth: 320},
{ maxHeight: 240}
]
},
audio: false
};
O código final pode ser encontrado em:
https://jsfiddle.net/alexandreaquiles/xqhLL2wm/4/
Consideração importante: tem que ser HTTPS
Para que o código acima funcione no Chrome e Opera quando publicado em um servidor real, não em sua máquina local, é preciso que seja usado HTTPS.
Para o Firefox não há essa restrição.
Alexandre como sempre muito bom o seu post, Gostei muito da explicação do WebRTC. parabéns.
Bom dia Alexandre, minha câmera está finalizando sozinha e não exibe mensagem, diz no console que está com erro na variável stream, consegue me ajudar?
Olá, Carlos.
Qual o navegador e versão?
Como está exatamente a mensagem de erro?