SRSWebRtcPlayer.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * srs webrtc palyer
  3. * auth Caner
  4. */
  5. import MD5 from 'js-md5'
  6. class WebRtcPlayer {
  7. private Peer: any
  8. private TIMER: any
  9. constructor(option: { HOST: string; TOKEN: string; UUID: string; PROFILE: number; PORT: number; DOM: HTMLVideoElement, starttime?: number; endtime?: number }) {
  10. this.initWebRtc(option.HOST, option.PORT, option.TOKEN, option.UUID, option.PROFILE, option.DOM, option.starttime, option.endtime).then(res => {
  11. this.Peer = res
  12. }).catch(() => {
  13. this.Peer = null
  14. })
  15. }
  16. /**
  17. * 同步睡眠
  18. * @param ms 毫秒
  19. * @returns
  20. */
  21. private sleep(ms: number) { return new Promise((resolve) => { setTimeout(resolve, ms) }) }
  22. /**
  23. * 初始化webrtc
  24. * @param HOST 媒体服务器地址
  25. * @param TOKEN 用户token
  26. * @param UUID 摄像头uuid
  27. * @param PROFILE 码流
  28. * @param DOM video节点
  29. * @returns
  30. */
  31. private async initWebRtc(HOST: string, PORT: number, TOKEN: string, UUID: string, PROFILE: number, DOM: HTMLVideoElement, STIME?: number, ETIME?: number) {
  32. try {
  33. const Peer = new RTCPeerConnection() as any
  34. Peer.addTransceiver('video', { direction: 'recvonly' })
  35. const offer = await Peer.createOffer()
  36. await Peer.setLocalDescription(offer)
  37. // 监听视频=播放
  38. Peer.ontrack = (event: Event | any) => {
  39. if (DOM) {
  40. const { streams } = event
  41. DOM.srcObject = streams[0]
  42. console.log('track', event)
  43. }
  44. }
  45. Peer.oniceconnectionstatechange = () => {
  46. const state = Peer.iceConnectionState
  47. console.log('ICE状态', state)
  48. }
  49. Peer.onicegatheringstatechange = () => {
  50. console.log('GatheringState: ', Peer.iceGatheringState)
  51. }
  52. Peer.onconnectionstatechange = () => {
  53. const state = Peer.connectionState
  54. console.log('连接状态', state)
  55. }
  56. // SDP SRS params
  57. const STREAM = STIME && ETIME ? MD5(UUID + PROFILE + Math.floor(new Date().getTime() * Math.random() * 100)) : UUID + PROFILE
  58. const params = STIME && ETIME ? {
  59. api: `http://${HOST}:${PORT}/rtc/v1/play/?token=${TOKEN}&uuid=${UUID}&stream=${UUID + PROFILE}&profile=${PROFILE}`,
  60. clientip: null,
  61. sdp: offer.sdp,
  62. streamurl: `webrtc://${HOST}/replay/${STREAM}?token=${TOKEN}&starttime=${STIME}&endtime=${ETIME}&uuid=${UUID}&stream=${STREAM}&profile=${PROFILE}`,
  63. tid: Number(Math.floor(new Date().getTime() * Math.random() * 100)).toString(16).slice(0, 7)
  64. } : {
  65. api: `http://${HOST}:${PORT}/rtc/v1/play/?token=${TOKEN}&uuid=${UUID}&stream=${UUID + PROFILE}&profile=${PROFILE}`,
  66. clientip: null,
  67. sdp: offer.sdp,
  68. streamurl: `webrtc://${HOST}/live/${STREAM}?token=${TOKEN}&uuid=${UUID}&stream=${STREAM}&profile=${PROFILE}`,
  69. tid: Number(Math.floor(new Date().getTime() * Math.random() * 100)).toString(16).slice(0, 7)
  70. }
  71. console.log('params', params)
  72. // 发送offer
  73. const res = await window.fetch(params.api, {
  74. method: 'POST',
  75. headers: { 'Content-Type': 'application/json' },
  76. body: JSON.stringify(params)
  77. })
  78. // 接收 answer
  79. const { sdp } = await res.json()
  80. if (sdp) await Peer.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp }))
  81. return Peer
  82. } catch (error) {
  83. console.warn('webRtcInit:', error);
  84. return null
  85. }
  86. }
  87. /**
  88. * 云台控制
  89. * @param URL
  90. * @param UUID
  91. * @param TOKEN
  92. * @param command 指令:数字键盘1-9去掉5,10:焦距放大,11:焦距缩小 12:亮度,13:色彩饱和度,14:对比度,15:清晰度
  93. * @param number [云台速度|焦距参数|色彩饱和度]等值 亮度值 0-100
  94. * @returns
  95. */
  96. public contrl(URL: string, UUID: string, TOKEN: string, command: number, number: number) {
  97. if (!UUID) return
  98. if (this.TIMER !== null) clearTimeout(this.TIMER)
  99. this.TIMER = setTimeout(() => {
  100. window.fetch(URL, {
  101. method: 'POST',
  102. headers: { 'Content-Type': 'application/json' },
  103. body: JSON.stringify({
  104. code: 'cloudcontrol.control',
  105. token: TOKEN,
  106. body: {
  107. uuid: UUID,
  108. command,
  109. number
  110. }
  111. })
  112. })
  113. }, 500);
  114. }
  115. /**
  116. * 视频截图
  117. * @param option
  118. * @returns base64
  119. */
  120. public capPhoto(DOM: HTMLVideoElement) {
  121. const canvas = document.createElement('canvas')
  122. canvas.width = DOM.offsetWidth
  123. canvas.height = DOM.offsetHeight
  124. const context = canvas.getContext('2d')
  125. context?.drawImage(DOM, 0, 0, DOM.offsetWidth, DOM.offsetHeight)
  126. const base64 = canvas.toDataURL('image/jpg')
  127. return base64
  128. }
  129. /**
  130. * 视频录像
  131. * @param DOM
  132. * @param TIME ms
  133. * @returns
  134. */
  135. public async capVideo(DOM: HTMLVideoElement, TIME: number) {
  136. const recordedBlobs = [] as any
  137. const MediaStream = DOM['srcObject'] as MediaStream
  138. const mediaRecorder = new MediaRecorder(MediaStream, { mimeType: 'video/webm;codecs=h264' })
  139. mediaRecorder.ondataavailable = (event: { data: { size: number } }) => {
  140. if (event.data && event.data.size > 0) {
  141. recordedBlobs.push(event.data);
  142. }
  143. }
  144. mediaRecorder.start()
  145. await this.sleep(TIME || 1000 * 3)
  146. mediaRecorder.stop()
  147. return await new Promise((resolve, reject) => {
  148. mediaRecorder.onstop = () => {
  149. const blob = new Blob(recordedBlobs, { type: 'video/mp4' });
  150. resolve(blob)
  151. }
  152. mediaRecorder.onerror = reject
  153. })
  154. }
  155. /**
  156. * 倍数播放
  157. * @param URL
  158. * @param TOKEN
  159. * @param uuid
  160. * @param stream
  161. */
  162. public async speedPlay(URL: string, TOKEN: string, uuid: string, stream: string) {
  163. window.fetch(URL, {
  164. method: 'POST',
  165. headers: { 'Content-Type': 'application/json' },
  166. body: JSON.stringify({
  167. code: 'replay.play',
  168. token: TOKEN,
  169. body: {
  170. uuid: uuid, // 摄像头id
  171. stream: stream, // 播放视频生成的stream
  172. startTime: 1667836800000, // 播放开始时间,默认0,即当前时间
  173. endTime: 1667923199000, // 播放结束时间。默认0,即一直播放,不主动结束
  174. speed: 4.0 // 速度 0.25、0.5、1、2、4。
  175. }
  176. })
  177. })
  178. }
  179. /**
  180. * 关闭webrtc
  181. */
  182. public async close() {
  183. if (this.Peer) this.Peer.close()
  184. if (this.TIMER) clearTimeout(this.TIMER)
  185. }
  186. }
  187. export default WebRtcPlayer