Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

134 lines
4.2KB

  1. addEventListener('rpc-new', ({detail}) => {
  2. const {uid, kind, target} = detail.value
  3. if(kind === 'video') {
  4. const sender = VideoShare.streams[State.username]
  5. const receiver = VideoShare.resetStream(target)
  6. signal({kind: 'rpc-setup', value: {uid, sender, receiver}})
  7. }
  8. })
  9. const Video = {
  10. view({attrs: {username}}) {
  11. const styleVideo = {
  12. objectFit: Settings.get('blackBars') ? 'contain' : 'cover',
  13. width: '100%',
  14. height: '100%',
  15. transform: username === State.username ? 'scaleX(-1)' : 'scaleX(1)',
  16. }
  17. return m('.video-container',
  18. m('.video-info',
  19. m(`.label-${username}`, username),
  20. ),
  21. m('video[playsinline][autoplay]', {
  22. style: styleVideo,
  23. oncreate: ({dom}) => {
  24. dom.muted = username === State.username
  25. dom.srcObject = VideoShare.streams[username]
  26. },
  27. onupdate: ({dom}) => {
  28. if(dom.srcObject !== VideoShare.streams[username]) {
  29. dom.srcObject = VideoShare.streams[username]
  30. }
  31. },
  32. onremove: () => VideoShare.resetStream(username),
  33. }),
  34. )
  35. },
  36. }
  37. const Toggle = {
  38. view({attrs: {key, label}}) {
  39. const onclick = () => {
  40. VideoShare[key] = !VideoShare[key]
  41. VideoShare.getStream()
  42. }
  43. const checked = VideoShare[key]
  44. return m('label.styled',
  45. m('input[type=checkbox]', {onclick, checked}),
  46. label,
  47. )
  48. },
  49. }
  50. const VideoShareConfig = {
  51. get video() {
  52. return VideoShare.videoOn
  53. && State.online.length < 10
  54. && params.get('v') !== '0'
  55. && {width: {ideal: 320}, facingMode: 'user', frameRate: 26}
  56. },
  57. get audio() {
  58. return VideoShare.audioOn
  59. && params.get('a') !== '0'
  60. },
  61. view() {
  62. return [
  63. m(Toggle, {key: 'videoOn', label: 'video'}),
  64. m(Toggle, {key: 'audioOn', label: 'audio'}),
  65. ]
  66. },
  67. }
  68. const VideoShare = {
  69. videoOn: true,
  70. audioOn: true,
  71. streams: {},
  72. oncreate(e) {
  73. VideoShare.getStream()
  74. },
  75. resetStream(target) {
  76. if(VideoShare.streams[target]) {
  77. VideoShare.streams[target].getTracks().forEach(tr => tr.stop())
  78. }
  79. VideoShare.streams[target] = new MediaStream()
  80. return VideoShare.streams[target]
  81. },
  82. async getStream() {
  83. VideoShare.resetStream(State.username)
  84. await navigator.mediaDevices.getUserMedia(VideoShareConfig)
  85. .catch(error => console.error(error))
  86. .then(stream => {VideoShare.streams[State.username] = stream})
  87. m.redraw()
  88. State.others.forEach(target => {
  89. signal({kind: 'rpc-needed', value: {kind: 'video', target}})
  90. })
  91. },
  92. reLayout({dom}) {
  93. const COUNT = dom.children.length || 1
  94. let [fnx, fny] = [
  95. Math.floor((1 + Math.sqrt(4 * COUNT - 3)) / 2),
  96. Math.ceil(Math.sqrt(COUNT)),
  97. ]
  98. let max = 0
  99. for([nx, ny] of [[1, COUNT], [fnx, fny], [COUNT, 1]]) {
  100. let h = dom.clientHeight / ny
  101. let w = dom.clientWidth / nx
  102. if(w > h) {
  103. w = Math.min(w, h * 4/3)
  104. }
  105. else {
  106. h = Math.min(h, w * 3/4)
  107. }
  108. if(h * w > max) {
  109. max = h * w
  110. fnx = nx
  111. fny = ny
  112. }
  113. }
  114. dom.style['grid-template-columns'] = Array(fnx).fill('1fr').join(' ')
  115. dom.style['grid-template-rows'] = Array(fny).fill('1fr').join(' ')
  116. },
  117. view() {
  118. const style = {
  119. backgroundColor: 'black',
  120. overflow: 'hidden',
  121. display: 'grid',
  122. }
  123. const oncreate = VideoShare.reLayout
  124. const onupdate = VideoShare.reLayout
  125. return m('.videos', {style, oncreate, onupdate},
  126. State.online.map(username => m(Video, {key: username, username}))
  127. )
  128. },
  129. }
  130. addEventListener('load', () => {
  131. Headers.push([VideoShareConfig])
  132. Apps.push([VideoShare, {key: 'stream-container'}])
  133. })