Многие браузеры теперь могут получать доступ к видео- и аудиовходам пользователя. Однако в зависимости от браузера это может быть полностью динамический и встроенный интерфейс или же он может быть делегирован другому приложению на устройстве пользователя.
Начните с простого и постепенно усложняйте
Проще всего попросить пользователя предоставить предварительно записанный файл. Для этого создайте простой элемент ввода файла и добавьте фильтр accept, который указывает, что мы можем принимать только видеофайлы, и атрибут capture, который указывает, что мы хотим получить его непосредственно с камеры.
<input type="file" accept="video/*" capture />
Этот способ работает на всех платформах. На компьютере пользователю будет предложено загрузить файл из файловой системы (атрибут capture будет проигнорирован). В Safari на iOS откроется приложение камеры, в котором можно записать видео и отправить его на веб-страницу. На Android пользователю будет предложено выбрать приложение для записи видео, которое затем будет отправлено на веб-страницу.
На многих мобильных устройствах есть несколько камер. Если вы хотите использовать фронтальную камеру, задайте для атрибута capture значение user, а если основную – environment.
<input type="file" accept="video/*" capture="user" />
<input type="file" accept="video/*" capture="environment" />
Обратите внимание, что это всего лишь подсказка. Если браузер не поддерживает эту функцию или запрашиваемый тип камеры недоступен, браузер может выбрать другую камеру.
Когда пользователь закончит запись и вернется на сайт, вам нужно будет получить данные файла. Чтобы быстро получить доступ к этим данным, прикрепите событие onchange к элементу ввода, а затем прочитайте свойство files объекта события.
<input type="file" accept="video/*" capture="camera" id="recorder" />
<video id="player" controls></video>
<script>
var recorder = document.getElementById('recorder');
var player = document.getElementById('player');
recorder.addEventListener('change', function (e) {
var file = e.target.files[0];
// Do something with the video file.
player.src = URL.createObjectURL(file);
});
</script>
Получив доступ к файлу, вы можете делать с ним все, что захотите. в том числе:
- Прикрепите его непосредственно к элементу
<video>, чтобы воспроизвести. - Скачайте его на устройство пользователя.
- Загрузите его на сервер, прикрепив к
XMLHttpRequest. - Рисовать кадры на холсте и применять к ним фильтры.
Хотя использование метода элемента ввода для получения доступа к видеоданным повсеместно, это наименее привлекательный вариант. Мы действительно хотим получить доступ к камере и обеспечить приятный опыт прямо на странице.
Интерактивный доступ к камере
Современные браузеры могут иметь прямую линию с камерой, что позволяет нам создавать впечатления, которые полностью интегрированы с веб-страницей, и пользователь никогда не покинет браузер.
Получить доступ к камере
Мы можем получить прямой доступ к камере, используя API в спецификации WebRTC под названием getUserMedia(). getUserMedia() запросит у пользователя доступ к подключенным микрофонам и камерам.
В случае успеха API вернет объект Stream, который будет содержать данные с камеры или микрофона. Затем мы можем прикрепить его к элементу <video>, прикрепить к потоку WebRTC или сохранить с помощью API MediaRecorder.
Чтобы получить данные с камеры, мы просто устанавливаем video: true в объекте ограничений, который передается в API getUserMedia().
<video id="player" controls></video>
<script>
var player = document.getElementById('player');
var handleSuccess = function (stream) {
player.srcObject = stream;
};
navigator.mediaDevices
.getUserMedia({audio: true, video: true})
.then(handleSuccess);
</script>
Если вы хотите выбрать определенную камеру, сначала можно перечислить доступные камеры.
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices = devices.filter((d) => d.kind === 'videoinput');
});
Затем вы можете передать идентификатор deviceId, который хотите использовать при вызове getUserMedia.
navigator.mediaDevices.getUserMedia({
audio: true,
video: {
deviceId: devices[0].deviceId,
},
});
Сам по себе этот параметр не очень полезен. Все, что мы можем сделать, это взять видеоданные и воспроизвести их.
Доступ к необработанным данным с камеры
Чтобы получить доступ к необработанным видеоданным с камеры, вы можете отрисовать каждый кадр в <canvas> и напрямую управлять пикселями.
Для 2D-холста можно использовать метод drawImage контекста, чтобы нарисовать текущий кадр элемента <video> на холсте.
context.drawImage(myVideoElement, 0, 0);
При использовании холста WebGL вы можете использовать элемент <video> в качестве источника текстуры.
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.RGBA,
gl.UNSIGNED_BYTE,
myVideoElement,
);
Обратите внимание, что в обоих случаях будет использоваться текущий кадр воспроизводимого видео. Чтобы обработать несколько кадров, вам нужно каждый раз перерисовывать видео на холсте.
Подробнее о том, как применять эффекты к изображениям и видео в реальном времени…
Сохраните данные с камеры.
Проще всего сохранить данные с камеры с помощью API MediaRecorder.
API MediaRecorder возьмет поток, созданный getUserMedia, и будет постепенно сохранять данные из него в выбранном вами месте назначения.
<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
let shouldStop = false;
let stopped = false;
const downloadLink = document.getElementById('download');
const stopButton = document.getElementById('stop');
stopButton.addEventListener('click', function() {
shouldStop = true;
})
var handleSuccess = function(stream) {
const options = {mimeType: 'video/webm'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function(e) {
if (e.data.size > 0) {
recordedChunks.push(e.data);
}
if(shouldStop === true && stopped === false) {
mediaRecorder.stop();
stopped = true;
}
});
mediaRecorder.addEventListener('stop', function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
downloadLink.download = 'acetest.webm';
});
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(handleSuccess);
</script>
В нашем случае мы сохраняем данные непосредственно в массив, который затем можно преобразовать в Blob. После этого данные можно сохранить на веб-сервере или непосредственно на устройстве пользователя.
Запрашивайте разрешение на использование камеры ответственно
Если пользователь ранее не предоставлял вашему сайту доступ к камере, то в момент вызова getUserMedia браузер предложит пользователю предоставить вашему сайту разрешение на доступ к камере.
Пользователям не нравится, когда у них запрашивают доступ к мощным устройствам на их компьютере, и они часто блокируют запрос или игнорируют его, если не понимают контекст, в котором он был создан. Лучше всего запрашивать доступ к камере только тогда, когда это необходимо. Если пользователь предоставит доступ, запрос больше не появится. Если же он откажется, вы не сможете запросить разрешение повторно.
Проверьте, есть ли у вас доступ, с помощью Permissions API
API getUserMedia не предоставляет информацию о том, есть ли у вас доступ к камере. Это создает проблему: чтобы предоставить хороший пользовательский интерфейс, позволяющий пользователю предоставить вам доступ к камере, вы должны запросить доступ к камере.
В некоторых браузерах эту проблему можно решить с помощью Permission API. navigator.permission API позволяет запрашивать состояние доступа к определенным API без повторного запроса.
Чтобы узнать, есть ли у вас доступ к камере пользователя, передайте в метод query значение {name: 'camera'}. Он вернет один из следующих результатов:
granted– пользователь ранее предоставил вам доступ к камере;prompt– пользователь не предоставил вам доступ и получит запрос, когда вы позвоните по номеруgetUserMedia;denied– доступ к камере заблокирован системой или пользователем.
Теперь вы можете быстро проверить, нужно ли вам изменить пользовательский интерфейс, чтобы учесть действия, которые должен предпринять пользователь.
navigator.permissions.query({name: 'camera'}).then(function (result) {
if (result.state == 'granted') {
} else if (result.state == 'prompt') {
} else if (result.state == 'denied') {
}
result.onchange = function () {};
});