From 074785cea106179cb3305637055ab0a009ca74f2 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:52 -0500 Subject: initial commit --- .../api/webrtc_api/taking_still_photos/index.html | 222 +++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 files/ru/web/api/webrtc_api/taking_still_photos/index.html (limited to 'files/ru/web/api/webrtc_api/taking_still_photos/index.html') diff --git a/files/ru/web/api/webrtc_api/taking_still_photos/index.html b/files/ru/web/api/webrtc_api/taking_still_photos/index.html new file mode 100644 index 0000000000..ec5e7ec42d --- /dev/null +++ b/files/ru/web/api/webrtc_api/taking_still_photos/index.html @@ -0,0 +1,222 @@ +--- +title: Захват кадров с WebRTC +slug: Web/API/WebRTC_API/Taking_still_photos +tags: + - Захват WebRTC +translation_of: Web/API/WebRTC_API/Taking_still_photos +--- +

{{WebRTCSidebar}}

+ +

В этой статье объясняется как использовать WebRTC для получения доступа к камере компьютера или мобильного устройства, и захвата кадров с их помощью. Ознакомтесь с примером, а затем узнайте как это работает.

+ +

Uz WebRTC balstīta attēla uztveršanas lietotne - kreisajā pusē un bez tīmekļa kameras uzņemšanas video straumē un poga

+ +

Перейдите непостредственно к коду на Github , при желании.

+ +

Разметка HTML

+ +

Наш HTML интерфейс состоит из двух секций : панель отображения видео потока, из которого будет производиться захват и панель отображения результата захвата. Каждая панель имеет свой элемент {{HTMLElement ("div")}}, для облегчения стилизации и управления.

+ +

Первая панель слева содержит два компонента : элемент {{HTMLElement ("video")}} , который будет получать поток, отводимый с камеры, и элемент  {{HTMLElement("button")}}, каторый будет использоваться пользователем для активации захвата видео кадра.

+ +
  <div class="camera">
+    <video id="video">Video stream not available.</video>
+    <button id="startbutton">Take photo</button>
+  </div>
+ +

Все это просто, и мы увидим как они связаны между собой, когда обратимся к коду  JavaScript .

+ +

В разметке имеется элемент {{HTMLElement("canvas")}} , который сохраняет захваченный кадр, который может быть дополнительно обработан и конвертируется в выходной файл изображения. Элемент canvas является скрытым, в его стиле свойство {{cssxref("display")}}:none, во избежании поломки интерфейса, где пользователю совершенно не обязательно видеть служебные элементы.

+ +

Для отображения пользователю результата захвата кадра, в интерфейсе расположен элемент {{HTMLElement("img")}}.

+ +
  <canvas id="canvas">
+  </canvas>
+  <div class="output">
+    <img id="photo" alt="The screen capture will appear in this box.">
+  </div>
+ +

Вот и все, что касается HTML. Остальное - просто пух макета страницы и немного текста, предлагающего ссылку на эту страницу.

+ +

Код JavaScript

+ +

Посмотрим на  JavaScript code. Разобъем его на части, для упрощения объяснения.

+ +

Инициализация

+ +

Начнем с обертки всего скрипта в анонимную функцию, во избежании конфликтов глобальных переменных, затем инициализируем различные нужные  переменные.

+ +
(function() {
+  var width = 320;    // Этим создадим ширину фотографии
+  var height = 0;    // Это будет вычисляться на основе входящего потока
+
+  var streaming = false;
+
+  var video = null;
+  var canvas = null;
+  var photo = null;
+  var startbutton = null;
+ +

Все переменные выше:

+ +
+
width
+
Какой бы не был размер входящего видео, мы намерены масштабировать результирующее изображение к ширине в 320 px.
+
height
+
Результирующая высота изображения будет вычисляться на основе переданной ширины и соотношению сторон потока с камеры.
+
streaming
+
Указывает на текущую активность видеопотока.
+
video
+
Будет содержать ссылку на элемент {{HTMLElement("video")}}  после загрузки страницы.
+
canvas
+
Содержит ссылку на элемент  {{HTMLElement("canvas")}} после загрузки страницы.
+
photo
+
Содержит ссылку на элемент  {{HTMLElement("img")}} после загрузки страницы.
+
startbutton
+
Содержит ссылку на элемент  {{HTMLElement("button")}} после загрузки страницы, используюется для старта захвата.
+
+ +

Функция  startup()

+ +

Функция startup() запускается, когда страница закончила загрузку, благодаря установке {{domxref("window.addEventListener()")}}. Работа функции состоит в том, что бы запросить доступ у пользователя к его камере, инициализировать элемент  {{HTMLElement("img")}} в значение по умолчанию, и установить обработчики событий, необходимых для получения каждого видеокадра с камеры, запускать захват изображения, при нажатии на кнопку.

+ +

Получаем ссылки на элементы

+ +

Сначала, получим ссылки на основные элементы, доступ к которым нам необходим.

+ +
  function startup() {
+    video = document.getElementById('video');
+    canvas = document.getElementById('canvas');
+    photo = document.getElementById('photo');
+    startbutton = document.getElementById('startbutton');
+ +

Получаем медиапоток 

+ +

Следующая задача - получение медиапотока:

+ +
    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
+    .then(function(stream) {
+        video.srcObject = stream;
+        video.play();
+    })
+    .catch(function(err) {
+        console.log("An error occurred: " + err);
+    });
+
+ +

Здесь мы вазываем метод  {{domxref("MediaDevices.getUserMedia()")}} , запрашивая медиапоток без аудиопотока (audio : false). Он возвращает промис, на котором мы определяем методы успешного и не успешного выполнений.

+ +

Успешное выполнение промиса передает объект потока( stream ) в качестве параметра функции метода then()., который присваевается свойству srcObject элемента {{HTMLElement("video")}}, направляя поток в него.

+ +

Как только поток связан с элементом <video> ,  запускаем его воспроизведение, вызовом метода HTMLMediaElement.play().

+ +

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

+ +

Обработка события начала воспроизведения

+ +

После момента вызова метода  HTMLMediaElement.play() на элементе {{HTMLElement("video")}}, возникает промежуток времени до начала воспроизведения видеопотока. Для недопущения блокирования интерфейса пользователя в это промежуток, нужно установить обработчик события {{event("canplay")}} элемента video , который сработает, когда элемент начнет воспроизведение видеопотока. В этот момент все свойства элемента video конфигурируются на основе формата потока.

+ +
    video.addEventListener('canplay', function(ev){
+      if (!streaming) {
+        height = video.videoHeight / (video.videoWidth/width);
+
+        video.setAttribute('width', width);
+        video.setAttribute('height', height);
+        canvas.setAttribute('width', width);
+        canvas.setAttribute('height', height);
+        streaming = true;
+      }
+    }, false);
+ +

Функциональность обработчика не будет запущена, если он запускается повторно. Это отслеживает переменная streaming , которая содержит значение  false при первом запуске обработчика.

+ +

Если это действительно первый запуск, мы устанавливаем высоту видео на основе разницы в размере между фактическим размером видео - video.videoWidth и шириной, на которую мы собираемся его визуализировать - width

+ +

Наконец, свойства width и height элементов video и canvas устанавливаются так, чтобы соответствовать друг другу, вызывая метод {{domxref("Element.setAttribute()")}} на каждом из двух свойств каждого элемента, и, при необходимости, устанавливая ширину и высоту. Наконец, мы установили для  переменной streaming значение true, чтобы предотвратить случайное повторное выполнение этого установочного кода.

+ +

Обработка нажатий кнопки

+ +

Для захвата кадра, пользователь каждый раз нажимает кнопку  startbutton, нужно добавить обработчик события кнопки, для его вызова при возникновении события  {{event("click")}} :

+ +
    startbutton.addEventListener('click', function(ev){
+      takepicture();
+      ev.preventDefault();
+    }, false);
+ +

Метод прост, он вызывает функцию  takepicture(), определяемую ниже в секции  {{anch("Capturing a frame from the stream")}}, затем вызывает метод {{domxref("Event.preventDefault()")}} на полученном объекте события, для предотвращения действия обработки события более одного раза.

+ +

Завершение метода  startup() 

+ +

Еще пара строк кода в методе startup():

+ +
    clearphoto();
+  }
+ +

Вызов метода clearphoto() описывается в секции {{anch("Clearing the photo box")}}.

+ +

Отчистка бокса для фотографии

+ +

Очистка бокса фотографии включает создание изображения, а затем преобразование его в формат, используемый элементом {{HTMLElement ("img")}}, который отображает последний снятый кадр. Код ниже:

+ +
  function clearphoto() {
+    var context = canvas.getContext('2d');
+    context.fillStyle = "#AAA";
+    context.fillRect(0, 0, canvas.width, canvas.height);
+
+    var data = canvas.toDataURL('image/png');
+    photo.setAttribute('src', data);
+  }
+ +

Начнем с получения ссылки на скрытый элемент {{HTMLElement ("canvas")}}, который мы используем для рендеринга за пределами экрана. Затем мы устанавливаем свойсто fillStyle в  #AAA ( светло-серый) и заполняем весь холст этим цветом, вызывая метод {{domxref("CanvasRenderingContext2D.fillRect()","fillRect()")}}.

+ +

Наконец, в этой функции мы конвертируем canvas в изображение PNG и вызываем метод {{domxref("Element.setAttribute", "photo.setAttribute()")}} отображая захваченный цветовой фон в элементе изображения (бокса для фотографии).

+ +

Захват кадра из видеопотока

+ +

Последняя функция, требующая определения и являющаяся основным смыслом всего примера - takepicture() , которая захватывает текущий видеокадр, конвертирует его в формат PNG, и отображает его в блоке отображения кадра. Код её ниже:

+ +
  function takepicture() {
+    var context = canvas.getContext('2d');
+    if (width && height) {
+      canvas.width = width;
+      canvas.height = height;
+      context.drawImage(video, 0, 0, width, height);
+
+      var data = canvas.toDataURL('image/png');
+      photo.setAttribute('src', data);
+    } else {
+      clearphoto();
+    }
+  }
+ +

Как и в случае, когда нам нужно работать с содержимым canvas, мы начинаем с {{domxref("CanvasRenderingContext2D","2D drawing context")}}  для скрытого canvas.

+ +

Затем, если ширина и высота не равны нулю (имеется в виду, что есть, по крайней мере, потенциально допустимые данные изображения), мы устанавливаем ширину и высоту canvas, чтобы они соответствовали ширине захваченного кадра, затем вызываем метод {{domxref("CanvasRenderingContext2D.drawImage()", "drawImage()")}} , что бы отрисовать текущий кадр видео в контексте canvas, заполнив весь холст изображением кадра.

+ +
+

Примечание : Используется факт того, что интерфейс {{domxref("HTMLVideoElement")}} похож на интерфейс {{domxref("HTMLImageElement")}} для любых API , которые принимают HTMLImageElement в качестве параметра, с текущим кадром видео, представленным как содержимое изображения.

+
+ +

Как тоько  canvas будет содержать захваченное видео, конвертируем его в  PNG формат, вызывая метод {{domxref("HTMLCanvasElement.toDataURL()")}} на нем; наконец вызываем метод {{domxref("Element.setAttribute", "photo.setAttribute()")}} отображая захваченное изображение в элементе изображения (бокса фотографии).

+ +

Если подходящее изображение не доступно (то есть, width и height равны  0), отчищаем содержимое элемента изображения, вызывая метод clearphoto().

+ +

Приколы с фильтрами

+ +

Поскольку мы снимаем изображения с веб-камеры пользователя, захватывая кадры из элемента {{HTMLElement("video")}} , можно легко применить фильтры и забавные эффекты к элементу video. Оказывается, любые CSS-фильтры, которые вы применяете к элементу с помощью свойства {{cssxref ("filter")}}, влияют на захваченную фотографию.Эти фильтры могут варьироваться от простых (делая изображение черно-белым) до экстремальных (размытие по Гауссу и вращение оттенка).

+ +

Вы можете экспериментировать с этими эффектами, используя, например, инструмент разработчика FirefoxYou  редактор стилей; смотрим Редактирование с CSS фильтрами о подробностях выполнения.

+ +

Использование определенных устройств

+ +

При необходимости вы можете ограничить набор разрешенных источников видео, определенным устройством или набором устройств. Для этого нужно вызвать метод {{domxref("navigator.mediaDevices.enumerateDevices()")}}. Когда промис разрешиться массивом объектов {{domxref("MediaDeviceInfo")}} , описывающих доступные устройства , выберите те, которым хотите разрешить доступ и укажите соответствующий идентификатор устройства {{domxref("MediaTrackConstraints.deviceId", "deviceId")}} или несколько deviceId в объекте  {{domxref("MediaTrackConstraints")}} , переданном в  {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}}.

+ +

Смотри так же

+ + -- cgit v1.2.3-54-g00ecf