Введение
Захват аудио/видео долгое время был « Святым Граалем» веб-разработки. В течение многих лет нам приходилось полагаться на плагины браузера ( Flash или Silverlight ), чтобы выполнить свою работу. Ну давай же!
HTML5 в помощь. Это может быть неочевидно, но появление HTML5 привело к резкому увеличению доступа к аппаратному обеспечению устройств. Геолокация (GPS), API ориентации (акселерометр), WebGL (графический процессор) и API веб-аудио (аудиооборудование) являются прекрасными примерами. Эти функции невероятно мощны и предоставляют API-интерфейсы JavaScript высокого уровня, которые находятся поверх базовых аппаратных возможностей системы.
В этом руководстве представлен новый API GetUserMedia , который позволяет веб-приложениям получать доступ к камере и микрофону пользователя.
Путь к getUserMedia()
Если вы не знакомы с его историей, то то, как мы пришли к API getUserMedia()
представляет собой интересную историю.
За последние несколько лет появилось несколько вариантов «API захвата мультимедиа». Многие люди осознавали необходимость иметь доступ к собственным устройствам в Интернете, но это побудило всех и их мам собрать новую спецификацию. Дела пошли настолько запутанно, что W3C наконец решил сформировать рабочую группу. Их единственная цель? Осмыслите безумие! Рабочей группе по политике API устройств (DAP) было поручено консолидировать и стандартизировать множество предложений.
Попробую подвести итог тому, что произошло в 2011 году…
Этап 1. Захват HTML-медиа
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 Мобильная версия 10.0
- iOS6 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
запрашивает список доступных устройств ввода и вывода мультимедиа, таких как микрофоны, камеры, гарнитуры и т. д. Возвращенное обещание разрешается с помощью массива объектов 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);
}
Посмотрите великолепную демонстрацию Сэма Даттона о том, как предоставить пользователям возможность выбирать источник мультимедиа.
Безопасность
Браузеры отображают диалоговое окно разрешений при вызове 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;
}