1

Pitch: capturar áudio de reunião no navegador sem bot (o que o getDisplayMedia() de fato entrega)

Estou construindo o LiveSuggest, um assistente de reunião que mostra sugestões enquanto a conversa ainda está rolando. Sem bot no Zoom ou no Teams; o navegador processa só o áudio que a pessoa escolhe compartilhar.

Chamar o LLM foi a parte fácil. O que me segurou semanas foi descobrir de onde vem o áudio. Tem combinação de sistema operacional e superfície de compartilhamento em que a faixa não existe.

A armadilha da janela

O caso clássico é compartilhar a janela do app de videoconferência. A prévia parece certa e o MediaStream chega. Só não vem faixa de áudio, e o navegador não avisa.

No código pedimos áudio de sistema explicitamente, como a documentação do Chrome recomenda:

navigator.mediaDevices.getDisplayMedia({
  video: true,
  audio: { systemAudio: "include" },
});

Isso não cria capacidade que o Chromium já não oferece naquela plataforma.

A matriz

SuperfícieÁudio
Aba do Chromiumsim, em desktop (checkbox costuma vir marcada)
Tela inteirasó Windows e ChromeOS, com opt-in manual
Janelanunca

No Windows, compartilhar a tela inteira pode levar o áudio do sistema: Teams desktop, Zoom desktop, qualquer app que esteja tocando som. Linux e macOS não expõem essa opção no navegador. No Mac, a saída prática é driver virtual (BlackHole, Loopback). No Linux, o caminho nativo que dá para documentar é reunião numa aba do Chromium (Meet, Teams web, Zoom web) e compartilhar essa aba.

Firefox e Safari não entregam áudio de aba comparável. Sob Linux sobrou Chromium, pacote da distro ou Chrome. Não gosto de escrever isso, mas não tenho plano B no produto.

Dá para montar null-sink no PipeWire e rerrotear o som. Funciona, e em Linux costuma ser mais limpo que um BlackHole no Mac. O LiveSuggest não faz isso, e não vou colar tutorial de pavucontrol num pitch.

Depois que o áudio chega

Aí o pipeline fica mais banal. AudioWorklet, PCM16 mono 16 kHz, pedaços pequenos numa WebSocket, STT em streaming. As sugestões só disparam em transcrições finalizadas, e o sistema pode ficar em silêncio.

A outra coisa que peguei foi não chamar o LLM em todo transcript. Conversa não chega fatiada em assuntos prontos. Acumulamos contexto e só geramos quando surgiu informação nova suficiente desde a última sugestão. Se a sugestão sai cedo demais, falta contexto; se sai tarde, o assunto já mudou. Subir a frequência só aumenta o ruído.

Dá para medir qualidade sem guardar o conteúdo das conversas (latência, tempo até o primeiro token, feedback explícito positivo/negativo/cópia). O texto da reunião não precisa ir para analytics.

Onde estou

O LiveSuggest ainda está em construção. O que mais me custou até aqui foi entender o que getDisplayMedia() promete em cada SO, antes de prometer qualquer coisa ao usuário final.

Artigo técnico mais longo (em inglês) com o pipeline completo: Building Real-Time AI Suggestions Without a Meeting Bot.

Carregando publicação patrocinada...