RecordPlayer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import PlayerItem from './PlayerItem.js'
  2. import {Spinner} from './spin.js'
  3. import CONSTANT from './CONSTANT.js'
  4. const PlayerControl = window.PlayerControl;
  5. /* ---------------- RecordPlayerItem ---------------- */
  6. class RecordPlayerItem extends PlayerItem {
  7. /**
  8. * @param {*} opt.wrapperDomId 父级id
  9. * @param {*} opt.index 索引
  10. */
  11. constructor(opt) {
  12. super(opt)
  13. this.canvasId = `${this.domId}-recordcanvas`
  14. this.videoId = `${this.domId}-recordVideo`
  15. this.curTimestamp = 0
  16. this.initDom()
  17. this.defaultStatus = $('.default-status', this.$el)
  18. this.error = $('.error', this.$el)
  19. this.controller = $('.player-control', this.$el)
  20. this.progressBar = $('.record-control-bar', this.$el)
  21. this.timeInfo = $('.time-info', this.$el)
  22. this.initMouseEvent()
  23. /**
  24. * this.state 当前Player状态
  25. * created, ready, playing, pause, stop, closed, error
  26. */
  27. this.setStatus('created')
  28. }
  29. /**
  30. * 播放器模板
  31. */
  32. getTemplate() {
  33. let template = `
  34. <div id="${this.domId}" class="wsplayer-item wsplayer-item-${this.index} ${this.index === 0 ? 'selected' : 'unselected'}">
  35. <canvas id="${this.canvasId}" class="kind-stream-canvas" kind-channel-id="0" width="800" height="600"></canvas>
  36. <video id="${this.videoId}" class="kind-stream-canvas" kind-channel-id="0" muted style="display:none" width="800" height="600"></video>
  37. <div class="default-status">
  38. <img src="./static/WSPlayer/icon/default.png" alt="">
  39. </div>
  40. <div class="player-control top-control-bar">
  41. <span class="stream-info"></span>
  42. <div class="opt-icons">
  43. <div class="opt-icon audio-icon off"></div>
  44. <div class="opt-icon capture-icon"></div>
  45. <div class="opt-icon close-icon"></div>
  46. </div>
  47. </div>
  48. <div class="player-control record-control-bar">
  49. <div class="wsplayer-progress-bar">
  50. <div class="progress-bar_background"></div>
  51. <div class="progress-bar_hover_light"></div>
  52. <div class="progress-bar_light"></div>
  53. </div>
  54. <div class="record-control-left">
  55. <div class="opt-icon play-ctrl-btn play-icon play"></div>
  56. <div class="time-info"></div>/<div class="time-long"></div>
  57. </div>
  58. <div class="record-control-right">
  59. <div class="opt-icon close-icon"></div>
  60. </div>
  61. </div>
  62. <div class="error">
  63. <div class="error-message"></div>
  64. </div>
  65. <div class="play-pause-wrapper">
  66. <div class="play-ctrl-btn center-play-icon"></div>
  67. </div>
  68. </div>
  69. `
  70. return template
  71. }
  72. /**
  73. * 事件监听
  74. */
  75. initMouseEvent() {
  76. super.initMouseEvent()
  77. this.hideTimer = null
  78. this.$el.on('mouseenter mousemove', (evt) => {
  79. if (this.status === 'playing') {
  80. this.hideTimer && clearTimeout(this.hideTimer)
  81. this.setDomVisible($('.player-control', $(`#${this.domId}`)), true)
  82. } else if (this.status === 'ready') {
  83. this.setDomVisible(this.progressBar, true)
  84. }
  85. })
  86. this.$el.on('mouseleave', (evt) => {
  87. if (this.status === 'pause') {
  88. return
  89. }
  90. this.hideTimer = setTimeout(() => {
  91. this.setDomVisible($('.player-control', $(`#${this.domId}`)), false)
  92. }, 300)
  93. })
  94. $('.wsplayer-progress-bar', this.$el).on('mousemove', (evt) => {
  95. $('.progress-bar_hover_light', this.$el).css({
  96. width: evt.offsetX + 'px'
  97. })
  98. })
  99. $('.wsplayer-progress-bar', this.$el).on('mouseleave', (evt) => {
  100. $('.progress-bar_hover_light', this.$el).css({
  101. width: 0
  102. })
  103. })
  104. $('.play-ctrl-btn', this.$el).click((evt) => {
  105. if (this.status === 'playing') {
  106. // 正在播放,暂停播放
  107. this.pause()
  108. $('.play-icon', this.$el).removeClass('play').addClass('pause')
  109. } else {
  110. // 暂停播放状态,打开
  111. this.play()
  112. $('.play-icon', this.$el).removeClass('pause').addClass('play')
  113. }
  114. })
  115. }
  116. /**
  117. * 设置状态,同时控制组件显示
  118. * created, ready, playing, pause, stop, closed, error
  119. */
  120. setStatus(status, msg) {
  121. this.status = status
  122. switch (this.status) {
  123. case 'created':
  124. case 'closed':
  125. this.setDomVisible(this.defaultStatus, true)
  126. this.setDomVisible(this.error, false)
  127. this.setDomVisible(this.controller, false)
  128. $('.audio-icon', this.$el).removeClass('on').addClass('off')
  129. break;
  130. case 'ready':
  131. this.setDomVisible(this.defaultStatus, false)
  132. this.setDomVisible(this.error, false)
  133. break;
  134. case 'playing':
  135. this.setDomVisible(this.defaultStatus, false)
  136. this.setDomVisible(this.error, false)
  137. this.setDomVisible($('.play-pause-wrapper', this.$el), false)
  138. break;
  139. case 'pause':
  140. this.setDomVisible(this.defaultStatus, false)
  141. this.setDomVisible(this.error, false)
  142. this.setDomVisible(this.controller, false)
  143. this.setDomVisible($('.play-pause-wrapper', this.$el), true)
  144. break;
  145. case 'error':
  146. this.setDomVisible(this.defaultStatus, false)
  147. $('.error-message', this.$el).text(CONSTANT.errorInfo[msg.errorCode] ? CONSTANT.errorInfo[msg.errorCode] : CONSTANT.errorInfo['defaultErrorMsg'])
  148. this.setDomVisible(this.error, true)
  149. break;
  150. default:
  151. break;
  152. }
  153. }
  154. /**
  155. * 播放录像
  156. * @param {String} options.decodeMode 可选参数 video | canvas
  157. * @param {String} options.wsURL 可选参数
  158. * @param {Function} options.recordSource 2=设备,3=中心
  159. * recordSource == 2 设备录像,按照时间方式播放
  160. * @param {String} options.rtspURL String
  161. * @param {Number | String} options.startTime 开始时间 时间戳或者'2021-09-18 15:40:00'格式的时间字符串
  162. * @param {Number | String} options.endTime 结束时间 时间戳或者'2021-09-18 15:40:00'格式的时间字符串
  163. * @param {Function} options.reload 重新拉流的回调函数,用于时间回放,返回promise
  164. * reload(newStarTime, endTime).then(newRtspUrl => { play continue})
  165. * recordSource == 3 中心录像,按照文件方式播放
  166. * @param {Function} options.RecordFiles 文件列表
  167. * @param {Function} options.getRtsp 文件列表
  168. * getRtsp(file).then(newRtspUrl => { play continue})
  169. */
  170. init(options) {
  171. if (this.player) {
  172. this.player.close()
  173. }
  174. if (this.spinner) {
  175. this.spinner.stop()
  176. }
  177. this.spinner = new Spinner({
  178. color: '#ffffff'
  179. }).spin(this.$el[0])
  180. let self = this
  181. this.player = new PlayerControl(Object.assign({
  182. wsURL: this.wsPlayer.wsURL
  183. }, options))
  184. this.options = options
  185. this.timeLong = options.endTime - options.startTime
  186. let seconds = this.timeLong % 60
  187. let minutes = (parseInt(this.timeLong / 60)) % 60
  188. let hours = (parseInt(this.timeLong / 3600)) % 60
  189. this.timeLongStr = `${hours > 0 ? hours + ':' : ''}${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`
  190. $('.time-long', this.$el).text(this.timeLongStr)
  191. this.setStatus('ready')
  192. this.player.on('ResolutionChanged', function (e) {
  193. console.log(e)
  194. });
  195. this.player.on('PlayStart', function (e) {
  196. console.log(e)
  197. self.setStatus('playing')
  198. });
  199. this.player.on('DecodeStart', function (e) {
  200. console.log('DecodeStart', e)
  201. self.spinner.stop()
  202. if (e.decodeMode === 'video') {
  203. self.videoElem.style.display = '';
  204. self.canvasElem.style.display = 'none';
  205. } else {
  206. self.videoElem.style.display = 'none';
  207. self.canvasElem.style.display = '';
  208. }
  209. $('.stream-info', $(`#${self.domId}`)).text(`${e.encodeMode}, ${e.width}*${e.height}`)
  210. });
  211. this.player.on('UpdateCanvas', function (e) {
  212. if (self.firstTime === 0) {
  213. // 使用请求时间段的时间作为
  214. self.firstTime = self.options.startTime
  215. // self.firstTime = e.timestamp;//获取录像文件的第一帧的时间戳
  216. }
  217. // 一秒数据帧timestamp相同,此判断可以减少计算
  218. if (e.timestamp > self.curTimestamp) {
  219. self.curTimestamp = e.timestamp
  220. let playtime = e.timestamp - self.firstTime
  221. playtime = playtime < 0 ? 0 : playtime;
  222. let seconds = playtime % 60
  223. let minutes = (parseInt(playtime / 60)) % 60
  224. let hours = (parseInt(playtime / 3600)) % 60
  225. let timeString = `${hours > 0 ? hours + ':' : ''}${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}`
  226. self.timeInfo.text(timeString)
  227. $('.progress-bar_light', self.$el).css({
  228. width: `${playtime * 100 / self.timeLong}%`
  229. })
  230. // ctrlBar.updateCanvas(e)
  231. // console.log('UpdateCanvas: ' + JSON.stringify(e))
  232. }
  233. });
  234. this.player.on('GetFrameRate', function (e) {
  235. console.log('GetFrameRate: ', e)
  236. });
  237. this.player.on('FrameTypeChange', function (e) {
  238. console.log('编码模式改变 FrameTypeChange: ', e)
  239. });
  240. this.player.on('Error', function (e) {
  241. self.spinner.stop()
  242. console.log('Error: ' + JSON.stringify(e))
  243. self.setStatus('error', e)
  244. });
  245. this.player.on('MSEResolutionChanged', function (e) {
  246. console.log('分辨率改变 MSEResolutionChanged: ', e)
  247. });
  248. this.player.on('audioChange', function (e) {
  249. console.log('音频编码改变 audioChange: ', e)
  250. });
  251. this.player.on('IvsDraw', function (e) {
  252. console.log('IvsDraw: ', e)
  253. });
  254. this.player.on('WorkerReady', function () {
  255. console.log('WorkerReady')
  256. self.player.connect();
  257. })
  258. this.player.on('FileOver', function (e) {
  259. console.log('回放播放完成 FileOver: ', e)
  260. });
  261. this.player.on('Waiting', function (e) {
  262. console.log('Waiting: ', e)
  263. });
  264. this.player.on('UpdateTime', function (e) {
  265. console.log('UpdateTime: ', e)
  266. });
  267. this.player.on('GetFirstFrame', function(){
  268. console.log('收到第一帧');
  269. });
  270. this.player.init(this.canvasElem, this.videoElem);
  271. }
  272. /**
  273. * 倍速播放
  274. * @param {Number} speed 倍速
  275. */
  276. playSpeed(speed) {
  277. this.player.playSpeed(speed)
  278. }
  279. /**
  280. * 时间跳转
  281. * @param {*} time
  282. */
  283. playByTime(time) {
  284. this.player.playByTime(time)
  285. }
  286. }
  287. export default RecordPlayerItem