Juntamente com o HTML5 foram introduzidas as APIs com acesso aos dispositivos de hardware, incluindo a API MediaDevices. Essa API fornece acesso aos dispositivos de entrada de mídia como áudio e vídeo.
Com a ajuda dessa API, os desenvolvedores podem acessar dispositivos de áudio e vídeo para transmitir e exibir feeds de vídeo ao vivo no navegador. Neste tutorial, você irá acessar o feed de vídeo do dispositivo do usuário e exibi-lo no navegador usando o método getUserMedia
.
A API getUserMedia
utiliza os dispositivos de entrada de mídia para produzir um MediaStream (transmissão de mídia). Esse MediaStream contém os tipos de mídia solicitados, seja áudio ou vídeo. Ao usar a transmissão retornada da API, é possível exibir os feeds de vídeo no navegador, o que é útil na comunicação em tempo real no navegador.
Quando usado em conjunto com a API de gravação do MediaStream, é possível gravar e armazenar os dados de mídia capturados no navegador. Essa API só funciona em origens seguras assim como as APIs recentemente introduzidas, mas também funciona no localhost
e URLs de arquivos.
Este tutorial irá explicar inicialmente alguns conceitos e demonstrar exemplos com o Codepen. No passo final, você irá criar um feed de vídeo funcional para o navegador.
Primeiro, você verá como verificar se o navegador do usuário oferece suporte à API mediaDevices
. Essa API existe dentra da interface do navegador e contém o estado atual e a identidade do agente do usuário. A verificação é realizada com o código a seguir que pode ser colado no Codepen:
if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
console.log("Let's get this party started")
}
Primeiro, ele verifica se a API mediaDevices
existe dentro de navigator
(navegador) e então verifica se a API getUserMedia
está disponível dentro dos mediaDevices
. Se o comando retorna true
, podemos iniciar.
Depois de confirmar que o navegador dá suporte à getUserMedia
, é necessário solicitar a permissão para utilizar os dispositivos de entrada de mídia no agente do usuário. Normalmente, depois que o usuário concede a permissão, uma Promise
é retornada e resolve para uma transmissão de mídia. Essa Promise
não é retornada quando a permissão é negada pelo usuário, bloqueando o acesso a esses dispositivos.
Cole a linha a seguir no Codepen para solicitar a permissão:
navigator.mediaDevices.getUserMedia({video: true})
O objeto fornecido como um argumento para o método getUserMedia
chama-se constraints
(restrições). Ele determina quais os dispositivos de entrada de mídia os quais você está solicitando permissão para acessar. Por exemplo, se o objeto contém audio: true
, o usuário será solicitado a conceder acesso ao dispositivo de entrada de áudio.
Esta seção irá abordar o conceito geral de contraints
. O objeto constraints
é um objeto MediaStreamConstraints
que especifica os tipos de mídia para solicitar e os requisitos de cada tipo de mídia. É possível especificar os requisitos para a transmissão solicitada usando o objeto constraints
, como a resolução da transmissão a ser usada (front
, back
).
É necessário especificar audio
ou video
ao fazer a solicitação. Um NotFoundError
será retornado caso os tipos de mídia solicitados não possam ser encontrados no navegador do usuário.
Se você pretende solicitar uma transmissão de vídeo de resolução 1280 x 720
, atualize o objeto constraints
para que fique assim:
{
video: {
width: 1280,
height: 720,
}
}
Com essa atualização, o navegador tentará utilizar as configurações de qualidade especificadas para a transmissão. Se o dispositivo de vídeo não puder entregar essa resolução, o navegador retornará outras resoluções disponíveis.
Para garantir que o navegador retorne uma resolução que não seja inferior àquela fornecida, será necessário utilizar a propriedade min
. Aqui está como atualizar o objeto constraints
para incluir a propriedade min
:
{
video: {
width: {
min: 1280,
},
height: {
min: 720,
}
}
}
Isso irá garantir que a resolução da transmissão retornada seja de pelo menos 1280 x 720
. Caso esse requisito mínimo não possa ser atendido, a promessa será rejeitada com um OverconstrainedError
.
Em alguns casos, você pode ter a preocupação de salvar dados e precisa que a transmissão não ultrapasse uma determinada resolução. Isso pode ser útil nos casos em que o usuário esteja em um plano limitado. Para habilitar essa funcionalidade, atualize o objeto de restrições para que contenha um campo max
:
{
video: {
width: {
min: 1280,
max: 1920,
},
height: {
min: 720,
max: 1080
}
}
}
Com essas configurações, o navegador irá garantir que a transmissão de retorno não tenha resolução inferior a 1280 x 720
nem superior a 1920 x 1080
.
Outros termos que podem ser utilizados incluem exact
e ideal
. A configuração ideal
é normalmente usada juntamente com as propriedades min
e max
para encontrar a melhor resolução possível, o mais perto dos valores ideais fornecidos.
Atualize as restrições para incluir a palavra-chave ideal
:
{
video: {
width: {
min: 1280,
ideal: 1920,
max: 2560,
},
height: {
min: 720,
ideal: 1080,
max: 1440
}
}
}
Para fazer o navegador usar a câmera frontal ou traseira (em portáteis) nos dispositivos, especifique uma propriedade facingMode
no objeto video
:
{
video: {
width: {
min: 1280,
ideal: 1920,
max: 2560,
},
height: {
min: 720,
ideal: 1080,
max: 1440
},
facingMode: 'user'
}
}
Essa configuração irá utilizar a câmera frontal o tempo todo em todos os dispositivos. Para utilizar a câmera traseira em dispositivos móveis, altere a propriedade facingMode
para environment
.
{
video: {
...
facingMode: {
exact: 'environment'
}
}
}
enumerateDevices
Quando o método enumerateDevices
é chamado, ele retorna todos os dispositivos de entrada de mídia disponíveis no PC do usuário.
Com esse método, é possível oferecer opções ao usuário sobre qual dispositivo de entrada de mídia usar para a transmissão de conteúdo de áudio ou vídeo. Esse método retorna uma Promise
resolvida para uma matriz MediaDeviceInfo contendo informações sobre cada dispositivo.
Um exemplo de como utilizar esse método é mostrado no trecho abaixo:
async function getDevices() {
const devices = await navigator.mediaDevices.enumerateDevices();
}
Uma amostra de resposta para cada um dos dispositivos se pareceria com a seguinte:
{
deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
kind: "audiooutput",
label: "",
}
Nota: um rótulo não será retornado a menos que uma transmissão esteja disponível, ou se o usuário tenha concedido permissões de acesso ao dispositivo.
Até aqui, você passou pelo processo de solicitar e ganhar acesso aos dispositivos de mídia, configurou restrições para incluir as resoluções necessárias e selecionou a câmera que será utilizada para gravar o vídeo.
Depois de todos esses passos, você irá pelo menos querer ver se a transmissão está sendo realizada com base nas configurações definidas. Para garantir isso, o elemento <video>
será usado para exibir a transmissão de vídeo no navegador.
Como mencionado anteriormente, o método getUserMedia
retorna uma Promise
que pode ser resolvida para uma transmissão. A transmissão retornada pode ser convertida em uma URL de objeto usando o método createObjectURL
. Essa URL será definida como uma fonte de vídeo.
Você irá criar uma pequena demonstração na qual deixamos o usuário escolher de sua lista de dispositivos de vídeo disponíveis usando o método enumerateDevices
.
Este é um método navigator.mediaDevices
. Ele lista os dispositivos de mídia disponíveis, como microfones e câmeras. Depois retorna uma Promise
resolvida para uma matriz de objetos detalhando os dispositivos de mídia disponíveis.
Crie um arquivo index.html
e atualize o conteúdo com o código abaixo:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div class="display-cover">
<video autoplay></video>
<canvas class="d-none"></canvas>
<div class="video-options">
<select name="" id="" class="custom-select">
<option value="">Select camera</option>
</select>
</div>
<img class="screenshot-image d-none" alt="">
<div class="controls">
<button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
<button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
<button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
</div>
</div>
<script src="https://unpkg.com/feather-icons"></script>
<script src="script.js"></script>
</body>
</html>
No trecho acima, foram configurados os elementos que serão necessários e alguns controles para o vídeo. Também foi incluído um botão para tirar capturas de tela do feed de vídeo atual.
Agora, vamos adicionar um pouco de estilo a esses componentes.
Crie um arquivo style.css
e copie os estilos a seguir nele. O Bootstrap foi incluído para reduzir a quantidade de CSS que você precisará escrever para que os componentes sejam iniciados.
.screenshot-image {
width: 150px;
height: 90px;
border-radius: 4px;
border: 2px solid whitesmoke;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
position: absolute;
bottom: 5px;
left: 10px;
background: white;
}
.display-cover {
display: flex;
justify-content: center;
align-items: center;
width: 70%;
margin: 5% auto;
position: relative;
}
video {
width: 100%;
background: rgba(0, 0, 0, 0.2);
}
.video-options {
position: absolute;
left: 20px;
top: 30px;
}
.controls {
position: absolute;
right: 20px;
top: 20px;
display: flex;
}
.controls > button {
width: 45px;
height: 45px;
text-align: center;
border-radius: 100%;
margin: 0 6px;
background: transparent;
}
.controls > button:hover svg {
color: white !important;
}
@media (min-width: 300px) and (max-width: 400px) {
.controls {
flex-direction: column;
}
.controls button {
margin: 5px 0 !important;
}
}
.controls > button > svg {
height: 20px;
width: 18px;
text-align: center;
margin: 0 auto;
padding: 0;
}
.controls button:nth-child(1) {
border: 2px solid #D2002E;
}
.controls button:nth-child(1) svg {
color: #D2002E;
}
.controls button:nth-child(2) {
border: 2px solid #008496;
}
.controls button:nth-child(2) svg {
color: #008496;
}
.controls button:nth-child(3) {
border: 2px solid #00B541;
}
.controls button:nth-child(3) svg {
color: #00B541;
}
.controls > button {
width: 45px;
height: 45px;
text-align: center;
border-radius: 100%;
margin: 0 6px;
background: transparent;
}
.controls > button:hover svg {
color: white;
}
O próximo passo é adicionar funcionalidade à demonstração. Usando o método enumerateDevices
, você irá obter os dispositivos de vídeo disponíveis e os definirá como opções dentro do elemento selecionado. Crie um arquivo chamado script.js
e atualize-o com o seguinte trecho:
feather.replace();
const controls = document.querySelector('.controls');
const cameraOptions = document.querySelector('.video-options>select');
const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const screenshotImage = document.querySelector('img');
const buttons = [...controls.querySelectorAll('button')];
let streamStarted = false;
const [play, pause, screenshot] = buttons;
const constraints = {
video: {
width: {
min: 1280,
ideal: 1920,
max: 2560,
},
height: {
min: 720,
ideal: 1080,
max: 1440
},
}
};
const getCameraSelection = async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
const options = videoDevices.map(videoDevice => {
return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
});
cameraOptions.innerHTML = options.join('');
};
play.onclick = () => {
if (streamStarted) {
video.play();
play.classList.add('d-none');
pause.classList.remove('d-none');
return;
}
if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
const updatedConstraints = {
...constraints,
deviceId: {
exact: cameraOptions.value
}
};
startStream(updatedConstraints);
}
};
const startStream = async (constraints) => {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleStream(stream);
};
const handleStream = (stream) => {
video.srcObject = stream;
play.classList.add('d-none');
pause.classList.remove('d-none');
screenshot.classList.remove('d-none');
streamStarted = true;
};
getCameraSelection();
No trecho acima, algumas coisas estão acontecendo. Vamos dividi-las:
feather.replace()
: essa chamada de método cria uma instância de feather, que é um ícone definido para o desenvolvimento Web.constraints
contém a configuração inicial para a transmissão. Ela será estendida para incluir o dispositivo de mídia escolhido pelo usuário.getCameraSelection
: essa função chama o método enumerateDevices
. Em seguida, você filtra a matriz gerada a partir da Promise
resolvida e seleciona os dispositivos de entrada de vídeo. A partir dos resultados filtrados, você cria <option>
para o elemento <select>
.getUserMedia
acontece dentro do ouvinte onclick
do botão play
. Aqui, você irá verificar se esse método é suportado pelo navegador do usuário antes de iniciar a transmissão.startStream
que recebe um argumento constraints
. Ela chama o método getUserMedia
com as constraints
fornecidas. O handleStream
é chamado usando a transmissão da Promise
resolvida. Esse método define a transmissão retornada para o srcObject
do elemento de vídeo.Em seguida, você irá adicionar um listener de clique aos controles dos botões na página para pause
, stop
e tirar screenshots
. Além disso, você irá adicionar um listener ao elemento <select>
para atualizar as restrições da transmissão com o dispositivo de vídeo selecionado.
Atualize o arquivo script.js
com o código abaixo:
...
cameraOptions.onchange = () => {
const updatedConstraints = {
...constraints,
deviceId: {
exact: cameraOptions.value
}
};
startStream(updatedConstraints);
};
const pauseStream = () => {
video.pause();
play.classList.remove('d-none');
pause.classList.add('d-none');
};
const doScreenshot = () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
screenshotImage.src = canvas.toDataURL('image/webp');
screenshotImage.classList.remove('d-none');
};
pause.onclick = pauseStream;
screenshot.onclick = doScreenshot;
Agora, quando ao se abrir o arquivo index.html
no navegador, clicar no botão Play irá iniciar a transmissão.
Aqui está uma demonstração completa:
https://codepen.io/chrisbeast/pen/ebYwpX
Esse tutorial introduziu a API getUserMedia
. É uma adição interessante ao HTML5 que facilita o processo de captura de mídia na Web.
A API recebe um parâmetro (constraints
) que pode ser usado para configurar o acesso aos dispositivos de entrada de áudio e vídeo. Ela também pode ser usada para especificar a resolução de vídeo necessária para o seu aplicativo.
É possível estender a demonstração ainda mais para dar ao usuário uma opção para salvar as capturas de tela feitas, bem como gravar e armazenar dados de vídeo e áudio com a ajuda da API MediaStream Recording.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!