При разработке для Интернета стандарт WebRTC предоставляет API для доступа к камерам и микрофонам, подключенным к компьютеру или смартфону. Эти устройства обычно называются мультимедийными устройствами, и к ним можно получить доступ с помощью JavaScript через объект navigator.mediaDevices
, который реализует интерфейс MediaDevices
. Из этого объекта мы можем перечислить все подключенные устройства, прослушивать изменения устройства (когда устройство подключено или отключено) и открыть устройство для получения медиапотока (см. ниже).
Самый распространенный способ использования — это функция getUserMedia()
, которая возвращает обещание, которое будет преобразовано в MediaStream
для соответствующих мультимедийных устройств. Эта функция принимает один объект MediaStreamConstraints
, который определяет наши требования. Например, чтобы просто открыть микрофон и камеру по умолчанию, мы должны сделать следующее.
Использование обещаний
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);
});
Использование 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);
}
Вызов getUserMedia()
вызовет запрос разрешений. Если пользователь принимает разрешение, обещание разрешается с помощью MediaStream
содержащего одну видео- и одну звуковую дорожку. Если в разрешении отказано, выдается PermissionDeniedError
. Если не подключено ни одного подходящего устройства, будет выдано NotFoundError
.
Полный справочник по API для интерфейса MediaDevices
доступен в веб-документации MDN .
Запрос мультимедийных устройств
В более сложном приложении мы, скорее всего, захотим проверить все подключенные камеры и микрофоны и предоставить пользователю соответствующую обратную связь. Это можно сделать, вызвав функцию enumerateDevices()
. Это вернет обещание, которое преобразуется в массив MediaDevicesInfo
, описывающий каждое известное мультимедийное устройство. Мы можем использовать это, чтобы представить пользователю пользовательский интерфейс, который позволит ему выбрать тот, который он предпочитает. Каждый MediaDevicesInfo
содержит свойство с именем kind
со значением audioinput
, audiooutput
или videoinput
, указывающим тип мультимедийного устройства.
Использование обещаний
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));
Использование асинхронности/ожидания
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);
Прослушивание изменений устройств
Большинство компьютеров поддерживают подключение различных устройств во время работы. Это может быть веб-камера, подключенная через USB, Bluetooth-гарнитура или набор внешних динамиков. Чтобы правильно поддерживать это, веб-приложение должно прослушивать изменения мультимедийных устройств. Это можно сделать, добавив прослушиватель в navigator.mediaDevices
для события 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);
});
Ограничения СМИ
Объект ограничений, который должен реализовывать интерфейс MediaStreamConstraints
, который мы передаем в качестве параметра функции getUserMedia()
позволяет нам открыть мультимедийное устройство, соответствующее определенному требованию. Это требование может быть очень расплывчатым (аудио и/или видео) или очень конкретным (минимальное разрешение камеры или точный идентификатор устройства). Приложениям, использующим API getUserMedia()
рекомендуется сначала проверить существующие устройства, а затем указать ограничение, соответствующее точному устройству, с помощью ограничения deviceId
. Устройства также, по возможности, будут настроены в соответствии с ограничениями. Мы можем включить эхоподавление на микрофонах или установить определенную или минимальную ширину и высоту видео с камеры.
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);
}
Полную документацию по интерфейсу MediaStreamConstraints
можно найти в веб-документации MDN .
Локальное воспроизведение
После того, как мультимедийное устройство открыто и у нас есть доступный MediaStream
, мы можем назначить его элементу видео или аудио для локального воспроизведения потока.
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);
}
}
HTML-код, необходимый для типичного видеоэлемента, используемого с getUserMedia()
обычно имеет атрибуты autoplay
и playsinline
. Атрибут autoplay
приведет к автоматическому воспроизведению новых потоков, назначенных элементу. Атрибут playsinline
позволяет воспроизводить видео онлайн, а не только в полноэкранном режиме, в некоторых мобильных браузерах. Также рекомендуется использовать controls="false"
для прямых трансляций, если только пользователь не должен иметь возможность приостанавливать их.
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>