Tutorial

Доступ к фронтальной и задней камерам с помощью getUserMedia() в JavaScript

Published on November 13, 2020
author

Chris Nwamba

Русский
Доступ к фронтальной и задней камерам с помощью getUserMedia() в JavaScript

Введение

В языке HTML5 появились API для доступа к аппаратному обеспечению устройств, включая MediaDevices API. Этот API предоставляет доступ к устройствам ввода мультимедиа. в том числе аудио и видео.

С помощью этого API разработчики могут получать доступ к устройствам аудио и видео для трансляции и вывода видеопотоков в реальном времени в браузере. В этом учебном модуле вы научитесь получать доступ к видеопотокам с устройства пользователя и выводить его в браузере, используя метод getUserMedia.

getUserMedia API использует устройства ввода мультимедиа для получения потока MediaStream. Этот поток MediaStream содержит запрошенные типы мультимедиа, то есть аудио или видео. С помощью API можно выводить видеопотоки через браузер, что можно использовать для связи в реальном времени через браузер.

При использовании вместе с MediaStream Recording API вы можете записывать и сохранять мультимедийные данные, полученные через браузер. Этот API работает только с безопасными источниками, как и остальные новые API, а также работает с localhost и файловыми URL.

Предварительные требования

В начале этого учебного модуля мы объясним концепции и продемонстрируем примеры с помощью Codepen. На заключительном шаге вы создадите рабочий видеопоток в браузере.

Шаг 1 — Проверка поддержки устройств

Вначале мы проверим, поддерживает ли браузер пользователя mediaDevices API. Этот API существует в интерфейсе navigator и содержит текущее состояние и идентификатор пользовательского агента. Для проверки используется следующий код, который можно вставить в Codepen:

if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
  console.log("Let's get this party started")
}

Вначале проверяется наличие mediaDevices API в navigator, а затем проверяется доступность getUserMedia API в mediaDevices. Если возвращается результат true, можно начинать работу.

Шаг 2 — Запрос разрешения пользователя

После подтверждения поддержки getUserMedia в браузере необходимо запросить разрешение на использование устройств ввода мультимедиа на пользовательском агенте. Обычно после предоставления пользователем разрешения возвращается промис, который разрешается в мультимедийный поток. Если пользователь не дает разрешения, этот промис не возвращается, и доступ к данным устройствам блокируется.

Вставьте в Codepen следующую строку, чтобы запросить разрешение:

navigator.mediaDevices.getUserMedia({video: true})

Объект, предоставляемый как аргумент для метода getUserMedia называется constraints (переводится как ограничения). Он определяет, к каким устройствам ввода мультимедиа вы запрашиваете разрешение на доступ. Например, если объект содержит текст audio: true, у пользователя запрашивается доступ к устройству ввода аудио.

Шаг 3 — Понимание концепции ограничений мультимедиа

В этом разделе мы расскажем о концепции contraints. Объект constraints — это объект MediaStreamConstraints, который указывает типы мультимедиа для запроса и требования каждого типа мультимедиа. Вы можете использовать объект constraints, чтобы указать требования к запрошенному потоку, например, требуемое разрешение (front, back).

При отправке этого запроса следует указать аргумент audio или video. Если запрошенные типы мультимедиа не будут найдены в браузере пользователя, будет выведено сообщение об ошибке NotFoundError.

Если вы планируете запросить видеопоток м разрешением 1280 x 720, вы можете обновить объект constraints, чтобы он выглядел так:

{
  video: {
    width: 1280,
    height: 720,
  }
}

С этим обновлением браузер попробует подобрать заданные настройки качества для потока. Если видеоустройство не сможет обеспечить это разрешение, браузер возвратит другие доступные разрешения.

Чтобы браузер гарантированно возвращал разрешение не ниже указанного, вам нужно будет использовать свойство min. Здесь вы можете обновить объект constraints, добавив в него свойство min:

{
  video: {
    width: {
      min: 1280,
    },
    height: {
      min: 720,
    }
  }
}

Это обеспечит возвращаемое разрешение потока не ниже 1280 x 720. Если это минимальное требование не удастся выполнить, промис будет отклонен с сообщением об ошибке OverconstrainedError.

В некоторых случаях вас может беспокоить сохранение данных и необходимость не превышать заданное разрешение потока. Это может быть полезно, если пользователь использует лимитированный тарифный план. Для реализации этой функции следует обновить объект constraints, добавив в него поле max:

{
  video: {
    width: {
      min: 1280,
      max: 1920,
    },
    height: {
      min: 720,
      max: 1080
    }
  }
}

При таких параметрах браузер будет использовать разрешение видеопотока не менее 1280 x 720 и не более 1920 x 1080.

Также можно использовать аргументы exact и ideal. Параметр ideal обычно используется со свойствами min и max для подбора наилучших возможных настроек, ближайших к идеальному значению.

Вы можете обновить объект constraints для использования ключевого слова ideal:

{
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    }
  }
}

Чтобы указать браузеру использовать фронтальную или заднюю камеру (на мобильных устройствах), вы можете указать свойство facingMode в объекте video:

{
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    },
    facingMode: 'user'
  }
}

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

{
  video: {
    ...
    facingMode: {
      exact: 'environment'
    }
  }
}

Шаг 4 — Использование метода enumerateDevices

При вызове метода enumerateDevices возвращаются все доступные на компьютере пользователя устройства ввода мультимедиа.

С помощью этого метода можно дать пользователю возможность выбрать устройство ввода мультимедиа для трансляции аудио- или видеоконтента. Этот метод возвращает промис, разрешающийся в массив MediaDeviceInfo, который содержит информацию о каждом устройстве.

Ниже приведен фрагмент кода с примером использования этого метода:

async function getDevices() {
  const devices = await navigator.mediaDevices.enumerateDevices();
}

Образец ответа для каждого из устройств выглядит следующим образом:

{
  deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
  groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
  kind: "audiooutput",
  label: "",
}

Примечание. Ярлык не возвращается, если отсутствует доступный поток, или если пользователь не предоставит разрешения на доступ к устройству.

Шаг 5 — Отображение видеопотока в браузере

Вы выполнили процедуру запроса и получения доступа к мультимедийным устройствам, настроили объект constraints для добавления требуемых разрешений и выбрали камеру, которая потребуется для записи видео.

После выполнения всех этих шагов нам нужно будет посмотреть соответствие потока трансляции заданным параметрам. Для этого мы используем элемент <video>, чтобы вывести видеопоток в браузере.

Как уже говорилось ранее. метод getUserMedia возвращает промис, который может быть разрешен в поток. Возвращаемый поток можно конвертировать в URL объекта, используя метод createObjectURL. Этот URL будет установлен как источник видео.

Вы создадите короткую демонстрационную программу, где пользователь сможет выбрать доступное видеоустройство из списка, используя метод enumerateDevices.

Это метод navigator.mediaDevices. Он перечисляет доступные мультимедийные устройства, в том числе микрофоны и камеры. Он возвращает промис, который разрешается в массив объектов с подробными сведениями о доступных мультимедийных устройствах.

Создайте файл index.html и обновите его содержимое с помощью следующего кода:

index.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>
<body>
<div class="display-cover">
    <video autoplay></video>
    <canvas class="d-none"></canvas>

    <div class="video-options">
        <select name="" id="" class="custom-select">
            <option value="">Select camera</option>
        </select>
    </div>

    <img class="screenshot-image d-none" alt="">

    <div class="controls">
        <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
        <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
        <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
    </div>
</div>

<script src="https://unpkg.com/feather-icons"></script>
<script src="script.js"></script>
</body>
</html>

В приведенном выше фрагменте кода вы настроили требуемые элементы и пару элементов управления видео. Также мы добавили кнопку для снимков экрана текущего видеопотока.

Теперь применим к этим компонентам стили.

Создайте файл style.css и скопируйте в него следующие стили. Мы включили Bootstrap, чтобы сократить объем кода CSS, который нужно будет написать для работы компонентов.

style.css
.screenshot-image {
    width: 150px;
    height: 90px;
    border-radius: 4px;
    border: 2px solid whitesmoke;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
    position: absolute;
    bottom: 5px;
    left: 10px;
    background: white;
}

.display-cover {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 70%;
    margin: 5% auto;
    position: relative;
}

video {
    width: 100%;
    background: rgba(0, 0, 0, 0.2);
}

.video-options {
    position: absolute;
    left: 20px;
    top: 30px;
}

.controls {
    position: absolute;
    right: 20px;
    top: 20px;
    display: flex;
}

.controls > button {
    width: 45px;
    height: 45px;
    text-align: center;
    border-radius: 100%;
    margin: 0 6px;
    background: transparent;
}

.controls > button:hover svg {
    color: white !important;
}

@media (min-width: 300px) and (max-width: 400px) {
    .controls {
        flex-direction: column;
    }

    .controls button {
        margin: 5px 0 !important;
    }
}

.controls > button > svg {
    height: 20px;
    width: 18px;
    text-align: center;
    margin: 0 auto;
    padding: 0;
}

.controls button:nth-child(1) {
    border: 2px solid #D2002E;
}

.controls button:nth-child(1) svg {
    color: #D2002E;
}

.controls button:nth-child(2) {
    border: 2px solid #008496;
}

.controls button:nth-child(2) svg {
    color: #008496;
}

.controls button:nth-child(3) {
    border: 2px solid #00B541;
}

.controls button:nth-child(3) svg {
    color: #00B541;
}

.controls > button {
    width: 45px;
    height: 45px;
    text-align: center;
    border-radius: 100%;
    margin: 0 6px;
    background: transparent;
}

.controls > button:hover svg {
    color: white;
}

Следующий шаг — добавление функционала в демонстрацию. Используя метод enumerateDevices, вы получите доступные видеоустройства и зададите их как опции выбранного элемента. Создайте файл script.js и добавьте в него следующий код:

script.js
feather.replace();

const controls = document.querySelector('.controls');
const cameraOptions = document.querySelector('.video-options>select');
const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const screenshotImage = document.querySelector('img');
const buttons = [...controls.querySelectorAll('button')];
let streamStarted = false;

const [play, pause, screenshot] = buttons;

const constraints = {
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    },
  }
};

const getCameraSelection = async () => {
  const devices = await navigator.mediaDevices.enumerateDevices();
  const videoDevices = devices.filter(device => device.kind === 'videoinput');
  const options = videoDevices.map(videoDevice => {
    return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
  });
  cameraOptions.innerHTML = options.join('');
};

play.onclick = () => {
  if (streamStarted) {
    video.play();
    play.classList.add('d-none');
    pause.classList.remove('d-none');
    return;
  }
  if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
    const updatedConstraints = {
      ...constraints,
      deviceId: {
        exact: cameraOptions.value
      }
    };
    startStream(updatedConstraints);
  }
};

const startStream = async (constraints) => {
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  handleStream(stream);
};

const handleStream = (stream) => {
  video.srcObject = stream;
  play.classList.add('d-none');
  pause.classList.remove('d-none');
  screenshot.classList.remove('d-none');
  streamStarted = true;
};

getCameraSelection();

В приведенном выше фрагменте кода выполняется ряд действий. Давайте рассмотрим их подробнее:

  1. feather.replace(): этот метод создает экземпляр feather, набора иконок для веб-разработки.
  2. Переменная constraints хранит начальную конфигурацию видеопотока. Она будет расширена, и в нее будет добавлено выбранное пользователем мультимедийное устройство.
  3. getCameraSelection: данная функция вызывает метод enumerateDevices. Затем мы выполняем фильтрацию массива на основе разрешенного промиса, и выбираем устройства ввода видео. Из отфильтрованных результатов вы создаете <option> для элемента <select>.
  4. Вызов метода getUserMedia выполняется в средстве прослушивания onclick кнопки play. Здесь перед началом трансляции мы проверяем, поддерживает ли браузер пользователя этот метод.
  5. Затем мы вызываем функцию startStream, принимающую аргумент constraints. Она вызывает метод getUserMedia с указанными constraints. handleStream вызывается на основе трансляции из разрешенного промиса. Этот метод устанавливает возвращаемый поток для объекта srcObject видеоэлемента.

Далее мы добавим средства прослушивания нажатий в элементы управления кнопками на страницах для приостановки, остановки и снимков экрана. Также вы добавите средство прослушивания в элемент <select>, чтобы обновить ограничения трансляции для выбранного видеоустройства.

Добавьте в файл script.js следующий код:

script.js
...
cameraOptions.onchange = () => {
  const updatedConstraints = {
    ...constraints,
    deviceId: {
      exact: cameraOptions.value
    }
  };
  startStream(updatedConstraints);
};

const pauseStream = () => {
  video.pause();
  play.classList.remove('d-none');
  pause.classList.add('d-none');
};

const doScreenshot = () => {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);
  screenshotImage.src = canvas.toDataURL('image/webp');
  screenshotImage.classList.remove('d-none');
};

pause.onclick = pauseStream;
screenshot.onclick = doScreenshot;

Теперь, когда вы откроете файл index.html в браузере, при нажатии Play начнется трансляция.

Вот полная версия демонстрационной программы:

https://codepen.io/chrisbeast/pen/ebYwpX

Заключение

В этом учебном модуле мы познакомились с getUserMedia API. Это интересное дополнение HTML5, упрощающее захват мультимедийного контента через интернет.

Данный API принимает параметр (constraints), который можно использовать для настройки доступа к устройствам ввода звука и видео. Также его можно использовать, чтобы задать требуемое разрешение видео для вашего приложения.

Вы можете расширить демонстрацию и предоставить пользователю возможность сохранить сделанные снимки экрана, а также записывать и сохранять аудио- и видеоданные с помощью MediaStream Recording API.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Chris Nwamba

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.