Skip to content

Instantly share code, notes, and snippets.

@flaviocopes
Created October 29, 2018 11:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save flaviocopes/e0844d71da2348d14ec8b6742d8b2795 to your computer and use it in GitHub Desktop.
Save flaviocopes/e0844d71da2348d14ec8b6742d8b2795 to your computer and use it in GitHub Desktop.
const ws = new WebSocket('ws://localhost:8080')
ws.onopen = () => {
console.log('Connected to the signaling server')
}
ws.onerror = err => {
console.error(err)
}
ws.onmessage = msg => {
console.log('Got message', msg.data)
const data = JSON.parse(msg.data)
switch (data.type) {
case 'login':
handleLogin(data.success)
break
case 'offer':
handleOffer(data.offer, data.username)
break
case 'answer':
handleAnswer(data.answer)
break
case 'candidate':
handleCandidate(data.candidate)
break
case 'close':
handleClose()
break
default:
break
}
}
let connection = null
let name = null
let otherUsername = null
const sendMessage = message => {
if (otherUsername) {
message.otherUsername = otherUsername
}
ws.send(JSON.stringify(message))
}
document.querySelector('div#call').style.display = 'none'
document.querySelector('button#login').addEventListener('click', event => {
username = document.querySelector('input#username').value
if (username.length < 0) {
alert('Please enter a username 🙂')
return
}
sendMessage({
type: 'login',
username: username
})
})
const handleLogin = async success => {
if (success === false) {
alert('😞 Username already taken')
} else {
document.querySelector('div#login').style.display = 'none'
document.querySelector('div#call').style.display = 'block'
let localStream
try {
localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
} catch (error) {
alert(`${error.name}`)
console.error(error)
}
document.querySelector('video#local').srcObject = localStream
const configuration = {
iceServers: [{ url: 'stun:stun2.1.google.com:19302' }]
}
connection = new RTCPeerConnection(configuration)
connection.addStream(localStream)
connection.onaddstream = event => {
document.querySelector('video#remote').srcObject = event.stream
}
connection.onicecandidate = event => {
if (event.candidate) {
sendMessage({
type: 'candidate',
candidate: event.candidate
})
}
}
}
}
document.querySelector('button#call').addEventListener('click', () => {
const callToUsername = document.querySelector('input#username-to-call').value
if (callToUsername.length === 0) {
alert('Enter a username 😉')
return
}
otherUsername = callToUsername
connection.createOffer(
offer => {
sendMessage({
type: 'offer',
offer: offer
})
connection.setLocalDescription(offer)
},
error => {
alert('Error when creating an offer')
console.error(error)
}
)
})
const handleOffer = (offer, username) => {
otherUsername = username
connection.setRemoteDescription(new RTCSessionDescription(offer))
connection.createAnswer(
answer => {
connection.setLocalDescription(answer)
sendMessage({
type: 'answer',
answer: answer
})
},
error => {
alert('Error when creating an answer')
console.error(error)
}
)
}
const handleAnswer = answer => {
connection.setRemoteDescription(new RTCSessionDescription(answer))
}
const handleCandidate = candidate => {
connection.addIceCandidate(new RTCIceCandidate(candidate))
}
document.querySelector('button#close-call').addEventListener('click', () => {
sendMessage({
type: 'close'
})
handleClose()
})
const handleClose = () => {
otherUsername = null
document.querySelector('video#remote').src = null
connection.close()
connection.onicecandidate = null
connection.onaddstream = null
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebRTC</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="login">
<label for="username">Login</label>
<input id="username" placeholder="Login" required="" autofocus="">
<button id="login">Login</button>
</div>
<div id="call">
<video id="local" autoplay></video>
<video id="remote" autoplay></video>
<div>
<input id="username-to-call" placeholder="Username to call" />
<button id="call">Call</button>
<button id="close-call">Close call</button>
</div>
</div>
</body>
<script src="client.js"></script>
</html>
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
const users = {}
const sendTo = (ws, message) => {
ws.send(JSON.stringify(message))
}
wss.on('connection', ws => {
console.log('User connected')
ws.on('message', message => {
let data = null
try {
data = JSON.parse(message)
} catch (error) {
console.error('Invalid JSON', error)
data = {}
}
switch (data.type) {
case 'login':
console.log('User logged', data.username)
if (users[data.username]) {
sendTo(ws, { type: 'login', success: false })
} else {
users[data.username] = ws
ws.username = data.username
sendTo(ws, { type: 'login', success: true })
}
break
case 'offer':
console.log('Sending offer to: ', data.otherUsername)
if (users[data.otherUsername] != null) {
ws.otherUsername = data.otherUsername
sendTo(users[data.otherUsername], {
type: 'offer',
offer: data.offer,
username: ws.username
})
}
break
case 'answer':
console.log('Sending answer to: ', data.otherUsername)
if (users[data.otherUsername] != null) {
ws.otherUsername = data.otherUsername
sendTo(users[data.otherUsername], {
type: 'answer',
answer: data.answer
})
}
break
case 'candidate':
console.log('Sending candidate to:', data.otherUsername)
if (users[data.otherUsername] != null) {
sendTo(users[data.otherUsername], {
type: 'candidate',
candidate: data.candidate
})
}
break
case 'close':
console.log('Disconnecting from', data.otherUsername)
users[data.otherUsername].otherUsername = null
if (users[data.otherUsername] != null) {
sendTo(users[data.otherUsername], { type: 'close' })
}
break
default:
sendTo(ws, {
type: 'error',
message: 'Command not found: ' + data.type
})
break
}
})
ws.on('close', () => {
if (ws.username) {
delete users[ws.username]
if (ws.otherUsername) {
console.log('Disconnecting from ', ws.otherUsername)
users[ws.otherUsername].otherUsername = null
if (users[ws.otherUsername] != null) {
sendTo(users[ws.otherUsername], { type: 'close' })
}
}
}
})
})
div#call {
position: relative;
display: block;
margin: 0 auto;
width: 500px;
height: 500px;
}
video {
background: black;
border: 1px solid gray;
}
video#local {
width: 150px;
height: 150px;
position: absolute;
top: 15px;
right: 15px;
}
video#remote {
width: 500px;
height: 500px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment