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

rpc.js 4.1KB

4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
4年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. })