Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

122 lines
3.7KB

  1. const VideoConfig = Object.seal({
  2. videoOn: true,
  3. audioOn: true,
  4. get video() {
  5. return VideoConfig.videoOn && {width: {ideal: 320}, facingMode: 'user', frameRate: 26}
  6. },
  7. get audio() {
  8. return VideoConfig.audioOn
  9. },
  10. toggle: (property) => () => {
  11. VideoConfig[property] = !VideoConfig[property]
  12. VideoSelf.update()
  13. }
  14. })
  15. const VideoSelf = {
  16. update() {
  17. const stream = document.querySelector('video.self').srcObject
  18. stream.getTracks().forEach(track => {
  19. track.stop()
  20. stream.removeTrack(track)
  21. delete track
  22. })
  23. if(VideoConfig.videoOn || VideoConfig.audioOn) {
  24. navigator.mediaDevices
  25. .getUserMedia(VideoConfig)
  26. .then(s => s.getTracks().forEach(t => stream.addTrack(t)))
  27. .catch(e => console.error(e))
  28. }
  29. },
  30. setUp: ({dom}) => {
  31. dom.playsinline = true
  32. dom.autoplay = true
  33. dom.muted = true
  34. dom.srcObject = new MediaStream()
  35. VideoSelf.update()
  36. },
  37. view({attrs: {username}}) {
  38. const styleOuter = {
  39. position: 'relative',
  40. display: 'block',
  41. backgroundColor: 'black',
  42. color: 'white',
  43. overflow: 'hidden',
  44. }
  45. const styleInner = {
  46. objectFit: 'cover',
  47. width: '100%',
  48. height: '100%',
  49. transform: 'scaleX(-1)',
  50. }
  51. return m('.video-container', {style: styleOuter},
  52. m('.video-info', {style: {position: 'absolute', zIndex: 999}},
  53. m('span', {style: {padding: '5px'}}, username),
  54. ),
  55. m('video.self', {style: styleInner, oncreate: this.setUp}),
  56. )
  57. },
  58. }
  59. const VideoOther = {
  60. setUp: ({dom}) => {
  61. dom.playsinline = true
  62. dom.autoplay = true
  63. dom.srcObject = new MediaStream()
  64. },
  65. view({attrs: {username}}) {
  66. const styleOuter = {
  67. position: 'relative',
  68. display: 'block',
  69. backgroundColor: 'black',
  70. color: 'white',
  71. overflow: 'hidden',
  72. }
  73. const styleInner = {
  74. objectFit: 'cover',
  75. width: '100%',
  76. height: '100%',
  77. transform: 'scaleX(-1)',
  78. }
  79. return m('.video-container', {style: styleOuter},
  80. m('.video-info', {style: {position: 'absolute', zIndex: 999}},
  81. m('span', {style: {padding: '5px'}}, username),
  82. ),
  83. m('video.self', {style: styleInner, oncreate: this.setUp}),
  84. )
  85. },
  86. }
  87. const StreamContainer = {
  88. getColumns() {
  89. const n = State.online.length
  90. if(n > 4) return '1fr 1fr 1fr'
  91. if(n > 1) return '1fr 1fr'
  92. return '1fr'
  93. },
  94. getRows() {
  95. const n = State.online.length
  96. if(n > 6) return '1fr 1fr 1fr'
  97. if(n > 2) return '1fr 1fr'
  98. return '1fr'
  99. },
  100. view() {
  101. const style = {
  102. display: 'grid',
  103. padding: '3px',
  104. gridGap: '3px',
  105. height: '80vh',
  106. gridTemplateColumns: StreamContainer.getColumns(),
  107. gridTemplateRows: StreamContainer.getRows(),
  108. }
  109. return m('div',
  110. m('.video-controls',
  111. m('button', {onclick: VideoConfig.toggle('videoOn')}, 'video'),
  112. m('button', {onclick: VideoConfig.toggle('audioOn')}, 'audio'),
  113. ),
  114. m('.videos', {style},
  115. m(VideoSelf, {username: State.username}),
  116. State.online.filter(username => username != State.username)
  117. .map(username => m(VideoOther, {username}))
  118. ),
  119. )
  120. },
  121. }