-
-
Save tsh-code/5d34542fe5274341439ef92de7861cd3 to your computer and use it in GitHub Desktop.
import express, { Application } from "express"; | |
import socketIO, { Server as SocketIOServer } from "socket.io"; | |
import { createServer, Server as HTTPServer } from "http"; | |
export class Server { | |
private httpServer: HTTPServer; | |
private app: Application; | |
private io: SocketIOServer; | |
private readonly DEFAULT_PORT = 5000; | |
constructor() { | |
this.initialize(); | |
this.handleRoutes(); | |
this.handleSocketConnection(); | |
} | |
private initialize(): void { | |
this.app = express(); | |
this.httpServer = createServer(this.app); | |
this.io = socketIO(this.httpServer); | |
} | |
private handleRoutes(): void { | |
this.app.get("/", (req, res) => { | |
res.send(`<h1>Hello World</h1>`); | |
}); | |
} | |
private handleSocketConnection(): void { | |
this.io.on("connection", socket => { | |
console.log("Socket connected."); | |
}); | |
} | |
public listen(callback: (port: number) => void): void { | |
this.httpServer.listen(this.DEFAULT_PORT, () => | |
callback(this.DEFAULT_PORT) | |
); | |
} | |
} |
async function callUser(socketId) { | |
const offer = await peerConnection.createOffer(); | |
await peerConnection.setLocalDescription(new RTCSessionDescription(offer)); | |
socket.emit("call-user", { | |
offer, | |
to: socketId | |
}); | |
} |
socket.on("call-user", data => { | |
socket.to(data.to).emit("call-made", { | |
offer: data.offer, | |
socket: socket.id | |
}); | |
}); |
function createUserItemContainer(socketId) { | |
const userContainerEl = document.createElement("div"); | |
const usernameEl = document.createElement("p"); | |
userContainerEl.setAttribute("class", "active-user"); | |
userContainerEl.setAttribute("id", socketId); | |
usernameEl.setAttribute("class", "username"); | |
usernameEl.innerHTML = `Socket: ${socketId}`; | |
userContainerEl.appendChild(usernameEl); | |
userContainerEl.addEventListener("click", () => { | |
unselectUsersFromList(); | |
userContainerEl.setAttribute("class", "active-user active-user--selected"); | |
const talkingWithInfo = document.getElementById("talking-with-info"); | |
talkingWithInfo.innerHTML = `Talking with: "Socket: ${socketId}"`; | |
callUser(socketId); | |
}); | |
return userContainerEl; | |
} |
navigator.getUserMedia( | |
{ video: true, audio: true }, | |
stream => { | |
const localVideo = document.getElementById("local-video"); | |
if (localVideo) { | |
localVideo.srcObject = stream; | |
} | |
}, | |
error => { | |
console.warn(error.message); | |
} | |
); |
private configureApp(): void { | |
this.app.use(express.static(path.join(__dirname, "../public"))); | |
} |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | |
<title>Dogeller</title> | |
<link | |
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700&display=swap" | |
rel="stylesheet" | |
/> | |
<link rel="stylesheet" href="./styles.css" /> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script> | |
</head> | |
<body> | |
<div class="container"> | |
<header class="header"> | |
<div class="logo-container"> | |
<img src="./img/doge.png" alt="doge logo" class="logo-img" /> | |
<h1 class="logo-text"> | |
Doge<span class="logo-highlight">ller</span> | |
</h1> | |
</div> | |
</header> | |
<div class="content-container"> | |
<div class="active-users-panel" id="active-user-container"> | |
<h3 class="panel-title">Active Users:</h3> | |
</div> | |
<div class="video-chat-container"> | |
<h2 class="talk-info" id="talking-with-info"> | |
Select active user on the left menu. | |
</h2> | |
<div class="video-container"> | |
<video autoplay class="remote-video" id="remote-video"></video> | |
<video autoplay muted class="local-video" id="local-video"></video> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script src="./scripts/index.js"></script> | |
</body> | |
</html> |
import { Server } from "./server"; | |
const server = new Server(); | |
server.listen(port => { | |
console.log(`Server is listening on http://localhost:${port}`); | |
}); |
private activeSockets: string[] = []; |
socket.on("make-answer", data => { | |
socket.to(data.to).emit("answer-made", { | |
socket: socket.id, | |
answer: data.answer | |
}); | |
}); |
npm init |
socket.on("call-made", async data => { | |
await peerConnection.setRemoteDescription( | |
new RTCSessionDescription(data.offer) | |
); | |
const answer = await peerConnection.createAnswer(); | |
await peerConnection.setLocalDescription(new RTCSessionDescription(answer)); | |
socket.emit("make-answer", { | |
answer, | |
to: data.socket | |
}); | |
}); |
socket.on("answer-made", async data => { | |
await peerConnection.setRemoteDescription( | |
new RTCSessionDescription(data.answer) | |
); | |
if (!isAlreadyCalling) { | |
callUser(data.socket); | |
isAlreadyCalling = true; | |
} | |
}); |
socket.on("update-user-list", ({ users }) => { | |
updateUserList(users); | |
}); | |
socket.on("remove-user", ({ socketId }) => { | |
const elToRemove = document.getElementById(socketId); | |
if (elToRemove) { | |
elToRemove.remove(); | |
} | |
}); |
{ | |
"scripts": { | |
"start": "ts-node src/index.ts", | |
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts" | |
}, | |
"devDependencies": { | |
"@types/express": "^4.17.2", | |
"@types/socket.io": "^2.1.4", | |
"nodemon": "^1.19.4", | |
"ts-node": "^8.4.1", | |
"typescript": "^3.7.2" | |
}, | |
"dependencies": { | |
"express": "^4.17.1", | |
"socket.io": "^2.3.0" | |
} | |
} |
navigator.getUserMedia( | |
{ video: true, audio: true }, | |
stream => { | |
const localVideo = document.getElementById("local-video"); | |
if (localVideo) { | |
localVideo.srcObject = stream; | |
} | |
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream)); | |
}, | |
error => { | |
console.warn(error.message); | |
} | |
); |
const { RTCPeerConnection, RTCSessionDescription } = window; |
private initialize(): void { | |
this.app = express(); | |
this.httpServer = createServer(this.app); | |
this.io = socketIO(this.httpServer); | |
this.configureApp(); | |
this.handleSocketConnection(); | |
} |
peerConnection.ontrack = function({ streams: [stream] }) { | |
const remoteVideo = document.getElementById("remote-video"); | |
if (remoteVideo) { | |
remoteVideo.srcObject = stream; | |
} | |
}; |
const socket = io.connect("localhost:5000"); |
this.io.on("connection", socket => { | |
const existingSocket = this.activeSockets.find( | |
existingSocket => existingSocket === socket.id | |
); | |
if (!existingSocket) { | |
this.activeSockets.push(socket.id); | |
socket.emit("update-user-list", { | |
users: this.activeSockets.filter( | |
existingSocket => existingSocket !== socket.id | |
) | |
}); | |
socket.broadcast.emit("update-user-list", { | |
users: [socket.id] | |
}); | |
} | |
} |
socket.on("disconnect", () => { | |
this.activeSockets = this.activeSockets.filter( | |
existingSocket => existingSocket !== socket.id | |
); | |
socket.broadcast.emit("remove-user", { | |
socketId: socket.id | |
}); | |
}); |
function updateUserList(socketIds) { | |
const activeUserContainer = document.getElementById("active-user-container"); | |
socketIds.forEach(socketId => { | |
const alreadyExistingUser = document.getElementById(socketId); | |
if (!alreadyExistingUser) { | |
const userContainerEl = createUserItemContainer(socketId); | |
activeUserContainer.appendChild(userContainerEl); | |
} | |
}); | |
} |
Also, I had a question as to why no ICE candidate information is required in this tutorial, yet the end result still ends up working?
Thanks for this good tuto !
It works very well with Chrome but not with Safari or Firefox (TypeError: navigator.getUserMedia is not a function).
I tired to replace navigator.getUserMedia by navigator.mediaDevice.getUserMedia but i have an "uncaught exception: Object".
Do you have an idea ?
Hi,
Be careful with "navigator.mediaDevice.getUserMedia" and the syntax to use, i modified it:
navigator.mediaDevices.getUserMedia( { video: true, audio: true }).then( (stream) =>{ const localVideo = document.getElementById("local-video"); ....
If it can help...
Hi,
Could anyone please help me. I'm fairy new to be a developer.
I've got an error after creating index.ts and server.ts thennpm run dev
.
Is there any solution for that? I've googled it, then installed typescript package globally, but not idea what this error is./node_modules/ts-node/src/index.ts:421 return new TSError(diagnosticText, diagnosticCodes) ^ TSError: ⨯ Unable to compile TypeScript:
Did You find a solution to this problem I am facing the same issue, Thanks.
I think on your article you confused the server code with the client (see image).
The code here is not the one should be in index.js right?
I totally missed the part where the client connects to the server. Where is that part?
He made a mistake, this code is in server.ts file, inside of handleSocketConnection function.
Hi,
Could anyone please help me. I'm fairy new to be a developer.
I've got an error after creating index.ts and server.ts thennpm run dev
.
Is there any solution for that? I've googled it, then installed typescript package globally, but not idea what this error is./node_modules/ts-node/src/index.ts:421 return new TSError(diagnosticText, diagnosticCodes) ^ TSError: ⨯ Unable to compile TypeScript:
Did You find a solution to this problem I am facing the same issue, Thanks.
It happened to me and I determined it was just a generic error. The real error was somewhere else. Check the full stack trace that it gives you.
Hi, I was trying to recreate the code and when started with Handle socket connection snippet, author was recommending to look for the socket connected message in console. apparently, I was not able to see "Socket connected" message. I have checked all the git repository files as well. Am I missing something.
Thank you.
V Raj
It is broken it doesn't work mate
…
On Wed 12 Aug, 2020, 10:28 AM VISHNU RAJ C L, @.> wrote: @.* commented on this gist. ------------------------------ Hi, I was trying to recreate the code and when started with Handle socket connection snippet, author was recommending to look for the socket connected message in console. apparently, I was not able to see "Socket connected" message. I have checked all the git repository files as well. Am I missing something. Thank you. V Raj — You are receiving this because you commented. Reply to this email directly, view it on GitHub https://gist.github.com/5d34542fe5274341439ef92de7861cd3#gistcomment-3413866, or unsubscribe https://github.com/notifications/unsubscribe-auth/AH2GJKLJXJ2C3F4BAFTSTTTSAIORDANCNFSM4LQSCDOA .
Thanks Sagarparker ! Is there any other workaround?
in tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true
}
}
to fix import errors
Does anyone know why this code is needed?
on-made-answer.js
if (!isAlreadyCalling) {
callUser(data.socket);
isAlreadyCalling = true;
}
When I place a console.log(socketId); in the callUser function.
I see that the callUser function is called twice with the same socket id as argument.
This code is needed, because without it the application doesn't work.
But can't find a sollution why.
I made an example and fixed all the mistakes of the author.
https://github.com/exzos28/webrtc-echo-server
I followed this tutorial, and based on the logic, why would you run
callUser
afteranswer-made
? That callback is only called by the original caller to begin with. if you put a log insidecallUser()
, it occurs twice.