aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/api/webrtc_api/taking_still_photos/index.html
blob: cdd65b56e938a05d98752714ee65adc3eb223de5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
---
title: Захват кадров с WebRTC
slug: Web/API/WebRTC_API/Taking_still_photos
tags:
  - Захват WebRTC
translation_of: Web/API/WebRTC_API/Taking_still_photos
---
<p dir="rtl"><span><span><span><span>{{WebRTCSidebar}}</span></span></span></span></p>

<p><span class="seoSummary"><span><span><span><span>В этой статье объясняется как использовать WebRTC для получения доступа к камере компьютера или мобильного устройства, и захвата кадров с их помощью. </span></span></span></span></span><a href="https://mdn-samples.mozilla.org/s/webrtc-capturestill"><span><span><span><span>Ознакомьтесь с примером,</span></span></span></span></a><span><span><span><span> а затем узнайте как это работает.</span></span></span></span></p>

<p><img alt="Uz WebRTC balstīta attēla uztveršanas lietotne - kreisajā pusē un bez tīmekļa kameras uzņemšanas video straumē un poga" src="https://mdn.mozillademos.org/files/10281/web-rtc-demo.png" style="display: block; height: 252px; margin: 0 auto; width: 677px;"></p>

<p><span><span><span><span>Перейдите непосредственно </span></span></span></span><a class="external" href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill" rel="noopener"><span><span><span><span>к коду на Github</span></span></span></span></a><span><span><span><span> , при желании.</span></span></span></span></p>

<h2 id="Разметка_HTML"><span><span><span><span>Разметка HTML</span></span></span></span></h2>

<p><a class="external" href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill/index.html" rel="noopener"><span><span><span><span>Наш HTML интерфейс</span></span></span></span></a><span><span><span><span> состоит из двух секций : панель отображения видео потока, из которого будет производиться захват и панель отображения результата захвата. Каждая панель имеет свой элемент </span></span></span><span><span><span>{{HTMLElement ("div")}}, для облегчения стилизации и управления.</span></span></span></span></p>

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

<pre>  &lt;div class="camera"&gt;
    &lt;video id="video"&gt;Video stream not available.&lt;/video&gt;
    &lt;button id="startbutton"&gt;Take photo&lt;/button&gt;
  &lt;/div&gt;</pre>

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

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

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

<pre>  &lt;canvas id="canvas"&gt;
  &lt;/canvas&gt;
  &lt;div class="output"&gt;
    &lt;img id="photo" alt="The screen capture will appear in this box."&gt;
  &lt;/div&gt;</pre>

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

<h2 id="Код_JavaScript">Код JavaScript</h2>

<p>Посмотрим на  <a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill/capture.js" rel="noopener">JavaScript code</a>. Разобьем его на части, для упрощения объяснения.</p>

<h3 id="Инициализация">Инициализация</h3>

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

<pre>(function() {
  var width = 320;    // Этим создадим ширину фотографии
  var height = 0;    // Это будет вычисляться на основе входящего потока

  var streaming = false;

  var video = null;
  var canvas = null;
  var photo = null;
  var startbutton = null;</pre>

<p>Все переменные выше:</p>

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

<h3 id="Функция_startup">Функция  startup()</h3>

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

<h4 id="Получаем_ссылки_на_элементы">Получаем ссылки на элементы</h4>

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

<pre>  function startup() {
    video = document.getElementById('video');
    canvas = document.getElementById('canvas');
    photo = document.getElementById('photo');
    startbutton = document.getElementById('startbutton');</pre>

<h4 id="Получаем_медиапоток">Получаем медиапоток </h4>

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

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

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

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

<p>Как только поток связан с элементом <code>&lt;video&gt;</code> ,  запускаем его воспроизведение, вызовом метода <code><a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#play">HTMLMediaElement.play()</a></code>.</p>

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

<h4 id="Обработка_события_начала_воспроизведения">Обработка события начала воспроизведения</h4>

<p>После момента вызова метода  <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#play">HTMLMediaElement.play()</a></code> на элементе {{HTMLElement("video")}}, возникает промежуток времени до начала воспроизведения видеопотока. Для недопущения блокирования интерфейса пользователя в это промежуток, нужно установить обработчик события {{event("canplay")}} элемента <code>video</code> , который сработает, когда элемент начнет воспроизведение видеопотока. В этот момент все свойства элемента <code>video</code> конфигурируются на основе формата потока.</p>

<pre>    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);</pre>

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

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

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

<h4 id="Обработка_нажатий_кнопки">Обработка нажатий кнопки</h4>

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

<pre>    startbutton.addEventListener('click', function(ev){
      takepicture();
      ev.preventDefault();
    }, false);</pre>

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

<h4 id="Завершение_метода_startup">Завершение метода  startup() </h4>

<p>Еще пара строк кода в методе <code>startup()</code>:</p>

<pre>    clearphoto();
  }</pre>

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

<h3 id="Отчистка_бокса_для_фотографии">Отчистка бокса для фотографии</h3>

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

<pre>  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);
  }</pre>

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

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

<h3 id="Захват_кадра_из_видеопотока">Захват кадра из видеопотока</h3>

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

<pre>  function takepicture() {
    var context = canvas.getContext('2d');
    if (width &amp;&amp; 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();
    }
  }</pre>

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

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

<div class="blockIndicator note">
<p><strong>Примечание :</strong> Используется факт того, что интерфейс {{domxref("HTMLVideoElement")}} похож на интерфейс {{domxref("HTMLImageElement")}} для любых API , которые принимают <code>HTMLImageElement</code> в качестве параметра, с текущим кадром видео, представленным как содержимое изображения.</p>
</div>

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

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

<h2 id="Приколы_с_фильтрами">Приколы с фильтрами</h2>

<p>Поскольку мы снимаем изображения с веб-камеры пользователя, захватывая кадры из элемента {{HTMLElement("video")}} , можно легко применить фильтры и забавные эффекты к элементу <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">video</span></font>. Оказывается, любые CSS-фильтры, которые вы применяете к элементу с помощью свойства {{cssxref ("filter")}}, влияют на захваченную фотографию.Эти фильтры могут варьироваться от простых (делая изображение черно-белым) до экстремальных (размытие по Гауссу и вращение оттенка).</p>

<p>Вы можете экспериментировать с этими эффектами, используя, например, инструмент разработчика FirefoxYou  <a href="https://wiki.developer.mozilla.org/en-US/docs/Tools/Style_Editor">редактор стилей</a>; смотрим <a href="https://wiki.developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Edit_CSS_filters">Редактирование с CSS фильтрами</a> о подробностях выполнения.</p>

<h2 id="Использование_определенных_устройств">Использование определенных устройств</h2>

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

<h2 id="Смотри_так_же">Смотри так же</h2>

<ul>
 <li><a href="https://mdn-samples.mozilla.org/s/webrtc-capturestill">Пробуем пример</a></li>
 <li><a href="https://github.com/mdn/samples-server/tree/master/s/webrtc-capturestill">Примеры на Github</a></li>
 <li>{{domxref("Navigator.mediaDevices.getUserMedia()")}}</li>
 <li>{{SectionOnPage("/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images","Использование изображений")}}</li>
 <li>{{domxref("CanvasRenderingContext2D.drawImage()")}}</li>
</ul>