Codelab do Firebase + WebRTC

1. Introdução

Neste codelab, você aprenderá a criar um aplicativo simples de chat por vídeo usando a API WebRTC no seu navegador e o Cloud Firestore para sinalização. O aplicativo é chamado de FirebaseRTC e funciona como um exemplo simples que ensina os conceitos básicos da criação de aplicativos compatíveis com WebRTC.

O que você vai aprender

  • Iniciar uma videochamada em um aplicativo da Web usando o WebRTC
  • Assinatura para a parte remota usando o Cloud Firestore

Pré-requisitos

Antes de iniciar este codelab, verifique se você instalou:

  • npm, que geralmente vem com Node.js: é recomendável usar o Node LTS

2. Criar e configurar um projeto do Firebase

Crie um projeto do Firebase

  1. No Console do Firebase, clique em "Adicionar projeto" e nomeie o projeto do FirebaseRTRTC.

Lembre-se do ID do projeto do Firebase.

  1. Clique em "Criar projeto".

O aplicativo que você criará usa dois serviços do Firebase disponíveis na Web:

  • Cloud Firestore para salvar dados estruturados na nuvem e receber notificações instantâneas quando os dados são atualizados
  • Firebase Hosting para hospedar e exibir seus recursos estáticos

Para este codelab específico, você já configurou o Firebase Hosting no projeto que será clonado. No entanto, para o Cloud Firestore, vamos orientar você sobre a configuração e a ativação dos serviços usando o Console do Firebase.

Ative o Cloud Firestore

O app usa o Cloud Firestore para salvar as mensagens de chat e receber novas mensagens de chat.

Você precisará ativar o Cloud Firestore:

  1. Na seção "Desenvolver" do Console do Firebase, clique em "Banco de dados".
  2. Clique em Criar banco de dados no painel do Cloud Firestore.
  3. Selecione a opção Iniciar no modo de teste e clique em "Ativar" depois de ler a exoneração de responsabilidade sobre as regras de segurança.

O modo de teste garante que você possa gravar livremente no banco de dados durante o desenvolvimento. Deixaremos nosso banco de dados mais seguro posteriormente neste codelab.

3. Como conseguir o exemplo de código

Clone o repositório do GitHub na linha de comando:

git clone https://github.com/webrtc/FirebaseRTC

O exemplo de código precisa ter sido clonado para o diretório FirebaseRTC. Verifique se a linha de comando está sendo executada nesse diretório a partir de agora:

cd FirebaseRTC

Importar o app inicial

Abra os arquivos em FirebaseRTC no seu editor e altere-os de acordo com as instruções abaixo. Esse diretório contém o código inicial do codelab, que consiste em um app WebRTC ainda não funcional. Vamos torná-lo funcional durante este codelab.

4. Instalar a interface de linha de comando do Firebase

Com a interface de linha de comando (CLI) do Firebase, é possível disponibilizar seu app da Web localmente e implantá-lo no Firebase Hosting.

  1. Instale a CLI executando o seguinte comando npm: sh npm -g install firebase-tools
  1. Verifique se a CLI foi instalada corretamente executando o seguinte comando: sh firebase --version

Verifique se a versão da CLI do Firebase é a v6.7.1 ou mais recente.

  1. Autorize a CLI do Firebase executando o seguinte comando: sh firebase login

Você configurou o modelo de app da Web para extrair a configuração do seu app do Firebase Hosting do diretório local e dos arquivos do seu app. No entanto, para fazer isso, você precisa associar o app ao projeto do Firebase.

  1. Associe o app ao projeto do Firebase executando o seguinte comando: sh firebase use --add.

  2. Quando solicitado, selecione o ID do projeto e atribua um alias ao projeto do Firebase.

Um alias será útil se você tiver vários ambientes (produção, preparo etc.). No entanto, para este codelab, vamos usar apenas o alias de default.

  1. Siga as instruções restantes na linha de comando.

5. Executar o servidor local

Você está pronto para começar a trabalhar no nosso app. Vamos executar o app localmente!

  1. Execute o seguinte comando da CLI do Firebase: sh firebase serve --only hosting

  2. A linha de comando exibirá a seguinte resposta: hosting: Local server: http://localhost:5000

Estamos usando o emulador do Firebase Hosting para disponibilizar nosso app localmente. Agora, o app da Web deve estar disponível em http://localhost:5000.

  1. Abra o aplicativo em http://localhost:5.000.

Você verá sua cópia do FirebaseRTC que foi conectada ao seu projeto do Firebase.

O app se conectou automaticamente ao projeto do Firebase.

6. Criar uma nova sala

Neste app, cada sessão de chat por vídeo é chamada de sala. Um usuário pode criar uma nova sala clicando em um botão no aplicativo. Isso vai gerar um ID que a parte remota pode usar para participar da mesma sala. O ID é usado como chave no Cloud Firestore para cada sala.

Cada sala conterá o RTCSessionDescriptions da oferta e da resposta, bem como duas coleções separadas com candidatos ICE de cada parte.

Sua primeira tarefa é implementar o código ausente para criar uma nova sala com a oferta inicial do autor da chamada. Abra public/app.js, encontre o comentário // Add code for creating a room here e adicione o seguinte código:

const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);

const roomWithOffer = {
    offer: {
        type: offer.type,
        sdp: offer.sdp
    }
}
const roomRef = await db.collection('rooms').add(roomWithOffer);
const roomId = roomRef.id;
document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!`

A primeira linha cria um RTCSessionDescription que representará a oferta do autor da chamada. Isso é definido como a descrição local e, por fim, escrito no novo objeto de sala no Cloud Firestore.

Em seguida, detectaremos alterações no banco de dados e detectaremos quando uma resposta do recebedor da chamada for adicionada.

roomRef.onSnapshot(async snapshot -> {
    console.log('Got updated room:', snapshot.data());
    const data = snapshot.data();
    if (!peerConnection.currentRemoteDescription && data.answer) {
        console.log('Set remote description: ', data.answer);
        const answer = new RTCSessionDescription(data.answer)
        await peerConnection.setRemoteDescription(answer);
    }
});

Isso aguardará até que o recebedor da chamada grave a RTCSessionDescription para a resposta e a defina como a descrição remota no autor da chamada RTCPeerConnection.

7. Participar de uma sala

A próxima etapa é implementar a lógica de participar de uma sala. O usuário faz isso clicando no botão Participar da sala e inserindo o código da sala para participar. Sua tarefa aqui é implementar a criação de RTCSessionDescription para a resposta e atualizar a sala no banco de dados de acordo com isso.

const offer = roomSnapshot.data().offer;
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);

const roomWithAnswer = {
    answer: {
        type: answer.type,
        sdp: answer.sdp
    }
}
await roomRef.update(roomWithAnswer);

No código acima, começamos extraindo a oferta do autor da chamada e criando um RTCSessionDescription que definimos como a descrição remota. Em seguida, criamos a resposta, definimos a descrição local e atualizamos o banco de dados. A atualização do banco de dados acionará o callback onSnapshot no lado do autor da chamada, que, por sua vez, definirá a descrição remota com base na resposta do recebedor da chamada. Isso conclui a troca de objetos RTCSessionDescription entre o autor da chamada e o recebedor da chamada.

8. Coletar candidatos do ICE

Antes que o autor da chamada e o recebedor da chamada possam se conectar uns aos outros, eles também precisam trocar candidatos ICE que informam ao WebRTC como se conectar ao peering remoto. A próxima tarefa é implementar o código que detecta os candidatos ICE e os adiciona a uma coleção no banco de dados. Encontre a função collectIceCandidates e adicione o seguinte código:

async function collectIceCandidates(roomRef, peerConnection,
                                    localName, remoteName) {
    const candidatesCollection = roomRef.collection(localName);

    peerConnection.addEventListener('icecandidate', event -> {
        if (event.candidate) {
            const json = event.candidate.toJSON();
            candidatesCollection.add(json);
        }
    });

    roomRef.collection(remoteName).onSnapshot(snapshot -> {
        snapshot.docChanges().forEach(change -> {
            if (change.type === "added") {
                const candidate = new RTCIceCandidate(change.doc.data());
                peerConnection.addIceCandidate(candidate);
            }
        });
    })
}

Essa função faz duas coisas. Ele coleta candidatos ICE da API WebRTC e os adiciona ao banco de dados e detecta candidatos ICE adicionados pelo terminal remoto e os adiciona à instância RTCPeerConnection. É importante detectar mudanças no banco de dados para filtrar qualquer coisa que não seja uma nova adição, já que de outra forma teríamos adicionado o mesmo conjunto de candidatos ICE novamente.

9. Conclusão

Neste codelab, você aprendeu a implementar a sinalização para o WebRTC usando o Cloud Firestore e como usá-lo para criar um aplicativo de chat por vídeo simples.

Para saber mais, visite:

  1. Código-fonte do FirebaseRTC
  2. Amostras de WebRTC
  3. Cloud Firestore