Skip to content

Commit

Permalink
Merge pull request #154 from CS3219-AY2324S1/implement-video
Browse files Browse the repository at this point in the history
Implement video
  • Loading branch information
wongyewjon authored Nov 3, 2023
2 parents fb9ffaa + e2517fb commit dc14c8b
Show file tree
Hide file tree
Showing 13 changed files with 1,350 additions and 13 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,3 @@ jobs:
run: |
git push -f origin HEAD:production
working-directory: .

15 changes: 15 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
REACT_APP_USERS_SERVICE_HOST: http://localhost:3002
REACT_APP_MATCHMAKING_SERVICE_HOST: http://localhost:3003
REACT_APP_COLLABORATION_SERVICE_HOST: http://localhost:3004
REACT_APP_VIDEO_SERVICE_HOST: http://localhost:3006
volumes:
- ./frontend-service:/app
command: yarn start
Expand Down Expand Up @@ -91,6 +92,20 @@ services:
- ./collaboration-service:/app
command: yarn start

video-service:
build:
context: ./video-service
container_name: video-service
ports:
- 3006:3006
environment:
PORT: 3006
USERS_SERVICE_HOST: http://users-service:3002
QUESTION_SERVICE_HOST: http://question-service:3001
volumes:
- ./video-service:/app
command: yarn start

postgresql:
image: postgres:15-alpine
container_name: postgresql
Expand Down
5 changes: 5 additions & 0 deletions frontend-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@
"axios": "^1.5.0",
"dompurify": "^3.0.5",
"easymde": "^2.18.0",
"ejs": "^3.1.9",
"express": "^4.18.2",
"marked": "^9.0.0",
"peerjs": "^1.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"react-simplemde-editor": "^5.2.0",
"simplemde": "^1.11.2",
"socket.io": "^4.7.2",
"socket.io-client": "^4.7.2",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion frontend-service/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { UserProvider } from "./contexts/UserContext";
import AuthGuard from "./components/AuthGuard";
import Collaboration from "./pages/Collaboration";
import EditQuestion from "./pages/EditQuestion";
import Error from "./pages/Error";
import Login from "./pages/Login";
import NewQuestion from "./pages/NewQuestion";
import Question from "./pages/Question";
import Questions from "./pages/Questions";
import Signup from "./pages/Signup";
import Error from "./pages/Error";

import Matchmaking from "./pages/Matchmaking";
import MatchmakingFind from "./pages/MatchmakingFind";
import NavBar from "./components/NavBar";
Expand Down
212 changes: 212 additions & 0 deletions frontend-service/src/components/VideoChat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import React, { useEffect, useRef, useState } from "react";
import { Peer } from "peerjs";
import { io } from "socket.io-client";
import { useSearchParams } from "react-router-dom";


const addVideoStream = (video, stream) => {
video.srcObject = stream;
video.addEventListener('loadedmetadata', () => {
video.play();
});
}

const VideoChat = () => {

const [searchParams] = useSearchParams();

const [peer, setPeer] = useState(null);
const [peerId, setPeerId] = useState(null);
const [socket, setSocket] = useState(null);
const [roomId, setRoomId] = useState(searchParams.get("roomId"));
const [videos, setVideos] = useState([]);

const videoGridRef = useRef(null);

useEffect(() => {

const peer = new Peer();
peer.on("open", (id) => {
console.log('My peer ID is: ' + id);
setPeerId(id);
});
setPeer(peer);
setSocket(io(`${process.env.REACT_APP_VIDEO_SERVICE_HOST}`))
}, []);

// useEffect(() => {
// if (!socket) return;
// socket.on("able-to-join-server-now", () => {
// socket.emit("join-server", {
// peerId: peerId,
// roomId: roomId
// });
// console.log(`User ${peerId} is attemping to join video-server.`);
// // if (!socket || !peer || !peerId) return;

// const userVideo = document.createElement("video");
// userVideo.muted = true;
// const matchedUserVideo = document.createElement("video");

// const startMediaDevices = async () => {
// try {
// const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
// console.log("hi")
// addVideoStream(userVideo, stream);
// setVideos([userVideo]);

// peer.on('call', (call) => {
// call.answer(stream);

// call.on('stream', (matchedUserVideoStream) => {

// addVideoStream(matchedUserVideo, matchedUserVideoStream);
// setVideos([userVideo, matchedUserVideo]);
// });

// call.on('close', () => {
// matchedUserVideo.remove();
// setVideos([userVideo]);
// })
// });

// socket.on("user-connected", (userId) => {
// console.log(`User ${userId} has joined the room ${roomId}.`);
// const call = peer.call(userId, stream);

// call.on('stream', (matchedUserVideoStream) => {
// addVideoStream(matchedUserVideo, matchedUserVideoStream);
// setVideos([userVideo, matchedUserVideo]);
// });

// call.on('close', () => {
// matchedUserVideo.remove();
// setVideos([userVideo]);

// })
// });


// socket.on("able-to-leave-server-now", () => {
// if (!peer) return;
// peer.close();
// setVideos([]);
// console.log(`User ${peerId} is attemping to leave video-server.`)
// socket.emit("user-disconnected", peerId, roomId);
// })
// // socket.on("user-disconnected", (userId) => {

// // })




// } catch (error) {
// console.error('Error accessing media devices.', error);
// }
// };

// startMediaDevices();

// })
// }, []);

useEffect(() => {
if (!socket || !peer || !peerId) return;

const userVideo = document.createElement("video");
userVideo.muted = true;
const matchedUserVideo = document.createElement("video");

const startMediaDevices = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
addVideoStream(userVideo, stream);
setVideos([userVideo]);
console.log("user video has been set up");
peer.on('call', (call) => {
call.answer(stream);

call.on('stream', (matchedUserVideoStream) => {

addVideoStream(matchedUserVideo, matchedUserVideoStream);
setVideos([userVideo, matchedUserVideo]);
console.log("matchedUser video has been set up");
});

call.on('close', () => {
matchedUserVideo.remove();
setVideos([userVideo]);
})
});

socket.on("user-connected", (userId) => {
console.log(`User ${userId} has joined the room ${roomId}.`);
const call = peer.call(userId, stream);

call.on('stream', (matchedUserVideoStream) => {
addVideoStream(matchedUserVideo, matchedUserVideoStream);
setVideos([userVideo, matchedUserVideo]);
console.log(`User and matched user videos are up`);
});

call.on('close', () => {
matchedUserVideo.remove();
setVideos([userVideo]);

})

});


// socket.on("able-to-leave-server-now", () => {
// if (!peer) return;
// peer.close();
// setVideos([]);
// console.log(`User ${peerId} is attemping to leave video-server.`)
// socket.emit("user-disconnected", peerId, roomId);
// })


socket.emit("join-server", {
peerId: peerId,
roomId: roomId
});
console.log(`User ${peerId} is attemping to join video-server.`);


} catch (error) {
console.error('Error accessing media devices.', error);
}
};

startMediaDevices();



// socket.on('user-disconnected', (userId) => {
// if (peers[userId]) {
// peers[userId].close();
// }
// });


}, [socket, peer ]);

useEffect(() => {
if (videoGridRef.current == null) return;

videos.map((video) => {
video.style.width = '300px'
video.style.height = '300px'
video.style.margin = '10px'
videoGridRef.current.appendChild(video);
return video;
});
}, [videos]);

return <div id="videos" ref={videoGridRef}></div>;

};

export default VideoChat;
34 changes: 30 additions & 4 deletions frontend-service/src/pages/Collaboration.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useNavigate, useSearchParams } from "react-router-dom";
import { io } from "socket.io-client";

import Page from "../components/Page";
import VideoChat from "../components/VideoChat";

import { styled } from "@mui/material/styles";

Expand All @@ -30,6 +31,7 @@ import AcknowledgementToast from "../components/AcknowledgementToast";

import VideocamIcon from "@mui/icons-material/Videocam";


marked.use({ breaks: true, gfm: true, silent: true });

const getDifficultyChipColor = (difficulty) => {
Expand Down Expand Up @@ -62,9 +64,14 @@ const Collaboration = () => {
? "# Insert your code here\n"
: "// Insert your code here\n"
);
const [isVideoCalling, setIsVideoCalling] = useState(false);
//const [roomId, setRoomId] = useState(null);



useEffect(() => {
const roomId = searchParams.get("roomId");
// setRoomId(roomId);
const difficulty = searchParams.get("difficulty");
if (!roomId) return navigate("/matchmaking");
const token = window.localStorage.getItem("token");
Expand Down Expand Up @@ -112,11 +119,24 @@ const Collaboration = () => {
return navigate("/matchmaking");
});



return async () => {
socket.disconnect();
};
}, [navigate, searchParams]);

// useEffect(() => {
// const videoSocket = io(`${process.env.REACT_APP_VIDEO_SERVICE_HOST}`);
// if (isVideoCalling) {
// videoSocket.emit("open-video", roomId);
// console.log("open-video");
// } else {
// videoSocket.emit("close-video", roomId);
// console.log("close-video")
// }
// }, [isVideoCalling]);

useEffect(() => {
editorRef.current?.focus();
}, [editorLanguage]);
Expand Down Expand Up @@ -189,12 +209,15 @@ const Collaboration = () => {
{collaboratingUser && (
<Tooltip title={"Start video call"} placement="top-end" arrow>
<Fab sx={{ marginLeft: 2 }} color="primary">
<VideocamIcon fontSize="large" />
<VideocamIcon fontSize="large"
onClick={() => {
setIsVideoCalling(!isVideoCalling)
}} />
</Fab>
</Tooltip>
)}
</Box>
<Box width="50%" height="100%" padding={1}>
{/* <Box width="50%" height="100%" padding={1}>
<Stack height="100%" spacing={1}>
<Stack direction="row" spacing={2} alignItems="center">
<Tooltip title="Back to questions" placement="top" arrow>
Expand Down Expand Up @@ -246,8 +269,8 @@ const Collaboration = () => {
/>
</Card>
</Stack>
</Box>
<Box width="50%" height="100%" padding={1}>
</Box> */}
{/* <Box width="50%" height= {isVideoCalling ?"70%" :"100%"} padding={1}>
<Paper
sx={{ height: "100%", width: "100%", overflow: "hidden" }}
elevation={2}
Expand Down Expand Up @@ -278,6 +301,9 @@ const Collaboration = () => {
}}
/>
</Paper>
</Box> */}
<Box>
{isVideoCalling && <VideoChat/>}
</Box>
</Box>
)}
Expand Down
17 changes: 17 additions & 0 deletions frontend-service/src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4} = require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})

app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
server.listen(3000)
Loading

0 comments on commit dc14c8b

Please sign in to comment.