選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

144 行
4.1KB

  1. const allRPCs = {}
  2. // ensure no data gets lost when sent through the wire
  3. function K(obj) {
  4. return JSON.parse(JSON.stringify(obj))
  5. }
  6. // https://stackoverflow.com/a/2117523
  7. function uuidv4() {
  8. return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
  9. (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  10. )
  11. }
  12. function rpcStatusCheck() {
  13. console.table(Object.values(allRPCs).map(({target, kind, rpc}) =>
  14. ({target, kind, status: rpc.iceConnectionState})
  15. ))
  16. }
  17. function rpcCleanUp(kind, target) {
  18. for([uid, info] of Object.entries(allRPCs)) {
  19. const sameKind = info.kind === kind
  20. const sameTarget = info.target === target
  21. const shouldClose = (false
  22. || !State.online.includes(info.target)
  23. || (sameKind && kind === 'screen')
  24. || (sameKind && kind === 'video' && sameTarget)
  25. )
  26. if(shouldClose) {
  27. info.rpc.close()
  28. delete allRPCs[uid]
  29. console.log(`${info.target} ${info.kind} closed`)
  30. }
  31. }
  32. }
  33. addEventListener('rpc-needed', ({detail}) => {
  34. const {kind, target} = detail.value
  35. const isInitiatedLocally = !detail.source
  36. const uid = detail.value.uid || uuidv4()
  37. const rpc = new RTCPeerConnection(rpcConfig)
  38. rpc.onicecandidate = ({candidate}) => {
  39. if(candidate && candidate.candidate) {
  40. wire({kind: 'ice-candidate', value: {...K(candidate), uid}, target})
  41. }
  42. }
  43. rpc.ontrack = ({streams: [stream]}) => {
  44. stream.getTracks().forEach(track => {
  45. allRPCs[uid].receiver.addTrack(track, stream)
  46. })
  47. }
  48. rpc.oniceconnectionstatechange = (e) => {
  49. console.log(`${target} ${kind} ${rpc.iceConnectionState}`)
  50. }
  51. rpc.onnegotiationneeded = (e) => {
  52. // if(isInitiatedLocally) {
  53. // signal({kind: 'rpc-initiate', value: {uid}})
  54. // }
  55. }
  56. rpcCleanUp(kind, target)
  57. allRPCs[uid] = {kind, target, rpc, isInitiatedLocally}
  58. signal({kind: 'rpc-new', value: {kind, target, uid}})
  59. if(isInitiatedLocally) {
  60. wire({kind: 'rpc-needed', value: {kind, target: State.username, uid}, target})
  61. }
  62. })
  63. addEventListener('rpc-setup', async ({detail}) => {
  64. const {uid, sender, receiver} = detail.value
  65. const {rpc, isInitiatedLocally} = allRPCs[uid]
  66. allRPCs[uid].sender = sender
  67. allRPCs[uid].receiver = receiver
  68. if(sender) {
  69. sender.getTracks().forEach(tr => rpc.addTrack(tr, sender))
  70. }
  71. allRPCs[uid].loadedMedia = true
  72. if(isInitiatedLocally) {
  73. signal({kind: 'rpc-initiate', value: {uid}})
  74. }
  75. else {
  76. signal({kind: 'rpc-respond', value: {uid}})
  77. }
  78. })
  79. addEventListener('rpc-initiate', async({detail}) => {
  80. const {uid} = detail.value
  81. const {rpc, target} = allRPCs[uid]
  82. const localOffer = await rpc.createOffer()
  83. await rpc.setLocalDescription(localOffer)
  84. wire({kind: 'rpc-offer', value: {...K(localOffer), uid}, target})
  85. })
  86. addEventListener('rpc-offer', async ({detail}) => {
  87. const {uid} = detail.value
  88. const {rpc} = allRPCs[uid]
  89. await rpc.setRemoteDescription(detail.value)
  90. allRPCs[uid].hasOffer = true
  91. signal({kind: 'rpc-respond', value: {uid}})
  92. })
  93. addEventListener('rpc-respond', async({detail}) => {
  94. const {uid} = detail.value
  95. const {rpc, target, loadedMedia, hasOffer} = allRPCs[uid]
  96. if(loadedMedia && hasOffer) {
  97. const localAnswer = await rpc.createAnswer()
  98. await rpc.setLocalDescription(localAnswer)
  99. wire({kind: 'rpc-answer', value: {...K(localAnswer), uid}, target})
  100. }
  101. })
  102. addEventListener('rpc-answer', async ({detail}) => {
  103. const {uid} = detail.value
  104. await allRPCs[uid].rpc.setRemoteDescription(detail.value)
  105. })
  106. addEventListener('ice-candidate', async ({detail}) => {
  107. const {uid} = detail.value
  108. await allRPCs[uid].rpc.addIceCandidate(detail.value)
  109. })
  110. addEventListener('leave', () => {
  111. rpcCleanUp()
  112. })
  113. addEventListener('screen-stop', () => {
  114. rpcCleanUp('screen')
  115. })
  116. addEventListener('load', () => {
  117. doNotLog.add('rpc-needed')
  118. doNotLog.add('rpc-offer')
  119. doNotLog.add('rpc-answer')
  120. doNotLog.add('ice-candidate')
  121. })