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

Francisco Beaufort
François Beaufort
Elad Alon
Elad Alon

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

¿Por qué usar Element Capture?

Teniendo 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 videoconferencias 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 utiliza una aplicación de terceros en una llamada de videoconferencia con François.

Si se llama a getDisplayMedia() y se permite que el usuario elija la pestaña actual, se transmitirá toda la pestaña actual. Es probable que esto les transmita el video de los usuarios. Puedes recortarla usando la Captura regional.

Sin embargo, ¿qué sucede si el presentador interactúa con la aplicación de videoconferencia y parte del contenido, como una lista desplegable, se dibuja sobre el contenido que está destinado a capturarse?

Captura de pantalla de una lista desplegable que cubre el contenido que se quiere capturar.
Aparece una lista desplegable sobre el contenido que se desea capturar.

La captura de región no te serviría. Es posible que parte de la lista desplegable quede visible en las pantallas de los participantes remotos.

Captura de pantalla de una lista desplegable.
La lista desplegable de Elad aparece en la parte superior del contenido que recibió Francisco.

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

  • Ocultar contenido puede obstruir la visualización del contenido que el usuario tiene la intención de compartir.
  • El contenido oculto puede ser privado (como las notificaciones de chat).
  • Incluir contenido puede ser confuso. (Por ejemplo, un nuevo diseño de la aplicación podría trasladar brevemente los videos de los participantes remotos al objetivo capturado).

La API de Element Capture resuelve todos estos problemas, ya que te permite 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 se usa Element Capture?

El captureTarget es un elemento de tu página que incluye el contenido que el usuario desea capturar. Quieres que la app web de videoconferencia capture captureTarget y lo comparta con participantes remotos. Por lo tanto, derivas un RestrictionTarget 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 continúa, sin requerir ninguna entrada adicional de ninguna de las apps web. Si se excluye el contenido que aparece, desaparece o se mueve, la pista de video no se requiere tratamiento especial.

Vuelve a revisar estos pasos:

Para comenzar, 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 objeto RestrictionTarget, llama a RestrictionTarget.fromElement() con un elemento de tu elección 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 resuelve 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 atributos

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

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

Cómo generar un RestrictionTarget

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

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

A diferencia de un elemento Element, 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 capturas 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 elementos subordinados de captureTarget; se eliminan de la captura los elementos del mismo nivel de captureTarget. Como resultado, todos los fotogramas entregados en la pista aparecerán como si se hubieran recortado para los contornos de captureTarget y se eliminará todo el contenido ocluido y oculto.

// 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 ejecuta correctamente, la promesa que se muestra se resuelve cuando se puede garantizar que todos los fotogramas de video posteriores se restringirán a captureTarget.

Si no tiene éxito, se rechaza la promesa. Las llamadas fallidas a restrictTo() pueden deberse a uno de los siguientes motivos:

  • Si el restrictionTarget se acuña en una pestaña que no sea la que se está capturando. (Ten en cuenta que con el botón "Compartir esta pestaña en su lugar", 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 el segmento tiene clonaciones. (consulta el error 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 autocaptura

Cuando una app llama a getDisplayMedia(), y el usuario elige capturar la pestaña de la app, lo llamamos "autocaptura".

El método restrictTo() se expone en cualquier pista de video de captura de pestaña, no solo para la autocaptura. Sin embargo, por el momento, Element Capture solo está habilitado para la autocaptura. Por lo tanto, es recomendable comprobar si el usuario seleccionó la pestaña actual antes de intentar restringir el segmento. Esto se puede lograr con el identificador de captura. También es posible pedirle al navegador que incentive al usuario a realizar una captura de pantalla con preferCurrentTab.

Transparencia

Los fotogramas de video que obtiene la app 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:

  • Es posible que cambien los colores. Los elementos de destino 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 el canal alfa se establecía al máximo, aparecerían una vez que se quita el canal alfa. Por ejemplo, esto podría generar regiones negras inesperadas en los marcos 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 que no es rectangular.
La transmisión de video de destino de captura transparente sin rectángulo (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 recorrido a cualquier objetivo de captura válido. Sin embargo, los fotogramas no se producirán en ciertas condiciones, por ejemplo, si el elemento o un principal es display:none. La lógica general es que la restricción solo se aplica a un elemento que comprende un área rectangular única, cohesiva, bidimensional, cuyos píxeles pueden determinarse lógicamente de forma independiente de cualquier elemento principal o del mismo nivel.

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

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

Ten en cuenta que el elemento de destino puede alternar entre ser apto y no apto para la restricción en cualquier momento arbitrario, 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, no se emitirán nuevos fotogramas 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 de Element Capture y se puede habilitar en chrome://flags/#element-capture.

Esta función también está ingresando a una prueba de origen de Chrome 121 para computadoras de escritorio, lo que permite a los desarrolladores habilitar la función para que los visitantes de sus sitios recopilen datos de usuarios reales. Consulte Cómo comenzar a usar las pruebas de origen para obtener más información.

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 conocer tu experiencia con Element Capture.

Cuéntanos sobre el diseño

¿Hay algo sobre la captura de región que no funciona como esperabas? ¿O faltan métodos o propiedades que necesitas para implementar tu idea? ¿Tienes alguna pregunta o comentario sobre el modelo de seguridad?

  • Informa sobre un problema específico en el repositorio de GitHub o agrega tus ideas a un problema existente.

¿Tienes problemas con la implementación?

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

  • Informa el error en https://new.crbug.com. Asegúrate de incluir todos los detalles que puedas, además de instrucciones simples para reproducir el contenido. Glitch funciona muy bien para compartir repros rápidos y fáciles.

Reconocimientos

Foto de Paul Skorupskas en Unsplash