選択できるのは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年前
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. let shouldClose = false
  22. shouldClose ||= !State.online.includes(info.target)
  23. shouldClose ||= (sameKind && kind === 'screen')
  24. shouldClose ||= (sameKind && kind === 'video' && sameTarget)
  25. if(shouldClose) {
  26. info.rpc.close()
  27. delete allRPCs[uid]
  28. console.log(`${info.target} ${info.kind} closed`)
  29. }
  30. }
  31. }
  32. addEventListener('rpc-needed', ({detail}) => {
  33. const {kind, target} = detail.value
  34. const isInitiatedLocally = !detail.source
  35. const uid = detail.value.uid || uuidv4()
  36. const rpc = new RTCPeerConnection(rpcConfig)
  37. rpc.onicecandidate = ({candidate}) => {
  38. if(candidate && candidate.candidate) {
  39. wire({kind: 'ice-candidate', value: {...K(candidate), uid}, target})
  40. }
  41. }
  42. rpc.ontrack = ({streams: [stream]}) => {
  43. stream.getTracks().forEach(track => {
  44. allRPCs[uid].receiver.addTrack(track, stream)
  45. })
  46. }
  47. rpc.oniceconnectionstatechange = (e) => {
  48. console.log(`${target} ${kind} ${rpc.iceConnectionState}`)
  49. }
  50. rpc.onnegotiationneeded = (e) => {
  51. // if(isInitiatedLocally) {
  52. // signal({kind: 'rpc-initiate', value: {uid}})
  53. // }
  54. }
  55. rpcCleanUp(kind, target)
  56. allRPCs[uid] = {kind, target, rpc, isInitiatedLocally}
  57. signal({kind: 'rpc-new', value: {kind, target, uid}})
  58. if(isInitiatedLocally) {
  59. wire({kind: 'rpc-needed', value: {kind, target: State.username, uid}, target})
  60. }
  61. })
  62. addEventListener('rpc-setup', async ({detail}) => {
  63. const {uid, sender, receiver} = detail.value
  64. const {rpc, isInitiatedLocally} = allRPCs[uid]
  65. allRPCs[uid].sender = sender
  66. allRPCs[uid].receiver = receiver
  67. if(sender) {
  68. sender.getTracks().forEach(tr => rpc.addTrack(tr, sender))
  69. }
  70. allRPCs[uid].loadedMedia = true
  71. if(isInitiatedLocally) {
  72. signal({kind: 'rpc-initiate', value: {uid}})
  73. }
  74. else {
  75. signal({kind: 'rpc-respond', value: {uid}})
  76. }
  77. })
  78. addEventListener('rpc-initiate', async({detail}) => {
  79. const {uid} = detail.value
  80. const {rpc, target} = allRPCs[uid]
  81. const localOffer = await rpc.createOffer()
  82. await rpc.setLocalDescription(localOffer)
  83. wire({kind: 'rpc-offer', value: {...K(localOffer), uid}, target})
  84. })
  85. addEventListener('rpc-offer', async ({detail}) => {
  86. const {uid} = detail.value
  87. const {rpc} = allRPCs[uid]
  88. await rpc.setRemoteDescription(detail.value)
  89. allRPCs[uid].hasOffer = true
  90. signal({kind: 'rpc-respond', value: {uid}})
  91. })
  92. addEventListener('rpc-respond', async({detail}) => {
  93. const {uid} = detail.value
  94. const {rpc, target, loadedMedia, hasOffer} = allRPCs[uid]
  95. if(loadedMedia && hasOffer) {
  96. const localAnswer = await rpc.createAnswer()
  97. await rpc.setLocalDescription(localAnswer)
  98. wire({kind: 'rpc-answer', value: {...K(localAnswer), uid}, target})
  99. }
  100. })
  101. addEventListener('rpc-answer', async ({detail}) => {
  102. const {uid} = detail.value
  103. await allRPCs[uid].rpc.setRemoteDescription(detail.value)
  104. })
  105. addEventListener('ice-candidate', async ({detail}) => {
  106. const {uid} = detail.value
  107. await allRPCs[uid].rpc.addIceCandidate(detail.value)
  108. })
  109. addEventListener('leave', () => {
  110. rpcCleanUp()
  111. })
  112. addEventListener('screen-stop', () => {
  113. rpcCleanUp('screen')
  114. })
  115. addEventListener('load', () => {
  116. doNotLog.add('rpc-needed')
  117. doNotLog.add('rpc-offer')
  118. doNotLog.add('rpc-answer')
  119. doNotLog.add('ice-candidate')
  120. })