Учебное пособие по использованию getUserMedia () для доступа в качестве устройств ввода пользователя.

В течение многих лет низкоуровневый доступ через браузер мог осуществляться только с помощью сложных флеш-программ или Java-апплетов. Когда флеш-память и апплеты начали исчезать, потребовалось новое решение для доступа к низкоуровневым частям компьютера пользователя. Познакомьтесь с WebRTC, фреймворком Javascript, который обеспечивает легкий доступ к устройствам ввода пользователя и создание одноранговых соединений.

WebRTC особенный, потому что он упрощает одноранговые соединения между браузерами. Это становится особенно полезным для обмена сообщениями, видеочата или даже приложений для обмена файлами. Для этого WebRTC включает в себя набор протоколов и API, которые упрощают обмен данными в реальном времени. В наших целях мы собираемся использовать getUserMedia api, чтобы легко получить доступ к встроенной камере пользователя для нашего приложения для селфи.

Ограничения WebRTC

Каким бы прекрасным ни был WebRTC, он все еще имеет множество ограничений. А именно, что некоторые API-интерфейсы поддерживаются не всеми браузерами. Хороший список того, что поддерживается, а что нет, можно найти на http://iswebrtcreadyyet.com/. В настоящее время для наших целей будут работать все браузеры Chrome, Firefox, Edge и Opera, поскольку все они позволяют использовать getUserMedia(). Edge отстает с некоторыми функциями, но быстро их догоняет, а Safari даже не реализует WebRTC (но они якобы над этим работают).

Начиная

Установка ноды и настройка нашего проекта

Прежде чем мы начнем, вам необходимо установить Node.js на вашем компьютере. Я не буду вдаваться в подробности того, как это сделать здесь, но https://nodejs.org/en/download/ содержит инструкции по установке узла в любой операционной системе.

После того, как вы установили узел, мы приступим к настройке нашей структуры папок. Вы можете легко создать папку проекта с помощью командной строки, набрав $ mkdir directoryname. Структура вашей папки должна выглядеть так:

SelfieApp
- run.js
- node_modules
- public
 | —  index.html
 | —  style.css
 | —  javascript.js

После того, как мы настроили наши папки, нам нужно установить несколько узловых модулей, чтобы упростить разработку нашего приложения для селфи. Модули узла - это внешние библиотеки, которые мы можем импортировать в наш проект, что помогает нам организовать наш код в отдельные части и берет на себя определенные обязанности. Думайте об этом как о блоке лего, который кто-то уже сделал для нас, который мы затем можем взять и собрать вместе с другими блоками лего, чтобы создать наше окончательное видение. Мы собираемся использовать node-static module, чтобы помочь нам обслуживать статический файл в нашем браузере.

В командной строке введите:

$ npm install node-static

Теперь в нашем файле run.js мы собираемся ввести следующий код:

Давайте быстро разберем, что происходит в этом коде:
var static = require('node-static'); находит и загружает модуль node-static, чтобы мы могли использовать связанные с ним функции.
new static.Server('./public'); создает экземпляр файлового сервера. Это одна из функций, которые мы получаем от модуля node-static. Мы передаем ему аргумент './public', чтобы он обслуживал файлы в этом каталоге.
require('http').createServer(function (request, response) { ... }
Эта функция запускает наш экземпляр сервера и передает ему наш HTTP-запрос. .
.listen(8080) просто устанавливает порт, который мы собираемся использовать при доступе к нашему селфи-приложению в браузере.

На этом наш сервер настроен! Теперь мы можем проверить это, войдя в нашу командную строку и набрав $ node run.js, а затем посетив http: // localhost: 8080. Если мы все сделали правильно, у нас должна получиться пустая веб-страница. Если вы получаете сообщение об ошибке, такое как localhost отказался подключиться, дважды проверьте свой код и убедитесь, что вы подключаетесь к правильному номеру порта (и не вводите ничего вроде https: // перед localhost) . Чтобы остановить работу нашего сервера, введите control + c в командной строке.

Далее нам нужно настроить нашу html-страницу. Код выглядит следующим образом:

Теперь, если мы снова запустим наш сервер с помощью $ node run.js,, мы должны получить html-страницу при загрузке http: // localhost: 8080, которая выглядит так:

Далее, части WebRTC и Javascript.

Теперь, когда у нас настроена html-страница, мы перейдем к файлу Javascript. Я разделю эту часть на более мелкие, чтобы мы могли понять, что мы делаем. Во-первых, давайте настроим некоторые из наших переменных, которые нам нужны

var constraints = { 
   audio: false, 
   video: { 
      width: 640, 
      height: 360 
   } 
};
var canvas = document.querySelector(‘canvas’);
var video = document.querySelector('video');
var filters = [‘’, ‘grayscale’, ‘sepia’, ‘invert’], currentFilter = 0;

Давайте посмотрим, что здесь происходит:
constraints - это параметры, которые мы собираемся передать в наш канал видео. Мы указываем размер канала камеры, чтобы он не был слишком большим, и отключаем звук, поскольку он нам не нужен.
canvas выбирает элемент холста, который можно использовать для рисования графики. Здесь мы будем отображать сделанное нами селфи.
filters - это массив, содержащий все возможные фильтры для использования. Мы также определяем здесь currentFilter и устанавливаем его равным «» или без фильтра.

Затем мы собираемся получить доступ к камере пользователя и отобразить ленту в нашем приложении для селфи. Здесь мы используем getUserMedia() api.

navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
   var videoTracks = stream.getVideoTracks();
   video.srcObject = stream;
})
.catch(function(err) { console.log(err.name + ": " + err.message); });

И с этим у нас работает наш видеопоток! На самом деле это довольно просто и понятно, особенно по сравнению с тем, где был api несколько месяцев назад. Давайте быстро посмотрим, что мы делаем:
navigator.mediaDevices.getUserMedia() запрашивает у пользователя разрешение на использование веб-камеры. Если доступ разрешен, с результирующим объектом потока возвращается обещание. Мы передаем ему наши ограничения, которые мы указали ранее.
stream.getVideoTracks() возвращает список всех видеодорожек в объекте потока. Здесь мы найдем все веб-камеры пользователя, если к ним подключено несколько.
video.srcObject = stream; позволяет связать наш видеопоток с элементом на нашей странице. Другими словами, в итоге наш видеопоток попадает на нашу веб-страницу.
Последняя часть этого кода, инструкция .catch(...), предназначена для обработки ошибок. Имея дело с любой экспериментальной технологией, такой как WebRTC, важно иметь решения для устранения любых ошибок, которые могут возникнуть, вместо того, чтобы рисковать сбой всей нашей программы.

Теперь, когда наш видеопоток работает, пора

document.querySelector(‘#capture’).addEventListener(‘click’, function (event) {
   if (video) {
     canvas.width = video.clientWidth;
     canvas.height = video.clientHeight;
     var context = canvas.getContext(‘2d’);
     context.drawImage(video, 0, 0);
   }
});

Этот код здесь довольно простой. Сначала мы добавляем прослушиватель событий к кнопке захвата. После нажатия кнопки захвата в нашей программе функция проверяет, работает ли наш видеопоток. context.drawimage(..) делает снимок нашего видеопотока и рисует его на холсте.

Наша программа почти готова! Все, что нам осталось, это реализовать наши фильтры. Это двухэтапный процесс. Во-первых, в нашем файле Javascript нам нужно разместить прослушиватели событий на наших кнопках фильтров. После этого нам нужно определить наши фильтры в нашем файле CSS. Начнем сначала с файла Javascript:

document.querySelector(‘#noFilter’).addEventListener(‘click’, function (event) {
    canvas.className = filters[0];
});
document.querySelector(‘#blackAndWhite’).addEventListener(‘click’, function (event) {
    canvas.className = filters[1];
});
document.querySelector(‘#sephia’).addEventListener(‘click’, function (event) {
    canvas.className = filters[2];
 });
document.querySelector(‘#invert’).addEventListener(‘click’, function (event) {
    canvas.className = filters[3];
});

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

Прежде чем мы перейдем к CSS, давайте добавим последнюю функцию в наш Javascript, чтобы мы могли сохранить наше селфи:

document.querySelector(‘#downloadLnk’).addEventListener(‘click’, function (event) {
    var dt = canvas.toDataURL(‘image/jpeg’);
    this.href = dt;
});

Опять же, это фрагмент кода, аналогичный тому, что мы написали ранее. canvas.toDataUrl(..) преобразует наш объект холста в изображение jpeg, а this.href назначает jpeg нашему элементу href обратно в нашем файле html.

И мы закончили с нашим файлом Javascript! Вот как должен выглядеть готовый файл:

Вы заметите, что функция getUserMedia (..) претерпела несколько изменений. В основном это просто журналы нашей консоли, чтобы убедиться, что все работает нормально. Для нашего приложения они не обязательны, но в реальном мире значительно упрощают отладку и тестирование. Теперь перейдем к нашему файлу CSS. Здесь мы создаем наши фактические фильтры, которые будут применяться.

.grayscale {
    -webkit-filter: grayscale(1);
    -moz-filter: grayscale(1);
    -ms-filter: grayscale(1);
    -o-filter: grayscale(1);
    filter: grayscale(1);
}
.sepia {
    -webkit-filter: sepia(1);
    -moz-filter: sepia(1);
    -ms-filter: sepia(1);
    -o-filter: sepia(1);
    filter: sepia(1);
}
.invert {
    -webkit-filter: invert(1);
    -moz-filter: invert(1);
    -ms-filter: invert(1);
    -o-filter: invert(1);
    -filter: invert(1);
}

Этот код довольно прост. .grayscale преобразует наше изображение в черно-белое, .sephia преобразует в сепию, а .invert инвертирует наше изображение.

И мы с программой закончили! Давайте проверим, чтобы убедиться, что все работает. В командной строке введите $ node run.js, а затем откройте в браузере http: // localhost: 8080. Если все было сделано правильно, должна получиться такая страница:

Если ваше приложение для селфи не работает, дважды проверьте свой код и загляните в репозиторий git hub здесь.

Продолжая

Несмотря на то, что наше приложение для селфи готово, мы все равно можем развить проект, внедрив новые функции или сделав приложение более удобным для пользователя. Мы можем перевернуть видеопоток с помощью CSS для более естественного вида, реализовать наши фильтры с помощью Javascript для большего контроля или даже использовать объект холста, чтобы нарисовать шляпу над собой. Возможности WebRTC и Javascript безграничны.

Надеюсь, вам понравилось это введение в WebRTC. Если у вас есть вопросы, не стесняйтесь оставлять комментарии ниже, и я свяжусь с вами как можно скорее!