Захват видеопотока с любого элемента

Франсуа Бофор
François Beaufort

С помощью API захвата экрана вы можете захватить всю текущую вкладку. API захвата элемента позволяет захватывать и записывать определенный элемент HTML. Он преобразует захват всей вкладки в захват определенного поддерева DOM, захватывая только прямых потомков целевого элемента. Другими словами, он обрезает и удаляет как заслоняющий, так и заслоненный контент.

Зачем использовать Element Capture?

Рассмотрение требований приложения для видеоконференций может помочь вам понять, где Element Capture полезен. Если у вас есть приложение для видеоконференций, которое позволяет встраивать сторонние приложения в iframe, вам иногда может понадобиться захватить этот iframe как видео и передать его удаленным участникам.

Скриншот видеоконференц-звонка в Chrome.
Элад использует стороннее приложение для видеоконференцсвязи с Франсуа.

Вызов getDisplayMedia() и предоставление пользователю возможности выбрать текущую вкладку передаст всю текущую вкладку. Это, скорее всего, передаст обратно видео пользователя. Вы можете обрезать его с помощью Region Capture .

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

Скриншот раскрывающегося списка, скрывающего контент, предназначенный для захвата.
Поверх контента, предназначенного для захвата, отображается раскрывающийся список.

Region Capture вам тут не поможет. Часть выпадающего списка может оказаться видимой на экранах удаленных участников.

Скриншот раскрывающегося списка.
Раскрывающийся список Элада отображается поверх контента, полученного Франсуа.

Тот факт, что Region Capture захватывает части элементов таким образом (известным как перекрытие содержимого ), создает несколько проблем:

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

API Element Capture решает все эти проблемы, позволяя вам указать элемент, которым вы хотите поделиться.

Скриншот целевого элемента без раскрывающегося списка.
Франсуа не видит раскрывающийся список от Elad.

Как использовать Element Capture?

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

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

Еще раз просмотрите эти шаги:

Начните с предоставления пользователю возможности захватить текущую вкладку.

// Ask the user for permission to start capturing the current tab.
const stream = await navigator.mediaDevices.getDisplayMedia({
 preferCurrentTab: true,
});
const [track] = stream.getVideoTracks();

Определите RestrictionTarget , вызвав RestrictionTarget.fromElement() с элементом по вашему выбору в качестве входных данных.

// Associate captureTarget with a new RestrictionTarget
const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

Затем вызовите restrictTo() на видеодорожке с RestrictionTarget в качестве входных данных. После того, как последний promise будет разрешен, все последующие кадры будут ограничены.

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

// Enjoy! Transmit remotely.

Глубокое погружение

Обнаружение особенностей

Чтобы проверить, поддерживается ли RestrictionTarget.fromElement() , используйте:

if ("RestrictionTarget" in self && "fromElement" in RestrictionTarget) {
  // Deriving a restriction target is supported.
}

Вывести RestrictionTarget

Сосредоточьтесь на элементе , который называется captureTarget . Чтобы вывести из него RestrictionTarget , вызовите RestrictionTarget.fromElement(captureTarget) . Возвращенное Promise будет разрешено с новым объектом RestrictionTarget в случае успеха. В противном случае оно будет отклонено, если вы создали необоснованное количество объектов RestrictionTarget .

const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

В отличие от Element, объект RestrictionTarget является сериализуемым . Его можно передать в другой документ, например, с помощью Window.postMessage() .

Ограничивающий

При захвате вкладки видеодорожка выставляет restrictTo() . При захвате текущей вкладки допустимо вызывать restrictTo() либо с null , либо с любым RestrictionTarget полученным из Element в текущей вкладке.

Вызовы restrictTo(restrictionTarget) мутируют видеодорожку в захват captureTarget , как будто она была нарисована сама по себе, независимо от остальной части DOM. Все потомки captureTarget также захватываются; родственные элементы captureTarget исключаются из захвата. Результатом является то, что все кадры, доставленные на дорожку, выглядят так, как будто они были обрезаны по контурам captureTarget , а любой заслоняющий и заслоненный контент удаляется.

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

Вызовы restrictTo(null) возвращают дорожку в исходное состояние.

// Stop restricting.
await track.restrictTo(null);

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

Если не удалось, то Promise отклоняется. Неудачный вызов restrictTo() может быть по одной из следующих причин:

  • Если restrictionTarget был создан на вкладке, отличной от той, которая захватывается. (Обратите внимание, что с помощью кнопки «Поделиться этой вкладкой вместо этого» пользователи могут в любой момент времени изменить захватываемую вкладку.)
  • Если restrictionTarget был получен из элемента, который больше не существует.
  • Если у трека есть клоны. (См. выпуск 1509418 .)
  • Если текущая дорожка не является видеодорожкой самозахвата.
  • Если элемент, из которого был получен restrictionTarget , не подлежит ограничению.

Соображения относительно самозахвата

Когда приложение вызывает getDisplayMedia() и пользователь выбирает захват собственной вкладки приложения, мы называем это «самозахватом».

Метод restrictTo() доступен на любой видеодорожке с захватом вкладок, а не только для самозахвата. Но Element Capture пока включен только для самозахвата. Поэтому рекомендуется проверить, выбрал ли пользователь текущую вкладку, прежде чем пытаться ограничить дорожку. Это можно сделать с помощью Capture Handle . Также можно попросить браузер подтолкнуть пользователя к самозахвату с помощью preferCurrentTab .

Прозрачность

Видеокадры, которые приложение получает через getDisplayMedia() не включают альфа-канал. Если приложение устанавливает частично прозрачную цель захвата, удаление альфа-канала может иметь некоторые возможные последствия:

  • Цвета могут измениться. Частично прозрачные целевые элементы, нарисованные на светлом фоне, могут выглядеть темнее при удалении альфа-канала, а нарисованные на темном фоне могут выглядеть светлее.
  • Цвета, которые были невидимы или невоспринимаемы пользователем, когда альфа-канал был установлен на максимум, появятся после удаления альфа-канала. Например, это может привести к неожиданным черным областям в захваченных кадрах, если прозрачные участки имели код RGBA rgba(0, 0, 0, 0) .
Скриншот результата захвата непрямоугольной прозрачной цели.
Непрямоугольный прозрачный целевой видеопоток захвата (справа) представляет собой черный фоновый прямоугольник, содержащий непрозрачный синий круг.

Неприемлемые цели захвата

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

Одним из важных соображений для обеспечения того, что элемент имеет право на ограничение, является то, что он должен формировать свой собственный контекст стекирования . Чтобы обеспечить это, вы можете указать свойство CSS изоляции , установив его на isolate .

<div id="captureTarget" style="isolation: isolate;"></iframe>

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

Поддержка браузера

Element Capture доступен только на настольных компьютерах, начиная с Chrome 132.

Безопасность и конфиденциальность

Чтобы понять компромиссы в вопросах безопасности, ознакомьтесь с разделом « Вопросы конфиденциальности и безопасности» спецификации Element Capture.

Браузер Chrome рисует синюю рамку по краям захваченных вкладок.

Демо

Вы можете поиграть с Element Capture, запустив демо на Glitch. Обязательно ознакомьтесь с исходным кодом .

Обратная связь

Команда Chrome и сообщество веб-стандартов хотят услышать о вашем опыте использования Element Capture.

Расскажите нам о дизайне

Есть ли что-то в Element Capture, что не работает так, как вы ожидали? Или отсутствуют методы или свойства, которые вам нужны для реализации вашей идеи? Есть вопрос или комментарий по модели безопасности?

  • Опубликуйте спецификацию проблемы в репозитории GitHub или добавьте свои мысли к существующей проблеме.

Проблемы с реализацией?

Вы нашли ошибку в реализации Chrome? Или реализация отличается от спецификации?

Благодарности

Фото Пола Скорупскаса на Unsplash