Created
September 22, 2018 06:41
-
-
Save joduplessis/f5d16db59916060fdf9592a980e60b8f to your computer and use it in GitHub Desktop.
Straightforward implementation of (promisified) WebRTC using MQTT as a messaging protocol (over WebSockets). Needs some QA love, but illustrates the process of WebRTC offers, answers & "jingles".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title></title> | |
</head> | |
<body> | |
<video autoplay controls></video> | |
<button onClick="javascript:start()">Connect</button> | |
<script type="text/javascript" src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script> | |
<script type="text/javascript"> | |
// Setup some basic variables we're going to use | |
const video = document.querySelector('video'); | |
const constraints = { audio: true, video: true }; | |
const configuration = { iceServers: [{urls: 'stun:stun.example.org'}] }; | |
const peerConnection = new RTCPeerConnection(configuration); | |
const cid = new Date().getTime(); | |
const topic = "webrtc"; | |
// This is a public MQTT messaging broker | |
const client = mqtt.connect("ws://iot.eclipse.org:80/ws", { | |
clean: false, | |
clientId: cid, | |
will: { | |
topic: 'death', | |
payload: 'me' | |
} | |
}); | |
// Generic message send | |
// We label each message with a type to signify what type of message it is | |
// On the receiving side | |
const signaling = { | |
send: (payload) => { | |
const message = { | |
sender: cid, | |
type: 'WEBRTC', | |
payload | |
}; | |
client.publish(topic, JSON.stringify(message)); | |
} | |
} | |
// On successful connection | |
client.on('connect', function () { | |
console.log('Connected, ready.', cid, topic); | |
client.subscribe(topic); | |
}); | |
// This is where we start: | |
// Get both video and audio streams from user's camera | |
// Add the stream to the peerConnection to send | |
function start() { | |
navigator.mediaDevices.getUserMedia(constraints) | |
.then(stream => { | |
peerConnection.addStream(stream); | |
// Now we create an offer to send the other person | |
peerConnection.createOffer() | |
.then(sdp => peerConnection.setLocalDescription(sdp)) | |
.then(() => signaling.send(peerConnection.localDescription)) | |
.catch(error => console.error(error)); | |
}) | |
.catch(error => console.error(error)); | |
} | |
client.on('message', async (topic, msg) => { | |
const message = JSON.parse(msg.toString()); | |
// Only handle messages that we didn't send | |
// Also do TYPE specific handling here (much like Redux action types) | |
if (message.sender != cid) { | |
console.log(`::${message.payload.type}::`); | |
// SDP is the important bit here that tells the recipient what sort of | |
// capabilities our machine | |
// The offer is send from the initiator: we need to reply | |
if (message.payload.type == "offer") { | |
peerConnection.setRemoteDescription(message.payload) | |
.then(() => peerConnection.createAnswer()) | |
.then(sdp => peerConnection.setLocalDescription(sdp)) | |
.then(() => signaling.send(peerConnection.localDescription)) | |
.catch(error => console.error(error)); | |
} | |
// Once the recipient replies with an answer to our offer | |
if (message.payload.type == "answer") { | |
peerConnection.setRemoteDescription(message.payload) | |
.catch(error => console.error(error)); | |
} | |
if (message.payload.type == "candidate") { | |
const c = new RTCIceCandidate(message.payload.candidate) | |
peerConnection.addIceCandidate(c); | |
} | |
} | |
}); | |
// This is the stream from the remote source that we display | |
peerConnection.onaddstream = (event) => { | |
console.log('onaddstream', event); | |
video.srcObject = event.stream; | |
} | |
peerConnection.onicecandidate = (event) => { | |
const { candidate } = event; | |
// If the event's candidate property is null, ICE gathering has finished. | |
// "Every ICE candidate describes a method that the sending peer is able to use to communicate." | |
if (candidate) { | |
signaling.send({ | |
candidate, | |
type: "candidate" | |
}) | |
} | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment