No desenvolvimento para a Web, o padrão WebRTC fornece APIs para acessar
câmeras e microfones conectados ao computador ou smartphone. Esses dispositivos
são normalmente chamados de dispositivos de mídia e podem ser acessados com JavaScript
usando o objeto navigator.mediaDevices
, que implementa o MediaDevices
interface gráfica do usuário. A partir desse objeto, podemos enumerar todos os dispositivos conectados, detectar
mudanças no dispositivo (quando um dispositivo é conectado ou desconectado) e abre um dispositivo
para recuperar um fluxo de mídia (veja abaixo).
A forma mais comum é pela função getUserMedia()
, que
retorna uma promessa que se resolve em uma MediaStream
para a mídia correspondente
dispositivos. Essa função usa um único objeto MediaStreamConstraints
que
especifica os requisitos que temos. Por exemplo, para simplesmente abrir
microfone e 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()
vai 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, um
Uma PermissionDeniedError
é gerada. Caso não haja dispositivos correspondentes
conectado, uma NotFoundError
será gerada.
A referência completa da API para a interface MediaDevices
está disponível em MDN Web
docs.
Como consultar dispositivos de mídia
Em um aplicativo mais complexo, provavelmente vamos verificar todos os
câmeras e microfones conectados e fornecer o feedback apropriado para o
usuário. Para isso, chame a função enumerateDevices()
. Isso vai
retorna uma promessa que é resolvida em uma matriz de MediaDevicesInfo
que descreve
para cada dispositivo de mídia conhecido. Podemos usar isso para apresentar uma interface ao usuário
eles escolhem o que preferem. 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 nos dispositivos
A maioria dos computadores aceita a conexão de vários dispositivos durante o tempo de execução. Pode ser um
webcam conectada por USB, um fone de ouvido Bluetooth ou um conjunto de alto-falantes externos. Em
para oferecer um suporte adequado, um aplicativo da Web deve detectar as alterações
de dispositivos portáteis de mídia. Isso pode ser feito adicionando um listener
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 MediaStreamConstraints
.
interface, que transmitimos como um parâmetro para getUserMedia()
nos permite abrir uma
dispositivo de mídia que corresponde a um determinado requisito. Esse requisito pode ser muito
vagamente definida (áudio e/ou vídeo) ou muito específica (mínimo de câmera
ou um ID de dispositivo exato). É recomendado que os aplicativos que usam
a API getUserMedia()
verifica primeiro os dispositivos existentes e, em seguida, especifica um
que corresponde ao dispositivo exato usando a restrição deviceId
.
Se possível, os dispositivos também serão configurados de acordo com as restrições. Qa
É possível ativar o cancelamento de eco nos microfones ou definir uma largura específica ou mínima
e a altura do vídeo mostrado pela 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
na Web MDN
docs.
Reprodução local
Assim que um dispositivo de mídia é aberto e temos um MediaStream
disponível, nós
pode atribuí-lo a um elemento de vídeo ou áudio para reproduzir a transmissão 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 têm os atributos autoplay
e playsinline
. O autoplay
fará 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 somente até o fim.
em certos navegadores para dispositivos móveis. Também é recomendável usar
controls="false"
para transmissões ao vivo, a menos que o usuário consiga pausar
para resolvê-los com rapidez.
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>