Advertisement
Guest User

Untitled

a guest
Jul 26th, 2017
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.58 KB | None | 0 0
  1. // Please excuse the functional fluff
  2. const tap = fn => v => (fn(v), v)
  3. const log = key => (...args) => console.log(key, ...args)
  4. const pipe = (...fs) => v => fs.reduce((v, fn) => fn(v), v)
  5. const prop = k => v => v[k]
  6. const at = (...ks) => v => ks.reduce((a, b) => prop(b)(a), v)
  7. const error = when => err => console.error(when, err)
  8.  
  9. const connection = (name) => {
  10. const pc = new RTCPeerConnection({
  11. iceServers: [{ url: 'stun:stun.l.google.com:19302' }]
  12. }, null)
  13. pc.onsignalingstatechange = pipe(
  14. at('target', 'signalingState'),
  15. log(name + ' signal state change')
  16. )
  17. pc.ondatachannel = e => {
  18. log(name + ' received data channel')
  19. e.channel.onerror = error(name + ' channel')
  20. e.channel.onmessage = log(name + ' channel message')
  21. e.channel.onopen = log(name + ' channel open')
  22. e.channel.onclose = log(name + ' channel close')
  23. }
  24.  
  25. const channel = pc.createDataChannel(name, null)
  26. channel.onerror = error(name + ' channel')
  27. channel.onmessage = log(name + ' channel message')
  28. channel.onopen = () => channel.send('hello from ' + name)
  29. channel.onclose = log(name + ' channel close')
  30.  
  31. const ice = []
  32.  
  33. const client = {
  34. name: name,
  35. pc: pc,
  36. channel: channel,
  37. ice: ice,
  38. remote: null,
  39. }
  40.  
  41. pc.onicecandidate = e => {
  42. if (!e || !e.candidate) return
  43. log(name + ' ice candidate')(e.candidate)
  44. ice.push(e.candidate)
  45. if (!client.remote) return
  46. addIce(client.remote)(e.candidate)
  47. .then(log(name + ' added ice'))
  48. .catch(error(name + ' ice failed'))
  49. }
  50.  
  51. return client
  52. }
  53.  
  54. const addIce = client => ice => {
  55. log(client.name + ' added ice')(ice)
  56. return client.pc.addIceCandidate(new RTCIceCandidate(ice))
  57. }
  58. const swapIce = (a, b) => () => {
  59. a.remote = b
  60. b.remote = a
  61. return Promise.all([
  62. ...a.ice.map(addIce(b)),
  63. ...b.ice.map(addIce(a))
  64. ])
  65. }
  66.  
  67. const clientA = connection('Client A')
  68. const clientB = connection('Client B')
  69.  
  70. clientA.pc.createOffer()
  71. .then(tap(log('offer')))
  72. .then(tap(offer => clientA.pc.setLocalDescription(offer)))
  73. // new RTCSessionDescription isn't required, but would be if the offer was being deserialized from json for example.
  74. .then(offer => clientB.pc.setRemoteDescription(new RTCSessionDescription(offer)))
  75. .then(() => clientB.pc.createAnswer())
  76. .then(tap(log('answer')))
  77. .then(tap(answer => clientB.pc.setLocalDescription(answer)))
  78. .then(answer => clientA.pc.setRemoteDescription(new RTCSessionDescription(answer)))
  79. .then(swapIce(clientA, clientB))
  80. .then(() => log('connected')(clientA, clientB))
  81. .then(() => window.clients = { a: clientA, b: clientB })
  82. .catch(error('connection failed'))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement