Spec-Zone.ru › Web APIs

Возможности, ограничения и настройки

Базовая Широко доступная

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с сентября 2017 года.

  • Подробнее
  • Полная совместимость
  • Отправить отзыв

В этой статье рассматриваются два понятия: ограничения и возможности, а также настройки медиа, и приводится пример, который мы называем Утилитой настройки ограничений. Утилита настройки ограничений позволяет экспериментировать с результатами применения различных наборов ограничений к аудио- и видеопотокам, поступающим с устройств ввода A/V компьютера (например, веб-камеры и микрофона).

Исторически, написание скриптов для веб-приложений, которые тесно взаимодействуют с веб-API, имело хорошо известную проблему: часто ваш код должен знать, существует ли API и, если да, каковы его ограничения на агенте пользователя, на котором он выполняется. Выяснение этого часто было сложной задачей, и обычно она включала в себя проверку агента пользователя (или браузера), его версии, проверку существования определенных объектов, попытку определить, работают ли различные вещи, и определение возникающих ошибок и так далее. В результате получался очень хрупкий код или зависимость от библиотек, которые решают эти вопросы за вас, а затем реализуют полифилы для исправления недостатков реализации от вашего имени.

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

Обзор

Процесс работает следующим образом (используя MediaStreamTrack в качестве примера):

  1. При необходимости вызовите MediaDevices.getSupportedConstraints(), чтобы получить список поддерживаемых ограничений, который сообщает вам, какие настраиваемые свойства известны браузеру. Это не всегда необходимо, так как любые, которые не известны, будут проигнорированы при их указании — но если вам нужны какие-либо, без которых вы не можете обойтись, вы можете начать с проверки их наличия в списке.
  2. После того как скрипт узнает, поддерживается ли нужное свойство или свойства, он может проверить возможности API и его реализации, просмотрев объект, возвращаемый методом getCapabilities() трека; этот объект перечисляет каждое поддерживаемое ограничение и значения или диапазон значений, которые поддерживаются.
  3. Наконец, вызывается метод applyConstraints() трека, чтобы настроить API в соответствии с желанием, указав значения или диапазон значений, которые нужно использовать для любых настраиваемых свойств, по которым у вас есть предпочтения.
  4. Метод getConstraints() трека возвращает набор ограничений, переданных в последний вызов applyConstraints(). Это может не отражать фактическое текущее состояние трека из-за свойств, значения которых нужно было скорректировать, и потому что значения по умолчанию платформы не отображаются. Для получения полного представления о текущей конфигурации трека используйте getSettings().

В API захвата медиа и потоков настраиваемые свойства имеют как MediaStream, так и MediaStreamTrack.

Определение, поддерживается ли ограничение

Если вам нужно узнать, поддерживает ли агент пользователя данное ограничение, вы можете узнать это, вызвав navigator.mediaDevices.getSupportedConstraints(), чтобы получить список настраиваемых свойств, которые знает браузер, например так:

const supported = navigator.mediaDevices.getSupportedConstraints();

document.getElementById("frameRateSlider").disabled = !supported["frameRate"];

В этом примере извлекаются поддерживаемые ограничения, и элемент управления, позволяющий пользователю настроить частоту кадров, отключается, если ограничение frameRate не поддерживается.

Как определяются ограничения

Одно ограничение — это объект, имя которого соответствует настраиваемому свойству, желаемое значение или диапазон значений которого указываются. Этот объект содержит ноль или более отдельных ограничений, а также необъект под названием advanced, который содержит другой набор нулевых или более ограничений, которые агент пользователя должен удовлетворить, если это возможно. Агент пользователя пытается удовлетворить ограничения в указанном порядке.

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

Запрос конкретного значения для настройки

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

const constraints = {
  width: 1920,
  height: 1080,
  aspectRatio: 1.777777778,
};

myTrack.applyConstraints(constraints);

В этом случае ограничения указывают, что любые значения подходят почти для всех свойств, но желательно стандартное видео высокой четкости (HD) с стандартным соотношением сторон 16:9 соотношение сторон. Нет гарантии, что результирующий трек будет соответствовать какому-либо из этих значений, но агент пользователя должен сделать все возможное для соответствия как можно большему количеству.

Приоритет свойств прост: если запрошенные значения двух свойств взаимоисключают друг друга, то будет использовано первое из них в наборе ограничений. Например, если браузер, на котором выполняется код выше, не может предоставить трек 1920x1080, но может сделать 1920x900, то именно он и будет предоставлен.

Простые ограничения, указывающие одно значение, всегда обрабатываются как необязательные. Агент пользователя постарается предоставить то, что вы запрашиваете, но не гарантирует, что полученное значение будет соответствовать. Однако, если вы используете простые значения для свойств при вызове MediaStreamTrack.applyConstraints(), запрос всегда будет выполнен успешно, поскольку эти значения будут рассматриваться как запрос, а не как обязательное требование.

Указание диапазона значений

Иногда любое значение в диапазоне приемлемо для значения свойства. Вы можете указать диапазоны с минимальным и максимальным значениями, а также указать идеальное значение в диапазоне, если это необходимо. Если вы предоставляете идеальное значение, браузер попытается максимально приблизиться к этому значению, учитывая другие указанные ограничения.

const supports = navigator.mediaDevices.getSupportedConstraints();

if (
  !supports["width"] ||
  !supports["height"] ||
  !supports["frameRate"] ||
  !supports["facingMode"]
) {
  // We're missing needed properties, so handle that error.
} else {
  const constraints = {
    width: { min: 640, ideal: 1920, max: 1920 },
    height: { min: 400, ideal: 1080 },
    aspectRatio: 1.777777778,
    frameRate: { max: 30 },
    facingMode: { exact: "user" },
  };

  myTrack
    .applyConstraints(constraints)
    .then(() => {
      /* do stuff if constraints applied successfully */
    })
    .catch((reason) => {
      /* failed to apply constraints; reason is why */
    });
}

Здесь, убедившись, что настраиваемые свойства, для которых должны быть найдены соответствия, поддерживаются (width, height, frameRate, и facingMode), мы устанавливаем ограничения, которые запрашивают ширину не менее 640 и не более 1920 (но предпочтительно 1920), высоту не менее 400 (но предпочтительно 1080), соотношение сторон 16:9 (1.777777778) и частоту кадров не более 30 кадров в секунду. Кроме того, единственным допустимым устройством ввода является камера, направленная на пользователя («селфи-камера»). Если ограничения width, height, frameRate, или facingMode не могут быть выполнены, то промис, возвращаемый applyConstraints(), будет отклонен.

Примечание: Ограничения, которые указаны с использованием одного или нескольких из max, min, или exact всегда рассматриваются как обязательные. Если любое ограничение, использующее одно или несколько из них, не может быть выполнено при вызове applyConstraints(), промис будет отклонен.

Расширенные ограничения

Так называемые расширенные ограничения создаются путем добавления свойства advanced в набор ограничений; значение этого свойства — массив дополнительных наборов ограничений, которые считаются необязательными. Практических случаев применения этой функции мало или нет, и есть интерес к тому, чтобы исключить её из спецификации, поэтому она не будет обсуждаться здесь. Если вы хотите узнать больше, см. раздел 11 спецификации Media Capture and Streams, пример 2.

Проверка возможностей

Вы можете вызвать MediaStreamTrack.getCapabilities(), чтобы получить список всех поддерживаемых возможностей и значений или диапазонов значений, которые каждая из них принимает на текущей платформе и в агенте пользователя. Эта функция возвращает объект, который перечисляет каждое настраиваемое свойство, поддерживаемое браузером, и значение или диапазон значений, которые поддерживаются для каждого из них.

Например, следующий фрагмент кода приведет к тому, что пользователя попросят предоставить разрешение на доступ к локальной камере и микрофону. После получения разрешения будут выведены в консоль объекты MediaTrackCapabilities, которые подробно описывают возможности каждого MediaStreamTrack:

navigator.mediaDevices
  .getUserMedia({ video: true, audio: true })
  .then((stream) => {
    const tracks = stream.getTracks();
    tracks.map((t) => console.log(t.getCapabilities()));
  });

Объект возможностей выглядит примерно так:

{
  "autoGainControl": [
    true,
    false
  ],
  "channelCount": {
    "max": 1,
    "min": 1
  },
  "deviceId": "jjxEMqxIhGdryqbTjDrXPWrkjy55Vte70kWpMe3Lge8=",
  "echoCancellation": [
    true,
    false
  ],
  "groupId": "o2tZiEj4MwOdG/LW3HwkjpLm1D8URat4C5kt742xrVQ=",
  "noiseSuppression": [
    true,
    false
  ]
}

Точное содержимое объекта будет зависеть от браузера и оборудования для медиа.

Применение ограничений

Наиболее распространённый способ использования ограничений — указание их при вызове getUserMedia():

navigator.mediaDevices
  .getUserMedia({
    video: {
      width: { min: 640, ideal: 1920 },
      height: { min: 400, ideal: 1080 },
      aspectRatio: { ideal: 1.7777777778 },
    },
    audio: {
      sampleSize: 16,
      channelCount: 2,
    },
  })
  .then((stream) => {
    videoElement.srcObject = stream;
  })
  .catch(handleError);

В этом примере ограничения применяются в момент getUserMedia() , запрашивая идеальный набор параметров с резервными вариантами для видео.

Примечание: Вы можете указать один или несколько идентификаторов устройств ввода медиа для ограничения разрешённых источников ввода. Для получения списка доступных устройств можно вызвать navigator.mediaDevices.enumerateDevices(), а затем для каждого устройства, соответствующего требуемым критериям, добавить его deviceId в объект MediaConstraints, который в конечном итоге передаётся в getUserMedia().

Вы также можете изменить ограничения существующего MediaStreamTrack на лету, вызвав метод applyConstraints() трека, передав в него объект, представляющий ограничения, которые вы хотите применить к треку:

videoTrack.applyConstraints({
  width: 1920,
  height: 1080,
});

В этом фрагменте трек видео, на который ссылается videoTrack , обновляется так, чтобы его разрешение максимально приблизилось к 1920x1080 пикселей (1080p высокой чёткости).

Получение текущих ограничений и настроек

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

Получение действующих ограничений

Если в какой-либо момент вам необходимо получить набор ограничений, которые в настоящее время применяются к медиа, вы можете получить эту информацию, вызвав MediaStreamTrack.getConstraints(), как показано в примере ниже.

function switchCameras(track, camera) {
  const constraints = track.getConstraints();
  constraints.facingMode = camera;
  track.applyConstraints(constraints);
}

Эта функция принимает MediaStreamTrack и строку, указывающую режим ориентации камеры, получает текущие ограничения, устанавливает значение MediaTrackConstraints.facingMode на указанное значение, затем применяет обновлённый набор ограничений.

Получение текущих настроек для трека

Если вы не используете точные ограничения (что довольно ограничивает, поэтому убедитесь, что вы это имеете в виду!), нет гарантии, что вы получите именно то, что ожидаете, после применения ограничений. Значения настраиваемых свойств, как они фактически существуют в результирующем медиа, называются настройками. Если вам нужно узнать фактический формат и другие свойства медиа, вы можете получить эти настройки, вызвав MediaStreamTrack.getSettings(). Это возвращает объект, основанный на словаре MediaTrackSettings. Например:

function whichCamera(track) {
  return track.getSettings().facingMode;
}

Эта функция использует getSettings() для получения текущих значений, используемых треком, для настраиваемых свойств, и возвращает значение facingMode.

Пример: Тренировщик ограничений

В этом примере мы создаём тренировщик, который позволяет экспериментировать с ограничениями медиа путём редактирования исходного кода, описывающего наборы ограничений для аудио и видео треков. Затем вы можете применить эти изменения и увидеть результат, включая то, как выглядит поток, и какие фактические настройки медиа установлены после применения новых ограничений.

HTML и CSS для этого примера довольно просты и здесь не показаны. Вы можете ознакомиться с полным примером, нажав здесь.

Значения по умолчанию и переменные

Сначала у нас есть наборы ограничений по умолчанию, как строки. Эти строки представлены в редактируемых <textarea>, но это начальная конфигурация потока.

const videoDefaultConstraintString =
  '{\n  "width": 320,\n  "height": 240,\n  "frameRate": 30\n}';
const audioDefaultConstraintString =
  '{\n  "sampleSize": 16,\n  "channelCount": 2,\n  "echoCancellation": false\n}';

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

Затем мы инициализируем переменные, которые будут содержать объекты MediaTrackConstraints для видео и аудио треков, а также переменные, которые будут содержать ссылки на сами видео и аудио треки, в null.

let videoConstraints = null;
let audioConstraints = null;

let audioTrack = null;
let videoTrack = null;

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

const videoElement = document.getElementById("video");
const logElement = document.getElementById("log");
const supportedConstraintList = document.getElementById("supportedConstraints");
const videoConstraintEditor = document.getElementById("videoConstraintEditor");
const audioConstraintEditor = document.getElementById("audioConstraintEditor");
const videoSettingsText = document.getElementById("videoSettingsText");
const audioSettingsText = document.getElementById("audioSettingsText");

Эти элементы:

videoElement

Элемент <video>, который будет отображать поток.

logElement

<div>, в который будут записываться сообщения об ошибках или другой вывод в стиле журнала.

supportedConstraintList

<ul> (неупорядоченный список), в который мы программно добавляем имена каждого настраиваемого свойства, поддерживаемого браузером пользователя.

videoConstraintEditor

Элемент <textarea>, который позволяет пользователю редактировать код набора ограничений для видео трека.

audioConstraintEditor

Элемент <textarea>, который позволяет пользователю редактировать код набора ограничений для аудио трека.

videoSettingsText

<textarea> (всегда отключён), который отображает текущие настройки настраиваемых свойств видео трека.

audioSettingsText

<textarea> (всегда отключён), который отображает текущие настройки настраиваемых свойств аудио трека.

Наконец, мы устанавливаем текущее содержимое двух элементов редактора наборов ограничений в значения по умолчанию.

videoConstraintEditor.value = videoDefaultConstraintString;
audioConstraintEditor.value = audioDefaultConstraintString;

Обновление отображения настроек

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

function getCurrentSettings() {
  if (videoTrack) {
    videoSettingsText.value = JSON.stringify(videoTrack.getSettings(), null, 2);
  }

  if (audioTrack) {
    audioSettingsText.value = JSON.stringify(audioTrack.getSettings(), null, 2);
  }
}

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

Создание объектов набора ограничений треков

Функция buildConstraints() создаёт объекты MediaTrackConstraints для аудио и видео треков, используя код в полях редактирования набора ограничений для этих треков.

function buildConstraints() {
  try {
    videoConstraints = JSON.parse(videoConstraintEditor.value);
    audioConstraints = JSON.parse(audioConstraintEditor.value);
  } catch (error) {
    handleError(error);
  }
}

Это использует JSON.parse() для преобразования кода в каждом редакторе в объект. Если любой вызов JSON.parse() вызывает исключение, вызывается handleError() для вывода сообщения об ошибке в журнал.

Настройка и запуск потока

Метод startVideo() обрабатывает настройку и запуск видео потока.

function startVideo() {
  buildConstraints();

  navigator.mediaDevices
    .getUserMedia({
      video: videoConstraints,
      audio: audioConstraints,
    })
    .then((stream) => {
      const audioTracks = stream.getAudioTracks();
      const videoTracks = stream.getVideoTracks();

      videoElement.srcObject = stream;

      if (audioTracks.length > 0) {
        audioTrack = audioTracks[0];
      }

      if (videoTracks.length > 0) {
        videoTrack = videoTracks[0];
      }
    })
    .then(() => {
      return new Promise((resolve) => {
        videoElement.onloadedmetadata = resolve;
      });
    })
    .then(() => {
      getCurrentSettings();
    })
    .catch(handleError);
}

Здесь несколько шагов:

  1. Он вызывает buildConstraints() для создания объектов MediaTrackConstraints для двух треков из кода в полях редактирования.
  2. Он вызывает navigator.mediaDevices.getUserMedia(), передавая объекты ограничений для видео и аудио треков. Это возвращает MediaStream с аудио и видео из источника, соответствующего входным данным (обычно веб-камера, хотя при правильных ограничениях можно получить медиа из других источников).
  3. Когда поток получен, он прикрепляется к элементу <video>, чтобы он был виден на экране, и мы получаем аудио трек и видео трек в переменные audioTrack и videoTrack.
  4. Затем мы устанавливаем обещание, которое разрешается, когда происходит событие loadedmetadata в элементе видео.
  5. Когда это произойдёт, мы знаем, что видео начало воспроизводиться, поэтому вызываем нашу функцию getCurrentSettings() (описанную выше), чтобы отобразить фактические настройки, выбранные браузером после учёта наших ограничений и возможностей оборудования.
  6. Если произошла ошибка, мы регистрируем её с помощью метода handleError() , который мы рассмотрим далее в этой статье.

Также нам нужно установить обработчик событий, чтобы отслеживать нажатие кнопки «Запустить видео»:

document.getElementById("startButton").addEventListener(
  "click",
  () => {
    startVideo();
  },
  false,
);

Применение обновлений набора ограничений

Далее, мы устанавливаем обработчик события для кнопки "Применить ограничения". Если она нажата и нет активного потока, мы вызываем startVideo(), и эта функция займётся запуском потока с указанными настройками. В противном случае, мы выполняем следующие шаги, чтобы применить обновлённые ограничения к уже активному потоку:

  1. buildConstraints() используется для создания обновлённых объектов MediaTrackConstraints для аудиодорожки (audioConstraints) и видеодорожки (videoConstraints).
  2. MediaStreamTrack.applyConstraints() вызывается для видеодорожки (если она есть) для применения новых videoConstraints. Если операция успешна, содержимое поля текущих настроек видеодорожки обновляется на основе результата вызова метода getSettings().
  3. После этого, applyConstraints() вызывается для аудиодорожки (если она есть) для применения новых ограничений на аудио. Если операция успешна, содержимое поля текущих настроек аудиодорожки обновляется на основе результата вызова метода getSettings().
  4. Если при применении набора ограничений произошла ошибка, handleError() используется для вывода сообщения в журнал.
document.getElementById("applyButton").addEventListener(
  "click",
  () => {
    if (!videoTrack && !audioTrack) {
      startVideo();
    } else {
      buildConstraints();

      const prettyJson = (obj) => JSON.stringify(obj, null, 2);

      if (videoTrack) {
        videoTrack
          .applyConstraints(videoConstraints)
          .then(() => {
            videoSettingsText.value = prettyJson(videoTrack.getSettings());
          })
          .catch(handleError);
      }

      if (audioTrack) {
        audioTrack
          .applyConstraints(audioConstraints)
          .then(() => {
            audioSettingsText.value = prettyJson(audioTrack.getSettings());
          })
          .catch(handleError);
      }
    }
  },
  false,
);

Обработка кнопки остановки

Затем мы устанавливаем обработчик для кнопки остановки.

document.getElementById("stopButton").addEventListener("click", () => {
  if (videoTrack) {
    videoTrack.stop();
  }

  if (audioTrack) {
    audioTrack.stop();
  }

  videoTrack = audioTrack = null;
  videoElement.srcObject = null;
});

Это останавливает активные дорожки, устанавливает переменные videoTrack и audioTrack в null, чтобы мы знали, что они исчезли, и удаляет поток из элемента <video>, установив HTMLMediaElement.srcObject в null.

Простая поддержка табуляции в редакторе

Этот код добавляет простую поддержку табуляции к элементам <textarea>, заставляя клавишу Tab вставлять два пробела, когда активны поля редактирования ограничений.

function keyDownHandler(event) {
  if (event.key === "Tab") {
    const elem = event.target;
    const str = elem.value;

    const position = elem.selectionStart;
    const beforeTab = str.substring(0, position);
    const afterTab = str.substring(position, str.length);
    const newStr = `${beforeTab}  ${afterTab}`;
    elem.value = newStr;
    elem.selectionStart = elem.selectionEnd = position + 2;
    event.preventDefault();
  }
}

videoConstraintEditor.addEventListener("keydown", keyDownHandler, false);
audioConstraintEditor.addEventListener("keydown", keyDownHandler, false);

Отображение поддерживаемых браузером свойств, подлежащих ограничению

Последний важный фрагмент: код, который отображает для пользователя список свойств, подлежащих ограничению, которые поддерживает его браузер. Каждое свойство — ссылка на его документацию на MDN для удобства. Смотрите MediaDevices.getSupportedConstraints() примеры для подробностей о работе этого кода.

Примечание: Конечно, в этом списке могут быть нестандартные свойства, в этом случае ссылка на документацию, вероятно, не будет полезной.

const supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
for (const constraint in supportedConstraints) {
  if (Object.hasOwn(supportedConstraints, constraint)) {
    const elem = document.createElement("li");

    elem.innerHTML = `<code><a href='https://developer.mozilla.org/docs/Web/API/MediaTrackSupportedConstraints/${constraint}' target='_blank'>${constraint}</a></code>`;
    supportedConstraintList.appendChild(elem);
  }
}

Обработка ошибок

У нас также есть код для простой обработки ошибок; handleError() вызывается для обработки проваленных обещаний, а функция log() добавляет сообщение об ошибке в специальное поле регистрации ошибок <div> под видео.

function log(msg) {
  logElement.innerHTML += `${msg}<br>`;
}

function handleError(reason) {
  log(
    `Error <code>${reason.name}</code> in constraint <code>${reason.constraint}</code>: ${reason.message}`,
  );
}

Результат

Здесь вы можете увидеть полный пример в действии.

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

Спецификация
Захват медиа и потоки
# dom-mediadevices-getsupportedconstraints

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

Рабочие столы Мобильные устройства
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на iOS Samsung Internet WebView Android
Constraints 53 12 44 40 11 52 50 41 11 6.0 53

См. также

  • API захвата медиа и потоков
  • MediaTrackConstraints
  • MediaTrackSettings
  • MediaDevices.getSupportedConstraints()
  • MediaStreamTrack.applyConstraints()
  • MediaStreamTrack.getSettings()

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Media_Capture_and_Streams_API/Constraints

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API