diff --git a/package-lock.json b/package-lock.json index 6ddbdcf..68dd295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.1", + "socket.io-client": "^4.7.5", "zustand": "^4.5.4" }, "devDependencies": { @@ -1375,6 +1376,12 @@ "win32" ] }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@tanstack/query-core": { "version": "5.51.21", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.21.tgz", @@ -2105,6 +2112,28 @@ "dev": true, "license": "ISC" }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4465,6 +4494,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -4995,6 +5052,35 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index cf8171b..ed32e3b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.1", + "socket.io-client": "^4.7.5", "zustand": "^4.5.4" }, "devDependencies": { diff --git a/src/App.jsx b/src/App.jsx index 2ad33c4..2961dab 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -11,6 +11,7 @@ import HomeMain from "./pages/homepages/HomeMain"; import GlobalStyle from "./components/Common/GlobalStyle"; import { PeopleListPage } from "./pages/PeopleListPage"; import ChatPage from "./pages/chat/ChatPage"; +import RTCPage from "./pages/VideoPage"; function App() { return ( @@ -18,6 +19,7 @@ function App() { }> }> + }> ); diff --git a/src/pages/VideoPage.jsx b/src/pages/VideoPage.jsx index fd3d638..c7a7010 100644 --- a/src/pages/VideoPage.jsx +++ b/src/pages/VideoPage.jsx @@ -1,12 +1,126 @@ -import React from 'react'; - -export const VideoPage = () => { - React.useEffect(() => { - console.log('VideoPage'); - }) - return ( -
- hi hi -
- ) -} +import React, { useEffect, useRef } from "react"; +import { io } from "socket.io-client"; + +const RTCPage = () => { + const socketRef = useRef(null); + const myVideoRef = useRef(null); + const remoteVideoRef = useRef(null); + const pcRef = useRef(null); + const roomName = "testRoom"; // You can dynamically generate or pass this + + const getMedia = async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: true, + }); + + if (myVideoRef.current) { + myVideoRef.current.srcObject = stream; + } + stream.getTracks().forEach((track) => { + if (pcRef.current) { + pcRef.current.addTrack(track, stream); + } + }); + + pcRef.current.onicecandidate = (e) => { + if (e.candidate) { + console.log("ICE Candidate: ", e.candidate); + socketRef.current.emit("candidate", e.candidate, roomName); + } + }; + + pcRef.current.ontrack = (e) => { + if (remoteVideoRef.current) { + remoteVideoRef.current.srcObject = e.streams[0]; + console.log("Received remote track: ", e.streams[0]); + } + }; + } catch (e) { + console.error("Error accessing media devices.", e); + } + }; + + const createOffer = async () => { + try { + const offer = await pcRef.current.createOffer(); + await pcRef.current.setLocalDescription(offer); + console.log("Created offer: ", offer); + socketRef.current.emit("offer", offer, roomName); + } catch (e) { + console.error("Error creating offer: ", e); + } + }; + + const createAnswer = async (offer) => { + try { + await pcRef.current.setRemoteDescription(offer); + const answer = await pcRef.current.createAnswer(); + await pcRef.current.setLocalDescription(answer); + console.log("Created answer: ", answer); + socketRef.current.emit("answer", answer, roomName); + } catch (e) { + console.error("Error creating answer: ", e); + } + }; + + useEffect(() => { + socketRef.current = io("https://2242-122-45-139-174.ngrok-free.app", { + transports: ["websocket"], + }); + + pcRef.current = new RTCPeerConnection({ + iceServers: [{ urls: "stun:stun.l.google.com:19302" }], + }); + + socketRef.current.emit("join", roomName); + + socketRef.current.on("created", async () => { + console.log("Room created"); + await getMedia(); + }); + + socketRef.current.on("joined", async () => { + console.log("Joined room"); + await getMedia(); + createOffer(); + }); + + socketRef.current.on("offer", async (offer) => { + console.log("Received offer: ", offer); + await getMedia(); + createAnswer(offer); + }); + + socketRef.current.on("answer", (answer) => { + console.log("Received answer: ", answer); + pcRef.current.setRemoteDescription(answer); + }); + + socketRef.current.on("candidate", async (candidate) => { + console.log("Received candidate: ", candidate); + await pcRef.current.addIceCandidate(candidate); + }); + + return () => { + if (socketRef.current) socketRef.current.disconnect(); + if (pcRef.current) pcRef.current.close(); + }; + }, []); + + return ( +
+

WebRTC Video Call

+
+ ); +}; + +export default RTCPage;