Ćwiczenia z programowania Firebase i WebRTC

1. Wstęp

Z tego modułu dowiesz się, jak utworzyć prostą aplikację do obsługi czatu wideo, korzystając z interfejsu API WebRTC w przeglądarce i Cloud Firestore do sygnalizowania. Ta aplikacja nosi nazwę FirebaseRTC i stanowi prosty przykład tworzenia aplikacji obsługujących WebRTC.

Czego się nauczysz

  • Rozpoczynanie rozmowy wideo w aplikacji internetowej za pomocą WebRTC
  • Sygnalizowanie do zespołu zdalnego za pomocą Cloud Firestore

Czego potrzebujesz

Zanim rozpoczniesz te ćwiczenia z programowania, sprawdź, czy masz zainstalowane te programy:

  • npm, które zwykle jest dołączone do Node.js – zalecamy LTS węzła

2. Tworzenie i konfigurowanie projektu Firebase

Tworzenie projektu Firebase

  1. W konsoli Firebase kliknij Dodaj projekt, a następnie nazwij projekt FirebaseRTC.

Zapamiętaj identyfikator projektu Firebase.

  1. Kliknij Utwórz projekt.

Aplikacja, którą utworzysz, używa 2 usług Firebase dostępnych w internecie:

  • Cloud Firestore, aby zapisać uporządkowane dane w chmurze i otrzymać powiadomienie, gdy dane zostaną zaktualizowane
  • Hosting Firebase do obsługi i wyświetlania zasobów statycznych

W tym konkretnym ćwiczeniu z programowania udało Ci się już skonfigurować hosting Firebase w projekcie, który będziesz kopiować. W Cloud Firestore przeprowadzimy Cię jednak przez konfigurację i włączanie usług za pomocą konsoli Firebase.

Włącz Cloud Firestore

Aplikacja używa Cloud Firestore do zapisywania wiadomości na czacie i odbierania nowych wiadomości.

Konieczne będzie włączenie Cloud Firestore:

  1. W sekcji konsoli programisty w menu Firebase kliknij opcję Baza danych.
  2. Kliknij Utwórz bazę danych w panelu Cloud Firestore.
  3. Wybierz opcję Uruchom w trybie testowym, a następnie kliknij Włącz po przeczytaniu wyłączenia odpowiedzialności dotyczącego reguł zabezpieczeń.

Dzięki trybowi testowemu możesz swobodnie zapisywać się w bazie danych w trakcie programowania. W ramach naszych ćwiczeń z ćwiczeniami zajmiemy się lepszą bazą danych.

3. Pobieranie przykładowego kodu

Skopiuj repozytorium GitHub z wiersza poleceń:

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

Przykładowy kod powinien zostać skopiowany do katalogu FirebaseRTC. Upewnij się, że od tego momentu wiersz poleceń będzie uruchamiany z poziomu tego katalogu:

cd FirebaseRTC

Importuj aplikację startową

Otwórz pliki w edytorze FirebaseRTC i zmień je zgodnie z instrukcjami poniżej. Ten katalog zawiera kod początkowy ćwiczenia z programowania. Składa się on z jeszcze niedziałającej aplikacji WebRTC. Będzie on działał w tym całego ćwiczenia.

4. Zainstaluj interfejs wiersza poleceń Firebase

Interfejs wiersza poleceń Firebase (CLI) umożliwia udostępnianie aplikacji internetowej lokalnie i wdrażanie jej w Hostingu Firebase.

  1. Zainstaluj wiersz poleceń, uruchamiając to polecenie npm: sh npm -g install firebase-tools
  1. Aby sprawdzić, czy interfejs wiersza poleceń został prawidłowo zainstalowany, uruchom to polecenie: sh firebase --version

Upewnij się, że interfejs wiersza poleceń Firebase ma wersję 6.7.1 lub nowszą.

  1. Autoryzuj wiersz poleceń Firebase, uruchamiając następujące polecenie: sh firebase login

Udało Ci się skonfigurować szablon aplikacji internetowej tak, by pobierał dane o konfiguracji Twojej aplikacji do Firebase. Hostujesz ją z lokalnego katalogu i plików. Aby to jednak zrobić, musisz powiązać swoją aplikację z projektem Firebase.

  1. Powiąż aplikację z projektem Firebase, uruchamiając to polecenie: sh firebase use --add

  2. Gdy pojawi się prośba, wybierz identyfikator projektu, a następnie nadaj projektowi alias.

Alias jest przydatny, gdy masz wiele środowisk (produkcyjnego, przejściowego itp.). Na potrzeby tego ćwiczenia wybierzmy alias domeny default.

  1. Postępuj zgodnie z pozostałymi instrukcjami podanymi w wierszu polecenia.

5. Uruchom serwer lokalny

Możesz już zacząć pracować w naszej aplikacji. Uruchommy ją lokalnie.

  1. Uruchom to polecenie w wierszu poleceń Firebase: sh firebase serve --only hosting

  2. W wierszu poleceń powinna się wyświetlić ta odpowiedź: hosting: Local server: http://localhost:5000

Aby wyświetlać naszą aplikację lokalnie, korzystamy z emulatora Hostingu Firebase. Aplikacja internetowa powinna być teraz dostępna pod adresem http://localhost:5000.

  1. Otwórz aplikację na stronie http://localhost:5000.

Powinna wyświetlić się kopia FirebaseRTC, która została połączona z Twoim projektem Firebase.

Aplikacja automatycznie połączyła się z Twoim projektem Firebase.

6. Tworzę nowy pokój

W tej aplikacji każda sesja czatu wideo jest nazywana salą. Użytkownik może utworzyć nowy pokój, klikając przycisk w aplikacji. Wygeneruje to identyfikator, za pomocą którego strona zdalna może dołączyć do tego samego pokoju. Identyfikator jest używany jako klucz w Cloud Firestore dla każdej sali.

Każdy pokój będzie zawierał element RTCSessionDescriptions dotyczący oferty i odpowiedzi oraz 2 oddzielne kolekcje kandydatów ICE od każdej ze stron.

Pierwszym zadaniem jest wdrożenie brakującego kodu, aby utworzyć nowy pokój z początkową ofertą od rozmówcy. Otwórz public/app.js, znajdź komentarz // Add code for creating a room here i dodaj ten kod:

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

Pierwszy wiersz tworzy RTCSessionDescription, który reprezentuje ofertę od dzwoniącego. Następnie jest on ustawiany jako opis lokalny, a na koniec zapisywany w nowym obiekcie pokoju w Cloud Firestore.

Następnie będziemy wykrywać zmiany w bazie danych i wykrywać, kiedy została dodana odpowiedź rozmówcy.

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

Poczekaj, aż rozmówca wpisze RTCSessionDescription jako odpowiedź i ustawi to jako zdalny opis na urządzeniu rozmówcy RTCPeerConnection.

7. Dołączam do pokoju

Następnym krokiem jest wdrożenie logiki pozwalającej na dołączenie do istniejącego pokoju. Aby to zrobić, kliknij przycisk Dołącz do pokoju i wpisz identyfikator pokoju, do którego chcesz dołączyć. Twoim zadaniem jest wdrożenie tworzenia RTCSessionDescription dla odpowiedzi i odpowiednie zaktualizowanie pokoju w bazie danych.

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

W kodzie powyżej rozpoczynamy od wyodrębnienia oferty od rozmówcy i utworzenia RTCSessionDescription, który ustawiamy jako zdalny opis. Następnie tworzymy odpowiedź, ustawiamy ją jako opis lokalny i aktualizujemy bazę danych. Aktualizacja bazy danych wywoła wywołanie zwrotne onSnapshot po stronie rozmówcy, co spowoduje ustawienie zdalnego opisu na podstawie odpowiedzi rozmówcy. To powoduje wymianę obiektów RTCSessionDescription między rozmówcą a wywołającym.

8. Zbieraj kandydatów ICE

Zanim rozmówca i rozmówca będą mogli się ze sobą połączyć, muszą też wymienić kandydatów ICE, którzy informują WebRTC, jak nawiązać połączenie z odległym peerem. Następnym zadaniem jest wdrożenie kodu, który wykrywa kandydata ICE i dodaje je do kolekcji w bazie danych. Znajdź funkcję collectIceCandidates i dodaj ten kod:

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

Ta funkcja ma dwie funkcje. Zbiera podpowiedzi ICE z interfejsu API WebRTC, dodaje je do bazy danych oraz nasłuchuje dodanych kandydatów ICE ze zdalnego peera i dodaje ich do instancji RTCPeerConnection. Słuchając zmian w bazie danych, odfiltrowujesz wszystko, co nie jest nowością, bo w przeciwnym razie powtarzalibyśmy dodawanie tego samego zestawu kandydatów ICE.

9. Podsumowanie

Dzięki nim dowiesz się, jak wdrożyć sygnalizację dla WebRTC za pomocą Cloud Storage oraz jak utworzyć prostą aplikację do obsługi czatu wideo.

Więcej informacji na ten temat znajdziesz tutaj:

  1. Kod źródłowy FirebaseRTC
  2. Przykłady WebRTC
  3. Cloud Firestore