From 78c0fafca98cc2fb1a11a5fd376004ec78b946c9 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 19:56:09 -0800 Subject: [PATCH 01/45] notes on running tutorial... --- personalNotes.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 personalNotes.md diff --git a/personalNotes.md b/personalNotes.md new file mode 100644 index 0000000..d630e11 --- /dev/null +++ b/personalNotes.md @@ -0,0 +1,6 @@ +Following tutorial: https://webrtc.org/getting-started/firebase-rtc-codelab + +Notes: +* `sh npm -g install firebase-tools` doesn't work, omit `sh`, works. +* `firebase login` gives error: 'cannot run login from non-interactive mode. See login:ci to generate a token for use in non-interactive environments + * Am I supposed to be running these commands from within a terminal in Firebase? Cuz if so, could NOT find a terminal if one exists... From 15523519a8f72deab43c52bcc80833bdc65a7b8d Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 20:01:12 -0800 Subject: [PATCH 02/45] hooray for gitbash being noninteractive? --- personalNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/personalNotes.md b/personalNotes.md index d630e11..2a941de 100644 --- a/personalNotes.md +++ b/personalNotes.md @@ -4,3 +4,4 @@ Notes: * `sh npm -g install firebase-tools` doesn't work, omit `sh`, works. * `firebase login` gives error: 'cannot run login from non-interactive mode. See login:ci to generate a token for use in non-interactive environments * Am I supposed to be running these commands from within a terminal in Firebase? Cuz if so, could NOT find a terminal if one exists... + * ah...cool, becauase I'm using git-bash...it's 'non-interactive'. https://github.com/firebase/firebase-tools/issues/149 so we can force that with `--interactive`. From ba51bcedf572e212c1e1ee2c63ea8211cfa34abb Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 20:05:02 -0800 Subject: [PATCH 03/45] interactive mode all the time. --- personalNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/personalNotes.md b/personalNotes.md index 2a941de..15caadc 100644 --- a/personalNotes.md +++ b/personalNotes.md @@ -5,3 +5,4 @@ Notes: * `firebase login` gives error: 'cannot run login from non-interactive mode. See login:ci to generate a token for use in non-interactive environments * Am I supposed to be running these commands from within a terminal in Firebase? Cuz if so, could NOT find a terminal if one exists... * ah...cool, becauase I'm using git-bash...it's 'non-interactive'. https://github.com/firebase/firebase-tools/issues/149 so we can force that with `--interactive`. +* Okay so just add `--interactive` after every command, and don't ever use `sh` if you're using git-bash on windows... From 4af5ef7499614ac1670d7fe658ad0608fb6edf72 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 20:14:20 -0800 Subject: [PATCH 04/45] firebase has changed since tutorial written, I think. --- personalNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/personalNotes.md b/personalNotes.md index 15caadc..c73b289 100644 --- a/personalNotes.md +++ b/personalNotes.md @@ -6,3 +6,4 @@ Notes: * Am I supposed to be running these commands from within a terminal in Firebase? Cuz if so, could NOT find a terminal if one exists... * ah...cool, becauase I'm using git-bash...it's 'non-interactive'. https://github.com/firebase/firebase-tools/issues/149 so we can force that with `--interactive`. * Okay so just add `--interactive` after every command, and don't ever use `sh` if you're using git-bash on windows... +* Regarding the tutorial about Firebase: there's no longer a "developer" pane or what have you, you instead have to go to Build -> Cloud Firestore to do the create database step. From a2a0a98e5d8a7904a3dad2986ce517dc915b54c4 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 20:21:36 -0800 Subject: [PATCH 05/45] create the room code. --- public/app.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/public/app.js b/public/app.js index c5d9e52..50beed9 100644 --- a/public/app.js +++ b/public/app.js @@ -39,6 +39,31 @@ async function createRoom() { // 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!'; + + 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); + } + } + + // Code for creating room above localStream.getTracks().forEach(track => { From bc913a6adfa36f3d3cb827a068d2046e4b6f3cb3 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 20:55:58 -0800 Subject: [PATCH 06/45] join the room code --- public/app.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/public/app.js b/public/app.js index 50beed9..7125b03 100644 --- a/public/app.js +++ b/public/app.js @@ -137,6 +137,19 @@ async function joinRoomById(roomId) { }); // Code for creating SDP answer below + 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); // Code for creating SDP answer above From 7c0a882ce5f8e7d6389f0dcafb2bf78b36b18695 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 21:13:36 -0800 Subject: [PATCH 07/45] I hate tutorials that are not written well or maintained... --- personalNotes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/personalNotes.md b/personalNotes.md index c73b289..87e569f 100644 --- a/personalNotes.md +++ b/personalNotes.md @@ -7,3 +7,5 @@ Notes: * ah...cool, becauase I'm using git-bash...it's 'non-interactive'. https://github.com/firebase/firebase-tools/issues/149 so we can force that with `--interactive`. * Okay so just add `--interactive` after every command, and don't ever use `sh` if you're using git-bash on windows... * Regarding the tutorial about Firebase: there's no longer a "developer" pane or what have you, you instead have to go to Build -> Cloud Firestore to do the create database step. + +* okkkaaayyy so that tutorial is gloriously half-baked. In comparing the 'solution' code to the starter code and the instructions in the tutorial...there's no way anyone following the tutorial will make it functional without just copying the solution branch app.js into their own in frustration. Ugh. Let's.....start this over, shall we? From fbe1beca3e5408d6edbcdcd786dbeccb83a7aedf Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Wed, 16 Dec 2020 21:14:18 -0800 Subject: [PATCH 08/45] so annoyed. --- public/app.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/public/app.js b/public/app.js index 7125b03..a1d3636 100644 --- a/public/app.js +++ b/public/app.js @@ -159,6 +159,26 @@ async function joinRoomById(roomId) { } } +async function collectIceCandidates(roomRef, peerConnection, localName, remoteName) { + const candidatesCollection = roomRef.collection(localName); + + peerConnection.addEventListener('icecandidate', event -> { + if (event.candidate) { + const json = event.candidate.toJSON(); + candidatesColection.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); + } + }); + }); +} + async function openUserMedia(e) { const stream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); From 8473c50300efb7508eac1d5b076d0c971bbc33e0 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 21:22:28 -0800 Subject: [PATCH 09/45] overwriting with original from the parent repo --- public/app.js | 58 --------------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/public/app.js b/public/app.js index a1d3636..c5d9e52 100644 --- a/public/app.js +++ b/public/app.js @@ -39,31 +39,6 @@ async function createRoom() { // 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!'; - - 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); - } - } - - // Code for creating room above localStream.getTracks().forEach(track => { @@ -137,19 +112,6 @@ async function joinRoomById(roomId) { }); // Code for creating SDP answer below - 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); // Code for creating SDP answer above @@ -159,26 +121,6 @@ async function joinRoomById(roomId) { } } -async function collectIceCandidates(roomRef, peerConnection, localName, remoteName) { - const candidatesCollection = roomRef.collection(localName); - - peerConnection.addEventListener('icecandidate', event -> { - if (event.candidate) { - const json = event.candidate.toJSON(); - candidatesColection.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); - } - }); - }); -} - async function openUserMedia(e) { const stream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); From 0b4a67a496364544ba7f0b1339cb84f593c82143 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 21:51:43 -0800 Subject: [PATCH 10/45] Copying the instructions so I can make the better. --- correctedCodelab.md | 176 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 correctedCodelab.md diff --git a/correctedCodelab.md b/correctedCodelab.md new file mode 100644 index 0000000..27057de --- /dev/null +++ b/correctedCodelab.md @@ -0,0 +1,176 @@ +# Firebase + WebRTC Codelab + + +1. Introduction +In this codelab, you'll learn how to build a simple video chat application using the WebRTC API in your browser and Cloud Firestore for signaling. The application is called FirebaseRTC and works as a simple example that will teach you the basics of building WebRTC enabled applications. + + Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. + +__What you'll learn__ +* Initiating a video call in a web application using WebRTC +* Signaling to the remote party using Cloud Firestore + +__What you'll need__ +Before starting this codelab, make sure that you've installed: + +* npm which typically comes with Node.js - Node LTS is recommended + +1. Create and set up a Firebase project + +* Create a Firebase project +In the Firebase console, click Add project, then name the Firebase project FirebaseRTC. +Remember the Project ID for your Firebase project. + +Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) +Click Create project. +The application that you're going to build uses two Firebase services available on the web: + +Cloud Firestore to save structured data on the Cloud and get instant notification when the data is updated +Firebase Hosting to host and serve your static assets +For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. + +Enable Cloud Firestore +The app uses Cloud Firestore to save the chat messages and receive new chat messages. + +You'll need to enable Cloud Firestore: + +In the Firebase console menu's Develop section, click Database. +Click Create database in the Cloud Firestore pane. +Select the Start in test mode option, then click Enable after reading the disclaimer about the security rules. +Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. + +1. Get the sample code +Clone the GitHub repository from the command line: + + +git clone https://github.com/webrtc/FirebaseRTC +The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: + + +cd FirebaseRTC +Import the starter app +Open the files in FirebaseRTC in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. + +1. Install the Firebase Command Line Interface +The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. + +Note: To install the CLI, you need to install npm which typically comes with Node.js. +Install the CLI by running the following npm command: sh npm -g install firebase-tools +Note: Doesn't work? You may need to run the command using sudo instead. +Verify that the CLI has been installed correctly by running the following command: sh firebase --version +Make sure the version of the Firebase CLI is v6.7.1 or later. + +Authorize the Firebase CLI by running the following command: sh firebase login +You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. + +Associate your app with your Firebase project by running the following command: sh firebase use --add + +When prompted, select your Project ID, then give your Firebase project an alias. + +An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. + +Follow the remaining instructions in your command line. + +1. Run the local server +You're ready to actually start work on our app! Let's run the app locally! + +Run the following Firebase CLI command: sh firebase serve --only hosting + +Your command line should display the following response: hosting: Local server: http://localhost:5000 + +We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. + +Open your app at http://localhost:5000. +You should see your copy of FirebaseRTC which has been connected to your Firebase project. + +The app has automatically connected to your Firebase project. + +1. Creating a new room +In this application, each video chat session is called a room. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. + +Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with ICE candidates from each party. + +Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment // Add code for creating a room here and add the following code: + + +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!` +The first line creates an RTCSessionDescription that will represent the offer from the caller. This is then set as the local description, and finally written to the new room object in Cloud Firestore. + +Next, we will listen for changes to the database and detect when an answer from the callee has been added. + + +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); + } +}); +This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. + +1. Joining a room +The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. + + +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); +In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. + +1. Collect ICE candidates +Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: + +``` +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()); + peerConneciton.addIceCandidate(candidate); + } + }); + }) +} +``` +This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + +1. Conclusion +In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. + +To learn more, visit the following resources: + +FirebaseRTC Source Code +WebRTC samples +Cloud Firestore From 2d0b2580c147049ecd0956dc4c27e1b2474a85a5 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:01:34 -0800 Subject: [PATCH 11/45] Update correctedCodelab.md --- correctedCodelab.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 27057de..f62c488 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -1,21 +1,23 @@ # Firebase + WebRTC Codelab -1. Introduction +## Introduction In this codelab, you'll learn how to build a simple video chat application using the WebRTC API in your browser and Cloud Firestore for signaling. The application is called FirebaseRTC and works as a simple example that will teach you the basics of building WebRTC enabled applications. - Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. +> Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. -__What you'll learn__ +### What you'll learn * Initiating a video call in a web application using WebRTC * Signaling to the remote party using Cloud Firestore -__What you'll need__ +### What you'll need Before starting this codelab, make sure that you've installed: * npm which typically comes with Node.js - Node LTS is recommended -1. Create and set up a Firebase project +## Instructions + +1) Create and set up a Firebase project * Create a Firebase project In the Firebase console, click Add project, then name the Firebase project FirebaseRTC. @@ -39,7 +41,7 @@ Click Create database in the Cloud Firestore pane. Select the Start in test mode option, then click Enable after reading the disclaimer about the security rules. Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. -1. Get the sample code +1) Get the sample code Clone the GitHub repository from the command line: @@ -51,7 +53,7 @@ cd FirebaseRTC Import the starter app Open the files in FirebaseRTC in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. -1. Install the Firebase Command Line Interface +1) Install the Firebase Command Line Interface The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. Note: To install the CLI, you need to install npm which typically comes with Node.js. @@ -71,7 +73,7 @@ An alias is useful if you have multiple environments (production, staging, etc). Follow the remaining instructions in your command line. -1. Run the local server +1) Run the local server You're ready to actually start work on our app! Let's run the app locally! Run the following Firebase CLI command: sh firebase serve --only hosting @@ -85,7 +87,7 @@ You should see your copy of FirebaseRTC which has been connected to your Firebas The app has automatically connected to your Firebase project. -1. Creating a new room +1) Creating a new room In this application, each video chat session is called a room. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with ICE candidates from each party. @@ -121,7 +123,7 @@ roomRef.onSnapshot(async snapshot -> { }); This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. -1. Joining a room +1) Joining a room The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. @@ -139,7 +141,7 @@ const roomWithAnswer = { await roomRef.update(roomWithAnswer); In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. -1. Collect ICE candidates +1) Collect ICE candidates Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: ``` @@ -166,7 +168,7 @@ async function collectIceCandidates(roomRef, peerConnection, ``` This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. -1. Conclusion +1) Conclusion In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. To learn more, visit the following resources: From 0eb32557ce9b0135b6c507b339a2f7c27538ab2e Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:10:02 -0800 Subject: [PATCH 12/45] Update correctedCodelab.md --- correctedCodelab.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index f62c488..83a1702 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -1,7 +1,7 @@ # Firebase + WebRTC Codelab - ## Introduction + In this codelab, you'll learn how to build a simple video chat application using the WebRTC API in your browser and Cloud Firestore for signaling. The application is called FirebaseRTC and works as a simple example that will teach you the basics of building WebRTC enabled applications. > Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. @@ -12,18 +12,16 @@ In this codelab, you'll learn how to build a simple video chat application using ### What you'll need Before starting this codelab, make sure that you've installed: - * npm which typically comes with Node.js - Node LTS is recommended ## Instructions -1) Create and set up a Firebase project - -* Create a Firebase project -In the Firebase console, click Add project, then name the Firebase project FirebaseRTC. -Remember the Project ID for your Firebase project. - -Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) +1. Create and set up a Firebase project + 1. Create a Firebase project + - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". + - Remember the Project ID for your Firebase project. + - Disable Google Analytics and create the project +>Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) Click Create project. The application that you're going to build uses two Firebase services available on the web: From 3416c9085c0b7a89821cefa6d8b056055e091858 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:12:52 -0800 Subject: [PATCH 13/45] Update correctedCodelab.md --- correctedCodelab.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 83a1702..aa29b3d 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -20,10 +20,11 @@ Before starting this codelab, make sure that you've installed: 1. Create a Firebase project - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". - Remember the Project ID for your Firebase project. - - Disable Google Analytics and create the project ->Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) -Click Create project. -The application that you're going to build uses two Firebase services available on the web: + >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) + - Disable Google Analytics + - Click Create project. + + The application that you're going to build uses two Firebase services available on the web: Cloud Firestore to save structured data on the Cloud and get instant notification when the data is updated Firebase Hosting to host and serve your static assets From fce0d9fa33980e4c12dbd72773a0ee29f966fe05 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:14:36 -0800 Subject: [PATCH 14/45] Update correctedCodelab.md --- correctedCodelab.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index aa29b3d..73d0bde 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -25,12 +25,14 @@ Before starting this codelab, make sure that you've installed: - Click Create project. The application that you're going to build uses two Firebase services available on the web: + * Cloud Firestore + - to save structured data on the Cloud and get instant notification when the data is updated + * Firebase Hosting + - to host and serve your static assets -Cloud Firestore to save structured data on the Cloud and get instant notification when the data is updated -Firebase Hosting to host and serve your static assets -For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. + For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. -Enable Cloud Firestore +1. Enable Cloud Firestore The app uses Cloud Firestore to save the chat messages and receive new chat messages. You'll need to enable Cloud Firestore: From 16b01855aaf72181481acef538c26b54d6fa4bbd Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:25:59 -0800 Subject: [PATCH 15/45] Update correctedCodelab.md --- correctedCodelab.md | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 73d0bde..003877f 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -23,6 +23,7 @@ Before starting this codelab, make sure that you've installed: >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) - Disable Google Analytics - Click Create project. + - Creation takes a moment. When you project is created, click Continue. The application that you're going to build uses two Firebase services available on the web: * Cloud Firestore @@ -33,28 +34,22 @@ Before starting this codelab, make sure that you've installed: For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. 1. Enable Cloud Firestore -The app uses Cloud Firestore to save the chat messages and receive new chat messages. - -You'll need to enable Cloud Firestore: - -In the Firebase console menu's Develop section, click Database. -Click Create database in the Cloud Firestore pane. -Select the Start in test mode option, then click Enable after reading the disclaimer about the security rules. -Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. - -1) Get the sample code -Clone the GitHub repository from the command line: - - -git clone https://github.com/webrtc/FirebaseRTC -The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: - - -cd FirebaseRTC -Import the starter app -Open the files in FirebaseRTC in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. - -1) Install the Firebase Command Line Interface + The app uses Cloud Firestore to save the chat messages and receive new chat messages. + 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. + 1. Click __Create database__ in the Cloud Firestore pane. + 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. + Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. (TODO: ...this tutorial doesn't handle making the database more secure...just...so you know) + +1. Get the sample code + 1. On your local machine, clone the codelab GitHub repository from the command line: + `git clone https://github.com/webrtc/FirebaseRTC` + The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: + `cd FirebaseRTC` + 1. Import the starter app + + As you work through the tutorial, open the files in `FirebaseRTC`in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. + +1. Install the Firebase Command Line Interface The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. Note: To install the CLI, you need to install npm which typically comes with Node.js. From 2dfc596be0d823a4ff49a6a5fc0d8d1735e5cf57 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:33:05 -0800 Subject: [PATCH 16/45] Update correctedCodelab.md --- correctedCodelab.md | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 003877f..abf18a1 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -23,7 +23,7 @@ Before starting this codelab, make sure that you've installed: >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) - Disable Google Analytics - Click Create project. - - Creation takes a moment. When you project is created, click Continue. + - Creation takes a moment. When your project is created, click Continue. The application that you're going to build uses two Firebase services available on the web: * Cloud Firestore @@ -50,26 +50,24 @@ Before starting this codelab, make sure that you've installed: As you work through the tutorial, open the files in `FirebaseRTC`in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. 1. Install the Firebase Command Line Interface -The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. - -Note: To install the CLI, you need to install npm which typically comes with Node.js. -Install the CLI by running the following npm command: sh npm -g install firebase-tools -Note: Doesn't work? You may need to run the command using sudo instead. -Verify that the CLI has been installed correctly by running the following command: sh firebase --version -Make sure the version of the Firebase CLI is v6.7.1 or later. - -Authorize the Firebase CLI by running the following command: sh firebase login -You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. - -Associate your app with your Firebase project by running the following command: sh firebase use --add - -When prompted, select your Project ID, then give your Firebase project an alias. - -An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. - -Follow the remaining instructions in your command line. - -1) Run the local server + The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. + > Note: To install the CLI, you need to install npm which typically comes with Node.js. + 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` + - if you're in git-bash, run `npm -g install firebase-tools` instead. + - On unix and doesn't work? You may need to run the command using sudo instead. + 1. Verify that the CLI has been installed correctly by running the following command: `sh firebase --version` + - `firebase --version` on git-bash + - Make sure the version of the Firebase CLI is v6.7.1 or later. + 1. Authorize the Firebase CLI by running the following command: `sh firebase login` + - `firebase login --interactive` on git-bash + + You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. + 1. Associate your app with your Firebase project by running the following command: `sh firebase use --add` + - `firebase use --add --interactive` on git-bash + - When prompted, select your Project ID (from the Create Firestore Project step), then give your Firebase project an alias. An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. + 1. Follow the remaining instructions in your command line. + +1. Run the local server You're ready to actually start work on our app! Let's run the app locally! Run the following Firebase CLI command: sh firebase serve --only hosting From 12510d1bd111daa2ba4aa92bafce04362bc22920 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:37:06 -0800 Subject: [PATCH 17/45] Update correctedCodelab.md --- correctedCodelab.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index abf18a1..573b809 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -38,7 +38,8 @@ Before starting this codelab, make sure that you've installed: 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. 1. Click __Create database__ in the Cloud Firestore pane. 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. - Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. (TODO: ...this tutorial doesn't handle making the database more secure...just...so you know) + Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. + > TODO: ...this tutorial doesn't actually handle making the database more secure. 1. Get the sample code 1. On your local machine, clone the codelab GitHub repository from the command line: @@ -68,20 +69,17 @@ Before starting this codelab, make sure that you've installed: 1. Follow the remaining instructions in your command line. 1. Run the local server -You're ready to actually start work on our app! Let's run the app locally! - -Run the following Firebase CLI command: sh firebase serve --only hosting - -Your command line should display the following response: hosting: Local server: http://localhost:5000 - -We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. - -Open your app at http://localhost:5000. -You should see your copy of FirebaseRTC which has been connected to your Firebase project. - -The app has automatically connected to your Firebase project. - -1) Creating a new room + You're ready to actually start work on our app! Let's run the app locally! + 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` + - `firebase serve --only hosting --interactive` on git-bash + Your command line should display the following response: + > hosting: Local server: http://localhost:5000 + We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from [](http://localhost:5000). + 1. Open your app at [](http://localhost:5000) + You should see your copy of FirebaseRTC which has been connected to your Firebase project. + The app has automatically connected to your Firebase project. + +1. Creating a new room In this application, each video chat session is called a room. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with ICE candidates from each party. From 654ca0e4c111a3db5e2a8afd3f85961854757ebf Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:39:18 -0800 Subject: [PATCH 18/45] Update correctedCodelab.md --- correctedCodelab.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 573b809..fab418d 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -73,9 +73,9 @@ Before starting this codelab, make sure that you've installed: 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - `firebase serve --only hosting --interactive` on git-bash Your command line should display the following response: - > hosting: Local server: http://localhost:5000 - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from [](http://localhost:5000). - 1. Open your app at [](http://localhost:5000) + > hosting: Local server: http://localhost:5000 + We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from [http://localhost:5000]. + 1. Open your app at [http://localhost:5000] You should see your copy of FirebaseRTC which has been connected to your Firebase project. The app has automatically connected to your Firebase project. From 8920e3abbbc293b2bc07064ddb3938984f6f413c Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:40:17 -0800 Subject: [PATCH 19/45] Update correctedCodelab.md --- correctedCodelab.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index fab418d..a8f2c51 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -74,8 +74,8 @@ Before starting this codelab, make sure that you've installed: - `firebase serve --only hosting --interactive` on git-bash Your command line should display the following response: > hosting: Local server: http://localhost:5000 - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from [http://localhost:5000]. - 1. Open your app at [http://localhost:5000] + - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. + 1. Open your app at http://localhost:5000 You should see your copy of FirebaseRTC which has been connected to your Firebase project. The app has automatically connected to your Firebase project. From e85d4598dd32438e3312e528430c0f27f3a64378 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:45:46 -0800 Subject: [PATCH 20/45] Update correctedCodelab.md --- correctedCodelab.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index a8f2c51..8aa2f48 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -44,7 +44,7 @@ Before starting this codelab, make sure that you've installed: 1. Get the sample code 1. On your local machine, clone the codelab GitHub repository from the command line: `git clone https://github.com/webrtc/FirebaseRTC` - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: + - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: `cd FirebaseRTC` 1. Import the starter app @@ -72,7 +72,7 @@ Before starting this codelab, make sure that you've installed: You're ready to actually start work on our app! Let's run the app locally! 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - `firebase serve --only hosting --interactive` on git-bash - Your command line should display the following response: + - Your command line should display the following response: > hosting: Local server: http://localhost:5000 - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. 1. Open your app at http://localhost:5000 @@ -80,9 +80,9 @@ Before starting this codelab, make sure that you've installed: The app has automatically connected to your Firebase project. 1. Creating a new room -In this application, each video chat session is called a room. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. - -Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with ICE candidates from each party. + In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. + + Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment // Add code for creating a room here and add the following code: From 50609166262332caf39482c414a0f4222ba1d65a Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:49:05 -0800 Subject: [PATCH 21/45] okay it's bed time, this isn't complete but at least it's better. --- correctedCodelab.md | 163 ++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 8aa2f48..6b3a621 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -16,7 +16,7 @@ Before starting this codelab, make sure that you've installed: ## Instructions -1. Create and set up a Firebase project +1. __Create and set up a Firebase project__ 1. Create a Firebase project - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". - Remember the Project ID for your Firebase project. @@ -33,7 +33,7 @@ Before starting this codelab, make sure that you've installed: For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. -1. Enable Cloud Firestore +1. __Enable Cloud Firestore__ The app uses Cloud Firestore to save the chat messages and receive new chat messages. 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. 1. Click __Create database__ in the Cloud Firestore pane. @@ -41,7 +41,7 @@ Before starting this codelab, make sure that you've installed: Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. > TODO: ...this tutorial doesn't actually handle making the database more secure. -1. Get the sample code +1. __Get the sample code__ 1. On your local machine, clone the codelab GitHub repository from the command line: `git clone https://github.com/webrtc/FirebaseRTC` - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: @@ -50,7 +50,7 @@ Before starting this codelab, make sure that you've installed: As you work through the tutorial, open the files in `FirebaseRTC`in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. -1. Install the Firebase Command Line Interface +1. __Install the Firebase Command Line Interface__ The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. > Note: To install the CLI, you need to install npm which typically comes with Node.js. 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` @@ -68,7 +68,7 @@ Before starting this codelab, make sure that you've installed: - When prompted, select your Project ID (from the Create Firestore Project step), then give your Firebase project an alias. An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. 1. Follow the remaining instructions in your command line. -1. Run the local server +1. __Run the local server__ You're ready to actually start work on our app! Let's run the app locally! 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - `firebase serve --only hosting --interactive` on git-bash @@ -79,92 +79,95 @@ Before starting this codelab, make sure that you've installed: You should see your copy of FirebaseRTC which has been connected to your Firebase project. The app has automatically connected to your Firebase project. -1. Creating a new room +1. __Creating a new room__ In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. -Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment // Add code for creating a room here and add the following code: + Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment // Add code for creating a room here and add the following code: + ``` + const offer = await peerConnection.createOffer(); + await peerConnection.setLocalDescription(offer); -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!` -The first line creates an RTCSessionDescription that will represent the offer from the caller. This is then set as the local description, and finally written to the new room object in Cloud Firestore. - -Next, we will listen for changes to the database and detect when an answer from the callee has been added. - - -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); - } -}); -This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. - -1) Joining a room -The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. - - -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 + const roomWithOffer = { + offer: { + type: offer.type, + sdp: offer.sdp + } } -} -await roomRef.update(roomWithAnswer); -In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. - -1) Collect ICE candidates -Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: - -``` -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); + const roomRef = await db.collection('rooms').add(roomWithOffer); + const roomId = roomRef.id; + document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!` + ``` + The first line creates an RTCSessionDescription that will represent the offer from the caller. This is then set as the local description, and finally written to the new room object in Cloud Firestore. + + Next, we will listen for changes to the database and detect when an answer from the callee has been added. + + ``` + 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); } }); - - roomRef.collection(remoteName).onSnapshot(snapshot -> { - snapshot.docChanges().forEach(change -> { - if (change.type === "added") { - const candidate = new RTCIceCandidate(change.doc.data()); - peerConneciton.addIceCandidate(candidate); + ``` + This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. + +1. __Joining a room__ + The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. + + ``` + 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); + ``` + In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. + +1. Collect ICE candidates + Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: + + ``` + 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); } }); - }) -} -``` -This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. -1) Conclusion -In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. + roomRef.collection(remoteName).onSnapshot(snapshot -> { + snapshot.docChanges().forEach(change -> { + if (change.type === "added") { + const candidate = new RTCIceCandidate(change.doc.data()); + peerConneciton.addIceCandidate(candidate); + } + }); + }) + } + ``` + This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + +1. Conclusion + In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. -To learn more, visit the following resources: + To learn more, visit the following resources: -FirebaseRTC Source Code -WebRTC samples -Cloud Firestore + FirebaseRTC Source Code + WebRTC samples + Cloud Firestore From 9742817b2fd429f7bda1ea8b40ea65bc6a5895f0 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Wed, 16 Dec 2020 22:50:15 -0800 Subject: [PATCH 22/45] Update correctedCodelab.md --- correctedCodelab.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/correctedCodelab.md b/correctedCodelab.md index 6b3a621..83f1c52 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -34,6 +34,7 @@ Before starting this codelab, make sure that you've installed: For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. 1. __Enable Cloud Firestore__ + The app uses Cloud Firestore to save the chat messages and receive new chat messages. 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. 1. Click __Create database__ in the Cloud Firestore pane. @@ -51,6 +52,7 @@ Before starting this codelab, make sure that you've installed: As you work through the tutorial, open the files in `FirebaseRTC`in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. 1. __Install the Firebase Command Line Interface__ + The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. > Note: To install the CLI, you need to install npm which typically comes with Node.js. 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` @@ -69,6 +71,7 @@ Before starting this codelab, make sure that you've installed: 1. Follow the remaining instructions in your command line. 1. __Run the local server__ + You're ready to actually start work on our app! Let's run the app locally! 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - `firebase serve --only hosting --interactive` on git-bash @@ -80,6 +83,7 @@ Before starting this codelab, make sure that you've installed: The app has automatically connected to your Firebase project. 1. __Creating a new room__ + In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. @@ -118,6 +122,7 @@ Before starting this codelab, make sure that you've installed: This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. 1. __Joining a room__ + The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. ``` @@ -137,6 +142,7 @@ Before starting this codelab, make sure that you've installed: In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. 1. Collect ICE candidates + Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: ``` @@ -164,6 +170,7 @@ Before starting this codelab, make sure that you've installed: This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. 1. Conclusion + In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. To learn more, visit the following resources: From 72a5681f80bd3551670e56b424e9540835dad69f Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 20:31:26 -0800 Subject: [PATCH 23/45] gonna pick this apart to reverse engineer 'start' --- public/app_solution.js | 262 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 public/app_solution.js diff --git a/public/app_solution.js b/public/app_solution.js new file mode 100644 index 0000000..4e3b4d6 --- /dev/null +++ b/public/app_solution.js @@ -0,0 +1,262 @@ +mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button')); + +const configuration = { + iceServers: [ + { + urls: [ + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + ], + }, + ], + iceCandidatePoolSize: 10, +}; + +let peerConnection = null; +let localStream = null; +let remoteStream = null; +let roomDialog = null; +let roomId = null; + +function init() { + document.querySelector('#cameraBtn').addEventListener('click', openUserMedia); + document.querySelector('#hangupBtn').addEventListener('click', hangUp); + document.querySelector('#createBtn').addEventListener('click', createRoom); + document.querySelector('#joinBtn').addEventListener('click', joinRoom); + roomDialog = new mdc.dialog.MDCDialog(document.querySelector('#room-dialog')); +} + +async function createRoom() { + document.querySelector('#createBtn').disabled = true; + document.querySelector('#joinBtn').disabled = true; + const db = firebase.firestore(); + const roomRef = await db.collection('rooms').doc(); + + console.log('Create PeerConnection with configuration: ', configuration); + peerConnection = new RTCPeerConnection(configuration); + + registerPeerConnectionListeners(); + + localStream.getTracks().forEach(track => { + peerConnection.addTrack(track, localStream); + }); + + // Code for collecting ICE candidates below + const callerCandidatesCollection = roomRef.collection('callerCandidates'); + + peerConnection.addEventListener('icecandidate', event => { + if (!event.candidate) { + console.log('Got final candidate!'); + return; + } + console.log('Got candidate: ', event.candidate); + callerCandidatesCollection.add(event.candidate.toJSON()); + }); + // Code for collecting ICE candidates above + + // Code for creating a room below + const offer = await peerConnection.createOffer(); + await peerConnection.setLocalDescription(offer); + console.log('Created offer:', offer); + + const roomWithOffer = { + 'offer': { + type: offer.type, + sdp: offer.sdp, + }, + }; + await roomRef.set(roomWithOffer); + roomId = roomRef.id; + console.log(`New room created with SDP offer. Room ID: ${roomRef.id}`); + document.querySelector( + '#currentRoom').innerText = `Current room is ${roomRef.id} - You are the caller!`; + // Code for creating a room above + + peerConnection.addEventListener('track', event => { + console.log('Got remote track:', event.streams[0]); + event.streams[0].getTracks().forEach(track => { + console.log('Add a track to the remoteStream:', track); + remoteStream.addTrack(track); + }); + }); + + // Listening for remote session description below + roomRef.onSnapshot(async snapshot => { + const data = snapshot.data(); + if (!peerConnection.currentRemoteDescription && data && data.answer) { + console.log('Got remote description: ', data.answer); + const rtcSessionDescription = new RTCSessionDescription(data.answer); + await peerConnection.setRemoteDescription(rtcSessionDescription); + } + }); + // Listening for remote session description above + + // Listen for remote ICE candidates below + roomRef.collection('calleeCandidates').onSnapshot(snapshot => { + snapshot.docChanges().forEach(async change => { + if (change.type === 'added') { + let data = change.doc.data(); + console.log(`Got new remote ICE candidate: ${JSON.stringify(data)}`); + await peerConnection.addIceCandidate(new RTCIceCandidate(data)); + } + }); + }); + // Listen for remote ICE candidates above +} + +function joinRoom() { + document.querySelector('#createBtn').disabled = true; + document.querySelector('#joinBtn').disabled = true; + + document.querySelector('#confirmJoinBtn'). + addEventListener('click', async () => { + roomId = document.querySelector('#room-id').value; + console.log('Join room: ', roomId); + document.querySelector( + '#currentRoom').innerText = `Current room is ${roomId} - You are the callee!`; + await joinRoomById(roomId); + }, {once: true}); + roomDialog.open(); +} + +async function joinRoomById(roomId) { + const db = firebase.firestore(); + const roomRef = db.collection('rooms').doc(`${roomId}`); + const roomSnapshot = await roomRef.get(); + console.log('Got room:', roomSnapshot.exists); + + if (roomSnapshot.exists) { + console.log('Create PeerConnection with configuration: ', configuration); + peerConnection = new RTCPeerConnection(configuration); + registerPeerConnectionListeners(); + localStream.getTracks().forEach(track => { + peerConnection.addTrack(track, localStream); + }); + + // Code for collecting ICE candidates below + const calleeCandidatesCollection = roomRef.collection('calleeCandidates'); + peerConnection.addEventListener('icecandidate', event => { + if (!event.candidate) { + console.log('Got final candidate!'); + return; + } + console.log('Got candidate: ', event.candidate); + calleeCandidatesCollection.add(event.candidate.toJSON()); + }); + // Code for collecting ICE candidates above + + peerConnection.addEventListener('track', event => { + console.log('Got remote track:', event.streams[0]); + event.streams[0].getTracks().forEach(track => { + console.log('Add a track to the remoteStream:', track); + remoteStream.addTrack(track); + }); + }); + + // Code for creating SDP answer below + const offer = roomSnapshot.data().offer; + console.log('Got offer:', offer); + await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); + const answer = await peerConnection.createAnswer(); + console.log('Created answer:', answer); + await peerConnection.setLocalDescription(answer); + + const roomWithAnswer = { + answer: { + type: answer.type, + sdp: answer.sdp, + }, + }; + await roomRef.update(roomWithAnswer); + // Code for creating SDP answer above + + // Listening for remote ICE candidates below + roomRef.collection('callerCandidates').onSnapshot(snapshot => { + snapshot.docChanges().forEach(async change => { + if (change.type === 'added') { + let data = change.doc.data(); + console.log(`Got new remote ICE candidate: ${JSON.stringify(data)}`); + await peerConnection.addIceCandidate(new RTCIceCandidate(data)); + } + }); + }); + // Listening for remote ICE candidates above + } +} + +async function openUserMedia(e) { + const stream = await navigator.mediaDevices.getUserMedia( + {video: true, audio: true}); + document.querySelector('#localVideo').srcObject = stream; + localStream = stream; + remoteStream = new MediaStream(); + document.querySelector('#remoteVideo').srcObject = remoteStream; + + console.log('Stream:', document.querySelector('#localVideo').srcObject); + document.querySelector('#cameraBtn').disabled = true; + document.querySelector('#joinBtn').disabled = false; + document.querySelector('#createBtn').disabled = false; + document.querySelector('#hangupBtn').disabled = false; +} + +async function hangUp(e) { + const tracks = document.querySelector('#localVideo').srcObject.getTracks(); + tracks.forEach(track => { + track.stop(); + }); + + if (remoteStream) { + remoteStream.getTracks().forEach(track => track.stop()); + } + + if (peerConnection) { + peerConnection.close(); + } + + document.querySelector('#localVideo').srcObject = null; + document.querySelector('#remoteVideo').srcObject = null; + document.querySelector('#cameraBtn').disabled = false; + document.querySelector('#joinBtn').disabled = true; + document.querySelector('#createBtn').disabled = true; + document.querySelector('#hangupBtn').disabled = true; + document.querySelector('#currentRoom').innerText = ''; + + // Delete room on hangup + if (roomId) { + const db = firebase.firestore(); + const roomRef = db.collection('rooms').doc(roomId); + const calleeCandidates = await roomRef.collection('calleeCandidates').get(); + calleeCandidates.forEach(async candidate => { + await candidate.ref.delete(); + }); + const callerCandidates = await roomRef.collection('callerCandidates').get(); + callerCandidates.forEach(async candidate => { + await candidate.ref.delete(); + }); + await roomRef.delete(); + } + + document.location.reload(true); +} + +function registerPeerConnectionListeners() { + peerConnection.addEventListener('icegatheringstatechange', () => { + console.log( + `ICE gathering state changed: ${peerConnection.iceGatheringState}`); + }); + + peerConnection.addEventListener('connectionstatechange', () => { + console.log(`Connection state change: ${peerConnection.connectionState}`); + }); + + peerConnection.addEventListener('signalingstatechange', () => { + console.log(`Signaling state change: ${peerConnection.signalingState}`); + }); + + peerConnection.addEventListener('iceconnectionstatechange ', () => { + console.log( + `ICE connection state change: ${peerConnection.iceConnectionState}`); + }); +} + +init(); From a19359878da9b3ed011a36376bb2d29d7d3ca0e4 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Thu, 17 Dec 2020 20:48:11 -0800 Subject: [PATCH 24/45] remove the room ref code from solution --- public/app_solution.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/public/app_solution.js b/public/app_solution.js index 4e3b4d6..3e15e65 100644 --- a/public/app_solution.js +++ b/public/app_solution.js @@ -55,21 +55,7 @@ async function createRoom() { // Code for collecting ICE candidates above // Code for creating a room below - const offer = await peerConnection.createOffer(); - await peerConnection.setLocalDescription(offer); - console.log('Created offer:', offer); - - const roomWithOffer = { - 'offer': { - type: offer.type, - sdp: offer.sdp, - }, - }; - await roomRef.set(roomWithOffer); - roomId = roomRef.id; - console.log(`New room created with SDP offer. Room ID: ${roomRef.id}`); - document.querySelector( - '#currentRoom').innerText = `Current room is ${roomRef.id} - You are the caller!`; + // Code for creating a room above peerConnection.addEventListener('track', event => { From 5c6fe7d584b265522c003300907a758b55bb7fc9 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 20:53:50 -0800 Subject: [PATCH 25/45] fix 'create the room' instructions --- correctedCodelab.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 83f1c52..78e35f4 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -86,40 +86,40 @@ Before starting this codelab, make sure that you've installed: In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. - Each room will contain the RTCSessionDescriptions for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. + Each room will contain the `RTCSessionDescriptions` for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. - Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment // Add code for creating a room here and add the following code: + Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment `// Add code for creating a room here` and add the following code: ``` const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); - + console.log('Created offer:', offer); const roomWithOffer = { - offer: { + 'offer': { type: offer.type, sdp: offer.sdp } } - const roomRef = await db.collection('rooms').add(roomWithOffer); - const roomId = roomRef.id; + await roomRef.set(roomWithOffer); + roomId = roomRef.id; + console.log('New room created with SDP offer. Room ID: ${roomRef.id}'); document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!` ``` - The first line creates an RTCSessionDescription that will represent the offer from the caller. This is then set as the local description, and finally written to the new room object in Cloud Firestore. + The first line creates an `RTCSessionDescription` that will represent the offer from the caller. This is then set as the local description and, finally, written to the new room object in Cloud Firestore. - Next, we will listen for changes to the database and detect when an answer from the callee has been added. + Next, we will listen for changes to the database and detect when an answer from the callee has been added. Find the comment `// Listening for remote session description below` and add the following code: ``` - 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); - } - }); + roomRef.onSnapshot(async snapshot => { + const data = snapshot.data(); + if (!peerConnection.currentRemoteDescription && data && data.answer) { + console.log('Got remote description: ', data.answer); + const rtcSessionDescription = new RTCSessionDescription(data.answer); + await peerConnection.setRemoteDescription(rtcSessionDescription); + } + }); ``` - This will wait until the callee writes the RTCSessionDescription for the answer, and set that as the remote description on the caller RTCPeerConnection. + This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`. 1. __Joining a room__ From 6c5990101ef44236b0f2434f4c9a3e20bdcfe674 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Thu, 17 Dec 2020 20:55:38 -0800 Subject: [PATCH 26/45] remove the room ref code from solution --- public/app_solution.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/public/app_solution.js b/public/app_solution.js index 3e15e65..6b50d4a 100644 --- a/public/app_solution.js +++ b/public/app_solution.js @@ -67,14 +67,7 @@ async function createRoom() { }); // Listening for remote session description below - roomRef.onSnapshot(async snapshot => { - const data = snapshot.data(); - if (!peerConnection.currentRemoteDescription && data && data.answer) { - console.log('Got remote description: ', data.answer); - const rtcSessionDescription = new RTCSessionDescription(data.answer); - await peerConnection.setRemoteDescription(rtcSessionDescription); - } - }); + // Listening for remote session description above // Listen for remote ICE candidates below From aa83102e622a01843f4de1a63246b3920990d9aa Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Thu, 17 Dec 2020 21:05:06 -0800 Subject: [PATCH 27/45] remove the join room code from solution --- public/app_solution.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/public/app_solution.js b/public/app_solution.js index 6b50d4a..3808e6c 100644 --- a/public/app_solution.js +++ b/public/app_solution.js @@ -133,20 +133,7 @@ async function joinRoomById(roomId) { }); // Code for creating SDP answer below - const offer = roomSnapshot.data().offer; - console.log('Got offer:', offer); - await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); - const answer = await peerConnection.createAnswer(); - console.log('Created answer:', answer); - await peerConnection.setLocalDescription(answer); - - const roomWithAnswer = { - answer: { - type: answer.type, - sdp: answer.sdp, - }, - }; - await roomRef.update(roomWithAnswer); + // Code for creating SDP answer above // Listening for remote ICE candidates below From 002ed6c06bdac2afc0e06e7197e9c29f0c8ad710 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 21:40:39 -0800 Subject: [PATCH 28/45] fixed the collect ice candidates step --- correctedCodelab.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 78e35f4..cb4d957 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -117,33 +117,35 @@ Before starting this codelab, make sure that you've installed: const rtcSessionDescription = new RTCSessionDescription(data.answer); await peerConnection.setRemoteDescription(rtcSessionDescription); } - }); + }); ``` - This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`. + This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`. 1. __Joining a room__ - The next step is to implement the logic for joining an existing room. The user does this by clicking the Join room button and entering the ID for the room to join. Your task here is to implement the creation of the RTCSessionDescription for the answer and update the room in the database accordingly. + The next step is to implement the logic for joining an existing room. The user does this by clicking the "Join room" button and entering the ID for the room to join. Your task here is to implement the creation of the `RTCSessionDescription` for the answer and update the room in the database accordingly. Find the comment `// Code for creating SDP answer below` in the `joinRoomById(roomId)` method and add the following code: ``` const offer = roomSnapshot.data().offer; - await peerConnection.setRemoteDescription(offer); + console.log('Got offer:', offer); + await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); const answer = await peerConnection.createAnswer(); + console.log('Created answer:', answer); await peerConnection.setLocalDescription(answer); const roomWithAnswer = { - answer: { - type: answer.type, - sdp: answer.sdp - } - } + answer: { + type: answer.type, + sdp: answer.sdp, + }, + }; await roomRef.update(roomWithAnswer); ``` - In the code above, we start by extracting the offer from the caller and creating a RTCSessionDescription that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the onSnapshot callback on the caller side, which in turn will set the remote description based on the answer from the callee. This completes the exchange of the RTCSessionDescription objects between the caller and the callee. + In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback on the caller side which, in turn, will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. -1. Collect ICE candidates +1. __Collect ICE candidates__ - Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the function collectIceCandidates and add the following code: + Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the comment `// collect ICE Candidates function below` and add the following function: ``` async function collectIceCandidates(roomRef, peerConnection, @@ -167,7 +169,9 @@ Before starting this codelab, make sure that you've installed: }) } ``` - This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its RTCPeerConnection instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + + Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. 1. Conclusion From 225d3a3433e06bba102b7eb5073e1e1b9873944f Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 21:42:25 -0800 Subject: [PATCH 29/45] Update correctedCodelab.md --- correctedCodelab.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index cb4d957..8be4751 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -169,7 +169,10 @@ Before starting this codelab, make sure that you've installed: }) } ``` - This function does two things. It collects ICE candidates from the WebRTC API and adds them to the database, and listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + This function does two main things. + * collects ICE candidates from the WebRTC API and adds them to the database + * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. + It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. From 1e8e2ebd697776accc436a036f65453e6c5500a1 Mon Sep 17 00:00:00 2001 From: meldaravaniel Date: Thu, 17 Dec 2020 21:50:27 -0800 Subject: [PATCH 30/45] removed iceCollection and made match solution code, without spoilers --- public/app_solution.js | 64 ++++++++++-------------------------------- 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/public/app_solution.js b/public/app_solution.js index 3808e6c..127a5a0 100644 --- a/public/app_solution.js +++ b/public/app_solution.js @@ -12,6 +12,9 @@ const configuration = { iceCandidatePoolSize: 10, }; +const callerCandidatesString = 'callerCandidates'; +const calleeCandidatesString = 'calleeCandidates'; + let peerConnection = null; let localStream = null; let remoteStream = null; @@ -41,19 +44,10 @@ async function createRoom() { peerConnection.addTrack(track, localStream); }); - // Code for collecting ICE candidates below - const callerCandidatesCollection = roomRef.collection('callerCandidates'); - - peerConnection.addEventListener('icecandidate', event => { - if (!event.candidate) { - console.log('Got final candidate!'); - return; - } - console.log('Got candidate: ', event.candidate); - callerCandidatesCollection.add(event.candidate.toJSON()); - }); - // Code for collecting ICE candidates above + // Uncomment to collect ICE candidates below + // await collectIceCandidates(roomRef, peerConnection, callerCandidatesString, calleeCandidatesString); + // Code for creating a room below // Code for creating a room above @@ -69,18 +63,6 @@ async function createRoom() { // Listening for remote session description below // Listening for remote session description above - - // Listen for remote ICE candidates below - roomRef.collection('calleeCandidates').onSnapshot(snapshot => { - snapshot.docChanges().forEach(async change => { - if (change.type === 'added') { - let data = change.doc.data(); - console.log(`Got new remote ICE candidate: ${JSON.stringify(data)}`); - await peerConnection.addIceCandidate(new RTCIceCandidate(data)); - } - }); - }); - // Listen for remote ICE candidates above } function joinRoom() { @@ -112,17 +94,9 @@ async function joinRoomById(roomId) { peerConnection.addTrack(track, localStream); }); - // Code for collecting ICE candidates below - const calleeCandidatesCollection = roomRef.collection('calleeCandidates'); - peerConnection.addEventListener('icecandidate', event => { - if (!event.candidate) { - console.log('Got final candidate!'); - return; - } - console.log('Got candidate: ', event.candidate); - calleeCandidatesCollection.add(event.candidate.toJSON()); - }); - // Code for collecting ICE candidates above + + // Uncomment to collect ICE candidates below + // await collectIceCandidates(roomRef, peerConnection, calleeCandidatesString, callerCandidatesString); peerConnection.addEventListener('track', event => { console.log('Got remote track:', event.streams[0]); @@ -135,21 +109,13 @@ async function joinRoomById(roomId) { // Code for creating SDP answer below // Code for creating SDP answer above - - // Listening for remote ICE candidates below - roomRef.collection('callerCandidates').onSnapshot(snapshot => { - snapshot.docChanges().forEach(async change => { - if (change.type === 'added') { - let data = change.doc.data(); - console.log(`Got new remote ICE candidate: ${JSON.stringify(data)}`); - await peerConnection.addIceCandidate(new RTCIceCandidate(data)); - } - }); - }); - // Listening for remote ICE candidates above } } +// collect ICE Candidates function below + +// collect ICE Candidates function above + async function openUserMedia(e) { const stream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); @@ -191,11 +157,11 @@ async function hangUp(e) { if (roomId) { const db = firebase.firestore(); const roomRef = db.collection('rooms').doc(roomId); - const calleeCandidates = await roomRef.collection('calleeCandidates').get(); + const calleeCandidates = await roomRef.collection(calleeCandidatesString).get(); calleeCandidates.forEach(async candidate => { await candidate.ref.delete(); }); - const callerCandidates = await roomRef.collection('callerCandidates').get(); + const callerCandidates = await roomRef.collection(callerCandidatesString).get(); callerCandidates.forEach(async candidate => { await candidate.ref.delete(); }); From f4279cdd11649fee070f3ef840c17b1227ac9a05 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:00:35 -0800 Subject: [PATCH 31/45] fix empty codelab starting point This version did not match the solution correctly and made it confusing to do the tutorial without just looking at the solution code. I copied the (also changed) solution app.js into this and removed the code sections that are to be added by the tutorial. --- public/app.js | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/public/app.js b/public/app.js index c5d9e52..127a5a0 100644 --- a/public/app.js +++ b/public/app.js @@ -1,6 +1,5 @@ mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button')); -// DEfault configuration - Change these if you have a different STUN or TURN server. const configuration = { iceServers: [ { @@ -13,6 +12,9 @@ const configuration = { iceCandidatePoolSize: 10, }; +const callerCandidatesString = 'callerCandidates'; +const calleeCandidatesString = 'calleeCandidates'; + let peerConnection = null; let localStream = null; let remoteStream = null; @@ -31,28 +33,25 @@ async function createRoom() { document.querySelector('#createBtn').disabled = true; document.querySelector('#joinBtn').disabled = true; const db = firebase.firestore(); + const roomRef = await db.collection('rooms').doc(); console.log('Create PeerConnection with configuration: ', configuration); peerConnection = new RTCPeerConnection(configuration); registerPeerConnectionListeners(); - // Add code for creating a room here - - // Code for creating room above - localStream.getTracks().forEach(track => { peerConnection.addTrack(track, localStream); }); + + // Uncomment to collect ICE candidates below + // await collectIceCandidates(roomRef, peerConnection, callerCandidatesString, calleeCandidatesString); + // Code for creating a room below // Code for creating a room above - // Code for collecting ICE candidates below - - // Code for collecting ICE candidates above - peerConnection.addEventListener('track', event => { console.log('Got remote track:', event.streams[0]); event.streams[0].getTracks().forEach(track => { @@ -64,10 +63,6 @@ async function createRoom() { // Listening for remote session description below // Listening for remote session description above - - // Listen for remote ICE candidates below - - // Listen for remote ICE candidates above } function joinRoom() { @@ -99,9 +94,9 @@ async function joinRoomById(roomId) { peerConnection.addTrack(track, localStream); }); - // Code for collecting ICE candidates below - // Code for collecting ICE candidates above + // Uncomment to collect ICE candidates below + // await collectIceCandidates(roomRef, peerConnection, calleeCandidatesString, callerCandidatesString); peerConnection.addEventListener('track', event => { console.log('Got remote track:', event.streams[0]); @@ -114,13 +109,13 @@ async function joinRoomById(roomId) { // Code for creating SDP answer below // Code for creating SDP answer above - - // Listening for remote ICE candidates below - - // Listening for remote ICE candidates above } } +// collect ICE Candidates function below + +// collect ICE Candidates function above + async function openUserMedia(e) { const stream = await navigator.mediaDevices.getUserMedia( {video: true, audio: true}); @@ -162,13 +157,13 @@ async function hangUp(e) { if (roomId) { const db = firebase.firestore(); const roomRef = db.collection('rooms').doc(roomId); - const calleeCandidates = await roomRef.collection('calleeCandidates').get(); + const calleeCandidates = await roomRef.collection(calleeCandidatesString).get(); calleeCandidates.forEach(async candidate => { - await candidate.delete(); + await candidate.ref.delete(); }); - const callerCandidates = await roomRef.collection('callerCandidates').get(); + const callerCandidates = await roomRef.collection(callerCandidatesString).get(); callerCandidates.forEach(async candidate => { - await candidate.delete(); + await candidate.ref.delete(); }); await roomRef.delete(); } From ce8429392cfd1f0c88ad9437e0b10847e9231b3d Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:00:49 -0800 Subject: [PATCH 32/45] Delete app_solution.js --- public/app_solution.js | 194 ----------------------------------------- 1 file changed, 194 deletions(-) delete mode 100644 public/app_solution.js diff --git a/public/app_solution.js b/public/app_solution.js deleted file mode 100644 index 127a5a0..0000000 --- a/public/app_solution.js +++ /dev/null @@ -1,194 +0,0 @@ -mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button')); - -const configuration = { - iceServers: [ - { - urls: [ - 'stun:stun1.l.google.com:19302', - 'stun:stun2.l.google.com:19302', - ], - }, - ], - iceCandidatePoolSize: 10, -}; - -const callerCandidatesString = 'callerCandidates'; -const calleeCandidatesString = 'calleeCandidates'; - -let peerConnection = null; -let localStream = null; -let remoteStream = null; -let roomDialog = null; -let roomId = null; - -function init() { - document.querySelector('#cameraBtn').addEventListener('click', openUserMedia); - document.querySelector('#hangupBtn').addEventListener('click', hangUp); - document.querySelector('#createBtn').addEventListener('click', createRoom); - document.querySelector('#joinBtn').addEventListener('click', joinRoom); - roomDialog = new mdc.dialog.MDCDialog(document.querySelector('#room-dialog')); -} - -async function createRoom() { - document.querySelector('#createBtn').disabled = true; - document.querySelector('#joinBtn').disabled = true; - const db = firebase.firestore(); - const roomRef = await db.collection('rooms').doc(); - - console.log('Create PeerConnection with configuration: ', configuration); - peerConnection = new RTCPeerConnection(configuration); - - registerPeerConnectionListeners(); - - localStream.getTracks().forEach(track => { - peerConnection.addTrack(track, localStream); - }); - - - // Uncomment to collect ICE candidates below - // await collectIceCandidates(roomRef, peerConnection, callerCandidatesString, calleeCandidatesString); - - // Code for creating a room below - - // Code for creating a room above - - peerConnection.addEventListener('track', event => { - console.log('Got remote track:', event.streams[0]); - event.streams[0].getTracks().forEach(track => { - console.log('Add a track to the remoteStream:', track); - remoteStream.addTrack(track); - }); - }); - - // Listening for remote session description below - - // Listening for remote session description above -} - -function joinRoom() { - document.querySelector('#createBtn').disabled = true; - document.querySelector('#joinBtn').disabled = true; - - document.querySelector('#confirmJoinBtn'). - addEventListener('click', async () => { - roomId = document.querySelector('#room-id').value; - console.log('Join room: ', roomId); - document.querySelector( - '#currentRoom').innerText = `Current room is ${roomId} - You are the callee!`; - await joinRoomById(roomId); - }, {once: true}); - roomDialog.open(); -} - -async function joinRoomById(roomId) { - const db = firebase.firestore(); - const roomRef = db.collection('rooms').doc(`${roomId}`); - const roomSnapshot = await roomRef.get(); - console.log('Got room:', roomSnapshot.exists); - - if (roomSnapshot.exists) { - console.log('Create PeerConnection with configuration: ', configuration); - peerConnection = new RTCPeerConnection(configuration); - registerPeerConnectionListeners(); - localStream.getTracks().forEach(track => { - peerConnection.addTrack(track, localStream); - }); - - - // Uncomment to collect ICE candidates below - // await collectIceCandidates(roomRef, peerConnection, calleeCandidatesString, callerCandidatesString); - - peerConnection.addEventListener('track', event => { - console.log('Got remote track:', event.streams[0]); - event.streams[0].getTracks().forEach(track => { - console.log('Add a track to the remoteStream:', track); - remoteStream.addTrack(track); - }); - }); - - // Code for creating SDP answer below - - // Code for creating SDP answer above - } -} - -// collect ICE Candidates function below - -// collect ICE Candidates function above - -async function openUserMedia(e) { - const stream = await navigator.mediaDevices.getUserMedia( - {video: true, audio: true}); - document.querySelector('#localVideo').srcObject = stream; - localStream = stream; - remoteStream = new MediaStream(); - document.querySelector('#remoteVideo').srcObject = remoteStream; - - console.log('Stream:', document.querySelector('#localVideo').srcObject); - document.querySelector('#cameraBtn').disabled = true; - document.querySelector('#joinBtn').disabled = false; - document.querySelector('#createBtn').disabled = false; - document.querySelector('#hangupBtn').disabled = false; -} - -async function hangUp(e) { - const tracks = document.querySelector('#localVideo').srcObject.getTracks(); - tracks.forEach(track => { - track.stop(); - }); - - if (remoteStream) { - remoteStream.getTracks().forEach(track => track.stop()); - } - - if (peerConnection) { - peerConnection.close(); - } - - document.querySelector('#localVideo').srcObject = null; - document.querySelector('#remoteVideo').srcObject = null; - document.querySelector('#cameraBtn').disabled = false; - document.querySelector('#joinBtn').disabled = true; - document.querySelector('#createBtn').disabled = true; - document.querySelector('#hangupBtn').disabled = true; - document.querySelector('#currentRoom').innerText = ''; - - // Delete room on hangup - if (roomId) { - const db = firebase.firestore(); - const roomRef = db.collection('rooms').doc(roomId); - const calleeCandidates = await roomRef.collection(calleeCandidatesString).get(); - calleeCandidates.forEach(async candidate => { - await candidate.ref.delete(); - }); - const callerCandidates = await roomRef.collection(callerCandidatesString).get(); - callerCandidates.forEach(async candidate => { - await candidate.ref.delete(); - }); - await roomRef.delete(); - } - - document.location.reload(true); -} - -function registerPeerConnectionListeners() { - peerConnection.addEventListener('icegatheringstatechange', () => { - console.log( - `ICE gathering state changed: ${peerConnection.iceGatheringState}`); - }); - - peerConnection.addEventListener('connectionstatechange', () => { - console.log(`Connection state change: ${peerConnection.connectionState}`); - }); - - peerConnection.addEventListener('signalingstatechange', () => { - console.log(`Signaling state change: ${peerConnection.signalingState}`); - }); - - peerConnection.addEventListener('iceconnectionstatechange ', () => { - console.log( - `ICE connection state change: ${peerConnection.iceConnectionState}`); - }); -} - -init(); From 236c5a3b71463a5da2c151cb047b1da976a4c20d Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:04:45 -0800 Subject: [PATCH 33/45] add links --- correctedCodelab.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/correctedCodelab.md b/correctedCodelab.md index 8be4751..5051f01 100644 --- a/correctedCodelab.md +++ b/correctedCodelab.md @@ -39,8 +39,7 @@ Before starting this codelab, make sure that you've installed: 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. 1. Click __Create database__ in the Cloud Firestore pane. 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. - Test mode ensures that you can freely write to the database during development. We'll make our database more secure later on in this codelab. - > TODO: ...this tutorial doesn't actually handle making the database more secure. + Test mode ensures that you can freely write to the database during development. 1. __Get the sample code__ 1. On your local machine, clone the codelab GitHub repository from the command line: @@ -49,7 +48,7 @@ Before starting this codelab, make sure that you've installed: `cd FirebaseRTC` 1. Import the starter app - As you work through the tutorial, open the files in `FirebaseRTC`in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. + As you work through the tutorial, open the files in `FirebaseRTC` in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. 1. __Install the Firebase Command Line Interface__ @@ -182,6 +181,6 @@ Before starting this codelab, make sure that you've installed: To learn more, visit the following resources: - FirebaseRTC Source Code - WebRTC samples - Cloud Firestore + * [FirebaseRTC Source Code](https://github.com/webrtc/FirebaseRTC) + * [WebRTC samples](https://webrtc.github.io/samples) + * [Cloud Firestore](https://firebase.google.com/docs/firestore/) From ce51e5078447633fcc348b71a0c25773ce6f4e37 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:05:44 -0800 Subject: [PATCH 34/45] Delete personalNotes.md --- personalNotes.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 personalNotes.md diff --git a/personalNotes.md b/personalNotes.md deleted file mode 100644 index 87e569f..0000000 --- a/personalNotes.md +++ /dev/null @@ -1,11 +0,0 @@ -Following tutorial: https://webrtc.org/getting-started/firebase-rtc-codelab - -Notes: -* `sh npm -g install firebase-tools` doesn't work, omit `sh`, works. -* `firebase login` gives error: 'cannot run login from non-interactive mode. See login:ci to generate a token for use in non-interactive environments - * Am I supposed to be running these commands from within a terminal in Firebase? Cuz if so, could NOT find a terminal if one exists... - * ah...cool, becauase I'm using git-bash...it's 'non-interactive'. https://github.com/firebase/firebase-tools/issues/149 so we can force that with `--interactive`. -* Okay so just add `--interactive` after every command, and don't ever use `sh` if you're using git-bash on windows... -* Regarding the tutorial about Firebase: there's no longer a "developer" pane or what have you, you instead have to go to Build -> Cloud Firestore to do the create database step. - -* okkkaaayyy so that tutorial is gloriously half-baked. In comparing the 'solution' code to the starter code and the instructions in the tutorial...there's no way anyone following the tutorial will make it functional without just copying the solution branch app.js into their own in frustration. Ugh. Let's.....start this over, shall we? From b674413d49129ede231a593796f7e523dded128e Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:08:18 -0800 Subject: [PATCH 35/45] adding the corrected instructions to the README. I don't know how to update the actual instructions website, so barring that...I enhanced the readme to include them. --- README.md | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 188 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2e3ec2a..91f8f28 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,192 @@ # Firebase + WebRTC Codelab + ### Full code solution can be found under the branch: _solution_ -This is the GitHub repo for the FirebaseRTC codelab. This will teach you how -to use Firebase Cloud Firestore for signalling in a WebRTC video chat application. + This is the GitHub repo for the FirebaseRTC codelab. This will teach you how to use Firebase Cloud Firestore for signalling in a WebRTC video chat application. + The solution to this codelab can be seen in the _solution_ branch. + + See http://webrtc.org for details. + +## Introduction + +In this codelab, you'll learn how to build a simple video chat application using the WebRTC API in your browser and Cloud Firestore for signaling. The application is called FirebaseRTC and works as a simple example that will teach you the basics of building WebRTC enabled applications. + +> Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. + +### What you'll learn +* Initiating a video call in a web application using WebRTC +* Signaling to the remote party using Cloud Firestore + +### What you'll need +Before starting this codelab, make sure that you've installed: +* npm which typically comes with Node.js - Node LTS is recommended + +## Instructions + +1. __Create and set up a Firebase project__ + 1. Create a Firebase project + - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". + - Remember the Project ID for your Firebase project. + >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) + - Disable Google Analytics + - Click Create project. + - Creation takes a moment. When your project is created, click Continue. + + The application that you're going to build uses two Firebase services available on the web: + * Cloud Firestore + - to save structured data on the Cloud and get instant notification when the data is updated + * Firebase Hosting + - to host and serve your static assets + + For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. + +1. __Enable Cloud Firestore__ + + The app uses Cloud Firestore to save the chat messages and receive new chat messages. + 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. + 1. Click __Create database__ in the Cloud Firestore pane. + 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. + Test mode ensures that you can freely write to the database during development. + +1. __Get the sample code__ + 1. On your local machine, clone the codelab GitHub repository from the command line: + `git clone https://github.com/webrtc/FirebaseRTC` + - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: + `cd FirebaseRTC` + 1. Import the starter app + + As you work through the tutorial, open the files in `FirebaseRTC` in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. + +1. __Install the Firebase Command Line Interface__ + + The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. + > Note: To install the CLI, you need to install npm which typically comes with Node.js. + 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` + - if you're in git-bash, run `npm -g install firebase-tools` instead. + - On unix and doesn't work? You may need to run the command using sudo instead. + 1. Verify that the CLI has been installed correctly by running the following command: `sh firebase --version` + - `firebase --version` on git-bash + - Make sure the version of the Firebase CLI is v6.7.1 or later. + 1. Authorize the Firebase CLI by running the following command: `sh firebase login` + - `firebase login --interactive` on git-bash + + You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. + 1. Associate your app with your Firebase project by running the following command: `sh firebase use --add` + - `firebase use --add --interactive` on git-bash + - When prompted, select your Project ID (from the Create Firestore Project step), then give your Firebase project an alias. An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. + 1. Follow the remaining instructions in your command line. + +1. __Run the local server__ + + You're ready to actually start work on our app! Let's run the app locally! + 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` + - `firebase serve --only hosting --interactive` on git-bash + - Your command line should display the following response: + > hosting: Local server: http://localhost:5000 + - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. + 1. Open your app at http://localhost:5000 + You should see your copy of FirebaseRTC which has been connected to your Firebase project. + The app has automatically connected to your Firebase project. + +1. __Creating a new room__ + + In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. + + Each room will contain the `RTCSessionDescriptions` for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. + + Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment `// Add code for creating a room here` and add the following code: + + ``` + const offer = await peerConnection.createOffer(); + await peerConnection.setLocalDescription(offer); + console.log('Created offer:', offer); + const roomWithOffer = { + 'offer': { + type: offer.type, + sdp: offer.sdp + } + } + await roomRef.set(roomWithOffer); + roomId = roomRef.id; + console.log('New room created with SDP offer. Room ID: ${roomRef.id}'); + document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!` + ``` + The first line creates an `RTCSessionDescription` that will represent the offer from the caller. This is then set as the local description and, finally, written to the new room object in Cloud Firestore. + + Next, we will listen for changes to the database and detect when an answer from the callee has been added. Find the comment `// Listening for remote session description below` and add the following code: + + ``` + roomRef.onSnapshot(async snapshot => { + const data = snapshot.data(); + if (!peerConnection.currentRemoteDescription && data && data.answer) { + console.log('Got remote description: ', data.answer); + const rtcSessionDescription = new RTCSessionDescription(data.answer); + await peerConnection.setRemoteDescription(rtcSessionDescription); + } + }); + ``` + This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`. + +1. __Joining a room__ + + The next step is to implement the logic for joining an existing room. The user does this by clicking the "Join room" button and entering the ID for the room to join. Your task here is to implement the creation of the `RTCSessionDescription` for the answer and update the room in the database accordingly. Find the comment `// Code for creating SDP answer below` in the `joinRoomById(roomId)` method and add the following code: + + ``` + const offer = roomSnapshot.data().offer; + console.log('Got offer:', offer); + await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); + const answer = await peerConnection.createAnswer(); + console.log('Created answer:', answer); + await peerConnection.setLocalDescription(answer); + + const roomWithAnswer = { + answer: { + type: answer.type, + sdp: answer.sdp, + }, + }; + await roomRef.update(roomWithAnswer); + ``` + In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback on the caller side which, in turn, will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. + +1. __Collect ICE candidates__ + + Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the comment `// collect ICE Candidates function below` and add the following function: + + ``` + 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()); + peerConneciton.addIceCandidate(candidate); + } + }); + }) + } + ``` + This function does two main things. + * collects ICE candidates from the WebRTC API and adds them to the database + * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. + It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. + + Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. + +1. Conclusion + + In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. -The solution to this codelab can be seen in the _solution_ branch. + To learn more, visit the following resources: -See http://webrtc.org for details. + * [FirebaseRTC Source Code](https://github.com/webrtc/FirebaseRTC) + * [WebRTC samples](https://webrtc.github.io/samples) + * [Cloud Firestore](https://firebase.google.com/docs/firestore/) From 83e6a49f62f13c686991ecdffa1a3506a19904d9 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:08:52 -0800 Subject: [PATCH 36/45] Delete correctedCodelab.md --- correctedCodelab.md | 186 -------------------------------------------- 1 file changed, 186 deletions(-) delete mode 100644 correctedCodelab.md diff --git a/correctedCodelab.md b/correctedCodelab.md deleted file mode 100644 index 5051f01..0000000 --- a/correctedCodelab.md +++ /dev/null @@ -1,186 +0,0 @@ -# Firebase + WebRTC Codelab - -## Introduction - -In this codelab, you'll learn how to build a simple video chat application using the WebRTC API in your browser and Cloud Firestore for signaling. The application is called FirebaseRTC and works as a simple example that will teach you the basics of building WebRTC enabled applications. - -> Note: Another option for signaling could be Firebase Cloud Messaging. However, that is currently only supported in Chrome and in this codelab we will focus on a solution that works across all browsers supporting WebRTC. - -### What you'll learn -* Initiating a video call in a web application using WebRTC -* Signaling to the remote party using Cloud Firestore - -### What you'll need -Before starting this codelab, make sure that you've installed: -* npm which typically comes with Node.js - Node LTS is recommended - -## Instructions - -1. __Create and set up a Firebase project__ - 1. Create a Firebase project - - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". - - Remember the Project ID for your Firebase project. - >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) - - Disable Google Analytics - - Click Create project. - - Creation takes a moment. When your project is created, click Continue. - - The application that you're going to build uses two Firebase services available on the web: - * Cloud Firestore - - to save structured data on the Cloud and get instant notification when the data is updated - * Firebase Hosting - - to host and serve your static assets - - For this specific codelab, you've already configured Firebase Hosting in the project you'll be cloning. However, for Cloud Firestore, we'll walk you through the configuration and enabling of the services using the Firebase console. - -1. __Enable Cloud Firestore__ - - The app uses Cloud Firestore to save the chat messages and receive new chat messages. - 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. - 1. Click __Create database__ in the Cloud Firestore pane. - 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. - Test mode ensures that you can freely write to the database during development. - -1. __Get the sample code__ - 1. On your local machine, clone the codelab GitHub repository from the command line: - `git clone https://github.com/webrtc/FirebaseRTC` - - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: - `cd FirebaseRTC` - 1. Import the starter app - - As you work through the tutorial, open the files in `FirebaseRTC` in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. - -1. __Install the Firebase Command Line Interface__ - - The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. - > Note: To install the CLI, you need to install npm which typically comes with Node.js. - 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` - - if you're in git-bash, run `npm -g install firebase-tools` instead. - - On unix and doesn't work? You may need to run the command using sudo instead. - 1. Verify that the CLI has been installed correctly by running the following command: `sh firebase --version` - - `firebase --version` on git-bash - - Make sure the version of the Firebase CLI is v6.7.1 or later. - 1. Authorize the Firebase CLI by running the following command: `sh firebase login` - - `firebase login --interactive` on git-bash - - You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. - 1. Associate your app with your Firebase project by running the following command: `sh firebase use --add` - - `firebase use --add --interactive` on git-bash - - When prompted, select your Project ID (from the Create Firestore Project step), then give your Firebase project an alias. An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. - 1. Follow the remaining instructions in your command line. - -1. __Run the local server__ - - You're ready to actually start work on our app! Let's run the app locally! - 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - - `firebase serve --only hosting --interactive` on git-bash - - Your command line should display the following response: - > hosting: Local server: http://localhost:5000 - - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. - 1. Open your app at http://localhost:5000 - You should see your copy of FirebaseRTC which has been connected to your Firebase project. - The app has automatically connected to your Firebase project. - -1. __Creating a new room__ - - In this application, each video chat session is called a __room__. A user can create a new room by clicking a button in their application. This will generate an ID that the remote party can use to join the same room. The ID is used as the key in Cloud Firestore for each room. - - Each room will contain the `RTCSessionDescriptions` for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. - - Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment `// Add code for creating a room here` and add the following code: - - ``` - const offer = await peerConnection.createOffer(); - await peerConnection.setLocalDescription(offer); - console.log('Created offer:', offer); - const roomWithOffer = { - 'offer': { - type: offer.type, - sdp: offer.sdp - } - } - await roomRef.set(roomWithOffer); - roomId = roomRef.id; - console.log('New room created with SDP offer. Room ID: ${roomRef.id}'); - document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!` - ``` - The first line creates an `RTCSessionDescription` that will represent the offer from the caller. This is then set as the local description and, finally, written to the new room object in Cloud Firestore. - - Next, we will listen for changes to the database and detect when an answer from the callee has been added. Find the comment `// Listening for remote session description below` and add the following code: - - ``` - roomRef.onSnapshot(async snapshot => { - const data = snapshot.data(); - if (!peerConnection.currentRemoteDescription && data && data.answer) { - console.log('Got remote description: ', data.answer); - const rtcSessionDescription = new RTCSessionDescription(data.answer); - await peerConnection.setRemoteDescription(rtcSessionDescription); - } - }); - ``` - This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`. - -1. __Joining a room__ - - The next step is to implement the logic for joining an existing room. The user does this by clicking the "Join room" button and entering the ID for the room to join. Your task here is to implement the creation of the `RTCSessionDescription` for the answer and update the room in the database accordingly. Find the comment `// Code for creating SDP answer below` in the `joinRoomById(roomId)` method and add the following code: - - ``` - const offer = roomSnapshot.data().offer; - console.log('Got offer:', offer); - await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); - const answer = await peerConnection.createAnswer(); - console.log('Created answer:', answer); - await peerConnection.setLocalDescription(answer); - - const roomWithAnswer = { - answer: { - type: answer.type, - sdp: answer.sdp, - }, - }; - await roomRef.update(roomWithAnswer); - ``` - In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback on the caller side which, in turn, will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. - -1. __Collect ICE candidates__ - - Before the caller and callee can connect to each other, they also need to exchange ICE candidates that tell WebRTC how to connect to the remote peer. Your next task is to implement the code that listens for ICE candidates and adds them to a collection in the database. Find the comment `// collect ICE Candidates function below` and add the following function: - - ``` - 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()); - peerConneciton.addIceCandidate(candidate); - } - }); - }) - } - ``` - This function does two main things. - * collects ICE candidates from the WebRTC API and adds them to the database - * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. - It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. - - Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. - -1. Conclusion - - In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. - - To learn more, visit the following resources: - - * [FirebaseRTC Source Code](https://github.com/webrtc/FirebaseRTC) - * [WebRTC samples](https://webrtc.github.io/samples) - * [Cloud Firestore](https://firebase.google.com/docs/firestore/) From fed80dac4a7e68bd3ea1ed2a1088614512b0035d Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:09:27 -0800 Subject: [PATCH 37/45] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91f8f28..3d26db4 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ Before starting this codelab, make sure that you've installed: Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. -1. Conclusion +1. __Conclusion__ In this codelab you learned how to implement signaling for WebRTC using Cloud Firestore, as well as how to use that for creating a simple video chat application. From e355e92f68e0911756d9cc0a636844edaa877533 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Thu, 17 Dec 2020 22:15:54 -0800 Subject: [PATCH 38/45] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3d26db4..8336ec7 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ The solution to this codelab can be seen in the _solution_ branch. See http://webrtc.org for details. + +Note: the following instructions were copied and updated/corrected from https://webrtc.org/getting-started/firebase-rtc-codelab. ## Introduction From 8e1d6826c1f002349975c027006592f0f1b8029d Mon Sep 17 00:00:00 2001 From: Nat Kuhn Date: Sat, 19 Dec 2020 11:13:12 -0500 Subject: [PATCH 39/45] updated README with corrections and suggestions --- README.md | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8336ec7..bbddc8f 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Before starting this codelab, make sure that you've installed: 1. __Create and set up a Firebase project__ 1. Create a Firebase project - - [In the Firebase console](https://console.firebase.google.com/), click Add project, then name the Firebase project "FirebaseRTC". + - [In the Firebase console](https://console.firebase.google.com/), click __Create a project__, then name the Firebase project "FirebaseRTC". - Remember the Project ID for your Firebase project. >Note: If you go to the home page of your project, you can see it in Settings > Project Settings (or look at the URL!) - Disable Google Analytics @@ -46,15 +46,15 @@ Before starting this codelab, make sure that you've installed: The app uses Cloud Firestore to save the chat messages and receive new chat messages. 1. In the Firebase sidebar, navigate to Build -> Cloud Firestore. 1. Click __Create database__ in the Cloud Firestore pane. - 1. Select the __Start in test mode__ option, then click __Enable__ after reading the disclaimer about the security rules. + 1. Select the __Start in test mode__ option, read the warning about database security, and click __Next__. Then click __Enable__ after reading the warning about the security rules. Test mode ensures that you can freely write to the database during development. 1. __Get the sample code__ 1. On your local machine, clone the codelab GitHub repository from the command line: - `git clone https://github.com/webrtc/FirebaseRTC` - - The sample code should have been cloned into the FirebaseRTC directory. Make sure your command line is run from this directory from now on: + `git clone https://github.com/webrtc/FirebaseRTC` \[__DELETE in PR:__ for now, use `git clone https://github.com/meldaravaniel/FirebaseRTC`\] + - The sample code should have been cloned into the FirebaseRTC directory. + 1. Make sure your command line is run from this directory from now on by executing `cd FirebaseRTC` - 1. Import the starter app As you work through the tutorial, open the files in `FirebaseRTC` in your editor and change them according to the instructions below. This directory contains the starting code for the codelab which consists of a not-yet functional WebRTC app. We'll make it functional throughout this codelab. @@ -88,6 +88,7 @@ Before starting this codelab, make sure that you've installed: 1. Open your app at http://localhost:5000 You should see your copy of FirebaseRTC which has been connected to your Firebase project. The app has automatically connected to your Firebase project. + > Note: if you want to access the server from other devices on your local network, you need to add `-o 0.0.0.0` at the end of the `firebase serve` command. 1. __Creating a new room__ @@ -95,7 +96,7 @@ Before starting this codelab, make sure that you've installed: Each room will contain the `RTCSessionDescriptions` for both the offer and the answer, as well as two separate collections with [ICE candidates](https://webrtcglossary.com/ice/#:~:text=ICE%20stands%20for%20Interactive%20Connectivity,NAT%20traversal%20used%20in%20WebRTC.) from each party. - Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js and find the comment `// Add code for creating a room here` and add the following code: + Your first task is to implement the missing code for creating a new room with the initial offer from the caller. Open public/app.js, find the comment `// Add code for creating a room below`, and add the following code. (You should add the code before the corresponding `// Add code for creating a room above`; the same goes for the rest of the code blocks.) ``` const offer = await peerConnection.createOffer(); @@ -148,7 +149,7 @@ Before starting this codelab, make sure that you've installed: }; await roomRef.update(roomWithAnswer); ``` - In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback on the caller side which, in turn, will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. + In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback that we added in the previous on the caller side. That callback will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. 1. __Collect ICE candidates__ @@ -159,18 +160,18 @@ Before starting this codelab, make sure that you've installed: localName, remoteName) { const candidatesCollection = roomRef.collection(localName); - peerConnection.addEventListener('icecandidate', event -> { + peerConnection.addEventListener('icecandidate', event => { if (event.candidate) { const json = event.candidate.toJSON(); candidatesCollection.add(json); } }); - roomRef.collection(remoteName).onSnapshot(snapshot -> { - snapshot.docChanges().forEach(change -> { + roomRef.collection(remoteName).onSnapshot(snapshot => { + snapshot.docChanges().forEach(change => { if (change.type === "added") { const candidate = new RTCIceCandidate(change.doc.data()); - peerConneciton.addIceCandidate(candidate); + peerConnection.addIceCandidate(candidate); } }); }) @@ -181,7 +182,23 @@ Before starting this codelab, make sure that you've installed: * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. - Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods. + Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods; you can find them after `// Uncomment to collect ICE candidates below`. + +1. __Try it out__ + + 1. Open a new browswer tab at http://localhost:5000, or refresh the tab you opened above. + 1. Mute your speakers to avoid loud piercing feedback! + 1. Click on __Open camera & microphone__; give permission to the app to use them, if requested. + 1. Click on __Create room__. The app will display the room ID. + 1. Open another browswer tab, click __Open camera & microphone__, and click __Join room__. Paste in the ID from the previous step. The two instances should connect. + + To connect with a different device on your local network: + + 1. Make sure that your start the server with `-o 0.0.0.0`. + 1. Find the IP address of your server. Let's say it's `192.168.1.5`. + 1. On your remote device, you need to enable the camera for "insecure orgins," since you are connecting to the server via http, not https. For Chrome, you can do this by navigating to `chrome://flags/#unsafely-treat-insecure-origin-as-secure`, and adding `http://192.168.1.5:5000/` to the list of origins. + 1. Connect as above, except that you will have to enter the room ID manually. + 1. __Conclusion__ From 68ae0600aa7dd49a425ba9cc94cd73c89d839e29 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Sat, 19 Dec 2020 12:01:39 -0800 Subject: [PATCH 40/45] adding a missing word :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbddc8f..6598289 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Before starting this codelab, make sure that you've installed: }; await roomRef.update(roomWithAnswer); ``` - In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback that we added in the previous on the caller side. That callback will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. + In the code above, we start by extracting the offer from the caller and creating a `RTCSessionDescription` that we set as the remote description. Next, we create the answer, set it as the local description, and update the database. The update of the database will trigger the `onSnapshot` callback that we added in the previous step on the caller side. That callback will set the remote description based on the answer from the callee. This completes the exchange of the `RTCSessionDescription` objects between the caller and the callee. 1. __Collect ICE candidates__ From dbcedd3af35b21b79d4fff94952b0b8de4f6cb58 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Sat, 19 Dec 2020 12:04:44 -0800 Subject: [PATCH 41/45] formatting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6598289..4fae7fa 100644 --- a/README.md +++ b/README.md @@ -178,8 +178,8 @@ Before starting this codelab, make sure that you've installed: } ``` This function does two main things. - * collects ICE candidates from the WebRTC API and adds them to the database - * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. + * collects ICE candidates from the WebRTC API and adds them to the database + * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods; you can find them after `// Uncomment to collect ICE candidates below`. From 7c8bceaa26392f5ba9beb3788d2d5ba6bbd0aee2 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Sat, 19 Dec 2020 12:05:36 -0800 Subject: [PATCH 42/45] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4fae7fa..48e9c4c 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ Before starting this codelab, make sure that you've installed: This function does two main things. * collects ICE candidates from the WebRTC API and adds them to the database * listens for added ICE candidates from the remote peer and adds them to its `RTCPeerConnection` instance. + It is important when listening to database changes to filter out anything that isn't a new addition, since we otherwise would have added the same set of ICE candidates over and over again. Complete this step by uncommenting the calls to this function in both the `joinRoomById` and `createRoom` methods; you can find them after `// Uncomment to collect ICE candidates below`. From 81fa0e3741876aa13f0734193bc0314d53dc0304 Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Sat, 19 Dec 2020 15:11:13 -0800 Subject: [PATCH 43/45] removing `sh` from commands in favor of bash-based --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 48e9c4c..ba05d9c 100644 --- a/README.md +++ b/README.md @@ -62,26 +62,24 @@ Before starting this codelab, make sure that you've installed: The Firebase Command Line Interface (CLI) allows you to serve your web app locally and deploy your web app to Firebase Hosting. > Note: To install the CLI, you need to install npm which typically comes with Node.js. - 1. Install the CLI by running the following npm command: `sh npm -g install firebase-tools` - - if you're in git-bash, run `npm -g install firebase-tools` instead. + 1. Install the CLI by running the following npm command: `npm -g install firebase-tools` - On unix and doesn't work? You may need to run the command using sudo instead. - 1. Verify that the CLI has been installed correctly by running the following command: `sh firebase --version` - - `firebase --version` on git-bash + 1. Verify that the CLI has been installed correctly by running the following command: `firebase --version` - Make sure the version of the Firebase CLI is v6.7.1 or later. - 1. Authorize the Firebase CLI by running the following command: `sh firebase login` - - `firebase login --interactive` on git-bash + 1. Authorize the Firebase CLI by running the following command: `firebase login` + - if the terminal you use is 'non-interactive' (eg. git-bash) you may need to append `--interactive` to the above command. You've set up the web app template to pull your app's configuration for Firebase Hosting from your app's local directory and files. But to do this, you need to associate your app with your Firebase project. - 1. Associate your app with your Firebase project by running the following command: `sh firebase use --add` - - `firebase use --add --interactive` on git-bash + 1. Associate your app with your Firebase project by running the following command: `firebase use --add` + - you may need to append `--interactive` to the above command. - When prompted, select your Project ID (from the Create Firestore Project step), then give your Firebase project an alias. An alias is useful if you have multiple environments (production, staging, etc). However, for this codelab, let's just use the alias of default. 1. Follow the remaining instructions in your command line. 1. __Run the local server__ You're ready to actually start work on our app! Let's run the app locally! - 1. Run the following Firebase CLI command: `sh firebase serve --only hosting` - - `firebase serve --only hosting --interactive` on git-bash + 1. Run the following Firebase CLI command: `firebase serve --only hosting` + - you may need to append `--interactive` to the above command. - Your command line should display the following response: > hosting: Local server: http://localhost:5000 - We're using the Firebase Hosting emulator to serve our app locally. The web app should now be available from http://localhost:5000. From 993c53f41e7866369122cae489a9d0ed94cdf74c Mon Sep 17 00:00:00 2001 From: Amy Gleixner Date: Mon, 4 Jan 2021 21:37:20 -0800 Subject: [PATCH 44/45] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba05d9c..5e45606 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Firebase + WebRTC Codelab -### Full code solution can be found under the branch: _solution_ +### Full code solution can be found under the branch: [_solution_](https://github.com/meldaravaniel/FirebaseRTC/tree/solution) This is the GitHub repo for the FirebaseRTC codelab. This will teach you how to use Firebase Cloud Firestore for signalling in a WebRTC video chat application. The solution to this codelab can be seen in the _solution_ branch. From a3f40045542fc02a3fccc79c380b12a8ced1218f Mon Sep 17 00:00:00 2001 From: Moritz Schmidt Date: Thu, 11 Mar 2021 11:54:58 +0100 Subject: [PATCH 45/45] minor readme fixes * fix template string for log message * add indentation level in session description listener code --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5e45606..6dadfaa 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Before starting this codelab, make sure that you've installed: } await roomRef.set(roomWithOffer); roomId = roomRef.id; - console.log('New room created with SDP offer. Room ID: ${roomRef.id}'); + console.log(`New room created with SDP offer. Room ID: ${roomRef.id}`); document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!` ``` The first line creates an `RTCSessionDescription` that will represent the offer from the caller. This is then set as the local description and, finally, written to the new room object in Cloud Firestore. @@ -117,12 +117,12 @@ Before starting this codelab, make sure that you've installed: ``` roomRef.onSnapshot(async snapshot => { - const data = snapshot.data(); - if (!peerConnection.currentRemoteDescription && data && data.answer) { - console.log('Got remote description: ', data.answer); - const rtcSessionDescription = new RTCSessionDescription(data.answer); - await peerConnection.setRemoteDescription(rtcSessionDescription); - } + const data = snapshot.data(); + if (!peerConnection.currentRemoteDescription && data && data.answer) { + console.log('Got remote description: ', data.answer); + const rtcSessionDescription = new RTCSessionDescription(data.answer); + await peerConnection.setRemoteDescription(rtcSessionDescription); + } }); ``` This will wait until the callee writes the `RTCSessionDescription` for the answer, and set that as the remote description on the caller `RTCPeerConnection`.