app.js
const frontendsocket = io(); const myface = document.getElementById("myface"); const mute = document.getElementById("mute"); const camera = document.getElementById("camera"); const camerasSelect = document.getElementById("cameras"); const call = document.getElementById("call"); call.hidden = true; let myStream; let muted = false; let cameraOff = false; let roomName; async function getCamera() { try { const devices = await navigator.mediaDevices.enumerateDevices(); //장치 리스트 가져오기 const cameras = devices.filter((device) => device.kind ==="videoinput"); > //videoinput만 필터링 const CurrentCamera = myStream.getVideoTracks()[0]; cameras.forEach((camera) => { const option = document.createElement("option"); option.value = camera.deviceId; //카메라의 고유 값을 value에 넣기 option.innerText = camera.label; //사용자가 선택할 때는 label을 보고 선택 가능하게 하기 if (CurrentCamera.lable === camera.label) { option.selected = true; } camerasSelect.appendChild(option); //카메라 정보를 option 항목에 넣어주기 }); } catch (e) { console.log(e); } } async function getMedia(deviceId) { const initialConstraints = { //deviceId가 없을 때 실행 audio: true, video: { facingMode: "environment" }, //카메라가 전후면에 달려있을 경우 후면 카메라의 정보를 받음 }; const cameraConstraints = { //deviceId가 있을 때 실행 audio: true, video: { deviceId: { exact: deviceId } }, }; try { myStream = await navigator.mediaDevices.getUserMedia( deviceId ? cameraConstraints : initialConstraints ); myface.srcObject = myStream; if (!deviceId) { //처음 딱 1번만 실행(getMedia를 실행할 때만) await getCamera(); } } catch (e) { console.log(e); } } function handleMuteButton() { myStream .getAudioTracks() .forEach((track) => (track.enabled = !track.enabled)); if (!muted) { mute.innerText = "Unmute"; muted = true; } else { mute.innerText = "Mute"; muted = false; } } function handleCameraButton() { console.log(myStream.getVideoTracks()); myStream .getVideoTracks() .forEach((track) => (track.enabled = !track.enabled)); if (!cameraOff) { camera.innerText = "Turn Camera On"; cameraOff = true; } else { camera.innerText = "Turn Camera Off"; cameraOff = false; } } async function handleCameraChange() { await getMedia(camerasSelect.value); } mute.addEventListener("click", handleMuteButton); camera.addEventListener("click", handleCameraButton); camerasSelect.addEventListener("input", handleCameraChange); //카메라 변경 확인 //Welcome Form (choose a room) const welcome = document.getElementById("welcome"); const welcomeForm = welcome.querySelector("form"); function startMedia() {
welcome.hidden = true; call.hidden = false; getMedia(); } function handleWelcomeSubmit(event) { event.preventDefault(); const input = welcomeForm.querySelector("input"); frontendsocket.emit("join_room", input.value, startMedia); //서버로 input.value 보내기 roomName = input.value; //방에 참가했을 때 나중에 쓸 수 있도록 방 이름 변수에 저장 input.value = ""; } welcomeForm.addEventListener("submit", handleWelcomeSubmit); // Socket Code frontendsocket.on("welcome", () => { console.log("someone joined"); });
server.js
import http from "http"; import SocketIO from "socket.io"; import { instrument } from "@socket.io/admin-ui"; import express from "express"; const app = express(); const PORT = 3000; app.set("view engine", "pug"); app.set("views", __dirname + "/views"); // 기본 path를 /public으로 설정 app.use("/public", express.static(__dirname + "/public")); app.get("/", (_, res) => res.render("home")); app.get("/*", (_, res) => res.redirect("/")); const httpServer = http.createServer(app); const wsServer = SocketIO(httpServer); wsServer.on("connection", (backendsocket) => { backendsocket.on("join_room", (roomName, done) => { backendsocket.join(roomName); done(); backendsocket.to(roomName).emit("welcome"); }); }); const handleListen = () => console.log(`Listening on > http://localhost:${PORT}`); httpServer.listen(PORT, handleListen);
home.pug
doctype html html(lang="en") head meta(charset="UTF-8") meta(http-equiv="X-UA-Compatible", content="IE=edge") meta(name="viewport", content="width=device-width, initial-scale=1.0") title Noom link(rel="stylesheet", href="https://unpkg.com/mvp.css") body header h1 Noom main div#welcome form input(placeholder="room > name",required,type="text") button Enter Room div#call div#mystream //playsline은 모바일 브라우저가 필요로 하는 property //모바일 기기로 재생하면 비디오가 전체화면이 되기 때문에 playsline으로 이를 방지 > video#myface(autoplay,playsinline,width="100",height="100") button#mute Mute button#camera Turn Camera Off select#cameras option(value="device") Face Camera script(src="/socket.io/socket.io.js") script(src="/public/js/app.js")