Введение
Захват аудио и видео долгое время был святым Граалем веб-разработки. В течение многих лет для этого приходилось использовать плагины браузера (Flash или Silverlight). Да ладно!
HTML5 to the rescue. Возможно, это не очевидно, но с появлением HTML5 у разработчиков появилось больше возможностей для доступа к аппаратным компонентам устройств. Геолокация (GPS), Orientation API (акселерометр), WebGL (графический процессор) и Web Audio API (аудиооборудование) – отличные примеры. Эти функции невероятно мощные, они предоставляют высокоуровневые API-интерфейсы JavaScript, которые находятся поверх базовых аппаратных возможностей системы.
В этом руководстве рассказывается о новом API GetUserMedia, который позволяет веб-приложениям получать доступ к камере и микрофону пользователя.
Путь к getUserMedia()
Если вы не знаете историю создания getUserMedia() API, то вам будет интересно узнать, как мы к нему пришли.
За последние несколько лет появилось несколько вариантов Media Capture API. Многие осознавали необходимость доступа к устройствам из веб-приложений, но это привело к тому, что все кому не лень начали создавать собственные спецификации. Ситуация стала настолько запутанной, что W3C наконец решила сформировать рабочую группу. Их единственная цель? Разберитесь в этом безумии! Рабочей группе по политике API устройств (DAP) было поручено консолидировать и стандартизировать множество предложений.
Я постараюсь кратко рассказать о том, что произошло в 2011 году.
Раунд 1. HTML Media Capture
HTML Media Capture – это первая попытка DAP стандартизировать запись медиаконтента в интернете. Для этого перегружается функция <input type="file"> и добавляются новые значения для параметра accept.
Если вы хотите, чтобы пользователи могли сделать снимок с помощью веб-камеры,
используйте capture=camera:
<input type="file" accept="image/*;capture=camera">
Запись видео и аудио выполняется аналогично:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
Здорово, правда? Мне особенно нравится, что он повторно использует входной файл. С точки зрения семантики
это имеет большой смысл. Однако этот API не позволяет применять эффекты в реальном времени, например передавать данные с веб-камеры в элемент <canvas> и применять фильтры WebGL.
HTML Media Capture позволяет только записывать медиафайлы или делать снимки.
Поддержка
- Браузер Android 3.0 – одна из первых реализаций. Посмотрите это видео, чтобы узнать, как это работает.
- Chrome для Android (0,16)
- Firefox Mobile 10.0
- iOS 6: Safari и Chrome (частичная поддержка)
Раунд 2. Элемент устройства
Многие считали, что HTML Media Capture слишком ограничен, поэтому появилась новая спецификация, которая поддерживала любой тип (будущего) устройства. Неудивительно, что в дизайне потребовался
новый элемент, элемент <device>,
который стал предшественником getUserMedia().
Opera был одним из первых браузеров, в которых была реализована первоначальная поддержка захвата видео на основе элемента <device>. Вскоре после этого (если быть точным, в тот же день) WhatWG решила отказаться от тега <device> в пользу другого перспективного решения – JavaScript API под названием navigator.getUserMedia(). Через неделю Opera выпустила новые сборки с поддержкой обновленной спецификации getUserMedia(). Позже в том же году Microsoft выпустила Lab для IE9 с поддержкой новой спецификации.
Вот как выглядел бы <device>:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Поддержка
К сожалению, ни в одной из выпущенных версий браузера не было <device>.
Одним API меньше. :) У <device> было два преимущества: 1) он был семантическим; 2) его можно было легко расширить для поддержки не только аудио- и видеоустройств.
Сделайте глубокий вдох. Они двигаются очень быстро!
Раунд 3. WebRTC
Элемент <device> в конечном итоге ушел в прошлое.
Поиск подходящего API для захвата ускорился благодаря более масштабному проекту WebRTC (Web Real Time Communications). За спецификацию отвечает рабочая группа W3C WebRTC. Google, Opera, Mozilla и некоторые другие компании уже реализовали эту технологию.
getUserMedia() относится к WebRTC, поскольку является шлюзом для этого набора API.
Он позволяет получить доступ к локальной камере и микрофону пользователя.
Поддержка
getUserMedia() поддерживается в Chrome 21, Opera 18 и Firefox 17 и более поздних версиях этих браузеров.
Начало работы
С помощью navigator.mediaDevices.getUserMedia() мы наконец-то можем использовать веб-камеру и микрофон без плагина.
Теперь доступ к камере можно получить с помощью вызова, а не установки. Она встроена прямо в браузер. Заинтересовались?
Распознавание объектов
Чтобы проверить, поддерживается ли navigator.mediaDevices.getUserMedia, выполните следующие действия:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Получение доступа к устройству ввода
Чтобы использовать веб-камеру или микрофон, необходимо запросить разрешение.
Первый параметр navigator.mediaDevices.getUserMedia() – это объект, в котором указываются сведения и требования для каждого типа медиа, к которому вы хотите получить доступ. Например, если вы хотите получить доступ к веб-камере, первым параметром должен быть {video: true}. Чтобы использовать и микрофон, и камеру, передайте {video: true, audio: true}:
<video autoplay></video>
<script>
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((localMediaStream) => {
const video = document.querySelector("video");
video.srcObject = localMediaStream;
})
.catch((error) => {
console.log("Rejected!", error);
});
</script>
ОК. Что же здесь происходит? Захват медиаданных – отличный пример совместной работы новых API HTML5. Он работает в сочетании с другими элементами HTML5, такими как <audio> и <video>.
Обратите внимание, что мы не задаем атрибут src и не включаем элементы <source> в элемент <video>. Вместо того чтобы передавать видео URL медиафайла, мы задаем для srcObject объект LocalMediaStream, представляющий веб-камеру.
Я также указываю <video> autoplay, иначе он застынет на первом кадре. Добавление controls также работает как ожидалось.
Настройка ограничений для медиаконтента (разрешение, высота, ширина)
Первый параметр getUserMedia() также можно использовать, чтобы задать дополнительные требования (или ограничения) для возвращаемого медиапотока. Например, вместо того чтобы просто указать, что вам нужен базовый доступ к видео ({video: true}), вы можете дополнительно потребовать, чтобы поток был в формате HD:
const hdConstraints = {
video: { width: { exact: 1280} , height: { exact: 720 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
const vgaConstraints = {
video: { width: { exact: 640} , height: { exact: 360 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
Дополнительные настройки можно найти в API ограничений.
Как выбрать источник мультимедиа
Метод enumerateDevices() интерфейса MediaDevices запрашивает список доступных устройств ввода и вывода медиаданных, таких как микрофоны, камеры, гарнитуры и т. д. Возвращенный объект Promise разрешается с массивом объектов MediaDeviceInfo, описывающих устройства.
В этом примере в качестве источника медиапотока выбираются последние найденные микрофон и камера:
if (!navigator.mediaDevices?.enumerateDevices) {
console.log("enumerateDevices() not supported.");
} else {
// List cameras and microphones.
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
let audioSource = null;
let videoSource = null;
devices.forEach((device) => {
if (device.kind === "audioinput") {
audioSource = device.deviceId;
} else if (device.kind === "videoinput") {
videoSource = device.deviceId;
}
});
sourceSelected(audioSource, videoSource);
})
.catch((err) => {
console.error(`${err.name}: ${err.message}`);
});
}
async function sourceSelected(audioSource, videoSource) {
const constraints = {
audio: { deviceId: audioSource },
video: { deviceId: videoSource },
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
}
Посмотрите демонстрацию Сэма Даттона (Sam Dutton), в которой показано, как разрешить пользователям выбирать источник медиаконтента.
Безопасность
При вызове navigator.mediaDevices.getUserMedia() браузеры показывают диалоговое окно, в котором пользователь может разрешить или запретить доступ к камере и микрофону. Например, вот диалоговое окно
разрешений в Chrome:
Резервные объявления
Если у пользователя нет поддержки navigator.mediaDevices.getUserMedia(), то в случае, когда API не поддерживается и/или вызов по какой-то причине не выполняется, можно использовать резервный видеофайл:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}