【前端】智能库房综合管理系统前端项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
5.8 KiB

  1. 'use strict'
  2. // import TS from '../demuxer/ts'
  3. export default class WSSource {
  4. timer = {
  5. heartbeat: null,
  6. streamInterrupt: null
  7. }
  8. reconnectInterval
  9. shouldAttemptReconnect
  10. progress = 0
  11. reconnectTimeoutId = 0
  12. reconnectCount = 0
  13. callbacks = { connect: [], data: [] }
  14. streaming = true
  15. completed = false
  16. established = false
  17. isPaused = false
  18. isStreamInterrupt = false
  19. /** @type {TS} */
  20. destination
  21. /** @type {WebSocket} */
  22. socket
  23. /** @type {string} */
  24. url
  25. onEstablishedCallback
  26. onCompletedCallback
  27. onClosedCallback
  28. onStreamInterruptCallback
  29. onConnectedCallback
  30. onStreamTimeoutFirstReceiveCallback
  31. /**
  32. *
  33. * @param {string} url
  34. * @param {import('../../types').PlayerOptions} options
  35. */
  36. constructor(url, options) {
  37. this.url = url
  38. this.options = options
  39. this.reconnectInterval =
  40. options.reconnectInterval !== undefined ? options.reconnectInterval : 5
  41. this.shouldAttemptReconnect = !!this.reconnectInterval
  42. this.onEstablishedCallback = options.onSourceEstablished
  43. this.onCompletedCallback = options.onSourceCompleted // Never used
  44. this.onClosedCallback = options.onSourceClosed
  45. this.onConnectedCallback = options.onSourceConnected
  46. this.onStreamInterruptCallback = options.onSourceStreamInterrupt
  47. this.onStreamContinueCallback = options.onSourceStreamContinue
  48. }
  49. connect(destination) {
  50. this.destination = destination
  51. }
  52. changeUrl(url = '') {
  53. clearTimeout(this.timer.streamInterrupt)
  54. if (typeof url === 'string' && url !== '') {
  55. if (this.url !== url) {
  56. this.destroy()
  57. this.url = url
  58. this.start()
  59. }
  60. } else {
  61. this.destroy()
  62. this.url = ''
  63. }
  64. }
  65. reload() {
  66. this.destroy()
  67. this.start()
  68. }
  69. destroy() {
  70. clearTimeout(this.reconnectTimeoutId)
  71. this.reconnectTimeoutId = 0
  72. this.shouldAttemptReconnect = false
  73. this.socket?.close()
  74. if (this.socket) {
  75. this.socket.onmessage = null
  76. this.socket.onopen = null
  77. this.socket.onerror = null
  78. this.socket.onclose = null
  79. this.socket.onmessage = null
  80. this.socket = null
  81. }
  82. }
  83. start() {
  84. this.reconnectTimeoutId = 0
  85. this.reconnectCount = 0
  86. this.shouldAttemptReconnect = !!this.reconnectInterval
  87. this.progress = 0
  88. this.established = false
  89. this.isPaused = false
  90. this.wsConnect()
  91. }
  92. wsConnect() {
  93. if (!this.url) return
  94. // 连java的websocket时,第二个参数要么传值,要么不传值,不能传null,否则会一直出现连接失败的问题
  95. try {
  96. this.socket = new WebSocket(this.url, this.options?.protocols)
  97. this.socket.binaryType = 'arraybuffer'
  98. this.socket.onmessage = this.onMessage.bind(this)
  99. this.socket.onopen = this.onOpen.bind(this)
  100. this.socket.onerror = this.onError.bind(this)
  101. this.socket.onclose = this.onClose.bind(this)
  102. } catch (error) {
  103. console.error('websocket connect error: ', error)
  104. }
  105. }
  106. pause() {
  107. if (!this.isPaused) {
  108. clearTimeout(this.timer.streamInterrupt)
  109. this.isPaused = true
  110. if (this.socket?.readyState === WebSocket.OPEN) {
  111. this.socket.onmessage = null
  112. }
  113. }
  114. // if (this.reconnectTimeoutId) {
  115. // clearTimeout(this.reconnectTimeoutId)
  116. // this.reconnectTimeoutId = null
  117. // }
  118. }
  119. continue() {
  120. // Nothing to do here
  121. if (this.isPaused) {
  122. this.isPaused = false
  123. if (this.socket == null) {
  124. this.start()
  125. } else if (this.socket?.readyState === WebSocket.OPEN) {
  126. this.socket.onmessage = this.onMessage.bind(this)
  127. this.startStreamTimeoutTimer()
  128. }
  129. }
  130. }
  131. onOpen() {
  132. this.progress = 1
  133. this.reconnectTimeoutId = 0
  134. this.reconnectCount = 0
  135. this.isOpened = true
  136. if (this.onConnectedCallback) {
  137. this.onConnectedCallback(this)
  138. }
  139. this.startStreamTimeoutTimer()
  140. }
  141. onError(err) {
  142. console.error(err)
  143. }
  144. onClose() {
  145. this.established = false
  146. if (this.progress >= 1) {
  147. // progress>=1,表示已经建立连接后的断开
  148. this.progress = 0
  149. if (this.onClosedCallback) {
  150. this.onClosedCallback(this)
  151. }
  152. clearTimeout(this.reconnectTimeoutId)
  153. this.reconnectTimeoutId = setTimeout(this.start.bind(this), 5000)
  154. return
  155. }
  156. if (this.shouldAttemptReconnect && this.reconnectCount < 10) {
  157. // 最多重连10次
  158. clearTimeout(this.reconnectTimeoutId)
  159. this.reconnectTimeoutId = setTimeout(
  160. this.wsConnect.bind(this),
  161. this.reconnectInterval * 1000
  162. )
  163. this.reconnectCount += 1
  164. console.log('websocket 重连次数: ', this.reconnectCount)
  165. }
  166. }
  167. /**
  168. *
  169. * @param {MessageEvent} ev
  170. */
  171. onMessage(ev) {
  172. this.startStreamTimeoutTimer()
  173. try {
  174. if (!this.established) {
  175. this.established = true
  176. this.isStreamInterrupt = false
  177. this.onEstablishedCallback?.(this)
  178. console.log(ev)
  179. } else if (this.isStreamInterrupt) {
  180. this.isStreamInterrupt = false
  181. this.onStreamContinueCallback?.(this)
  182. }
  183. if (this.destination) {
  184. this.destination.write(ev.data)
  185. }
  186. } catch (error) {
  187. if (error.message?.indexOf('memory access out of bounds') > -1) {
  188. this.reload()
  189. } else {
  190. console.error(error)
  191. }
  192. }
  193. if (this.recorder) {
  194. try {
  195. this.recorder.write?.(ev.data)
  196. } catch (error) {
  197. this.recorder = null
  198. }
  199. }
  200. }
  201. startStreamTimeoutTimer() {
  202. if (this.timer.streamInterrupt) {
  203. clearTimeout(this.timer.streamInterrupt)
  204. }
  205. this.timer.streamInterrupt = setTimeout(() => {
  206. console.warn('[JSMpeg]: 等待视频流超时')
  207. this.timer.streamInterrupt = null
  208. this.isStreamInterrupt = true
  209. if (this.onStreamInterruptCallback) {
  210. this.onStreamInterruptCallback()
  211. }
  212. }, 5000)
  213. }
  214. }