Skip to content

Instantly share code, notes, and snippets.

@joduplessis
Created September 22, 2018 06:41
Show Gist options
  • Save joduplessis/f5d16db59916060fdf9592a980e60b8f to your computer and use it in GitHub Desktop.
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".
<!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