index.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <div class="videoBox">
  3. <div class="box">
  4. <div class="list">
  5. <ul>
  6. <li
  7. v-for="(item, index) in cList"
  8. :key="index"
  9. :style="`color: ${index === cIndex ? 'red':''}`"
  10. @click="play(item.uuid,index)"
  11. >
  12. {{ item.name }}
  13. </li>
  14. </ul>
  15. </div>
  16. <video
  17. id="test"
  18. crossorigin="anonymous"
  19. autoplay
  20. playsinline
  21. muted
  22. controls
  23. />
  24. </div>
  25. <div class="contrl">
  26. <n-button>
  27. 播放
  28. </n-button>
  29. </div>
  30. </div>
  31. </template>
  32. <script setup lang="ts">
  33. import axios from '@/utils/axios'
  34. import { onMounted, onUnmounted, ref } from 'vue'
  35. import MD5 from 'js-md5'
  36. let webRtc: any = null
  37. const TOKEN = '2fc01ae175e8676b6a3edc32d2d76cb3'
  38. const cList = ref()
  39. const cUUID = ref('')
  40. const cIndex = ref(-1)
  41. /**
  42. * rtc 初始化
  43. * @param HOST
  44. * @param token
  45. * @param uuid
  46. * @param callBack
  47. * @param profile 0 主 1 子
  48. */
  49. const initWebRtc = async (HOST: String, token: String, uuid: string, callBack: Function, profile:number, startTime:number, endTime:number) => {
  50. try {
  51. // init RTC
  52. const Peer = new RTCPeerConnection() as any
  53. // Peer.addTransceiver('audio', { direction: 'recvonly' })
  54. Peer.addTransceiver('video', { direction: 'recvonly' })
  55. const offer = await Peer.createOffer()
  56. await Peer.setLocalDescription(offer)
  57. // 监听视频
  58. Peer.ontrack = callBack
  59. Peer.oniceconnectionstatechange = () => {
  60. const state = Peer.iceConnectionState
  61. console.log('ICE状态', state)
  62. }
  63. Peer.onicegatheringstatechange = () => {
  64. console.log('GatheringState: ', Peer.iceGatheringState)
  65. }
  66. Peer.onconnectionstatechange = () => {
  67. const state = Peer.connectionState
  68. console.log('连接状态', state)
  69. }
  70. // SDP SRS params
  71. const stream = MD5(uuid + profile)
  72. const params = {
  73. api: `http://${HOST}:1985/rtc/v1/play/?token=${token}&uuid=${uuid}&stream=${uuid + profile}&profile=${profile}`,
  74. clientip: null,
  75. sdp: offer.sdp,
  76. streamurl: `webrtc://${HOST}/replay/${stream}?token=${token}&starttime=${startTime}&endtime=${endTime}&uuid=${uuid}&stream=${stream}&profile=${profile}`,
  77. tid: Number(Math.floor(new Date().getTime() * Math.random() * 100)).toString(16).slice(0, 7)
  78. }
  79. console.log('params', params)
  80. // 发送offer
  81. const res = await window.fetch(params.api, {
  82. method: 'POST',
  83. headers: { 'Content-Type': 'application/json' },
  84. body: JSON.stringify(params)
  85. })
  86. // 接收 answer
  87. const { sdp } = await res.json()
  88. if (sdp) await Peer.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp }))
  89. return Peer
  90. } catch (error) {
  91. return null
  92. }
  93. }
  94. /**
  95. * 播放
  96. * @param uuid
  97. */
  98. const play = async (uuid: string, index:number) => {
  99. if (!uuid) return
  100. if (uuid === cUUID.value) return
  101. if (webRtc) webRtc.close()
  102. cUUID.value = uuid
  103. cIndex.value = index
  104. webRtc = await initWebRtc('47.108.192.202', TOKEN, uuid, (event: any) => {
  105. const dom = document.getElementById('test') as any
  106. const { streams } = event
  107. dom.srcObject = streams[0]
  108. console.log('track', event)
  109. }, 1, 1667836800000, 1667923199000)
  110. }
  111. // 获取摄像头播放地址
  112. const getCamer = async () => {
  113. const { data } = await axios.post('http://47.108.192.202:8089', {
  114. code: 'categorycamera.list',
  115. token: TOKEN,
  116. body: {}
  117. })
  118. // 35可控
  119. cList.value = data.filter((el: { name: string }) => (el.name.includes('卡哈洛')))
  120. console.log('摄像头列表', cList.value)
  121. }
  122. onMounted(() => {
  123. getCamer()
  124. })
  125. onUnmounted(() => {
  126. if (webRtc) webRtc.close()
  127. })
  128. </script>
  129. <style lang="less" scoped>
  130. .videoBox {
  131. .box {
  132. display: flex;
  133. justify-content: center;
  134. .list {
  135. color: white;
  136. li {
  137. padding: 10px;
  138. &:hover {
  139. color: aqua;
  140. cursor: pointer;
  141. }
  142. }
  143. }
  144. video {
  145. width: 800px;
  146. height: 500px;
  147. background: none;
  148. object-fit: fill;
  149. font-size: 0;
  150. }
  151. }
  152. .contrl {
  153. margin-top: 10px;
  154. text-align: center;
  155. button {
  156. margin-left: 10px;
  157. }
  158. }
  159. }
  160. </style>