Captura una transmisión de video por Internet desde cualquier elemento

François Beaufort
François Beaufort

Con la API de Screen Capture, puedes capturar toda la pestaña actual. La API de Element Capture te permite capturar y registrar un elemento HTML específico. Transforma una captura de toda la pestaña, en una captura de un subárbol del DOM específico, capturando solo los subordinados directos del elemento de destino. En otras palabras, recorta y quita el contenido ocluido.

¿Por qué usar la captura de elementos?

Tener en cuenta los requisitos de una aplicación de videoconferencia puede ayudarte a comprender dónde es útil Element Capture. Si tienes una aplicación de videoconferencia que te permite incorporar aplicaciones de terceros en un iframe, es posible que, a veces, quieras capturar ese iframe como un video y transmitirlo a participantes remotos.

Captura de pantalla de una llamada de videoconferencia en Chrome
Elad usa una aplicación de terceros en una llamada de videoconferencia con Francisco.

Si se llama a getDisplayMedia() y se permite que el usuario elija la pestaña actual, se transmitiría toda la pestaña actual. Eso probablemente les transmita el propio video de las personas. Puedes recortarla con la función Region Capture.

Sin embargo, ¿qué sucede si el presentador interactúa con la aplicación de videoconferencia y algo de contenido, como una lista desplegable, se dibuja encima del contenido que se va a capturar?

Captura de pantalla de una lista desplegable que cubre el contenido que se va a capturar.
Aparece una lista desplegable sobre el contenido que se va a capturar.

Region Capture no te ayudaría en ese caso. Es posible que parte de la lista desplegable termine visible en las pantallas de los participantes remotos.

Se tomó la captura de pantalla de una lista desplegable.
La lista desplegable de Elad aparece en la parte superior del contenido que recibió François.

El hecho de que la captura de región captura partes de los elementos de esta manera (conocido como contenido oculto) crea varios problemas:

  • La exclusión de contenido podría impedir la visualización del contenido que el usuario desea compartir.
  • Es posible que excluir contenido sea privado (como las notificaciones de chat).
  • Excluir contenido puede ser confuso. (Por ejemplo, un rediseño de la aplicación podría hacer que los videos propios de los participantes remotos se acerquen brevemente al objetivo capturado).

La API de Element Capture resuelve todos estos problemas al permitirte seleccionar el elemento que deseas compartir.

Captura de pantalla del elemento de destino sin una lista desplegable a la vista.
Francisco no ve la lista desplegable de Elad.

¿Cómo uso la captura de elementos?

captureTarget es un Elemento de tu página que incluye el contenido que el usuario desea capturar. Quieres que la app web de videoconferencias capture a captureTarget y la comparta con participantes remotos. Por lo tanto, derivas un RestrictionTarget a partir de captureTarget. Después de restringir la pista de video con este RestrictionTarget, los fotogramas de esa pista de video ahora consisten solo de los píxeles que forman parte de captureTarget y sus subordinados directos del DOM.

Si captureTarget cambia de tamaño, forma o ubicación, la pista de video sigue el proceso, sin necesidad de ninguna entrada adicional de ninguna de las apps web. Excluir el contenido que aparece, desaparece o se mueve de un lado a otro, no requiere tratamiento especial.

Vuelve a revisar estos pasos:

Primero, permite que el usuario capture la pestaña actual.

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

Para definir un RestrictionTarget, llama a RestrictionTarget.fromElement() con un elemento que elijas como entrada.

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

Luego, llama a restrictTo() en la pista de video con RestrictionTarget como entrada. Una vez que se resuelva la última promesa, se restringirán todos los fotogramas posteriores.

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

// Enjoy! Transmit remotely.

Análisis detallado

Detección de funciones

Para comprobar si RestrictionTarget.fromElement() es compatible, usa lo siguiente:

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

Cómo derivar un RestrictionTarget

Enfócate en el Elemento llamado captureTarget. Para derivar un RestrictionTarget a partir de él, llama a RestrictionTarget.fromElement(captureTarget). Si se ejecuta correctamente, la promesa que se muestra se resolverá con un objeto RestrictionTarget nuevo. De lo contrario, se rechazará si acuñaste una cantidad injustificada de objetos RestrictionTarget.

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

A diferencia de un elemento, un objeto RestrictionTarget es serializable. Por ejemplo, se puede pasar a otro documento con Window.postMessage().

Restringida

Cuando se captura una pestaña, la pista de video expone restrictTo(). Cuando se captura la pestaña actual, es válido llamar a restrictTo() con null o cualquier RestrictionTarget derivado de un elemento dentro de la pestaña actual.

Las llamadas a restrictTo(restrictionTarget) mutan la pista de video en una captura de captureTarget, como si se hubiera dibujado sola, independientemente del resto del DOM. También se capturan todos los subordinados de captureTarget; los elementos del mismo nivel de captureTarget se eliminan de la captura. Como resultado, los fotogramas publicados en la pista aparecen como si se recortaran a los contornos de captureTarget, y se quita todo el contenido ocluido.

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

Las llamadas a restrictTo(null) revierten el segmento a su estado original.

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

Si la llamada a restrictTo() se realiza correctamente, se resuelve la promesa que se muestra cuando se puede garantizar que todos los fotogramas de video posteriores se restringirán a captureTarget.

Si no tiene éxito, se rechaza la promesa. Si no puedes realizar una llamada a restrictTo(), puede deberse a uno de los siguientes motivos:

  • Si se acuña el restrictionTarget en una pestaña diferente de la que se está capturando. (Ten en cuenta que si usas el botón "Compartir esta pestaña", los usuarios pueden cambiar la pestaña que se captura en cualquier momento).
  • Si el restrictionTarget se derivó de un elemento que ya no existe.
  • Si la pista tiene clonaciones. (Consulta el problema 1509418).
  • Si la pista actual no es una pista de video de captura automática.
  • Si el elemento del que se derivó restrictionTarget no es apto para la restricción.

Consideraciones sobre la captura personal

Cuando una app llama a getDisplayMedia(), y el usuario elige capturar la pestaña propia de la app, lo llamamos "captura automática".

El método restrictTo() se expone en cualquier pista de video de captura de pestañas, no solo en el de la captura personal. Sin embargo, por el momento, la captura de elementos solo está habilitada para la captura automática. Por lo tanto, se recomienda verificar si el usuario seleccionó la pestaña actual antes de intentar restringir la pista. Esto se puede lograr con el Controlador de la captura. También es posible solicitarle al navegador que guíe al usuario para que tome su foto con preferCurrentTab.

Transparencia

Los fotogramas de video que la app obtiene a través de getDisplayMedia() no incluyen un canal alfa. Si una app establece un objetivo de captura parcialmente transparente, quitar el canal alfa tiene algunas consecuencias posibles:

  • Los colores pueden cambiar. Los elementos objetivo parcialmente transparentes dibujados sobre un fondo claro pueden parecer más oscuros cuando se quita el canal alfa, y los dibujados sobre un fondo oscuro pueden parecer más claros.
  • Los colores que eran invisibles o imperceptibles para el usuario cuando se configuraba el canal alfa al máximo aparecen una vez que se eliminó el canal alfa. Por ejemplo, esto podría generar regiones negras inesperadas en los fotogramas capturados si las secciones transparentes tuvieran el código RGBA rgba(0, 0, 0, 0).
Captura de pantalla del resultado de un objetivo de captura transparente sin rectángulo.
La transmisión de video de destino transparente no rectangular (derecha) es un rectángulo de fondo negro que contiene un círculo azul opaco.

Objetivos de captura no aptos

Siempre es posible comenzar a restringir un segmento a cualquier objetivo de captura válido. Sin embargo, no se producirán fotogramas bajo ciertas condiciones, por ejemplo, si el elemento o un principal es display:none. La lógica general es que la restricción se aplica únicamente a un elemento que constituya un área rectangular bidimensional y cohesiva, cuyos píxeles pueden determinarse lógicamente de forma aislada de cualquier elemento superior o del mismo nivel.

Una consideración importante a la hora de garantizar que el elemento sea apto para la restricción es que debe formar su propio contexto de apilamiento. Para garantizar esto, puedes especificar la propiedad CSS de aislamiento y establecerla en isolate.

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

Ten en cuenta que el elemento de destino puede alternar entre ser apto o no apto para la restricción en cualquier momento, por ejemplo, si la app cambia sus propiedades de CSS. Depende de la app usar objetivos de captura razonables y evitar cambiar sus propiedades de forma inesperada. Si el elemento de destino deja de ser apto, simplemente no se emitirán fotogramas nuevos en el segmento hasta que el elemento de destino vuelva a ser apto para la restricción.

Cómo habilitar la captura de elementos

La API de Element Capture está disponible en Chrome para computadoras de escritorio detrás de la marca Element Capture y se puede habilitar en chrome://flags/#element-capture.

Esta función también está presentando una prueba de origen a partir de Chrome 121 para computadoras, que permite a los desarrolladores habilitar la función para que los visitantes de sus sitios recopilen datos de usuarios reales. Consulta Comienza a usar las pruebas de origen para obtener más información al respecto.

Seguridad y privacidad

Para comprender las compensaciones de seguridad, consulta la sección Consideraciones de privacidad y seguridad de la especificación de Element Capture.

El navegador Chrome dibuja un borde azul alrededor de los bordes de las pestañas capturadas.

Demostración

Para jugar con Element Capture, ejecuta la demostración en Glitch. Asegúrate de consultar el código fuente.

Comentarios

El equipo de Chrome y la comunidad de estándares de la Web quieren saber acerca de tus experiencias con Element Capture.

Cuéntanos sobre el diseño

¿Hay algo en Region Capture que no funcione como esperabas? ¿O faltan métodos o propiedades que necesites para implementar tu idea? ¿Tienes alguna pregunta o comentario sobre el modelo de seguridad?

  • Informa un problema de especificaciones en el repositorio de GitHub o agrega tus ideas sobre un problema existente.

¿Tiene problemas con la implementación?

¿Encontraste un error en la implementación de Chrome? ¿O la implementación es diferente de la especificación?

  • Informa un error en https://new.crbug.com. Asegúrate de incluir tantos detalles como puedas, así como instrucciones sencillas para reproducirlo. Glitch funciona muy bien para compartir repros rápidos y fáciles.

Agradecimientos

Foto de Paul Skorupskas en Unsplash