Ao desenvolver para a Web, o padrão WebRTC fornece APIs para acessar
câmeras e microfones conectados ao computador ou smartphone. Eles
são geralmente chamados de dispositivos de mídia e podem ser acessados com JavaScript
pelo objeto navigator.mediaDevices
, que implementa a interface
MediaDevices
. A partir desse objeto, podemos enumerar todos os dispositivos conectados, detectar
mudanças de dispositivo (quando um dispositivo está conectado ou desconectado) e abrir um dispositivo
para recuperar um streaming de mídia (veja abaixo).
A maneira mais comum de usá-la é pela função getUserMedia()
, que retorna uma promessa que será resolvida como um MediaStream
para os dispositivos de mídia correspondentes. Essa função usa um único objeto MediaStreamConstraints
que especifica os requisitos que temos. Por exemplo, para simplesmente abrir o
microfone e a câmera padrão, faríamos o seguinte.
Como usar promessas
const constraints = {
'video': true,
'audio': true
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('Got MediaStream:', stream);
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
Como usar async/await
const openMediaDevices = async (constraints) => {
return await navigator.mediaDevices.getUserMedia(constraints);
}
try {
const stream = openMediaDevices({'video':true,'audio':true});
console.log('Got MediaStream:', stream);
} catch(error) {
console.error('Error accessing media devices.', error);
}
A chamada para getUserMedia()
acionará uma solicitação de permissões. Se o usuário
aceitar a permissão, a promessa será resolvida com um MediaStream
contendo
um vídeo e uma faixa de áudio. Se a permissão for negada, uma
PermissionDeniedError
será gerada. Caso não haja dispositivos correspondentes
conectados, uma NotFoundError
será gerada.
A referência completa da API para a interface MediaDevices
está disponível nos documentos da Web do MDN.
Como consultar dispositivos de mídia
Em um aplicativo mais complexo, provavelmente é melhor verificar todas as
câmeras e microfones conectados e fornecer o feedback adequado ao
usuário. Para isso, chame a função enumerateDevices()
. Isso retornará uma promessa que é resolvida em uma matriz de MediaDevicesInfo
que descreve cada dispositivo de mídia conhecido. Podemos usar isso para apresentar uma interface ao usuário e permitir
que ele escolha a que preferir. Cada MediaDevicesInfo
contém uma propriedade chamada
kind
com o valor audioinput
, audiooutput
ou videoinput
, indicando
o tipo de dispositivo de mídia.
Como usar promessas
function getConnectedDevices(type, callback) {
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const filtered = devices.filter(device => device.kind === type);
callback(filtered);
});
}
getConnectedDevices('videoinput', cameras => console.log('Cameras found', cameras));
Como usar async/await
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
const videoCameras = getConnectedDevices('videoinput');
console.log('Cameras found:', videoCameras);
Detectar mudanças em dispositivos
A maioria dos computadores oferece suporte a vários dispositivos durante a execução. Pode ser uma
webcam conectada por USB, um fone de ouvido Bluetooth ou um conjunto de alto-falantes externos. Para oferecer
suporte adequado, um aplicativo da Web precisa detectar as mudanças
de dispositivos de mídia. Isso pode ser feito adicionando um listener ao
navigator.mediaDevices
para o evento devicechange
.
// Updates the select element with the provided set of cameras
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.map(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
}).forEach(cameraOption => listElement.add(cameraOption));
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Get the initial set of cameras connected
const videoCameras = getConnectedDevices('videoinput');
updateCameraList(videoCameras);
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('video');
updateCameraList(newCameraList);
});
Restrições de mídia
O objeto de restrições, que precisa implementar a interface MediaStreamConstraints
,
que é transmitida como um parâmetro para getUserMedia()
, permite abrir um
dispositivo de mídia que corresponde a um determinado requisito. Esse requisito pode ser definido de forma muito
vaga (áudio e/ou vídeo) ou muito específico (resolução mínima da câmera
ou um ID exato do dispositivo). É recomendável que os aplicativos que usam
a API getUserMedia()
verifiquem primeiro os dispositivos atuais e, em seguida, especifiquem uma
restrição que corresponda ao dispositivo exato usando a restrição deviceId
.
Os dispositivos também, se possível, serão configurados de acordo com as restrições. Podemos
ativar o cancelamento de eco em microfones ou definir uma largura e altura específicas ou mínimas
para o vídeo da câmera.
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Open camera with at least minWidth and minHeight capabilities
async function openCamera(cameraId, minWidth, minHeight) {
const constraints = {
'audio': {'echoCancellation': true},
'video': {
'deviceId': cameraId,
'width': {'min': minWidth},
'height': {'min': minHeight}
}
}
return await navigator.mediaDevices.getUserMedia(constraints);
}
const cameras = getConnectedDevices('videoinput');
if (cameras && cameras.length > 0) {
// Open first available video camera with a resolution of 1280x720 pixels
const stream = openCamera(cameras[0].deviceId, 1280, 720);
}
A documentação completa da interface MediaStreamConstraints
pode ser encontrada
nos documentos da Web
MDN.
Reprodução local
Assim que um dispositivo de mídia for aberto e tivermos um MediaStream
disponível, poderemos
atribuí-lo a um elemento de vídeo ou áudio para reproduzir o stream localmente.
async function playVideoFromCamera() {
try {
const constraints = {'video': true, 'audio': true};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const videoElement = document.querySelector('video#localVideo');
videoElement.srcObject = stream;
} catch(error) {
console.error('Error opening video camera.', error);
}
}
O HTML necessário para um elemento de vídeo típico usado com getUserMedia()
geralmente terá os atributos autoplay
e playsinline
. O atributo autoplay
vai fazer com que os novos streams atribuídos ao elemento sejam reproduzidos automaticamente.
O atributo playsinline
permite que o vídeo seja reproduzido inline, em vez de apenas em tela
inteira, em determinados navegadores para dispositivos móveis. Também é recomendável usar
controls="false"
para transmissões ao vivo, a menos que o usuário possa
pausar as transmissões.
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>