Skip to content

Instantly share code, notes, and snippets.

@code-boxx
Last active November 9, 2023 03:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save code-boxx/d8028c553a94b24e5e99e195b8c4695c to your computer and use it in GitHub Desktop.
Save code-boxx/d8028c553a94b24e5e99e195b8c4695c to your computer and use it in GitHub Desktop.
NodeJS Video Call

NODEJS VIDEO CALL

https://code-boxx.com/javascript-video-call/

NOTES

  1. Run unpack.bat (Windows) unpack.sh (Linux/Mac). This will automatically:
    • Create an assets folder. Move 3-vid-chat.js and 4-vid-chat.css inside.
    • Install the required modules npm i peer express
    • Run the video call server - node 1-server.js
  2. Access https://YOUR-SERVER-IP-ADDRESS on 2 different devices - Continue with the self-signed certificate and allow webcam access.

LICENSE

Copyright by Code Boxx

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// (A) LOAD MODULES
const http = require("http");
const https = require("https");
const express = require("express");
const peer = require("peer");
const fs = require("fs");
const path = require("path");
// (B) EXPRESS SERVER
const app = express();
// (C) ROUTES + STATIC FILES
app.use("/assets", express.static(path.join(__dirname, "/assets")));
app.get("/", (req, res) => res.sendFile(path.join(__dirname, "/2-vid-chat.html")));
// (D) HTTPS + PEER SERVER
const httpsServer = https.createServer({
key: fs.readFileSync(path.join(__dirname, "server.key")),
cert: fs.readFileSync(path.join(__dirname, "server.crt"))
}, app);
httpsServer.listen(443, () => console.log("HTTPS Server running on port 443"));
app.use("/peer", peer.ExpressPeerServer(httpsServer, { debug : true }));
<!DOCTYPE html>
<html>
<head>
<title>JS Video Chat</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="assets/4-vid-chat.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.4.7/peerjs.min.js"></script>
<script src="assets/3-vid-chat.js"></script>
</head>
<body>
<!-- (A) ENTER NAME -->
<form id="stepA" onsubmit="return vChat.open()">
<label>Enter Your Name</label>
<input type="text" id="nameA" disabled required>
<input type="submit" value="Next">
</form>
<!-- (B) CONNECT TO -->
<form id="stepB" class="hide" onsubmit="return vChat.call()">
<label>Connect to</label>
<input type="text" id="nameB" required>
<div>
Or ask someone to call you - <span id="nameAA"></span>
</div>
<input type="submit" value="Next">
</form>
<!-- (C) VIDEO -->
<div id="stepC" class="hide">
<video id="vLocal" muted></video>
<video id="vRemote"></video>
<input type="button" value="End Call" onclick="vChat.endcall()">
</div>
</body>
</html>
var vChat = {
// (A) PROPERTIES
peerOpt : {
host: "192.168.0.101", // CHANGE TO YOUR OWN!
port: 443, path: "/peer", secure : true
},
peerObj : null, peerCall : null,
hSteps : null, hNameA : null, hNameB: null,
hVidL : null, hVidR : null,
vStream : null,
// (B) INIT - GET HTML SECTIONS
init : () => {
// (B1) GET HTML ELEMENTS
vChat.hSec = [
document.getElementById("stepA"),
document.getElementById("stepB"),
document.getElementById("stepC")
];
vChat.hNameA = document.getElementById("nameA");
vChat.hNameB = document.getElementById("nameB");
vChat.hVidL = document.getElementById("vLocal");
vChat.hVidR = document.getElementById("vRemote");
// (B2) GET WEBCAM PERMISSION
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
vChat.vStream = stream;
vChat.hVidL.srcObject = stream;
vChat.hVidL.play();
vChat.hNameA.disabled = false;
})
.catch(err => {
console.error(err);
alert("Please attach a webcam and allow access.");
});
},
// (C) SWITCH TO HTML SECTION
switch : n => { for (let i in vChat.hSec) {
if (i==n) { vChat.hSec[i].classList.remove("hide"); }
else { vChat.hSec[i].classList.add("hide"); }
}},
// (D) CONNECT TO PEERJS SERVER
open : () => {
// (D1) NEW PEER OBJECT
vChat.peerObj = new Peer(vChat.hNameA.value, vChat.peerOpt);
// (D2) ON OPEN
vChat.peerObj.on("open", id => {
document.getElementById("nameAA").innerHTML = vChat.peerObj.id;
vChat.switch(1);
});
// (D3) AUTO ANSWER CALLS
vChat.peerObj.on("call", call => {
vChat.peerCall = call;
vChat.setcall();
vChat.peerCall.answer(vChat.vStream);
});
// (D4) ON ERRORS
vChat.peerObj.on("error", err => {
console.error(err);
alert(err.type);
});
return false;
},
// (E) CALL USER
call : () => {
vChat.peerCall = vChat.peerObj.call(
vChat.hNameB.value, vChat.vStream
);
vChat.setcall();
return false;
},
// (F) SETUP CALLS
setcall : () => {
vChat.peerCall.on("stream", stream => {
vChat.hVidR.srcObject = stream;
vChat.hVidR.play();
vChat.switch(2);
});
vChat.peerCall.on("data", stream => vChat.hVidR.srcObject = stream);
vChat.peerCall.on("error", err => console.error(err));
vChat.peerCall.on("close", vChat.endcall);
},
// (G) END CALL
endcall : () => { try {
vChat.switch(1);
vChat.peerCall.close();
} catch (err) { console.error(err); }}
};
window.onload = vChat.init;
/* (A) WHOLE PAGE */
* {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
}
/* (B) SHARED LAYOUT */
body {
display: flex;
width: 100vw; height: 100vh;
padding: 10px; margin: 0;
justify-content: center;
align-items: center;
background: #f2f2f2;
}
.hide { display: none !important; }
#stepA, #stepB, #stepC {
width: 400px;
padding: 20px;
border: 1px solid #e3e3e3;
background: #fff;
}
/* (C) FORM */
label, input {
display: block;
width: 100%;
}
label {
color: #7e7e7e;
padding: 10px 0;
}
input { padding: 10px; }
input[type=submit], input[type=button] {
margin-top: 20px;
border: 0;
color: #fff;
background: #1773a9;
cursor: pointer;
}
/* (D) VIDEO */
#vLocal, #vRemote {
width: 100%;
height: 200px;
}
-----BEGIN CERTIFICATE-----
MIIDiDCCAnCgAwIBAgIELEhy9zANBgkqhkiG9w0BAQsFADBbMScwJQYDVQQDDB5SZWdlcnkgU2Vs
Zi1TaWduZWQgQ2VydGlmaWNhdGUxIzAhBgNVBAoMGlJlZ2VyeSwgaHR0cHM6Ly9yZWdlcnkuY29t
MQswCQYDVQQGEwJVQTAgFw0yMjA4MjAwMDAwMDBaGA8yMTIyMDgyMDA1NTQ1NFowSjEWMBQGA1UE
AwwNMTkyLjE2OC4wLjEwMTEjMCEGA1UECgwaUmVnZXJ5LCBodHRwczovL3JlZ2VyeS5jb20xCzAJ
BgNVBAYTAlVBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArbtRpdmHOx7Lag2/tfSd
JrHeefJSUCCBKB2VSFOGIYPlqzB18IF4BV+Ow383Id4mEqe2vI/OP9QJRmIl5+5MdUdoL5894Q+4
WpMdXET60alP3ZEfFKFx7JHSha8wWac0NKoz/Bq4BL67zOFNCsM+NO/WOuF2fC3oQ5omqWXc9aXA
vINAVTODRBHC6BVIHhSWgolEDA76iin8sgFBRbG7uzqaJ01NGkoHT++ekn5vkh1PitCFPiostAfv
2EYlnEatPtncUFNoZ4hmO7/0PHACpFphl1Tor3kQNLboffI7z2msne5u4PBrboYV8LTQQugu5g6H
5LL39npyZyjwm7fOlQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
BgNVHQ4EFgQU6Ftg3ZtbV67U5r+2jIRpNtwGDH0wHwYDVR0jBBgwFoAU6Ftg3ZtbV67U5r+2jIRp
NtwGDH0wDQYJKoZIhvcNAQELBQADggEBAIhM9TWYHdLa1iro8KiSFdUQpwMYVFnVJl6wuyQu/2x+
d5fBP6xIy7IGYsxDgeBQWic4OJ29GZ8ero9jqWu3a6q0UEJZ6uVrgiViZhtbbsqWOGB8U/PT90DA
NobyZssrbPiW2DzksPor6pGVodF3LwNMTE6GGeXEIWJIRw5JK50z9oQlnFKKeVLxLcxEiNkxN/99
QFGd1EBzut0EsjywODkw4XBZwJSW1QLL1A92bsQFsj40j3MiSl69IM7qfPaorMYUAKu4uDqJoVhJ
R1w1OF3y0PyXNf9DaGvSE2u3hOWU7c06KWeRkzMxwnWHg0kJJyymbCZ9nSRJwPUVh63/eLc=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEArbtRpdmHOx7Lag2/tfSdJrHeefJSUCCBKB2VSFOGIYPlqzB1
8IF4BV+Ow383Id4mEqe2vI/OP9QJRmIl5+5MdUdoL5894Q+4WpMdXET60alP3ZEf
FKFx7JHSha8wWac0NKoz/Bq4BL67zOFNCsM+NO/WOuF2fC3oQ5omqWXc9aXAvINA
VTODRBHC6BVIHhSWgolEDA76iin8sgFBRbG7uzqaJ01NGkoHT++ekn5vkh1PitCF
PiostAfv2EYlnEatPtncUFNoZ4hmO7/0PHACpFphl1Tor3kQNLboffI7z2msne5u
4PBrboYV8LTQQugu5g6H5LL39npyZyjwm7fOlQIDAQABAoIBABfouduYhH0dJ5Ls
mfflVnRTEcJYGD5pPsUhzx/hSMoXgBXPaUfEVZtts3TRrul1DGnQVXx9UP3lRTJ/
wuY4nCKexWaaZHZzBqTGbsm6P4d3pONh+7RH5KkRR3b47W9FLNDbpKRbG/yhMDDJ
x+PFi2vXHvVC/p6n5blZ/1ycRWyz3Fsupr87XF0jzaeM3hZF36hCwCvdFKsXx0r+
9reniVrr2qaUX9v61KbhLKqij5RKjBCJxDasyFZVUZDRvbmtmpC6ZIwXd9GBgoAw
ScFE7fIqFAStJdqHbKyGN34HgqRakAgtTMYdkMN0E9QIGEjCBUFoQTiYhIiMv+2J
TpT0YQECgYEA+mSWds/hrCDsstC8b5xTsVUQt6X/jAsNR1eSYyGyZU9sJPuQtEhL
5D30e9LGTFYVKSkykRKPRTbT+/B4x7VAia6GiqbCXmJ4Pp9Asy/XTbFcwz6JYVKU
jbTUllAwITNVI+4fKCODVmL7Oun/hoXepQQfu4eRuvbtWkNgtG0q2ZUCgYEAsZ9C
ma7tW/dO9ozMXugtZN9KXrnbKX2hbxpSNVMzD0AGlaEo1T08OtJK9rPnuyfRq+OO
MZSIY/+FvkyDYFE9outU/i+t5M1mf3QOZueFqY67R9N1LXAr5Wzn0eQ3RGMmnEsJ
uhgGQirnYrnKWM6peTWLPRu0pUHDKntTO+XN4QECgYBHiKMf0GX1ifZ1Y4LiE/Qp
DldXUBpAxQuHHTKaFY9kuBr6OdBuYNUloLmPAnlaZ6Fl//oNThMWQAKx0FKBI3wL
mYugYuHWmyUlJtQSHRHShWAt+1i+MBx2m3m8c648XfDt8eJ+0h58eIyzmRRCN06T
MClspt9wXYkTp3oiManzPQKBgHA4jN2INZLLN2pyHpU/355BrT0X863NH/eQC6yB
OltCOmbDAYdoPuty/cXMeHY4fDI7fRU6IMn9IMTQlph9+5E5WeOWwCk01LNl4cuG
L16nPQ/uO7RPkIxNOFHV/E0Kz9QjXKvkjd+0N6iadYuSVmSsPaKIJKPcXrAyZq4L
TP4BAoGAayZ62ZffMgdDuFTIkdnUnt1fAtDNLEomxGaZyxxR0MYt30i2renuRm6b
DFWmcVPgENmlOQHr1+7jBa/UED8SsJ37Z8Jaa0Uv1Dzly9l9Xog0JHi8WBgztk/P
xm+brsOYchbxRuTiX9y2bQS7f9zrVpw3DsYR6r8ixE6rjGNfcW8=
-----END RSA PRIVATE KEY-----
md assets
move 3-vid-chat.js assets
move 4-vid-chat-css assets
call npm i peer express
node 1-server.js
mkdir -m 777 assets
mv ./3-vid-chat.js ./assets
mv ./4-vid-chat-css ./assets
source "npm i peer express"
node ./1-server.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment