No desenvolvimento para a Web, o padrão WebRTC fornece APIs para acessar câmeras e microfones conectados ao computador ou smartphone. Esses dispositivos
geralmente são chamados de dispositivos de mídia e podem ser acessados com JavaScript
usando o objeto navigator.mediaDevices
, que implementa a interface
MediaDevices
. Nesse objeto, podemos enumerar todos os dispositivos conectados, detectar
mudanças quando um dispositivo estiver conectado ou desconectado e abri-lo
para recuperar um Media Stream (veja abaixo).
A maneira mais comum de usar isso é 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, faremos 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ão. Se o usuário
aceitar a permissão, a promessa será resolvida com um MediaStream
que contém
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 em documentos da Web da MDN.
Como consultar dispositivos de mídia
Em um aplicativo mais complexo, é provável que você queira verificar todas as câmeras e os microfones conectados e fornecer 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 usá-la para apresentar uma IU ao usuário, que pode
escolher a que preferir. Cada MediaDevicesInfo
contém uma propriedade chamada
kind
com o valor audioinput
, audiooutput
ou videoinput
, indicando
que tipo de dispositivo de mídia ele é.
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 as mudanças nos dispositivos
A maioria dos computadores é compatível com a conexão de vários dispositivos durante o tempo de execução. Pode ser uma webcam conectada por USB, um fone de ouvido Bluetooth ou um conjunto de alto-falantes externos. Para
oferecer compatibilidade adequada com isso, um app da Web precisa detectar as mudanças
de dispositivos de mídia. Isso pode ser feito adicionando um listener a
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
,
transmitido como um parâmetro para getUserMedia()
, permite abrir um
dispositivo de mídia que corresponda a um determinado requisito. Esse requisito pode ser muito flexível (áudio e/ou vídeo) ou muito específico (resolução mínima da câmera ou um ID de dispositivo exato). Recomenda-se que os aplicativos que usam
a API getUserMedia()
verifiquem primeiro os dispositivos existentes e, em seguida, especifique uma
restrição que corresponde exatamente ao dispositivo que usa a restrição deviceId
.
Se possível, os dispositivos também serão configurados de acordo com as restrições. Podemos
ativar o cancelamento de eco nos 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
da MDN.
Reprodução local
Depois que um dispositivo de mídia for aberto e tiver 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
fará com que novos streams atribuídos ao elemento sejam exibidos automaticamente.
O atributo playsinline
permite que o vídeo seja exibido inline, em vez de apenas na tela
completa, em determinados navegadores para dispositivos móveis. Também é recomendado usar
controls="false"
para transmissões ao vivo, a menos que o usuário possa pausá-las.
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>