Há pouco mais de 10 anos, o Google comprou uma empresa menor chamada GIPS por pouco menos de US $ 70 milhões. Isso marcou o início dos esforços do Google para levar a comunicação em tempo real (RTC) nativamente aos navegadores. Ao abrir o código-fonte do produto principal do GIPS, o Google definiu um padrão que foi implementado pela primeira vez pela Ericsson e depois solidificado em uma especificação W3C.

Hoje, esse padrão é conhecido como WebRTC e é amplamente suportado por todos os principais navegadores e plataformas. Essa tecnologia oferece a capacidade de fornecer uma maneira de trocar vídeo, áudio e outros dados entre diferentes usuários em sua página, independentemente do navegador específico do usuário e dos tipos de dispositivo.

Uma das desvantagens do WebRTC é que é bastante complexo. Felizmente, podemos usar PeerJS -uma biblioteca que simplifica WebRTC e fornece um par completo, configurável e fácil de usar-to-peer connection API.

Instalando PeerJS

Como a maioria das bibliotecas JS hoje em dia, você pode usar PeerJS em seu projeto agrupado por meio de uma declaração de importação ou incluindo o script diretamente de um CDN, como unpkg .

Você pode incluir PeerJS em uma página com um script como abaixo:

Isso tornará a classe Peer disponível, que nos permite criar uma instância e iniciar algo como um chat:

const peer=new Peer ({host:”0.peerjs.com”, porta: 443, caminho:”/”, pingInterval: 5000,});

Acima, criamos uma instância usando as configurações padrão. O PeerJS requer um servidor central para identificar quais pares estão disponíveis.

Para completar o bate-papo, ainda precisamos abrir uma conexão com um parceiro de bate-papo e enviar e receber mensagens. Vamos ver como isso é feito no PeerJS:

const conn=peer.connect (“other-peer-id”); conn.on (“open”, ()=> {conn.send (“Hello World!”);}); conn.on (“dados”, (dados)=> {console.log (“Dados recebidos”, dados);});

Muitas vezes, não se trata de criar uma conexão, mas de realmente receber ou lidar com uma solicitação de conexão. As solicitações de entrada podem ser tratadas pelo evento de conexão presente em uma instância de Peer.

peer.on (“connection”, (conn)=> {conn.on (“data”, (data)=> {console. log (“Dados recebidos”, dados);});});

Partindo daqui, o PeerJS também nos permite utilizar a conexão de dados para mais do que apenas texto. Em combinação com a API getUserMedia do navegador, podemos fazer:

getUserMedia ({video: true, audio: true}, (stream)=> {const call=peer.call (“other-peer-id”, stream); call.on (“stream”, (remoteStream)=> {//Mostrar stream em algum elemento de vídeo/tela.});}, console.error);

Da mesma forma, também é possível receber streams de vídeo e áudio:

peer.on (“call”, (call)=> {getUserMedia ({video: true, audio: true}, (stream)=> {call.answer (stream); call.on (“stream”, (remoteStream)=> {//Mostrar stream em algum elemento de vídeo/tela.});}, console.error);});

Criando um aplicativo de bate-papo simples com PeerJS

Vamos usar o código da seção anterior para construir um exemplo simples que nos permitirá fazer e receber chamadas de vídeo um a um. Nosso exemplo deve seguir este fluxo de trabalho:

Você abre um site que pede seu nome Depois de inserir seu nome, uma tela pede que você insira o nome da pessoa para a qual deseja ligar

Começaremos inicializando um novo projeto e adicionando todas as dependências:

npm init-y npm i peerjs react-dom react-roteador react-router-dom–save npm i @ types/react @ types/react-dom @ types/react-router-dom typescript parcel @ next–save-dev

Agora, precisamos criar um arquivo HTML que funcionará como um ponto de entrada para Parcel.

Parcel é um bundler que converterá todas as diferentes fontes que usaremos para escrever nosso aplicativo em arquivos que o navegador da web possa entender. Por exemplo, para estilização usamos SASS e para o script usamos TypeScript-dois formatos que nenhum navegador entende.

Exemplo de PeerJS

Começaremos sem nenhum elemento de design e, em vez disso, nos concentraremos na criação de um aplicativo de bate-papo baseado em texto para dois pares. Por uma questão de conveniência, o aplicativo de exemplo foi escrito usando React, mas fique à vontade para escolher sua biblioteca ou estrutura de IU favorita.

Basicamente, o que faremos é:

const App=( )=> {return ( ); }; render (, document.querySelector (“# app”));

Usaremos três componentes para as três áreas diferentes de nosso aplicativo:

NameInput para permitir que o usuário escolha seu nome Visão geral para permitir que os usuários façam ou recebam chamadas Chamada para lidar com uma chamada em andamento

Nós’Também manterei as coisas simples, colocando os valores para Peer e conexão globalmente. Eles ainda podem ser usados ​​nos componentes-por exemplo, para o NameComponent, escrevemos:

const NameInput=()=> {const history=useHistory ();//use a cópia local do global para gerenciar os diferentes comportamentos de forma confiável const [availablePeer, setAvailablePeer]=React.useState (peer); const submit=React.useCallback ((ev)=> {const input=ev.currentTarget.elements.namedItem (“name”); const user=input.value; ev.preventDefault ();//vamos definir o par setAvailablePeer ( novo PeerJs (usuário));}, []); React.useEffect (()=> {//aplique o par local às variáveis ​​globais peer=availablePeer;//inserir o nome só é necessário se ainda não tivermos um par;//se tivermos, vamos mostrar o visão geral if (availablePeer) {history.replace (“/overview”);}}, [availablePeer]); return (

); };

O mesmo é verdadeiro para o componente Visão geral:

const Visão geral=()=> {const history=useHistory (); const [availablePeer]=React.useState (par);//use a cópia local do global para gerenciar os diferentes comportamentos de forma confiável const [availableConnection, setAvailableConnection]=React.useState (conexão); const submit=React.useCallback ((ev)=> {const input=ev.currentTarget.elements.namedItem (“name”); const otherUser=input.value; ev.preventDefault ();//fazer a chamada setAvailableConnection (availablePeer.connect (otherUser));}, [availablePeer]); React.useEffect (()=> {connection=availableConnection; if (! AvailablePeer) {//nenhum par ainda? Precisamos começar com o nome input history.replace (“/”);} else if (availableConnection) {//já é uma conexão? então vamos mostrar a chamada em andamento history.replace (“/call”);} else {//vamos esperar que uma conexão seja feita peer.on (“connection”, setAvailableConnection); return ()=> peer.off (“conexão”, setAvailableConnection);}}, [availablePeer, availableConnection]); return (

Olá, {availablePeer?.id}

); };

Para Call, trata-se principalmente de usar três funções:

Encerrar a chamada ativamente (“desconectar”) Gerenciar o envio de mensagens (“enviar”) Reagir a desconexões e mensagens do outro lado (efeito colateral usando o gancho useEffect)

No código, isso se parece com o seguinte:

React.useEffect (()=> {connection=availableConnection; if (! availableConnection) {history.replace (‘/overview’);} else {const dataHandler=(data: string)=> {setMessages ((msgs)=> [… msgs, data]);}; const closeHandler=()=> {setAvailableConnection (undefined);}; availableConnection.on (‘data’, dataHandler); availableConnection.on (‘close’, closeHandler); return ()=> {availableConnection.off (‘data’, dataHandler); availableConnection.off (‘close’, closeHandler);};} }, [availableConnection]); const submit=React.useCallback ((ev)=> {const input=ev.currentTarget.elements.namedItem (‘message’); const message=input.value; ev.preventDefault (); availableConnection.send (message); input.value=”;}, [availableConnection],); const desconectar=React.useCallback (()=> {availableConnection.close (); setAvailableConnection (undefined);}, [availableConnection]);

O resultado do código acima deve ser semelhante ao apresentado a seguir. Lembre-se de que a experiência do usuário e o estilo geral não foram nosso foco nesta parte da demo. O chat funciona muito bem e, do ponto de vista da funcionalidade, atende aos requisitos.

Um simples bate-papo PeerJS

Agora, é hora de tornar o bate-papo ainda melhor adicionando recursos de áudio e vídeo.

Adicionando áudio e vídeo

PeerJS fornece fluxos de áudio e vídeo com base na API do navegador getUserMedia.

A primeira coisa a fazer é obter uma referência para getUserMedia. Usaremos exclusivamente a API “antiga” e mais estabelecida, que pode ser acessada diretamente do navegador, embora haja uma nova API disponível em navigator.mediaDevices . Como isso funciona de maneira um pouco diferente, mas o mais importante, ainda não é tão difundido e suportado como a API antiga, vamos evitá-lo por enquanto.

Com isso em mente, é uma maneira sólida de obter uma referência para getUserMedia é:

const getUserMedia=navigator.getUserMedia || navigator [“webkitGetUserMedia”] || navigator [“mozGetUserMedia”];

Depois de obtermos uma referência de trabalho (ou não-neste caso, deve realmente apresentar um erro ou ter algum tipo de fallback), podemos usá-la.

Vamos desenvolver o exemplo de mensagem de bate-papo anterior e adicione as informações sobre quem realmente chamou quem na conexão:

//se formos nós que chamamos connection [“caller”]=availablePeer.id;//se a outra parte ligou e recebemos const handler=(conexão)=> {conexão [“chamador”]=conexão.peer; setAvailableConnection (conexão); };

Agora, podemos adicionar o seguinte gancho ao componente Call:

React.useEffect (()=> {if (availableConnection && availablePeer) {let dispose=()=> {}; const handler=( call)=> {getUserMedia ({video: true, audio: true}, (stream)=> {showVideo (stream, selfVideo.current); call.answer (stream);}, (erro)=> {console.log (“Falha ao obter fluxo local”, erro);}); dispose=showStream (call, otherVideo.current);}; if (availableConnection [“caller”]===availablePeer.id) {getUserMedia ({video: true , audio: true}, (stream)=> {showVideo (stream, selfVideo.current); dispose=showStream (availablePeer.call (availableConnection.peer, stream), otherVideo.current);}, (erro)=> {console.log (“Falha ao obter fluxo local”, erro);});} else {availableP eer.on (“chamada”, manipulador); } return ()=> {availablePeer.off (“call”, handler); dispose (); }; }}, [availableConnection, availablePeer]);

Muitas coisas estão acontecendo aqui, então vamos repassá-las uma por uma:

Este gancho só deve funcionar se uma chamada estiver ativa Nós definimos um manipulador, que será usado quando uma chamada estiver prestes a ser recebido Se iniciarmos a chamada, também devemos iniciar o stream. Independentemente de quem iniciou ou recebeu a chamada, obtemos o stream local usando getUserMedia. O stream remoto é então recebido chamando ou sendo chamado

Os elementos de vídeo são apenas referências , ou seja:

//define refs const otherVideo=React.useRef (); const selfVideo=React.useRef ();//…

Ótimo, tentando isso pode resultar em um aplicativo como este:

Um simples vídeo chat PeerJS

Você pode encontrar o exemplo que construímos neste artigo no meu GitHub .

Aplicativos práticos

Então, o que você pode fazer com o PeerJS? Muito. Você pode pensar em sua própria alternativa para soluções desajeitadas de chat de vídeo, como Teams ou Slack. Claro, você pode concluir rapidamente que essas soluções, embora volumosas, vêm com alguns recursos interessantes e a resiliência necessária que você deseja para software de nível de produção. Mas para coisas simples, não há muito que você precise além do que construímos neste artigo.

Eu usei isso para construir um aplicativo da web de comunicação doméstica. Cada tablet e computador em minha casa tem acesso a um pequeno servidor da web hospedado em um Raspberry Pi, que oferece a página de bate-papo e um Instância do servidor PeerJS . Isso permite a descoberta de todos em minha casa e para uma comunicação fácil e sem atrito, sem qualquer camada intermediária.

Conclusão

PeerJS é uma ótima biblioteca para usar se você precisar domar a besta WebRTC e habilite facilmente os recursos de chamada de vídeo e áudio em seu aplicativo da web. É conveniente começar e fornece todos os meios necessários para ajudá-lo a se preparar para a produção rapidamente.

Embora usar o serviço de nuvem fornecido seja um ótimo começo, o servidor Node.js de código aberto fornecido também funciona como um boilerplate para hospedar seu próprio peer broker.

O que você construirá com WebRTC e PeerJS?