Firebase + WebRTC の Codelab

1. はじめに

この Codelab では、ブラウザの WebRTC API とシグナリング用の Cloud Firestore を使用してシンプルなビデオチャット アプリケーションを構築する方法を学びます。このアプリケーションは FirebaseRTC と呼ばれ、WebRTC 対応アプリケーションを作成するための基本的な手順を示す簡単なサンプルです。

学習内容

  • WebRTC を使用してウェブ アプリケーションでビデオ通話を開始する
  • Cloud Firestore を使用したリモート パーティーへのシグナリング

必要なもの

この Codelab を開始する前に、以下がインストールされていることを確認してください。

  • 通常は Node.js に付属する npm - ノード LTS をおすすめします

2. Firebase プロジェクトを作成して設定する

Firebase プロジェクトを作成する

  1. Firebase コンソールで [プロジェクトを追加] をクリックし、Firebase プロジェクトに FirebaseRTC という名前を付けます。

Firebase プロジェクトのプロジェクト ID を覚えておいてください。

  1. [プロジェクトを作成] をクリックします。

構築するアプリケーションは、ウェブ上で利用できる Firebase サービスを 2 つ使用します。

  • Cloud Firestore を使用して構造化データをクラウドに保存し、データが更新されたらすぐに通知を受け取れます。
  • 静的アセットをホストして提供する Firebase Hosting

この Codelab では、クローンを作成するプロジェクト内で Firebase Hosting をすでに構成しています。ただし、Cloud Firestore の場合は、Firebase コンソールを使用してサービスの構成と有効化を行う方法について説明します。

Cloud Firestore を有効にする

アプリは Cloud Firestore を使用してチャット メッセージを保存し、新しいチャット メッセージを受信します。

Cloud Firestore を有効にする必要があります。

  1. Firebase コンソールのメニューの [開発] セクションで、[データベース] をクリックします。
  2. [Cloud Firestore] ペインで [データベースを作成] をクリックします。
  3. [テストモードで開始] オプションを選択し、セキュリティ ルールに関する免責条項を確認してから [有効にする] をクリックします。

テストモードでは、開発中に自由にデータベースに書き込むことができます。この Codelab の後半で、データベースのセキュリティを強化します。

3. サンプルコードを取得する

コマンドラインから GitHub リポジトリのクローンを作成します。

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

サンプルコードのクローンは FirebaseRTC ディレクトリに作成されています。今後は、次のディレクトリからコマンドラインが実行されるようにします。

cd FirebaseRTC

スターター アプリをインポートする

エディタで FirebaseRTC 内のファイルを開き、次の手順で変更します。このディレクトリには、Codelab のスターター コードが含まれています。これは、まだ機能していない WebRTC アプリで構成されています。この Codelab 全体を通して機能させます。

4. Firebase コマンドライン インターフェースをインストールする

Firebase コマンドライン インターフェース(CLI)を使用すると、ウェブアプリをローカルで提供し、Firebase Hosting にデプロイできます。

  1. 次の npm コマンドを実行して、CLI をインストールします。sh npm -g install firebase-tools
  1. sh firebase --version コマンドを実行して、CLI が正しくインストールされたことを確認します。

Firebase CLI のバージョンが v6.7.1 以降であることを確認します。

  1. Firebase CLI を承認するには、コマンド sh firebase login を実行します。

アプリのローカル ディレクトリとファイルから Firebase Hosting の構成を pull するようにウェブアプリ テンプレートを設定しました。ただし、これを行うには、アプリを Firebase プロジェクトに関連付ける必要があります。

  1. 次のコマンドを実行して、アプリを Firebase プロジェクトに関連付けます。sh firebase use --add

  2. プロンプトが表示されたら、プロジェクト ID を選択し、Firebase プロジェクトにエイリアスを指定します。

エイリアスは、複数の環境(本番環境、ステージング環境など)がある場合に便利です。ただし、この Codelab では、default というエイリアスのみを使用します。

  1. コマンドラインの残りの手順に従います。

5. ローカル サーバーを実行する

アプリで実際に作業を開始する準備が整いました。アプリをローカルで実行しましょう。

  1. Firebase CLI コマンドを実行します。sh firebase serve --only hosting

  2. コマンドラインに次のレスポンスが表示されます。 hosting: Local server: http://localhost:5000

Firebase Hosting エミュレータを使用してアプリをローカルで提供しています。これで、http://localhost:5000 からウェブアプリが利用可能になります。

  1. http://localhost:5000 でアプリを開きます。

Firebase プロジェクトに接続されている FirebaseRTC のコピーが表示されます。

アプリが自動的に Firebase プロジェクトに接続されました。

6. 新しい部屋を作成しています

このアプリケーションでは、各ビデオチャット セッションをチャットルームと呼びます。ユーザーは、アプリケーションのボタンをクリックして新しいチャットルームを作成できます。これにより、リモート パーティーで同じ会議室への参加に使用できる ID が生成されます。この ID は、Cloud Firestore で各チャットルームのキーとして使用されます。

各部屋には、オファーと回答の両方の RTCSessionDescriptions と、各当事者からの ICE 候補を含む 2 つの個別のコレクションが含まれます。

最初のタスクでは、欠落しているコードを実装して、呼び出し元からの最初のオファーで新しいチャットルームを作成します。public/app.js を開いて、コメント // Add code for creating a room here を探して、次のコードを追加します。

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

最初の行は、呼び出し元からのオファーを表す RTCSessionDescription を作成します。その後、ローカルの説明として設定され、Cloud Firestore の新しい Room オブジェクトに書き込まれます。

次に、データベースに対する変更をリッスンし、呼び出し先からの回答が追加されたことを検出します。

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

これは、呼び出し先が回答用の RTCSessionDescription を書き込むまで待機し、これを呼び出し側 RTCPeerConnection のリモート記述として設定します。

7. チャットルームへの参加

次のステップでは、既存のチャットルームに参加するためのロジックを実装します。ユーザーは、[チャットルームに参加] ボタンをクリックして、参加するチャットルームの ID を入力します。ここでのタスクは、回答用の RTCSessionDescription の作成を実装し、それに応じてデータベース内の部屋を更新することです。

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

上記のコードでは、まず呼び出し元から特典を抽出し、リモートの説明として設定する RTCSessionDescription を作成します。次に、回答を作成し、それをローカルの説明として設定し、データベースを更新します。データベースが更新されると呼び出し元で onSnapshot コールバックがトリガーされ、呼び出し先からの回答に基づいてリモートの説明が設定されます。これにより、呼び出し元と呼び出し先の間の RTCSessionDescription オブジェクトの交換が完了します。

8. ICE 候補の収集

呼び出し元と呼び出し先が相互に接続するには、リモートピアとの接続方法を WebRTC に指示する ICE 候補を交換する必要があります。次のタスクでは、ICE 候補をリッスンし、データベース内のコレクションに追加するコードを実装します。関数 collectIceCandidates を見つけて、次のコードを追加します。

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

この関数は 2 つのことを行います。WebRTC API から ICE 候補を収集してデータベースに追加し、リモートピアから追加された ICE 候補をリッスンして RTCPeerConnection インスタンスに追加します。データベースの変更をリッスンして新しい追加ではないものを除外することが重要です。そうしないと、同じ ICE 候補のセットが繰り返し追加されます。

9. まとめ

この Codelab では、Cloud Firestore を使用して WebRTC 用のシグナリングを実装する方法と、それを使用して簡単なビデオチャット アプリケーションを作成する方法について学習しました。

詳しくは以下のページをご覧ください。

  1. FirebaseRTC ソースコード
  2. WebRTC のサンプル
  3. Cloud Firestore