Начало работы с мультимедийными устройствами

При разработке для Интернета стандарт 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>