
WebRTC (Web Real-Time Communication) is a powerful technology that enables real-time communication between web browsers and mobile applications. PeerJS is a JavaScript library that simplifies the implementation of WebRTC by providing an easy-to-use API for establishing peer-to-peer connections. In this tutorial, we will explore how to build a basic video chat application using PeerJS and Node.js.
Pre-requisites
To follow along with this tutorial, you should have a basic understanding of JavaScript, React, and Node.js. Make sure you have Node.js installed on your machine before proceeding.
Setting up the Project
Let's start by setting up a new React project and installing the necessary dependencies. Open your terminal and run the following commands:
npx create-react-app peerjs-webrtc
cd peerjs-webrtc
Next, install the PeerJS library by running:
npm install peerjs
Implementing the Video Chat Application
In the src
directory of your React project, open the App.js
file and replace its content with the following code:
import React, { useEffect, useRef, useState } from "react";
import "./App.css";
function App() {
const [peerId, setPeerId] = useState();
const [remotePeerId, setRemotePeerId] = useState("");
const peerObject = useRef(null);
const callObject = useRef(null);
const connectionObject = useRef(null);
const [muteToggle, setMuteToggle] = useState(true);
const [chatMessage, setChatMessage] = useState("");
const remoteMediaStream = useRef(null);
const ownMediaStream = useRef(null);
const [myStream, setMyStream] = useState();
const [chatHistory, setChatHistory] = useState([]);
let isScreenSharing = false;
useEffect(() => {
const peer = new window.Peer();
peer.on("open", async (id) => {
setPeerId(id);
});
peer.on("connection", function (conn) {
connectionObject.current = conn;
conn.on("open", function () {
// Receive messages
conn.on("data", function (data) {
setChatHistory([
...chatHistory,
{
owner: false,
message: chatMessage,
},
]);
console.log("Received", data);
});
});
});
peer.on("call", (call) => {
callObject.current = call;
setRemotePeerId(call.peer);
const conn = peerObject.current.connect(call.peer);
connectionObject.current = conn;
conn.on("open", function () {
// Receive messages
conn.on("data", function (data) {
setChatHistory([
...chatHistory,
{
owner: false,
message: chatMessage,
},
]);
console.log("Received", data);
});
});
const getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
getUserMedia(
{ audio: true },
(stream) => {
setMyStream(stream);
ownMediaStream.current.srcObject = stream;
ownMediaStream.current.play();
call.answer(myStream); // Answer the call with an A/V stream.
call.on("stream", (remoteStream) => {
remoteMediaStream.current.srcObject = remoteStream;
remoteMediaStream.current.play();
});
},
(err) => {
console.log(err);
}
);
});
peerObject.current = peer;
}, []);
function muteMic() {
setMuteToggle((muteToggle) => !muteToggle);
myStream
.getAudioTracks()
.forEach((track) => (track.enabled = !track.enabled));
}
function muteVideo() {
myStream
.getVideoTracks()
.forEach((track) => (track.enabled = !track.enabled));
}
const shareScreenWithRemote = async (remotePeerId) => {
if (isScreenSharing) {
return;
}
isScreenSharing = true;
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: false,
});
const audioStream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
const hybridStream = new MediaStream();
hybridStream.addTrack(audioStream.getAudioTracks()[0]);
hybridStream.addTrack(screenStream.getVideoTracks()[0]);
setMyStream(hybridStream);
ownMediaStream.current.srcObject = screenStream;
ownMediaStream.current.play();
const call = await peerObject.current.call(remotePeerId, hybridStream);
callObject.current = call;
const videoTrack = screenStream.getVideoTracks()[0];
videoTrack.onended = () => {
stopScreenSharing();
};
call.on("stream", (remoteStream) => {
remoteMediaStream.current.srcObject = remoteStream;
remoteMediaStream.current.play();
});
};
const callRemotePeer = async (remotePeerId) => {
const con = peerObject.current.connect(remotePeerId);
connectionObject.current = con;
const getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
getUserMedia(
{ audio: true },
async (mediaStream) => {
const call = await peerObject.current.call(remotePeerId, mediaStream);
callObject.current = call;
setMyStream(mediaStream);
ownMediaStream.current.srcObject = mediaStream;
ownMediaStream.current.play();
call.on("stream", (remoteStream) => {
remoteMediaStream.current.srcObject = remoteStream;
remoteMediaStream.current.play();
});
},
(err) => {
console.log("Failed to get local stream", err);
}
);
};
const stopScreenSharing = async () => {
if (!isScreenSharing) {
return;
}
ownMediaStream.current.srcObject = null;
remoteMediaStream.current.srcObject = null;
isScreenSharing = false;
await callRemotePeer(remotePeerId);
};
const sendMessage = () => {
setChatHistory([
...chatHistory,
{
owner: true,
message: chatMessage,
},
]);
connectionObject.current.send(chatMessage);
setChatMessage("");
document.getElementById("chatMessage").value = "";
};
return (
<div className="App">
<h1>Welcome To PeerJS Tutorial</h1>
<p>My peerId is {peerId}</p>
<div>
<input
type="text"
placeholder="peerId"
onChange={(e) => setRemotePeerId(e.target.value)}
/>
<input
type="text"
id="chatMessage"
placeholder="Chat Message"
onChange={(e) => setChatMessage(e.target.value)}
/>
<button onClick={() => callRemotePeer(remotePeerId)}>Call</button>
<button onClick={() => shareScreenWithRemote(remotePeerId)}>
Share Screen
</button>
<button onClick={() => muteMic()}>
{muteToggle ? "Mute" :
"Unmute"}
</button>
<button onClick={() => sendMessage()}>Send Message</button>
</div>
<div>
{chatHistory.map((item, index) => {
return (
<div key={index}>
{item.owner ? "Me" : "Peer"}: {item.message}
</div>
);
})}
</div>
<div>
<video width={500} height={500} ref={remoteMediaStream}></video>
<br />
<video width={500} height={500} ref={ownMediaStream}></video>
</div>
</div>
);
}
export default App;
Explanation of the Code
Let's go through the key parts of the code to understand how the video chat application using PeerJS works:
The App component is a functional component that renders the video chat application UI.
We use useState hooks to manage the state variables, such as peerId, remotePeerId, muteToggle, chatMessage, myStream, and chatHistory.
The useEffect hook is used to initialize the PeerJS library and set up event listeners for incoming connections and calls. When the component mounts, it creates a new Peer object, listens for the
open
event to get the peer ID, and sets it in the state.When a connection is established, the application sets up a data channel to exchange chat messages. The received messages are added to the chatHistory state.
When a call is received, the application establishes a connection, answers the call with the local audio/video stream, and sets up event listeners to handle the remote stream.
The muteMic function toggles the mute state of the local audio stream by enabling or disabling the audio tracks.
The shareScreenWithRemote function shares the screen with the remote peer by capturing the screen and creating a hybrid stream with audio from the local microphone. It then calls the remote peer with the hybrid stream and sets up event listeners for the remote stream.
The callRemotePeer function initiates a call to the remote peer by connecting to the peer and obtaining the local audio stream. It then calls the remote peer with the audio stream and sets up event listeners for the remote stream.
The stopScreenSharing function stops the screen sharing by resetting the local and remote media streams.
The sendMessage function sends a chat message to the remote peer by adding it to the
chatHistory
state, sending it through the data channel, and resetting the chat message input.The return statement renders the UI of the video chat application, including the peer ID display, input fields for peer ID and chat message, buttons for calling, screen sharing, muting/unmuting, and sending messages, and video elements for displaying the remote and local streams.
Running the Application
To run the application, open your terminal and navigate to the project's root directory. Run the following command:
npm start
This command starts the development server and opens the application in your default web browser. You can open the application in multiple browser tabs or devices to test the video chat functionality.
Conclusion
In this tutorial, we have built a basic video chat application using PeerJS and Node.js. We explored how to establish peer-to-peer connections, exchange chat messages, share screens, and enable audio and video streaming. You can further enhance the application by adding features such as user authentication, multiple participants, and chat room functionality. PeerJS provides a powerful and flexible API for building real-time communication applications, and with the knowledge gained from this tutorial, you can explore and build more advanced applications using WebRTC.
Comments
Post a Comment