Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Please excuse the functional fluff
- const tap = fn => v => (fn(v), v)
- const log = key => (...args) => console.log(key, ...args)
- const pipe = (...fs) => v => fs.reduce((v, fn) => fn(v), v)
- const prop = k => v => v[k]
- const at = (...ks) => v => ks.reduce((a, b) => prop(b)(a), v)
- const error = when => err => console.error(when, err)
- const connection = (name) => {
- const pc = new RTCPeerConnection({
- iceServers: [{ url: 'stun:stun.l.google.com:19302' }]
- }, null)
- pc.onsignalingstatechange = pipe(
- at('target', 'signalingState'),
- log(name + ' signal state change')
- )
- pc.ondatachannel = e => {
- log(name + ' received data channel')
- e.channel.onerror = error(name + ' channel')
- e.channel.onmessage = log(name + ' channel message')
- e.channel.onopen = log(name + ' channel open')
- e.channel.onclose = log(name + ' channel close')
- }
- const channel = pc.createDataChannel(name, null)
- channel.onerror = error(name + ' channel')
- channel.onmessage = log(name + ' channel message')
- channel.onopen = () => channel.send('hello from ' + name)
- channel.onclose = log(name + ' channel close')
- const ice = []
- const client = {
- name: name,
- pc: pc,
- channel: channel,
- ice: ice,
- remote: null,
- }
- pc.onicecandidate = e => {
- if (!e || !e.candidate) return
- log(name + ' ice candidate')(e.candidate)
- ice.push(e.candidate)
- if (!client.remote) return
- addIce(client.remote)(e.candidate)
- .then(log(name + ' added ice'))
- .catch(error(name + ' ice failed'))
- }
- return client
- }
- const addIce = client => ice => {
- log(client.name + ' added ice')(ice)
- return client.pc.addIceCandidate(new RTCIceCandidate(ice))
- }
- const swapIce = (a, b) => () => {
- a.remote = b
- b.remote = a
- return Promise.all([
- ...a.ice.map(addIce(b)),
- ...b.ice.map(addIce(a))
- ])
- }
- const clientA = connection('Client A')
- const clientB = connection('Client B')
- clientA.pc.createOffer()
- .then(tap(log('offer')))
- .then(tap(offer => clientA.pc.setLocalDescription(offer)))
- // new RTCSessionDescription isn't required, but would be if the offer was being deserialized from json for example.
- .then(offer => clientB.pc.setRemoteDescription(new RTCSessionDescription(offer)))
- .then(() => clientB.pc.createAnswer())
- .then(tap(log('answer')))
- .then(tap(answer => clientB.pc.setLocalDescription(answer)))
- .then(answer => clientA.pc.setRemoteDescription(new RTCSessionDescription(answer)))
- .then(swapIce(clientA, clientB))
- .then(() => log('connected')(clientA, clientB))
- .then(() => window.clients = { a: clientA, b: clientB })
- .catch(error('connection failed'))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement