Запись видео от пользователя

Mat Scales

Многие браузеры теперь могут получать доступ к видео- и аудиовходам пользователя. Однако в зависимости от браузера это может быть полностью динамический и встроенный интерфейс или же он может быть делегирован другому приложению на устройстве пользователя.

Начните с простого и постепенно усложняйте

Проще всего попросить пользователя предоставить предварительно записанный файл. Для этого создайте простой элемент ввода файла и добавьте фильтр 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 () {};
});

Как оставить отзыв