Capturar um stream de vídeo de qualquer elemento

François Beaufort
François Beaufort

Com a API Screen Capture, você pode capturar toda a guia atual. A API Element Capture permite capturar e gravar um elemento HTML específico. Ela transforma uma captura de toda a guia em uma captura de uma subárvore do DOM específica, capturando somente descendentes diretos do elemento-alvo. Em outras palavras, ele corta e remove o conteúdo escondido.

Por que usar a captura de elementos?

Considerar os requisitos de um aplicativo de videoconferência pode ajudar a entender onde o Element Capture é útil. Se você tiver um aplicativo de videoconferência que permite incorporar aplicativos de terceiros em um iframe, talvez queira capturar esse iframe como um vídeo e transmiti-lo aos participantes remotos.

Captura de tela de uma chamada de videoconferência no Chrome.
Elad usa um aplicativo de terceiros em uma videoconferência com Francisco.

Chamar getDisplayMedia() e permitir que o usuário escolha a guia atual transmitiria toda a guia atual. Isso provavelmente transmitirá o vídeo das pessoas de volta para elas. Você pode cortar usando a Captura de região.

Mas e se o apresentador usar o aplicativo de videoconferência e algum conteúdo, como uma lista suspensa, for sobreposto ao conteúdo destinado à captura?

Captura de tela de uma lista suspensa cobrindo o conteúdo destinado à captura.
Uma lista suspensa aparece acima do conteúdo destinado à captura.

A captura de região não ajudaria nisso. Parte da lista suspensa pode acabar visível nas telas dos participantes remotos.

Captura de tela de uma lista suspensa.
A lista suspensa de Elad aparece na parte superior do conteúdo recebido por François.

O fato de a captura de região capturar partes dos elementos dessa maneira (conhecido como conteúdo de obstrução) cria vários problemas:

  • A obstrução pode impedir a visualização do conteúdo que o usuário pretendia compartilhar.
  • A obstrução de conteúdo pode ser particular, por exemplo, notificações de chat.
  • A obstrução de conteúdo pode ser confusa. Por exemplo, um novo layout do aplicativo poderia trazer brevemente os próprios vídeos dos participantes remotos para o alvo capturado.

A API Element Capture resolve todos esses problemas, permitindo que você direcione o elemento que quer compartilhar.

Captura de tela do elemento de destino sem lista suspensa na visualização.
François não vê a lista suspensa do Elad.

Como uso a Captura de elementos?

O captureTarget é um elemento da página que tem o conteúdo que o usuário quer capturar. Você quer que o app da Web de videoconferência capture captureTarget e compartilhe com os participantes remotos. Então, você deriva um RestrictionTarget de captureTarget. Depois de restringir a faixa de vídeo usando este RestrictionTarget, os frames nessa faixa de vídeo agora consistem apenas nos pixels que fazem parte de captureTarget e dos descendentes diretos do DOM.

Se a captureTarget mudar de tamanho, forma ou localização, a faixa de vídeo vai acompanhar, sem precisar de outras entradas de nenhum app da Web. A obstrução do conteúdo que aparece, desaparece ou se move de um lado para o outro não exige tratamento especial.

Revise estas etapas:

Comece permitindo que o usuário capture a guia atual.

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

Defina um RestrictionTarget chamando RestrictionTarget.fromElement() com um elemento da sua escolha como entrada.

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

Em seguida, chame restrictTo() na faixa de vídeo com o RestrictionTarget como entrada. Quando a última promessa for resolvida, todos os frames subsequentes serão restritos.

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

// Enjoy! Transmit remotely.

Análise detalhada

Detecção de recursos

Para verificar se RestrictionTarget.fromElement() é compatível, use:

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

Derivar um RestrictionTarget

Concentre-se no Elemento chamado captureTarget. Para derivar um RestrictionTarget com base nele, chame RestrictionTarget.fromElement(captureTarget). A promessa retornada será resolvida com um novo objeto RestrictionTarget se for bem-sucedida. Caso contrário, ela será rejeitada se você tiver criado um número desnecessário de objetos RestrictionTarget.

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

Ao contrário de um elemento, um objeto RestrictionTarget é serializável. Ele pode ser transmitido para outro documento usando Window.postMessage(), por exemplo.

Restrito

Ao capturar uma guia, a faixa de vídeo expõe restrictTo(). Ao capturar a guia atual, é válido chamar restrictTo() com null ou qualquer RestrictionTarget derivada de um elemento na guia atual.

As chamadas para restrictTo(restrictionTarget) modificam a faixa de vídeo em uma captura de captureTarget, como se ela fosse desenhada sozinha, independentemente do restante do DOM. Todos os descendentes de captureTarget também são capturados. Os irmãos de captureTarget são eliminados da captura. O resultado é que todos os frames enviados na faixa aparecem como se tivessem sido cortados de acordo com os contornos de captureTarget, e qualquer conteúdo obstruído é removido.

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

As chamadas para restrictTo(null) revertem a faixa ao estado original.

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

Se a chamada para restrictTo() for bem-sucedida, a promessa retornada será resolvida quando for possível garantir que todos os frames de vídeo subsequentes serão restritos a captureTarget.

Se falhar, a promessa vai ser rejeitada. Uma chamada malsucedida para restrictTo() vai ocorrer por um dos seguintes motivos:

  • Se a restrictionTarget foi criada em uma guia diferente da que está sendo capturada. Ao usar o botão "compartilhar esta guia em vez disso", os usuários podem alterar a guia capturada a qualquer momento.
  • Se o restrictionTarget tiver sido derivado de um elemento que não existe mais.
  • Se a faixa tiver clones. Consulte o problema 1509418 (link em inglês).
  • Se a faixa atual não for uma faixa de vídeo com captura automática.
  • Se o elemento do qual restrictionTarget foi derivado não estiver qualificado para restrição.

Considerações sobre a autocaptura

Quando um app chama getDisplayMedia() e o usuário opta por capturar a própria guia, chamamos isso de "autocaptura".

O método restrictTo() é exposto em qualquer faixa de vídeo de captura de guia, e não apenas na autocaptura. No entanto, a Captura de elementos só está ativada para autocaptura por enquanto. Portanto, é aconselhável verificar se o usuário selecionou a guia atual antes de tentar restringir a faixa. Para isso, use o Capture Handle. Também é possível pedir ao navegador para orientar o usuário a fazer a autocaptura usando o preferCurrentTab.

Transparência

Os frames de vídeo que o app recebe pelo getDisplayMedia() não incluem um canal alfa. Se um aplicativo definir um destino de captura parcialmente transparente, a remoção do canal alfa terá algumas consequências possíveis:

  • As cores podem mudar. Elementos alvo parcialmente transparentes desenhados sobre um fundo claro podem parecer mais escuros quando o canal alfa é removido, e aqueles desenhados sobre um fundo escuro podem parecer mais claros.
  • Cores que eram invisíveis ou imperceptíveis para o usuário quando o canal alfa estava definido para o máximo apareceriam quando o canal alfa fosse removido. Por exemplo, isso poderia levar a regiões pretas inesperadas nos frames capturados se as seções transparentes tivessem o código RGBA rgba(0, 0, 0, 0).
Captura de tela do resultado de um destino de captura transparente e não retangular.
O stream de vídeo de destino de captura transparente não retangular (à direita) é um retângulo com plano de fundo preto que contém um círculo azul opaco.

Destinos de captura não qualificados

É sempre possível começar a restringir uma faixa a qualquer destino de captura válido. No entanto, os frames não serão produzidos sob determinadas condições, por exemplo, se o elemento ou um ancestral for display:none. A lógica geral é que a restrição se aplica somente a um elemento que compreende uma área retangular única, coesa, bidimensional, cujos pixels podem ser determinados logicamente isoladamente de qualquer elemento pai ou irmão.

Uma consideração importante para garantir que o elemento esteja qualificado para restrição é que ele precisa formar o próprio contexto de empilhamento. Para garantir isso, especifique a propriedade CSS de isolamento, definindo-a como isolate.

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

O elemento de destino pode alternar entre estar qualificado e não qualificado para restrições a qualquer momento, por exemplo, se o app mudar as propriedades de CSS. Cabe ao app usar destinos de captura razoáveis e evitar mudanças inesperadas nas propriedades. Se o elemento de destino se tornar inelegível, os novos frames não serão emitidos na faixa até que esse elemento volte a estar qualificado para restrição.

Como ativar a captura de elementos

A API Element Capture está disponível no Chrome para computadores com a sinalização Element Capture e pode ser ativada em chrome://flags/#element-capture.

Esse recurso também está entrando em um teste de origem do Chrome 121 para computadores. Com ele, os desenvolvedores podem ativar o recurso para que os visitantes dos sites coletem dados de usuários reais. Consulte Introdução aos testes de origem para mais informações.

Segurança e privacidade

Para entender os prós e contras da segurança, consulte a seção Considerações sobre privacidade e segurança da especificação Element Capture.

O navegador Chrome desenha uma borda azul ao redor das bordas das guias capturadas.

Demonstração

Você pode testar a Captura de elementos executando a demonstração no Glitch. Confira o código-fonte.

Feedback

A equipe do Chrome e a comunidade de padrões da Web querem saber mais sobre suas experiências com o Element Capture.

Conte-nos sobre o design

Alguma coisa na captura de região não funciona como você esperava? Ou faltam métodos ou propriedades que você precisa para implementar sua ideia? Tem uma pergunta ou comentário sobre o modelo de segurança?

  • Registre um problema de especificação no repositório do GitHub (link em inglês) ou adicione sua opinião a um problema.

Problemas com a implementação?

Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente das especificações?

  • Registre um bug em https://new.crbug.com. Inclua o máximo de detalhes possível e instruções simples para reproduzi-lo. O Glitch é ótimo para compartilhar repetições rápidas e fáceis.

Agradecimentos

Foto de Paul Skorupskas no Unsplash