Last active
February 5, 2025 00:16
-
-
Save wchargin/1ec26297b2a41995eed14628383d39f7 to your computer and use it in GitHub Desktop.
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> | |
<style> | |
#form { | |
display: flex; | |
gap: 10px; | |
} | |
p { | |
margin: 0.5em 0; | |
} | |
</style> | |
<div id="form"> | |
<label> | |
<input type="checkbox" id="ckbxChannel" checked /> Offer channel? | |
</label> | |
<button id="go">Open connection</button> | |
</div> | |
<div id="sink"></div> | |
<script> | |
async function main(options) { | |
const newPeer = () => { | |
const config = {}; // can put ICE servers here if wanted | |
return new RTCPeerConnection(config); | |
}; | |
const alice = newPeer(); | |
const bob = newPeer(); | |
function log(text) { | |
const el = document.createElement("p"); | |
el.innerText = text; | |
sink.appendChild(el); | |
} | |
function listen(targetName, target, eventName) { | |
target.addEventListener(eventName, (event) => { | |
const fields = Object.fromEntries( | |
["connectionState", "signalingState", "iceGatheringState"].map( | |
(k) => [k, event.target.connectionState], | |
), | |
); | |
let parts = [targetName, eventName, JSON.stringify(fields)]; | |
if (event.channel) { | |
parts.push("label=" + JSON.stringify(event.channel.label)); | |
} | |
log(parts.join(" ")); | |
}); | |
} | |
for (const [name, peer] of Object.entries({ alice, bob })) { | |
listen(name, peer, "connectionstatechange"); | |
listen(name, peer, "datachannel"); | |
listen(name, peer, "icecandidate"); | |
listen(name, peer, "icecandidateerror"); | |
listen(name, peer, "iceconnectionstatechange"); | |
listen(name, peer, "icegatheringstatechange"); | |
listen(name, peer, "signalingstatechange"); | |
listen(name, peer, "track"); | |
} | |
alice.addEventListener("icecandidate", (event) => { | |
bob.addIceCandidate(event.candidate); | |
}); | |
bob.addEventListener("icecandidate", (event) => { | |
alice.addIceCandidate(event.candidate); | |
}); | |
let ch1; | |
if (options?.offerChannel) { | |
ch1 = alice.createDataChannel("in-band, pre-offer", { id: 7 }); | |
} | |
bob.addEventListener("datachannel", (event) => { | |
const { channel } = event; | |
channel.addEventListener("message", (message) => { | |
const context = `bob.channels[${channel.label}]`; | |
log(`${context} message: ${message.data}`); | |
}); | |
}); | |
const offerConfig = { | |
// Need to have either opened a data channel already, or else provide one | |
// of `offerToReceiveAudio` or `offerToReceiveVideo`; otherwise, nothing | |
// will happen at all. | |
offerToReceiveAudio: 1, | |
}; | |
const offer = await alice.createOffer(offerConfig); | |
await alice.setLocalDescription(offer); | |
await bob.setRemoteDescription(offer); | |
console.log({ offer }); | |
console.log(offer.sdp); | |
const answer = await bob.createAnswer(); | |
await bob.setLocalDescription(answer); | |
await alice.setRemoteDescription(answer); | |
console.log({ answer }); | |
Object.assign(globalThis, { alice, bob, offer, answer, ch1 }); | |
} | |
go.addEventListener("click", () => { | |
sink.innerHTML = ""; // clear | |
const offerChannel = ckbxChannel.checked; | |
main({ offerChannel }); | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment