What is Node WebRTC?
The Node-WebRTC library acts as a bridge, implementing the WebRTC API for Node.js, thereby extending WebRTC's capabilities beyond the browser. This is particularly useful for backend services that need to handle real-time communication, such as signaling servers or media servers that manage multiple WebRTC connections. The library allows developers to use familiar JavaScript syntax and Node.js conventions to build these backend services, thus maintaining consistency across the tech stack.
With Node-WebRTC, developers can create highly interactive applications that perform smoothly in real-time. Whether you're building a video conferencing tool, a live streaming service, or a real-time collaboration platform, Node-WebRTC provides the necessary tools and flexibility to implement robust and scalable solutions.
Node-WebRTC is a powerful library that brings the capabilities of
WebRTC
(Web Real-Time Communication) to Node.js, allowing developers to create real-time, peer-to-peer communication applications such as video conferencing, file sharing, and live streaming directly from a Node.js server. By leveraging Node-WebRTC, developers can integrate real-time communication features into their applications without relying on traditional server-client architectures, making it easier to build scalable and efficient solutions.WebRTC is an open-source project that enables web applications and websites to capture and potentially broadcast audio and/or video media. It also allows them to exchange any type of data between browsers without the need for an intermediary. This technology is widely used for applications such as video chat, file transfer, and
live streaming
.For those interested in exploring Node-WebRTC further, the library's
GitHub repository
provides extensive documentation, examples, and resources to help get started. This repository is a valuable resource for understanding the implementation details and exploring various use cases of Node-WebRTC.Let's start to Build WebRTC Video Chat App with Nodejs
To harness the power of Node-WebRTC, you need to set up a new project and configure it correctly. This section will guide you through the initial steps of creating a Node-WebRTC application, including installation, project structure, and understanding the app architecture.
Creating a New Node-WebRTC App
Before you start, ensure that you have Node.js and npm (Node Package Manager) installed on your machine. You can download and install them from the
official Node.js website
. Once you have Node.js set up, follow these steps to create a new Node-WebRTC application:[a] Initialize a New Node.js Project
Open your terminal and create a new directory for your project. Navigate into this directory and initialize a new Node.js project by running the following command:
bash
1 mkdir node-webrtc-app
2 cd node-webrtc-app
3 npm init -y
This command will create a
package.json
file with default settings.[b] Installing Node-WebRTC
To use Node-WebRTC, you need to install it via npm. This can be done with a simple command:
bash
1npm install node-webrtc
This command will download and install the Node-WebRTC library and its dependencies into your project, making it ready for use.
Project Structure
A well-organized project structure is crucial for maintaining and scaling your application. Here’s a recommended structure for your Node-WebRTC app:
1node-webrtc-app/
2├── node_modules/
3├── src/
4│ ├── controllers/
5│ ├── models/
6│ ├── views/
7│ ├── utils/
8│ └── index.js
9├── .gitignore
10├── package.json
11└── README.md
- src/controllers: Contains the logic for handling different routes and endpoints.
- src/models: Defines data models and schemas.
- src/views: Holds the HTML/CSS files for the frontend (if applicable).
- src/utils: Utility functions and helper modules.
- src/index.js: The main entry point of your application.
App Architecture
Understanding the architecture of a typical Node-WebRTC application will help you build a robust and scalable solution. Here’s an overview of the key components:
- Signaling Server: Facilitates the exchange of signaling data (like session descriptions and ICE candidates) between peers. This server coordinates the connection setup.
- Media Streams: Handles audio and video streams, allowing users to share their media.
- Data Channels: Manages data transfer between peers, enabling features like file sharing or real-time messaging.
- Peer Connections: Establishes and maintains peer-to-peer connections, ensuring smooth communication between clients.
By organizing your project and understanding these components, you’ll be well-equipped to build a Node-WebRTC application. The following sections will delve deeper into each step, providing code snippets and detailed instructions to guide you through the development process.
Step 1: Initial Setup
In this section, we'll walk through the initial setup required to get your Node-WebRTC application up and running. This includes creating the entry point of your application and initializing the necessary components to establish a WebRTC connection.
Get Started with index.js
The
index.js
file will serve as the main entry point for your Node-WebRTC application. It will handle the setup of the server and the initialization of WebRTC connections. Follow these steps to get started:[a] Create the index.js
File
Navigate to the
src
directory and create a new file named index.js
:bash
1 touch src/index.js
[b] Set Up a Basic Server
Use Node.js to create a basic HTTP server. This server will later handle WebRTC signaling and manage connections between peers. Add the following code to your
index.js
file:JavaScript
1 const http = require('http');
2 const express = require('express');
3 const { Server } = require('socket.io');
4 const { RTCPeerConnection, RTCSessionDescription } = require('wrtc');
5
6 const app = express();
7 const server = http.createServer(app);
8 const io = new Server(server);
9
10 const port = 3000;
11
12 app.get('/', (req, res) => {
13 res.send('Node-WebRTC Server is running');
14 });
15
16 server.listen(port, () => {
17 console.log(`Server is listening on http://localhost:${port}`);
18 });
[c] Initialize WebRTC Peer Connections
Now, set up the logic to handle WebRTC peer connections. This includes managing signaling data and establishing connections between peers. Add the following code to your
index.js
file:JavaScript
1 let peers = {};
2
3 io.on('connection', socket => {
4 console.log('A user connected:', socket.id);
5
6 socket.on('offer', async (id, description) => {
7 const peer = new RTCPeerConnection();
8 peers[id] = peer;
9
10 await peer.setRemoteDescription(new RTCSessionDescription(description));
11 const answer = await peer.createAnswer();
12 await peer.setLocalDescription(answer);
13
14 socket.emit('answer', id, peer.localDescription);
15 });
16
17 socket.on('answer', async (id, description) => {
18 const peer = peers[id];
19 await peer.setRemoteDescription(new RTCSessionDescription(description));
20 });
21
22 socket.on('candidate', async (id, candidate) => {
23 const peer = peers[id];
24 await peer.addIceCandidate(new RTCIceCandidate(candidate));
25 });
26
27 socket.on('disconnect', () => {
28 delete peers[socket.id];
29 console.log('A user disconnected:', socket.id);
30 });
31 });
[d] Handling ICE Candidates
ICE (Interactive Connectivity Establishment) candidates are used to find the best path to connect peers. Add the following logic to handle ICE candidates:
JavaScript
1 io.on('connection', socket => {
2 console.log('A user connected:', socket.id);
3
4 const peer = new RTCPeerConnection();
5 peers[socket.id] = peer;
6
7 peer.onicecandidate = event => {
8 if (event.candidate) {
9 socket.emit('candidate', socket.id, event.candidate);
10 }
11 };
12
13 socket.on('offer', async (id, description) => {
14 await peer.setRemoteDescription(new RTCSessionDescription(description));
15 const answer = await peer.createAnswer();
16 await peer.setLocalDescription(answer);
17 socket.emit('answer', id, peer.localDescription);
18 });
19
20 socket.on('answer', async (id, description) => {
21 await peer.setRemoteDescription(new RTCSessionDescription(description));
22 });
23
24 socket.on('candidate', async (id, candidate) => {
25 await peer.addIceCandidate(new RTCIceCandidate(candidate));
26 });
27
28 socket.on('disconnect', () => {
29 delete peers[socket.id];
30 console.log('A user disconnected:', socket.id);
31 });
32 });
With these steps, you have set up the initial configuration for your Node-WebRTC application. This includes creating a basic server, initializing WebRTC peer connections, and handling ICE candidates. This foundational setup will allow you to build more advanced features and functionalities in the subsequent steps.
Step 2: Wireframe All the Components
In this section, we will define the structure and essential components required for your Node-WebRTC application. This involves setting up the user interface (UI) and the backend logic to manage connections and interactions between peers.
Define Component Structure
A well-organized component structure is crucial for the maintainability and scalability of your application. Here's an overview of the essential components we'll be working with:
- HTML/CSS for the UI
- JavaScript for handling user interactions and WebRTC logic
Let's start by creating the basic HTML and CSS for the UI.
HTML and CSS for the UI
[a] Create an HTML File
Create a new file named
index.html
in the src/views
directory:HTML
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Node-WebRTC App</title>
7 <link rel="stylesheet" href="styles.css">
8 </head>
9 <body>
10 <div id="container">
11 <h1>Node-WebRTC Video Chat</h1>
12 <div id="join-screen">
13 <input type="text" id="room-name" placeholder="Enter Room Name">
14 <button id="join-btn">Join</button>
15 </div>
16 <div id="video-chat" style="display: none;">
17 <video id="local-video" autoplay playsinline></video>
18 <div id="remote-videos"></div>
19 <button id="leave-btn">Leave</button>
20 </div>
21 </div>
22 <script src="/socket.io/socket.io.js"></script>
23 <script src="script.js"></script>
24 </body>
25 </html>
[b] Create a CSS File
Create a new file named
styles.css
in the src/views
directory and add the following styles:CSS
1 body {
2 font-family: Arial, sans-serif;
3 display: flex;
4 justify-content: center;
5 align-items: center;
6 height: 100vh;
7 margin: 0;
8 background-color: #f0f0f0;
9 }
10
11 #container {
12 text-align: center;
13 background: white;
14 padding: 20px;
15 border-radius: 8px;
16 box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
17 }
18
19 #join-screen, #video-chat {
20 margin-top: 20px;
21 }
22
23 video {
24 width: 300px;
25 border: 1px solid #ddd;
26 margin: 5px;
27 }
JavaScript for Handling User Interactions
Next, we will create the JavaScript logic to manage user interactions, such as joining a room and handling WebRTC connections.
[a] Create a JavaScript File
Create a new file named
script.js
in the src/views
directory and add the following code:JavaScript
1 const socket = io();
2
3 const joinScreen = document.getElementById('join-screen');
4 const videoChat = document.getElementById('video-chat');
5 const joinBtn = document.getElementById('join-btn');
6 const leaveBtn = document.getElementById('leave-btn');
7 const roomNameInput = document.getElementById('room-name');
8 const localVideo = document.getElementById('local-video');
9 const remoteVideos = document.getElementById('remote-videos');
10
11 let localStream;
12 let peerConnections = {};
13
14 joinBtn.addEventListener('click', () => {
15 const roomName = roomNameInput.value;
16 if (roomName) {
17 joinRoom(roomName);
18 }
19 });
20
21 leaveBtn.addEventListener('click', leaveRoom);
22
23 async function joinRoom(roomName) {
24 joinScreen.style.display = 'none';
25 videoChat.style.display = 'block';
26
27 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
28 localVideo.srcObject = localStream;
29
30 socket.emit('join', roomName);
31
32 socket.on('offer', async (id, description) => {
33 const peerConnection = new RTCPeerConnection();
34 peerConnections[id] = peerConnection;
35
36 localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
37
38 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
39 const answer = await peerConnection.createAnswer();
40 await peerConnection.setLocalDescription(answer);
41
42 socket.emit('answer', id, peerConnection.localDescription);
43
44 peerConnection.ontrack = event => {
45 const remoteVideo = document.createElement('video');
46 remoteVideo.srcObject = event.streams[0];
47 remoteVideo.autoplay = true;
48 remoteVideos.appendChild(remoteVideo);
49 };
50
51 peerConnection.onicecandidate = event => {
52 if (event.candidate) {
53 socket.emit('candidate', id, event.candidate);
54 }
55 };
56 });
57
58 socket.on('answer', async (id, description) => {
59 const peerConnection = peerConnections[id];
60 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
61 });
62
63 socket.on('candidate', (id, candidate) => {
64 const peerConnection = peerConnections[id];
65 peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
66 });
67
68 socket.on('leave', id => {
69 const peerConnection = peerConnections[id];
70 peerConnection.close();
71 delete peerConnections[id];
72 });
73 }
74
75 function leaveRoom() {
76 for (let id in peerConnections) {
77 peerConnections[id].close();
78 delete peerConnections[id];
79 }
80 localStream.getTracks().forEach(track => track.stop());
81 joinScreen.style.display = 'block';
82 videoChat.style.display = 'none';
83 socket.emit('leave');
84 }
In this step, we've set up the basic structure of our Node-WebRTC application. We've created the HTML and CSS for the user interface and added JavaScript logic to manage user interactions and WebRTC connections. This setup will serve as the foundation for building more advanced features in the subsequent steps.
Step 3: Implement Join Screen
In this section, we will implement the join screen functionality of your Node-WebRTC application. This screen will allow users to enter a room name and join a video chat session. We will create the necessary UI elements and add JavaScript to handle user input and signaling.
Create the Join Screen UI
The join screen is where users will input the room name they want to join. We have already created the basic HTML structure in the previous step. Now, let's focus on the JavaScript functionality to handle user input and connect them to the specified room.
Enhance the Join Screen HTML
Ensure your
index.html
file has the following structure for the join screen:HTML
1 <div id="join-screen">
2 <input type="text" id="room-name" placeholder="Enter Room Name">
3 <button id="join-btn">Join</button>
4 </div>
5 <div id="video-chat" style="display: none;">
6 <video id="local-video" autoplay playsinline></video>
7 <div id="remote-videos"></div>
8 <button id="leave-btn">Leave</button>
9 </div>
JavaScript to Handle Join Screen Functionality
In your
script.js
file, add the following code to manage the join screen interactions and signaling:JavaScript
1 const socket = io();
2
3 const joinScreen = document.getElementById('join-screen');
4 const videoChat = document.getElementById('video-chat');
5 const joinBtn = document.getElementById('join-btn');
6 const leaveBtn = document.getElementById('leave-btn');
7 const roomNameInput = document.getElementById('room-name');
8 const localVideo = document.getElementById('local-video');
9 const remoteVideos = document.getElementById('remote-videos');
10
11 let localStream;
12 let peerConnections = {};
13
14 joinBtn.addEventListener('click', () => {
15 const roomName = roomNameInput.value;
16 if (roomName) {
17 joinRoom(roomName);
18 }
19 });
20
21 leaveBtn.addEventListener('click', leaveRoom);
22
23 async function joinRoom(roomName) {
24 joinScreen.style.display = 'none';
25 videoChat.style.display = 'block';
26
27 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
28 localVideo.srcObject = localStream;
29
30 socket.emit('join', roomName);
31
32 socket.on('offer', async (id, description) => {
33 const peerConnection = new RTCPeerConnection();
34 peerConnections[id] = peerConnection;
35
36 localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
37
38 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
39 const answer = await peerConnection.createAnswer();
40 await peerConnection.setLocalDescription(answer);
41
42 socket.emit('answer', id, peerConnection.localDescription);
43
44 peerConnection.ontrack = event => {
45 const remoteVideo = document.createElement('video');
46 remoteVideo.srcObject = event.streams[0];
47 remoteVideo.autoplay = true;
48 remoteVideo.playsinline = true;
49 remoteVideos.appendChild(remoteVideo);
50 };
51
52 peerConnection.onicecandidate = event => {
53 if (event.candidate) {
54 socket.emit('candidate', id, event.candidate);
55 }
56 };
57 });
58
59 socket.on('answer', async (id, description) => {
60 const peerConnection = peerConnections[id];
61 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
62 });
63
64 socket.on('candidate', (id, candidate) => {
65 const peerConnection = peerConnections[id];
66 peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
67 });
68
69 socket.on('leave', id => {
70 const peerConnection = peerConnections[id];
71 peerConnection.close();
72 delete peerConnections[id];
73 });
74 }
75
76 function leaveRoom() {
77 for (let id in peerConnections) {
78 peerConnections[id].close();
79 delete peerConnections[id];
80 }
81 localStream.getTracks().forEach(track => track.stop());
82 joinScreen.style.display = 'block';
83 videoChat.style.display = 'none';
84 socket.emit('leave');
85 }
Join Screen Functionality
In the JavaScript code, we have implemented the following functionality:
Joining a Room
- When the user clicks the "Join" button, the
joinRoom
function is called with the room name entered by the user. - The join screen is hidden, and the video chat screen is displayed.
- The user's media stream is captured using
getUserMedia
and displayed in the local video element. - The user joins the specified room by emitting a
join
event to the server.
Handling Offers
- When an offer is received from the server, a new
RTCPeerConnection
is created for the peer. - The local media tracks are added to the peer connection.
- The remote description is set, and an answer is created and sent back to the server.
- The remote video streams are displayed when tracks are received.
Handling Answers and ICE Candidates
- Answers and ICE candidates received from the server are set on the appropriate peer connection.
Leaving a Room
- When the user clicks the "Leave" button, the
leaveRoom
function is called. - All peer connections are closed, and the local media tracks are stopped.
- The user leaves the room by emitting a
leave
event to the server.
With these steps, we have successfully implemented the join screen functionality for your Node-WebRTC application. Users can now join a room, establish peer connections, and start video chatting with other participants in the room. The next steps will focus on enhancing the user experience and adding more controls to manage the video chat session.
Step 4: Implement Controls
In this section, we will add user controls to our Node-WebRTC application. These controls will allow users to manage their video and audio streams, such as muting/unmuting the microphone and enabling/disabling the video. This step will enhance the user experience by providing essential functionality for real-time communication.
Adding User Controls
We will start by updating our HTML to include buttons for muting/unmuting the microphone and enabling/disabling the video. Then, we will implement the JavaScript logic to handle these controls.
[a] Enhance the Video Chat UI
Update your
index.html
file to include buttons for the user controls:HTML
1 <div id="video-chat" style="display: none;">
2 <video id="local-video" autoplay playsinline></video>
3 <div id="remote-videos"></div>
4 <div id="controls">
5 <button id="mute-btn">Mute</button>
6 <button id="video-btn">Disable Video</button>
7 <button id="leave-btn">Leave</button>
8 </div>
9 </div>
[b] Add CSS for the Controls
Update your
styles.css
file to style the control buttons:CSS
1 #controls {
2 margin-top: 10px;
3 }
4
5 #controls button {
6 margin: 5px;
7 padding: 10px;
8 font-size: 16px;
9 cursor: pointer;
10 }
[c] JavaScript to Handle Control Functionality
Update your
script.js
file to implement the logic for the user controls:JavaScript
1 const socket = io();
2
3 const joinScreen = document.getElementById('join-screen');
4 const videoChat = document.getElementById('video-chat');
5 const joinBtn = document.getElementById('join-btn');
6 const leaveBtn = document.getElementById('leave-btn');
7 const roomNameInput = document.getElementById('room-name');
8 const localVideo = document.getElementById('local-video');
9 const remoteVideos = document.getElementById('remote-videos');
10 const muteBtn = document.getElementById('mute-btn');
11 const videoBtn = document.getElementById('video-btn');
12
13 let localStream;
14 let peerConnections = {};
15 let isMuted = false;
16 let isVideoDisabled = false;
17
18 joinBtn.addEventListener('click', () => {
19 const roomName = roomNameInput.value;
20 if (roomName) {
21 joinRoom(roomName);
22 }
23 });
24
25 leaveBtn.addEventListener('click', leaveRoom);
26 muteBtn.addEventListener('click', toggleMute);
27 videoBtn.addEventListener('click', toggleVideo);
28
29 async function joinRoom(roomName) {
30 joinScreen.style.display = 'none';
31 videoChat.style.display = 'block';
32
33 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
34 localVideo.srcObject = localStream;
35
36 socket.emit('join', roomName);
37
38 socket.on('offer', async (id, description) => {
39 const peerConnection = new RTCPeerConnection();
40 peerConnections[id] = peerConnection;
41
42 localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
43
44 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
45 const answer = await peerConnection.createAnswer();
46 await peerConnection.setLocalDescription(answer);
47
48 socket.emit('answer', id, peerConnection.localDescription);
49
50 peerConnection.ontrack = event => {
51 const remoteVideo = document.createElement('video');
52 remoteVideo.srcObject = event.streams[0];
53 remoteVideo.autoplay = true;
54 remoteVideo.playsinline = true;
55 remoteVideos.appendChild(remoteVideo);
56 };
57
58 peerConnection.onicecandidate = event => {
59 if (event.candidate) {
60 socket.emit('candidate', id, event.candidate);
61 }
62 };
63 });
64
65 socket.on('answer', async (id, description) => {
66 const peerConnection = peerConnections[id];
67 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
68 });
69
70 socket.on('candidate', (id, candidate) => {
71 const peerConnection = peerConnections[id];
72 peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
73 });
74
75 socket.on('leave', id => {
76 const peerConnection = peerConnections[id];
77 peerConnection.close();
78 delete peerConnections[id];
79 });
80 }
81
82 function leaveRoom() {
83 for (let id in peerConnections) {
84 peerConnections[id].close();
85 delete peerConnections[id];
86 }
87 localStream.getTracks().forEach(track => track.stop());
88 joinScreen.style.display = 'block';
89 videoChat.style.display = 'none';
90 socket.emit('leave');
91 }
92
93 function toggleMute() {
94 isMuted = !isMuted;
95 localStream.getAudioTracks()[0].enabled = !isMuted;
96 muteBtn.textContent = isMuted ? 'Unmute' : 'Mute';
97 }
98
99 function toggleVideo() {
100 isVideoDisabled = !isVideoDisabled;
101 localStream.getVideoTracks()[0].enabled = !isVideoDisabled;
102 videoBtn.textContent = isVideoDisabled ? 'Enable Video' : 'Disable Video';
103 }
Control Handlers
In the JavaScript code, we have implemented the following functionality:
Mute/Unmute Microphone
- The
toggleMute
function toggles the audio track's enabled state. - The button text changes to reflect the current state ("Mute" or "Unmute").
Enable/Disable Video
- The
toggleVideo
function toggles the video track's enabled state. - The button text changes to reflect the current state ("Disable Video" or "Enable Video").
Joining a Room
- When the user clicks the "Join" button, the
joinRoom
function is called with the room name entered by the user. - The join screen is hidden, and the video chat screen is displayed.
- The user's media stream is captured using
getUserMedia
and displayed in the local video element. - The user joins the specified room by emitting a
join
event to the server.
Handling Offers
- When an offer is received from the server, a new
RTCPeerConnection
is created for the peer. - The local media tracks are added to the peer connection.
- The remote description is set, and an answer is created and sent back to the server.
- The remote video streams are displayed when tracks are received.
Handling Answers and ICE Candidates
- Answers and ICE candidates received from the server are set on the appropriate peer connection.
Leaving a Room
- When the user clicks the "Leave" button, the
leaveRoom
function is called. - All peer connections are closed, and the local media tracks are stopped.
- The user leaves the room by emitting a
leave
event to the server.
With these steps, we have successfully implemented user controls for muting/unmuting the microphone and enabling/disabling the video in your Node-WebRTC application. These controls enhance the user experience by providing essential functionality for managing the video chat session. The next step will focus on implementing the participant view to display remote video streams.
Step 5: Implement Participant View
In this section, we will focus on implementing the participant view for your Node-WebRTC application. This view will handle the display of remote video streams from other participants in the chat room. We will enhance our existing JavaScript code to manage multiple video streams effectively.
Rendering Participants
To effectively display remote video streams, we need to ensure that our application can dynamically create and manage video elements for each participant. We will update our
script.js
file to include the necessary logic.JavaScript to Handle Participant Views
Update your
script.js
file with the following code to manage remote video streams:JavaScript
1 const socket = io();
2
3 const joinScreen = document.getElementById('join-screen');
4 const videoChat = document.getElementById('video-chat');
5 const joinBtn = document.getElementById('join-btn');
6 const leaveBtn = document.getElementById('leave-btn');
7 const roomNameInput = document.getElementById('room-name');
8 const localVideo = document.getElementById('local-video');
9 const remoteVideos = document.getElementById('remote-videos');
10 const muteBtn = document.getElementById('mute-btn');
11 const videoBtn = document.getElementById('video-btn');
12
13 let localStream;
14 let peerConnections = {};
15 let isMuted = false;
16 let isVideoDisabled = false;
17
18 joinBtn.addEventListener('click', () => {
19 const roomName = roomNameInput.value;
20 if (roomName) {
21 joinRoom(roomName);
22 }
23 });
24
25 leaveBtn.addEventListener('click', leaveRoom);
26 muteBtn.addEventListener('click', toggleMute);
27 videoBtn.addEventListener('click', toggleVideo);
28
29 async function joinRoom(roomName) {
30 joinScreen.style.display = 'none';
31 videoChat.style.display = 'block';
32
33 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
34 localVideo.srcObject = localStream;
35
36 socket.emit('join', roomName);
37
38 socket.on('offer', async (id, description) => {
39 const peerConnection = new RTCPeerConnection();
40 peerConnections[id] = peerConnection;
41
42 localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
43
44 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
45 const answer = await peerConnection.createAnswer();
46 await peerConnection.setLocalDescription(answer);
47
48 socket.emit('answer', id, peerConnection.localDescription);
49
50 peerConnection.ontrack = event => {
51 const remoteVideo = createRemoteVideoElement(id, event.streams[0]);
52 remoteVideos.appendChild(remoteVideo);
53 };
54
55 peerConnection.onicecandidate = event => {
56 if (event.candidate) {
57 socket.emit('candidate', id, event.candidate);
58 }
59 };
60 });
61
62 socket.on('answer', async (id, description) => {
63 const peerConnection = peerConnections[id];
64 await peerConnection.setRemoteDescription(new RTCSessionDescription(description));
65 });
66
67 socket.on('candidate', (id, candidate) => {
68 const peerConnection = peerConnections[id];
69 peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
70 });
71
72 socket.on('leave', id => {
73 const peerConnection = peerConnections[id];
74 peerConnection.close();
75 delete peerConnections[id];
76 removeRemoteVideoElement(id);
77 });
78 }
79
80 function leaveRoom() {
81 for (let id in peerConnections) {
82 peerConnections[id].close();
83 delete peerConnections[id];
84 }
85 localStream.getTracks().forEach(track => track.stop());
86 joinScreen.style.display = 'block';
87 videoChat.style.display = 'none';
88 socket.emit('leave');
89 remoteVideos.innerHTML = '';
90 }
91
92 function toggleMute() {
93 isMuted = !isMuted;
94 localStream.getAudioTracks()[0].enabled = !isMuted;
95 muteBtn.textContent = isMuted ? 'Unmute' : 'Mute';
96 }
97
98 function toggleVideo() {
99 isVideoDisabled = !isVideoDisabled;
100 localStream.getVideoTracks()[0].enabled = !isVideoDisabled;
101 videoBtn.textContent = isVideoDisabled ? 'Enable Video' : 'Disable Video';
102 }
103
104 function createRemoteVideoElement(id, stream) {
105 const video = document.createElement('video');
106 video.id = `remote-video-${id}`;
107 video.srcObject = stream;
108 video.autoplay = true;
109 video.playsinline = true;
110 return video;
111 }
112
113 function removeRemoteVideoElement(id) {
114 const video = document.getElementById(`remote-video-${id}`);
115 if (video) {
116 video.remove();
117 }
118 }
Participant View Management
In the JavaScript code, we have implemented the following functionality to manage the participant view:
Creating Remote Video Elements
- The
createRemoteVideoElement
function dynamically creates a video element for a remote participant and sets its source to the remote media stream. - This function is called whenever a new stream is received from a remote peer.
Adding Remote Video Elements
- When a remote peer sends an offer, a new
RTCPeerConnection
is created, and the remote video stream is appended to theremoteVideos
container. - The
ontrack
event of theRTCPeerConnection
handles the addition of the remote video element.
Removing Remote Video Elements
- When a remote peer leaves the room, the corresponding
RTCPeerConnection
is closed, and the remote video element is removed from theremoteVideos
container. - The
removeRemoteVideoElement
function is used to remove the video element by its ID.
Updating the Local Stream
- The local media stream is captured using
getUserMedia
and displayed in the local video element. - The local stream tracks are added to the peer connections for sharing with remote participants.
Handling Room Leave
- When the user leaves the room, all peer connections are closed, the local media tracks are stopped, and the
remoteVideos
container is cleared.
With these steps, we have successfully implemented the participant view for your Node-WebRTC application. The application can now dynamically create and manage video elements for multiple participants, enhancing the user experience by providing a seamless real-time video chat experience. The final step will focus on running and testing your application to ensure everything works as expected.
Step 6: Run Your Code Now
In this final step, we will focus on running and testing your Node-WebRTC application to ensure that everything is working as expected. We will also address some common issues you might encounter and how to troubleshoot them.
Running the Node-WebRTC Application
Start the Server
Ensure you are in the root directory of your project (where
package.json
is located). Start your server using the following command:bash
1 node src/index.js
This command will start your Node.js server, and you should see a message indicating that the server is listening on
http://localhost:3000
.Open the Application in a Browser
Open your web browser and navigate to
http://localhost:3000
. You should see the join screen of your Node-WebRTC application.Join a Room
- Enter a room name in the input field and click the "Join" button.
- Allow access to your microphone and camera when prompted by the browser.
- You should see your local video stream displayed on the screen.
Test with Multiple Participants
- Open another browser tab or use a different device to join the same room.
- You should see both local and remote video streams displayed on the screen.
- Test the mute/unmute and enable/disable video functionality to ensure that the controls are working correctly.
Troubleshooting Common Issues
No Video/Audio Stream
- Ensure that you have granted the necessary permissions for the browser to access your microphone and camera.
- Check if your device's camera and microphone are working correctly with other applications.
Unable to Connect to Room
- Ensure that the server is running and accessible at
http://localhost:3000
. - Check if there are any errors in the browser console or the server logs that might indicate issues with the connection or signaling.
Remote Video Not Displayed
- Verify that the signaling messages (offer, answer, and ICE candidates) are being exchanged correctly between peers.
- Ensure that the
RTCPeerConnection
is properly set up and that media tracks are being added to the connection.
Poor Video/Audio Quality
- Check your network connection for any issues that might affect the quality of the video and audio streams.
- Consider adjusting the media constraints in the
getUserMedia
function to optimize the quality based on your network conditions.
Conclusion
With your Node-WebRTC application now up and running, you have a solid foundation for building real-time communication features. This tutorial has guided you through setting up the server, creating the join screen, implementing user controls, and managing participant views.
Want to level-up your learning? Subscribe now
Subscribe to our newsletter for more tech based insights
FAQ