WebRTC 를 이용하여 화상 채팅을 만들어 보도록 하겠습니다.
본 포스트는 turn 서버를 임대하여 내부 네트워크 와 외부 네트워크 간의 화상 채팅 성공 코드 입니다.
모든 기본 소스와 설명은 아래 링크를 참조했습니다.
SSL 을 이용해야 되기때문에 node 서버에 인증서 경로를 추가해준다.
이미 codetest.coforward.com 은 https 적용되어 있다.
// Dependencies
const fs = require('fs');
const http = require('http');
const https = require('https');
const express = require('express');
const app = express();
// Certificate 인증서 경로
const privateKey = fs.readFileSync('/etc/httpd/ssl/codetest.coforward.com_2022.key', 'utf8');
const certificate = fs.readFileSync('/etc/httpd/ssl/codetest.coforward.com_2022.crt', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
};
app.use((req, res) => {
res.send('Hello there 222222!');
});
// Starting both http & https servers
const httpServer = http.createServer(app);
const httpsServer = https.createServer(credentials, app);
httpsServer.listen(8443, () => {
console.log('HTTPS Server running on port 8443');
TURN 서버 미적용 : 같은 네트워크상에서는 정상작동한다.
#vi public/script.js
...
...
var peer = new Peer({
host: 'codetest.coforward.com',
port: 8443,
path: '/peerjs',
config: {
'iceServers': [
{ url: 'stun4.l.google.com:19302' },
]
/*
iceServers: [
{
urls: "stun:relay.metered.ca:80",
},
{
urls: "turn:relay.metered.ca:80",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443?transport=tcp",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
],
*/
},
debug: 3
});
TURN 서버 적용 : 같은 네트워크상에서는 정상작동한다.
#vi public/script.js
...
...
var peer = new Peer({
host: 'codetest.coforward.com',
port: 8443,
path: '/peerjs',
config: {
/*
'iceServers': [
{ url: 'stun4.l.google.com:19302' },
]
*/
iceServers: [
{
urls: "stun:relay.metered.ca:80",
},
{
urls: "turn:relay.metered.ca:80",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443?transport=tcp",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
],
},
debug: 3
});
TURN 서버 무료 이용 방법
월 0원에 50G 무료로 이용할수 있다.
https://dashboard.metered.ca/signup?tool=turnserver
mimas@ruu.kr
mimaspw
mimas.metered.live
더보기
적용서버 : 121.78.88.131
경로 : /home/codetest/base/video-chat-v1
서버실행
[codetest@servera131 video-chat-v1]$ pwd
/home/codetest/base/video-chat-v1
[codetest@servera131 video-chat-v1]$ node server.js
브라우져 접속
https://codetest.coforward.com:8443/aaaa
전체소스
package.json
{
"name": "zoom",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.3",
"express": "^4.17.1",
"nodemon": "^2.0.4",
"peer": "^0.5.3",
"socket.io": "^2.3.0",
"uuid": "^8.3.0"
}
}
$ npm install
$ vi server.js
const fs = require('fs');
const https = require('https');
const express = require("express");
const app = express();
const { v4: uuidv4 } = require("uuid");
app.set("view engine", "ejs");
// Certificate 인증서 경로
const privateKey = fs.readFileSync('/etc/httpd/ssl/codetest.coforward.com_2022.key', 'utf8');
const certificate = fs.readFileSync('/etc/httpd/ssl/codetest.coforward.com_2022.crt', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
};
const server = https.createServer(credentials, app);
const io = require("socket.io")(server, {
cors: {
origin: '*'
}
});
const { ExpressPeerServer } = require("peer");
const opinions = {
debug: true,
}
app.use("/peerjs", ExpressPeerServer(server, opinions));
app.use(express.static("public"));
app.get("/", (req, res) => {
//res.redirect(`/${uuidv4()}`);
res.redirect('aaaa');
});
app.get("/:room", (req, res) => {
res.render("room", { roomId: req.params.room });
});
io.on("connection", (socket) => {
socket.on("join-room", (roomId, userId, userName) => {
socket.join(roomId);
setTimeout(()=>{
socket.to(roomId).broadcast.emit("user-connected", userId);
}, 1000)
socket.on("message", (message) => {
io.to(roomId).emit("createMessage", message, userName);
});
});
});
server.listen(process.env.PORT || 8443);
vi public/script.js
const socket = io("/");
const videoGrid = document.getElementById("video-grid");
const myVideo = document.createElement("video");
const showChat = document.querySelector("#showChat");
const backBtn = document.querySelector(".header__back");
myVideo.muted = true;
backBtn.addEventListener("click", () => {
document.querySelector(".main__left").style.display = "flex";
document.querySelector(".main__left").style.flex = "1";
document.querySelector(".main__right").style.display = "none";
document.querySelector(".header__back").style.display = "none";
});
showChat.addEventListener("click", () => {
document.querySelector(".main__right").style.display = "flex";
document.querySelector(".main__right").style.flex = "1";
document.querySelector(".main__left").style.display = "none";
document.querySelector(".header__back").style.display = "block";
});
const user = prompt("Enter your name");
var peer = new Peer({
host: 'codetest.coforward.com',
port: 8443,
path: '/peerjs',
config: {
'iceServers': [
{ url: 'stun4.l.google.com:19302' },
]
/*
iceServers: [
{
urls: "stun:relay.metered.ca:80",
},
{
urls: "turn:relay.metered.ca:80",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
{
urls: "turn:relay.metered.ca:443?transport=tcp",
username: "f75c5c089422654fc7c61437",
credential: "dkLa3Am6b79Sizb9",
},
],
*/
},
debug: 3
});
let myVideoStream;
navigator.mediaDevices
.getUserMedia({
audio: false,
video: true,
})
.then((stream) => {
myVideoStream = stream;
addVideoStream(myVideo, stream);
peer.on("call", (call) => {
console.log('someone call me');
call.answer(stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
});
socket.on("user-connected", (userId) => {
connectToNewUser(userId, stream);
});
});
const connectToNewUser = (userId, stream) => {
console.log('I call someone' + userId);
const call = peer.call(userId, stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
};
peer.on("open", (id) => {
console.log('my id is' + id);
socket.emit("join-room", ROOM_ID, id, user);
});
const addVideoStream = (video, stream) => {
video.srcObject = stream;
video.addEventListener("loadedmetadata", () => {
video.play();
videoGrid.append(video);
});
};
let text = document.querySelector("#chat_message");
let send = document.getElementById("send");
let messages = document.querySelector(".messages");
send.addEventListener("click", (e) => {
if (text.value.length !== 0) {
socket.emit("message", text.value);
text.value = "";
}
});
text.addEventListener("keydown", (e) => {
if (e.key === "Enter" && text.value.length !== 0) {
socket.emit("message", text.value);
text.value = "";
}
});
const inviteButton = document.querySelector("#inviteButton");
const muteButton = document.querySelector("#muteButton");
const stopVideo = document.querySelector("#stopVideo");
muteButton.addEventListener("click", () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
html = `<i class="fas fa-microphone-slash"></i>`;
muteButton.classList.toggle("background__red");
muteButton.innerHTML = html;
} else {
myVideoStream.getAudioTracks()[0].enabled = true;
html = `<i class="fas fa-microphone"></i>`;
muteButton.classList.toggle("background__red");
muteButton.innerHTML = html;
}
});
stopVideo.addEventListener("click", () => {
const enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
html = `<i class="fas fa-video-slash"></i>`;
stopVideo.classList.toggle("background__red");
stopVideo.innerHTML = html;
} else {
myVideoStream.getVideoTracks()[0].enabled = true;
html = `<i class="fas fa-video"></i>`;
stopVideo.classList.toggle("background__red");
stopVideo.innerHTML = html;
}
});
inviteButton.addEventListener("click", (e) => {
prompt(
"Copy this link and send it to people you want to meet with",
window.location.href
);
});
socket.on("createMessage", (message, userName) => {
messages.innerHTML =
messages.innerHTML +
`<div class="message">
<b><i class="far fa-user-circle"></i> <span> ${userName === user ? "me" : userName
}</span> </b>
<span>${message}</span>
</div>`;
});
'프로그래밍 > Js' 카테고리의 다른 글
안드로이드 웹뷰와 웹워커 브릿지 (android + webview + webworker bridge example) 샘플 (0) | 2023.02.28 |
---|---|
방법2 WebRTC localhost Socket.io + Node.js 내부네트워크 정상 (1차완료) (0) | 2023.02.22 |
webRTC 카메라/오디오 권한 획득 기본 샘플 (0) | 2023.02.21 |
webRTC 이용하여 비디오 채팅 방법 with Node.js + Socket.io (0) | 2023.02.21 |
vue3 에 tradingview 차트 연동 (소스추가) (0) | 2023.01.19 |