Codelab su Firebase e WebRTC

1. Introduzione

In questo codelab, imparerai a creare una semplice applicazione per chat video utilizzando l'API WebRTC nel tuo browser e Cloud Firestore per la segnalazione. L'applicazione è chiamata FirebaseRTC e funziona come un semplice esempio che ti insegnerà le basi della creazione di applicazioni abilitate per WebRTC.

Cosa scoprirai

  • Avvio di una videochiamata in un'applicazione web con WebRTC
  • Segnalazione al gruppo remoto tramite Cloud Firestore

Che cosa ti serve

Prima di iniziare questo codelab, assicurati di avere installato:

  • npm che viene generalmente fornito con Node.js: è consigliabile utilizzare LTS di nodi

2. Creare e configurare un progetto Firebase

Crea un progetto Firebase

  1. Nella Console Firebase, fai clic su Aggiungi progetto, quindi assegna il nome Firebase al progetto FirebaseRTC.

Ricorda l'ID progetto per il progetto Firebase.

  1. Fai clic su Crea progetto.

L'applicazione che stai creando utilizza due servizi Firebase disponibili sul Web:

  • Cloud Firestore per salvare i dati strutturati nel cloud e ricevere notifiche immediate quando i dati vengono aggiornati
  • Firebase Hosting per ospitare e pubblicare le tue risorse statiche

Per questo specifico codelab, hai già configurato Firebase Hosting nel progetto che verrà clonato. Tuttavia, per Cloud Firestore, ti guideremo attraverso la procedura di configurazione e abilitazione dei servizi utilizzando la Console Firebase.

Abilita Cloud Firestore

L'app utilizza Cloud Firestore per salvare i messaggi di chat e riceverne di nuovi.

Dovrai abilitare Cloud Firestore:

  1. Nel menu della console Firebase, fai clic su Database.
  2. Fai clic su Crea database nel riquadro Cloud Firestore.
  3. Seleziona l'opzione Start in test mode, quindi fai clic su Abilita dopo aver letto il disclaimer sulle regole di sicurezza.

La modalità di test ti consente di scrivere liberamente nel database durante lo sviluppo. Creeremo più sicuro il nostro database in seguito in questo codelab.

3. Recupera il codice campione

Clona il repository GitHub dalla riga di comando:

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

Il codice di esempio avrebbe dovuto essere clonato nella directory FirebaseRTC. Assicurati che d'ora in avanti la riga di comando venga eseguita da questa directory:

cd FirebaseRTC

Importare l'app iniziale

Apri i file in FirebaseRTC nell'editor e modificali seguendo le istruzioni riportate di seguito. Questa directory contiene il codice iniziale per il codelab, che consiste in un'app WebRTC ancora non funzionante. Lo faremo funzionare in questo codelab.

4. Installa l'interfaccia a riga di comando di Firebase

L'interfaccia a riga di comando di Firebase consente di gestire l'applicazione web in locale ed eseguire il deployment dell'applicazione web in Firebase Hosting.

  1. Installa l'interfaccia a riga di comando eseguendo il comando npm seguente: sh npm -g install firebase-tools
  1. Verifica che l'interfaccia a riga di comando sia installata correttamente eseguendo questo comando: sh firebase --version

Assicurati che la versione dell'interfaccia a riga di comando di Firebase sia precedente alla 6.7.1 o successiva.

  1. Autorizza l'interfaccia a riga di comando di Firebase eseguendo questo comando: sh firebase login

Hai impostato il modello dell'app web per estrarre la configurazione dell'app per Firebase Hosting dalla directory locale e dai file della tua app. A tale scopo, devi associare la tua app al progetto Firebase.

  1. Associa la tua app al tuo progetto Firebase eseguendo questo comando: sh firebase use --add

  2. Quando richiesto, seleziona l'ID progetto, quindi assegna un alias al progetto Firebase.

Un alias è utile se hai più ambienti (produzione, gestione temporanea e così via). Tuttavia, per questo codelab, utilizziamo l'alias di default.

  1. Segui le istruzioni rimanenti nella riga di comando.

5. Esegui il server locale

Puoi iniziare a lavorare direttamente sulla nostra app. Esegui l'app localmente.

  1. Esegui il seguente comando dell'interfaccia a riga di comando di Firebase: sh firebase serve --only hosting

  2. La riga di comando dovrebbe mostrare la seguente risposta: hosting: Local server: http://localhost:5000

Stiamo utilizzando l'emulatore Firebase Hosting per pubblicare la nostra app localmente. L'app web dovrebbe essere disponibile all'indirizzo http://localhost:5000.

  1. Apri l'app all'indirizzo http://localhost:5000.

Dovresti vedere la tua copia di FirebaseRTC che è stata collegata al progetto Firebase.

L'app si è collegata automaticamente al progetto Firebase.

6. Creazione di una nuova stanza virtuale

In questa applicazione, ogni sessione di chat video è chiamata stanza. Un utente può creare una nuova stanza virtuale facendo clic su un pulsante nella sua applicazione. Verrà generato un ID che il gruppo remoto può utilizzare per partecipare alla stessa stanza. L'ID viene utilizzato come chiave in Cloud Firestore per ogni camera.

Ogni camera conterrà il valore RTCSessionDescriptions per l'offerta e la risposta, nonché due collezioni separate con candidati ICE per ogni parte.

La prima attività è quella di implementare il codice mancante per creare una nuova stanza virtuale con l'offerta iniziale del chiamante. Apri public/app.js, trova il commento // Add code for creating a room here e aggiungi il seguente codice:

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!`

La prima riga crea un elemento RTCSessionDescription che rappresenterà l'offerta del chiamante. Poi viene impostata come descrizione locale e infine scritta nel nuovo oggetto camera in Cloud Firestore.

In seguito ascoltiamo le modifiche apportate al database e rileviamo quando una risposta del destinatario è stata aggiunta.

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);
    }
});

In questo modo, il chiamante scrive RTCSessionDescription per la risposta e lo imposta come descrizione remota del chiamante RTCPeerConnection.

7. Entrare in una stanza virtuale

Il passaggio successivo è implementare la logica per partecipare a una stanza virtuale esistente. L'utente può fare clic sul pulsante Partecipa alla stanza virtuale e inserire l'ID della stanza virtuale per partecipare. L'incarico che ti è stato assegnato consiste nell'implementare la creazione dell'elemento RTCSessionDescription per la risposta e aggiornare di conseguenza la stanza virtuale nel database.

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);

Nel codice riportato sopra, iniziamo estraendo l'offerta dal chiamante e creando un RTCSessionDescription che impostiamo come descrizione remota. Poi creiamo la risposta, la impostiamo come descrizione locale e aggiorniamo il database. L'aggiornamento del database attiverà il callback onSnapshot sul lato chiamante, che a sua volta imposterà la descrizione remota in base alla risposta del chiamante. In questo modo, viene completato lo scambio degli oggetti RTCSessionDescription tra il chiamante e il chiamante.

8. Raccogli i candidati ICE

Prima che il chiamante e il chiamante possano comunicare tra loro, devono anche scambiare i candidati ICE che dicono a WebRTC come connettersi al peer remoto. L'attività successiva consiste nell'implementare il codice che rimane in ascolto dei candidati ICE e li aggiunge a una raccolta nel database. Trova la funzione collectIceCandidates e aggiungi il seguente codice:

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);
            }
        });
    })
}

Questa funzione svolge due operazioni. Raccoglie i candidati ICE dall'API WebRTC e li aggiunge al database, quindi ascolta i candidati ICE aggiunti dal peer remoto e li aggiunge alla sua istanza RTCPeerConnection. Quando si ascoltano modifiche al database, è importante escludere le informazioni che non rappresentano una nuova aggiunta, dato che altrimenti avremmo aggiunto di nuovo lo stesso gruppo di candidati ICE.

9. Conclusione

In questo codelab hai imparato come implementare la segnalazione per WebRTC utilizzando CloudFire e come utilizzarla per creare una semplice applicazione per chat video.

Per saperne di più, consulta le seguenti risorse:

  1. Codice sorgente FirebaseRTC
  2. Campioni di WebRTC
  3. Cloud Firestore