Skip to content

Instantly share code, notes, and snippets.

@lynaghk
Last active July 15, 2022 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lynaghk/e37f7dc54305382e803f8f6ddac4f40d to your computer and use it in GitHub Desktop.
Save lynaghk/e37f7dc54305382e803f8f6ddac4f40d to your computer and use it in GitHub Desktop.
"Serverless" WebRTC demo.

"Serverless" WebRTC demo

Start a server on localhost and open index.html (the host) and index.html#client (the client) in two different browser windows. They'll do their lil' p2p connection discovery dance via the wonderful public service that is https://patchbay.pub/ and, once established, a video-only camera feed from the client will display on the host.

<html>
<body>
<h1 id="status">Loading</h1>
<video id="remoteVideo" muted autoplay style="width: 100%;"></video>
<script>
let is_client = window.location.hash === "#client";
let url_send = "https://patchbay.pub/pubsub/webrtc-demo-12345-" + (is_client ? "host" : "client");
let url_recv = "https://patchbay.pub/pubsub/webrtc-demo-12345-" + (is_client ? "client" : "host");
function log(msg) {
document.getElementById("status").innerText = `${msg}\n\n`;
console.log(msg);
}
function send (msg) {
let json = JSON.stringify(msg);
fetch(url_send, {method: "POST", body: `data: ${json}\n\n`});
}
let pc = new RTCPeerConnection({
"iceServers": [
{"urls": ["stun:stun.l.google.com:19302",
"stun:stun.stunprotocol.org:3478"]},
]
});
pc.onicecandidate = (e) => {
if(e.candidate != null) {
send({"ice": e.candidate});
}
}
pc.onconnectionstatechange = (e) => {
let state = pc.connectionState;
log(`Connection: ${state}`);
}
if(!is_client){
pc.ontrack = ({track, streams: [stream]}) => {
//"unmute" here actually means "track is able to send data"
track.onunmute = () => {
status.innerText = "";
//Chrome will autoplay muted video.
remoteVideo.srcObject = stream;
};
}
}
if(is_client){
pc.onnegotiationneeded = (e) => {
pc.createOffer({offerToReceiveVideo: 1})
.then((offer) => pc.setLocalDescription(offer))
.then(() => {
log("Connecting to host.");
send({"sdp": pc.localDescription})
})
.catch(log)
}
log("Requesting camera.");
navigator.mediaDevices.getUserMedia({
video: {width: { ideal: 4096 }}, //Go big or go home; odds are we'll get a p2p connection over LAN
audio: false,
}).then((stream) => {
stream.getTracks().forEach((t) => pc.addTrack(t, stream));
}).catch(log);
}
let incoming = new EventSource(`${url_recv}?mime=text%2Fevent-stream&persist=true`);
incoming.onmessage = (e) => {
let msg = JSON.parse(e.data);
if(msg.sdp){
pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
.then(() => {
if("offer" == msg.sdp.type){
//create and send answer
pc.createAnswer()
.then((answer) => pc.setLocalDescription(answer))
.then(() => send({"sdp": pc.localDescription}))
.catch(log)
}
}).catch(log)
}
if(msg.ice){
pc.addIceCandidate(new RTCIceCandidate(msg.ice)).catch(log)
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment