Skip to content

Instantly share code, notes, and snippets.

@sudoanand
Last active January 12, 2020 10:39
Show Gist options
  • Save sudoanand/c7b7eb0034cfb4f75c53e26dc6b7f942 to your computer and use it in GitHub Desktop.
Save sudoanand/c7b7eb0034cfb4f75c53e26dc6b7f942 to your computer and use it in GitHub Desktop.
WebRTC one to one video call demo, usage https://websocket.in as signalling server. Need help? Tweet to @hack4mer
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=yes">
<style>
body {
display: flex;
height: 100vh;
margin: 0;
align-items: center;
justify-content: center;
padding: 0 50px;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
video {
max-width: calc(50% - 100px);
margin: 0 50px;
box-sizing: border-box;
border-radius: 2px;
padding: 0;
box-shadow: rgba(156, 172, 172, 0.2) 0px 2px 2px, rgba(156, 172, 172, 0.2) 0px 4px 4px, rgba(156, 172, 172, 0.2) 0px 8px 8px, rgba(156, 172, 172, 0.2) 0px 16px 16px, rgba(156, 172, 172, 0.2) 0px 32px 32px, rgba(156, 172, 172, 0.2) 0px 64px 64px;
}
.copy {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 16px;
color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="copy">Open the same URL in another Tab/URL to start the video-call</div>
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<script>
var peerConnectionConfig = {
'iceServers': [
{ 'urls': 'stun:stun.stunprotocol.org:3478' },
{ 'urls': 'stun:stun.l.google.com:19302' },
]
};
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');
var serverConnection = new WebSocket("wss://connect.websocket.in/hack4mer?room_id=2");
serverConnection.onmessage = function (message) {
var signal = JSON.parse(message.data);
console.log("M",signal);
if (signal.type == "callRequest") {
startVideoCall(true, signal);
console.log("Starting a call with: " + signal.from);
}
else if (signal.sdp) {
if (!peerConnection) startVideoCall(false, signal);
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function () {
// Only create answers in response to offers
if (signal.sdp.type == 'offer') {
console.log("Got an offer from " + signal.from, signal)
peerConnection.createAnswer().then((description) => {
//The remove description
peerConnection.setLocalDescription(description).then(function () {
serverConnection.send(JSON.stringify({
'peerInfo': "test",
'from': "test",
'to': signal.from,
'sdp': peerConnection.localDescription
}));
}).catch(errorHandler);
}).catch(errorHandler);
} else {
console.log("Got an asnwer from " + signal.from);
}
}).catch(errorHandler);
} else if (signal.ice) {
//Utilities.log("Adding ice.candidate : ",signal);
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
}
}
serverConnection.onopen = function () {
console.log("Socket connected");
requestVideo();
}
serverConnection.onerror = function () {
console.error("Socket disconnected");
}
var constraints = {
video: true,
audio: true
};
var peerConnection = null;
var isNegotiating = false;
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(getUserMediaSuccess).catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API');
}
function errorHandler(error) {
console.error(error);
}
function getUserMediaSuccess(stream) {
localVideo.srcObject = stream;
}
//New joiners make a video request
function requestVideo() {
console.log("Starting the video request");
serverConnection.send(JSON.stringify({
peerInfo: "test",
'from': "test",
'type': 'callRequest'
}
));
}
//Already connected peers startVideoCall with the new joiner as response to his requestVideo
function startVideoCall(isCaller, requestSignal) {
console.log("making pc:", requestSignal.from);
peerConnection = new RTCPeerConnection(peerConnectionConfig);
var peerFrom = peerConnection;
peerFrom.onicecandidate = (event) => {
if (event.candidate != null) {
serverConnection.send(JSON.stringify({
'peerInfo': "test",
'from': "test",
'to': requestSignal.from,
'ice': event.candidate
}));
}
}
peerFrom.ontrack = (event) => {
if (event.track.kind != "video") {
return;
}
console.log("Remote video found!!");
remoteVideo.srcObject = event.streams[0];
remoteVideo.style.height = "auto";
}
peerFrom.onsignalingstatechange = (e) => { // Workaround for Chrome: skip nested negotiations
isNegotiating = (peerConnection.signalingState != "stable");
}
//Add the track to be sent
var stream = localVideo.srcObject;
stream.getTracks().forEach((track) => { peerConnection.addTrack(track, stream); });
//Create offer when needed
isNegotiating = false;
peerFrom.onnegotiationneeded = async () => {
if (!isCaller) {
return;
}
console.log("I need it", window.theRemoteDesc);
if (isNegotiating) {
console.log(requestSignal);
console.log("SKIP nested negotiations");
return;
}
isNegotiating = true;
var description = await peerFrom.createOffer();
console.log('got local description');
console.log("a", isCaller, requestSignal);
await peerFrom.setLocalDescription(description);
console.log("b");
//if(isCaller){
console.log('Making offer');
//Send a call offer
serverConnection.send(JSON.stringify({
'peerInfo': "test",
'from': "test",
'to': requestSignal.from,
'sdp': peerConnection.localDescription
}));
//}
};
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment