---
title: Захват кадров с WebRTC
slug: Web/API/WebRTC_API/Taking_still_photos
tags:
  - Захват WebRTC
translation_of: Web/API/WebRTC_API/Taking_still_photos
---
<p><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="/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="/en-US/docs/Tools/Style_Editor">редактор стилей</a>; смотрим <a href="/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>