MediaDevices.getUserMedia()

Этот перевод не завершён. Пожалуйста, помогите перевести эту статью с английского

Метод MediaDevices.getUserMedia(), при выполнении, вызывает всплывающий диалог, запрашивающий разрешение пользователя на использование медиа устройства (камера, микрофон). Результат возвращает промис, содержащий поток, который  состоит из треков (дорожек), содержащих требуемые медиа типы. Этот поток может включать, к примеру, видеотрек, созданный либо аппаратным средством, либо виртуальным видеоисточником, такими как камера, устройство видеозаписи, сервис обмена изображениями и т.д);  аудиотрек, созданный физическим или виртуальным аудиоисточником, к примеру, микрофоном, аналого-цифровым преобразователем звуков и возможно иные типы треков.

Он возвращает Promise , который, в случае согласия пользователя, разрешается MediaStream объектом. Если пользователь отказывает в разрешении, или медиа устройсто не доступно, тогда промис отменяется с объектами типа NotAllowedError или NotFoundError соответственно.

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

Обычно, разработчик получает доступ к  единственному экземпляру объекта MediaDevices , используя navigator.mediaDevices.getUserMedia()  метод, возвращающий поток:

async function getMedia(constraints) {
  let stream = null;

  try {
    stream = await navigator.mediaDevices.getUserMedia(constraints);
    /* используем поток */
  } catch(err) {
    /* обработка ошибки */
  }
}

Тот же результат, но используя тип промиса :

navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
  /* используем поток */
})
.catch(function(err) {
  /* обработка ошибки */
});

Примечание : Если документ загружен не безопасно, значение navigator.mediaDevices будет undefined, и нельзя будет использовать метод  getUserMedia(). Смотри  Security для дополнительной информации о дальнейших вопросах безопасности, связанной с использованием метода getUserMedia().

Синтаксис

var promise = navigator.mediaDevices.getUserMedia(constraints);

Параметры

constraints (ограничения)

Объект  MediaStreamConstraints, определяющий запрашиваемые медиатипы, вместе с требованиями для каждого типа.

Тип параметра constraints (ограничения) - это объект типа MediaStreamConstraints с двумя членами: video и audio, описывающие запрашиваемые медиатипы. Один или оба должны определяться разработчиком. Если браузер не сможет обнаружить медиатреки определяемого типа, которые соответствуют переданным ограничениям, возвращаемый методом промис отменяется с объектом NotFoundError.

Следующий отрывок кода запрашивает и audio и video типы  без дополнительных условий:

{ audio: true, video: true }

Если определяется значение true для медиатипа, результирующий поток обязательно будет иметь в себе запрошенный медиатип. Если ни один из типов не включается в запрос, вызов метода  getUserMedia() приведет к ошибке.

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

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

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

Для минимального, максимального и точного определения значения можно использовать ключевые слова min, max, или exac. Следующий пример запрашивает минимальное разрешение камеры 1280x720:

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

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

Различие поведения происходит по причине того, что ключевые слова min, max, и exact  являются обязательными к выполнению. В то время как простые значения и ключевое слово  ideal - не обязательные к выполнению. Ниже, полный пример:

{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}

Значение свойства ideal  имеет приоритет. Это означает, что браузер будет пытаться обнаружить настройки с наименьшей  fitness distance (удовлетворяющей разницей) от переданных идеальных значений. Т.е. пытается найти самое близкое к переданному значение.

Простые значения работают как идеальные, поэтому один из первых примеров выше можно переписать, используя свойство  ideal :

{
  audio: true,
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
}

Не все значения ограничений являются числовыми. К примеру, на мобильных устройствах следующий отрывок будет запрашивать переднюю камеру (если присутствует) , поэтому в свойстве facingMode указано строковое значение user:

{ audio: true, video: { facingMode: "user" } }

Запрос обратной (задней) камеры:

{ audio: true, video: { facingMode: { exact: "environment" } } }

Следующее строковое свойство -  deviceId (идентификатор устройства). Его значение может быть получено из  метода mediaDevices.enumerateDevices(), возвращающего список, имеющихся на машине устройств, с их идентификаторами, и может быть использовано для запроса определнного устройства по идентификатору этого устройства:

{ video: { deviceId: идентификаторНужнойКамеры } }

Код выше вернет запрашиваемую камеру или другую камеру, если требуемая камера недоступна. Для получения доступа к потоку только определенной камеры, без альтернативы, используется свойство  exact (точно) :

{ video: { deviceId: { exact: идентификаторНужнойКамеры } } }

Возвращаемое значение

Промис Promise, при удачном запросе требуемого медиа устройства разрешается  объектом типа MediaStream .

Исключения (Ошибки)

При неудачном запросе медиа устройства, возвращаемый промис Promise отменяется объектом типа  ошибки DOMException. Возможными ошибками могут быть типы:

AbortError (Прервано)
Хотя пользователь и операционная система предоставили доступ к аппаратному устройству, и не возникло проблем с оборудованием, которые могли бы вызвать NotReadableError, возникла некоторая проблема, которая не позволила использовать устройство.
NotAllowedError (Доступ не разрешен)
Возникает если, одно или несколько запрашиваемых устройств не можут быть использованы в настоящее время. Это происходит тогда, когда контекст браузера является не безопасным (страница была загружена используя протокол HTTP вместо HTTPS), а также, если пользователь не разрешил доступ текущему экземпляру браузера к устройству, пользователь отказал в доступе в текущей сессии, или пользователь отказал в доступе к медиаустройствам глобально. Для браузеров, которые поддерживают управление медиаразрешениями с помощью  Feature Policy, такая ошибка возвращается если Feature Policy не сконфигурирована для разрешение доступа к медиаустройству или устройствам
 Более старые версии спецификации использовали вместо этого SecurityError. SecurityError имеет новое значение.
NotFoundError (Не найдено)
Возникает если, типы мидиа треков, удовлетворяющие переданным значениям, не найдены.
NotReadableError (Не читается)
Хотя пользователь и предоставил разрешение на использование соответствующих устройств, произошла аппаратная ошибка на уровне операционной системы, браузера или веб-страницы, которая препятствовала доступу к устройству..
OverconstrainedError (за границами ограничений)
Возникает если, в результате указанных ограничений не было найдено устройств, отвечающих запрошенным критериям. Ошибка является объектом типа OverconstrainedError и имеет свойство constraint, строковое значение которого является именем ограничения, которое было невозможно встретить, и свойство message, содержащее читаемую человеком строку, объясняющую проблему.
Ошибка может возникнуть даже, если пользователь еще не выдал разрешение на использование устройства, использующиеся как поверхность для идентификации отпечатка пальца.
SecurityError (ошибка безопасности)
Возникает если, медиа поддержка отключена в Document на котором был вызван метод  getUserMedia(). Механизм по которому медиа поддержка включается и отключается находиться в компетенции браузера пользователя.
TypeError (ошибка типа)
Возникает если, список ограничений пустой или все ограничения установлены в  false. Так же это происходит, если пытаться вызвать метод getUserMedia() в небезопасном контексте, поскольку в нем  navigator.mediaDevices равно undefined.

Конфеденциальность и безопасность

Поскольку  API могут существенно затрагивать  вопросы конфеденциальности, спецификация getUserMedia() предъявляет широкий спекрт требований защиты конфеденциальности и безопасности, которым современные браузеры обязаны следовать.

getUserMedia() -  это мощная функция, которая может быть использована только в безопасном контексте . В небезопасном контексте, navigator.mediaDevices равно undefined, предотвращая достук к методу getUserMedia(). Безопасный контекст  - это, если кратко, страница, загружаемая по протоколу HTTPS или  file:/// URL схеме, или страница, загружаемая из localhost.

В нем обязательно запрашивается пользовательское разрешение к доступу audio или video источникам. Только контекст документа верхнего уровня, проверенного источника может запросить доступ, используя метод getUserMedia(). Если контексту верхнего уровня явно не дается разрешение для данного <iframe> используя Feature Policy, пользователю никогда не будет предложено выдать разрешение на использование устройств, пока пользователь самостоятельно не отменит запрет в настройках браузера.

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

Конфеденциальность пользователя

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

Не менее важны правила уведомления. Браузеры обязательно отображают индикатор, который указывает на использование камеры или микрофона, выше или сверху любого, уже имеющегося индикатора аппартного средства (если присутствует). Они также должны отображать индикатор того, что разрешение уже выдано для использования устройств, даже, если устройства не используются в настоящий момент.

К примеру, в Firefox, в панели адреса отображается красная пульсирующая иконка, указывающая на использование аппаратного средства. Серая иконка указывает на выданное разрешение, но не использование аппаратного средства в данный момент. Физический (явный) свет устройства указывает на текущее использование (активизацию) аппаратного средства. Если отключить камеру програмно, свет активности камеры отключится, указывая на то, что она запись не производит, без отмены разрешение на использования, и включиться после запуска камеры в работу.

Безопасность

Существуют несколько способов управлением безопасностью и контролем в  user agent. Для этого можно использовать метод getUserMedia() , который возвращает объекты ошибок, относящиеся к безопасности.

 Примечание :  Модель безопасности для метода getUserMedia() находиться в процессе разработки. Первоначально спроектированный механизм безопасности находиться в процессе замещения Feature Policy, поэтому различные браузеры имеют разный уровень поддержки безопасности, используют различные механизмы. Вам необходимо осторожно тестировать свой код на различных устройствах и браузерах, чтобы удостовериться в его уверенной работоспособности.

Feature Policy (Функцианальная политика)

Функция управление безопасностью (Feature Policy) протокола HTTP находиться в процессе введения в браузеры, с поддержкой, доступной в различной степени во многих браузерах (но не всегда включенной в настройках по умолчанию, как в   Firefox). Метод getUserMedia() - один из методов, требующий использования функциональной политики и вашему коду нужно быть готовым к работе с ним. К примеру, чтобы метод был доступен в документах не высокого уровня, разработчику нужно использовать либо атрибут allow на элементе <iframe> , который использует getUserMedia(), либо  Feature-Policy заголовок для страниц , передающихся с сервера,  которые используют getUserMedia().

Два разрешения, которые обращаются к getUserMedia() - camera и microphone.

К примеру, строка  HTTP заголовка позволит использовать камеру документу и любым встроенныем  <iframe> элементам, которые загружаются  из одного источника:

Feature-Policy: camera 'self'

Строка ниже, запрашивает доступ к микрофону для текушего источника и указанном в заголовке источнике https://developer.mozilla.org:

Feature-Policy: microphone 'self' https://developer.mozilla.org

Если используется getUserMedia() внутри элемента <iframe>, можно запросить разрешение только для этого фрейма, которое явно более безопасее, чем  запрашивать более общее разрешение. Здесь нам нужно использовать и камеру и микрофон:

<iframe src="https://mycode.example.net/etc" allow="camera;microphone">
</iframe>

Прочитайте наше руководство Применение функциональной политики , изучив подробнее то, как это работает.

Безопасность на основе шифрования

Метод getUserMedia() доступен только для безопасных контекстов. Безопасный контекст - это уверенность браузера в том, что  документ был загружен безопасно, используя  HTTPS/TLS, и имеет ограниченную подверженность небезопасным контекстам. Если документ не загружен в безопасном контексте, свойство  navigator.mediaDevices равно undefined, делая невозможным доступ к методу getUserMedia(). Попытка получить доступ в такой ситуации приведет к ошибке TypeError.

Безопасность источника документа

Существуют несколько небезопасных способа загрузить документ, который может попытыться вызвать метод getUserMedia(). Ниже представлены примеры ситуаций, в которых getUserMedia() не разрешается вызывать:

  • Документ, загруженный в песочницу <iframe> элемента не может вызвать getUserMedia(), до тех пор пока, на элементе <iframe> находиться атрибут  sandbox, установленный в значение allow-same-origin.
  • Документ, загруженный по протоколам data:// или blob:// в URL-адресе, не имеющий источника  (такими являются типы URL-ов, введенные пользователями в строке адреса браузера) не может вызвать getUserMedia(). Подобные типы  URL-ов, загружаемые из JavaScript кода, наследуют разрешения скрипта.
  • Иные ситуации, документы которых не имеют источника, к примеру элемент, содержащий атрибут srcdoc, использующийся для указания содержимого фрейма.

Примеры

Ширина и высота

Этот пример выбирает указанное разрешение камеры и присваивает ссылку на объект MediaStream свойству srcObject элемента video .

// Выбирает разрешение камеры близкое к 1280x720.
var constraints = { audio: true, video: { width: 1280, height: 720 } }; 

navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
  var video = document.querySelector('video');
  video.srcObject = mediaStream;
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) { console.log(err.name + ": " + err.message); }); // always check for errors at the end.

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

Ниже, находятся примеры, использующие navigator.mediaDevices.getUserMedia(), с полифилами для работы в старых браузерах. Обратите внимание, что эти полифилы не корректируют все различия в синтаксисе, и не работают во всех браузерах. Рекомендуется использовать библиотеку  adapter.js , как производственный полифил.

// Старые браузеры могут не реализовывать свойство mediaDevices, 
//поэтому вначале присваеваем свойству ссылку на пустой объект

if (navigator.mediaDevices === undefined) {
  navigator.mediaDevices = {};
}

// Некоторые браузеры частично реализуют свойство mediaDevices, поэтому 
//мы не можем присвоить ссылку на объект свойству getUserMedia, поскольку 
//это переопределит существующие свойства. Здесь, просто добавим свойство 
//getUserMedia , если оно отсутствует.

if (navigator.mediaDevices.getUserMedia === undefined) {
  navigator.mediaDevices.getUserMedia = function(constraints) {

    // Сначала, если доступно, получим устаревшее getUserMedia
    
  var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

   //Некоторые браузеры не реализуют его, тогда вернем отмененный промис
   // с ошибкой для поддержания последовательности интерфейса 
  
    if (!getUserMedia) {
      return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
    }

    // Иначе, обернем промисом устаревший navigator.getUserMedia 

    return new Promise(function(resolve, reject) {
      getUserMedia.call(navigator, constraints, resolve, reject);
    });
  }
}

navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(function(stream) {
  var video = document.querySelector('video');
  // Устаревшие браузеры могут не иметь свойство srcObject
  if ("srcObject" in video) {
    video.srcObject = stream;
  } else {
    // Не используем в новых браузерах
    video.src = window.URL.createObjectURL(stream);
  }
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});

Частота кадров

В некоторых случаях может быть желательна более низкая частота кадров, например, при передаче WebRTC с ограничениями полосы пропускания

var constraints = { video: { frameRate: { ideal: 10, max: 15 } } };

Передняя и задняя камеры

На мобильных устройствах.

var front = false;
document.getElementById('flip-button').onclick = function() { front = !front; };

var constraints = { video: { facingMode: (front? "user" : "environment") } };

Спецификации

Спецификация Статус Комментарий
Media Capture and Streams
Определение 'MediaDevices.getUserMedia()' в этой спецификации.
Кандидат в рекомендации Начальное определение

Совместимость с браузерами

Update compatibility data on GitHub
КомпьютерыМобильные
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome для AndroidFirefox для AndroidOpera для AndroidSafari on iOSSamsung Internet
getUserMediaChrome Полная поддержка 53
Замечания
Полная поддержка 53
Замечания
Замечания If you need this capability before version 53, refer to navigator.webkitGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Edge Полная поддержка 12Firefox Полная поддержка 36
Замечания
Полная поддержка 36
Замечания
Замечания If you need this capability before version 36, refer to navigator.mozGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Замечания Before Firefox 55, getUserMedia() incorrectly returns NotSupportedError when the list of constraints specified is empty, or has all constraints set to false. Starting in Firefox 55, this situation now correctly calls the failure handler with a TypeError.
Замечания When using the Firefox-specific video constraint called mediaSource to request display capture, Firefox 66 and later consider values of screen and window to both cause a list of screens and windows to be shown.
Замечания Starting in Firefox 66, getUserMedia() can no longer be used in sandboxed <iframe>s or data URLs entered in the address bar by the user.
IE Нет поддержки НетOpera Полная поддержка 40
Замечания
Полная поддержка 40
Замечания
Замечания If you need this capability before version 40, refer to navigator.webkitGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Safari Полная поддержка 11WebView Android Полная поддержка 53Chrome Android Полная поддержка 53
Замечания
Полная поддержка 53
Замечания
Замечания If you need this capability before version 53, refer to navigator.webkitGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Firefox Android Полная поддержка 36
Замечания
Полная поддержка 36
Замечания
Замечания If you need this capability before version 36, refer to navigator.mozGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Замечания Before Firefox 55, getUserMedia() incorrectly returns NotSupportedError when the list of constraints specified is empty, or has all constraints set to false. Starting in Firefox 55, this situation now correctly calls the failure handler with a TypeError.
Замечания When using the Firefox-specific video constraint called mediaSource to request display capture, Firefox 66 and later consider values of screen and window to both cause a list of screens and windows to be shown.
Замечания Starting in Firefox 66, getUserMedia() can no longer be used in sandboxed <iframe>s or data URLs entered in the address bar by the user.
Opera Android Полная поддержка 41
Замечания
Полная поддержка 41
Замечания
Замечания If you need this capability before version 41, refer to navigator.webkitGetUserMedia, a prefixed form of the deprecated navigator.getUserMedia API.
Safari iOS Полная поддержка 11Samsung Internet Android Полная поддержка 6.0
Secure context requiredChrome Полная поддержка 53Edge Полная поддержка 79Firefox Полная поддержка 68IE Нет поддержки НетOpera Полная поддержка 40Safari ? WebView Android Полная поддержка 53Chrome Android Полная поддержка 53Firefox Android Полная поддержка 68Opera Android Полная поддержка 41Safari iOS ? Samsung Internet Android Полная поддержка 6.0

Легенда

Полная поддержка  
Полная поддержка
Нет поддержки  
Нет поддержки
Совместимость неизвестна  
Совместимость неизвестна
Смотрите замечания реализации.
Смотрите замечания реализации.

Смотри так же