deviceLiveStream.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div>
  3. <el-row align="middle">
  4. <span style="margin-left: 10px">通道名称:</span>
  5. <el-select
  6. v-model="channelId"
  7. placeholder="请选择"
  8. @change="changeChannel"
  9. size="small"
  10. style="width: 200px;"
  11. >
  12. <el-option
  13. v-for="option in channelList"
  14. :key="option.value"
  15. :label="option.label"
  16. :value="option.value"
  17. />
  18. </el-select>
  19. <span style="margin: 0 10px 0 30px">开启拉流:</span>
  20. <el-switch
  21. v-model="pushStream"
  22. active-color="#13ce66"
  23. inactive-color="#c4c6c9"
  24. :disabled="channelId === ''"
  25. @change="startPushStream"
  26. />
  27. <span style="margin: 0 10px 0 30px">开启直播录像:</span>
  28. <el-switch
  29. v-model="playrecord"
  30. active-color="#13ce66"
  31. inactive-color="#c4c6c9"
  32. :disabled="channelId === ''"
  33. @change="handleStartPlayRecord"
  34. />
  35. </el-row>
  36. <player
  37. ref="playerRef"
  38. :playerinfo="playinfo"
  39. class="components-container"
  40. style="margin-top: 10px;"
  41. />
  42. </div>
  43. </template>
  44. <script setup>
  45. import { ref, reactive, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
  46. import Player from './player.vue'
  47. import { startPlay, closeStream, listChannel } from '@/api/pms/video/channel'
  48. import { startPlayRecord } from '@/api/pms/video/record'
  49. // 定义props
  50. const props = defineProps({
  51. device: {
  52. type: Object,
  53. default: null
  54. }
  55. })
  56. // 定义响应式数据
  57. const playerRef = ref(null)
  58. const deviceInfo = ref({})
  59. const deviceId = ref('')
  60. const channelId = ref('')
  61. const streamId = ref('')
  62. const ssrc = ref('')
  63. const playurl = ref('')
  64. const playrecord = ref(false)
  65. const playrecording = ref(false)
  66. const playing = ref(false)
  67. const pushStream = ref(false)
  68. const retrycount = ref(0)
  69. const channelList = ref([])
  70. // 查询参数
  71. const queryParams = reactive({
  72. pageNum: 1,
  73. pageSize: 10,
  74. deviceSipId: null,
  75. channelSipId: null
  76. })
  77. // 播放信息
  78. const playinfo = reactive({
  79. playtype: 'play'
  80. })
  81. // Watch device prop变化
  82. watch(() => props.device, (newVal, oldVal) => {
  83. deviceInfo.value = newVal
  84. if (newVal?.channelId) {
  85. channelId.value = newVal.channelId
  86. changeChannel()
  87. }
  88. if (newVal && newVal.deviceId !== 0) {
  89. queryParams.deviceSipId = newVal.serialNumber
  90. deviceId.value = newVal.serialNumber
  91. }
  92. })
  93. // 组件挂载时执行
  94. onMounted(() => {
  95. if (props.device) {
  96. queryParams.deviceSipId = props.device.serialNumber
  97. deviceId.value = props.device.serialNumber
  98. getList()
  99. playinfo.deviceId = props.device.serialNumber
  100. }
  101. })
  102. // 组件销毁前执行
  103. onBeforeUnmount(() => {
  104. console.log("beforeDestroy")
  105. closeDestroy(false)
  106. })
  107. // 查询监控设备通道信息列表
  108. const getList = () => {
  109. listChannel(queryParams).then((response) => {
  110. channelList.value = response.list.map((item) => {
  111. return { value: item.channelSipId, label: item.channelName }
  112. })
  113. })
  114. }
  115. // 通道切换
  116. const changeChannel = () => {
  117. console.log('playinfo>>>>>>>>>>>>>>>>', playinfo.value)
  118. console.log('channelId>>>>>>>>>>>>>>>', channelId.value)
  119. playinfo.channelId = channelId.value
  120. startPlayer()
  121. }
  122. // 超时回调
  123. const TimeoutCallback = () => {
  124. closeDestroy(false)
  125. retrycount.value = 0
  126. setTimeout(() => {
  127. startPlayer()
  128. }, 1000)
  129. }
  130. // 开启/关闭推流
  131. const startPushStream = () => {
  132. if (!channelId.value) {
  133. console.log('开始通道: [' + channelId.value + ']')
  134. return
  135. }
  136. console.log('推流状态: [' + pushStream.value + ']')
  137. if (pushStream.value) {
  138. startPlayer()
  139. } else {
  140. closeDestroy(true)
  141. }
  142. }
  143. // 开启/关闭录像播放
  144. const handleStartPlayRecord = () => {
  145. console.log('录像状态: [' + playrecord.value + ']')
  146. closeDestroy(true)
  147. setTimeout(() => {
  148. startPlayer()
  149. }, 500)
  150. }
  151. // 开启直播播放器
  152. const startPlayer = () => {
  153. if (!channelId.value) {
  154. return
  155. }
  156. deviceId.value = queryParams.deviceSipId
  157. if (playing.value) {
  158. closeDestroy(false)
  159. }
  160. // 注册回调
  161. playerRef.value?.registercallback('loadingTimeout', TimeoutCallback)
  162. playerRef.value?.registercallback('delayTimeout', TimeoutCallback)
  163. if (playrecord.value) {
  164. // 录像播放
  165. startPlayRecord(deviceId.value, channelId.value).then((response) => {
  166. // const res = response.data
  167. streamId.value = response.streamId
  168. playurl.value = response.playurl
  169. playerRef.value?.play(playurl.value)
  170. playing.value = true
  171. playrecording.value = true
  172. pushStream.value = true
  173. })
  174. } else {
  175. // 直播播放
  176. startPlay(deviceId.value, channelId.value).then((response) => {
  177. console.log('开始播放:' + deviceId.value + ' : ' + channelId.value)
  178. streamId.value = response.streamId
  179. playurl.value = response.playurl
  180. playerRef.value?.play(playurl.value)
  181. playing.value = true
  182. playrecording.value = false
  183. pushStream.value = true
  184. })
  185. }
  186. }
  187. // 关闭流
  188. const handleCloseStream = (force) => {
  189. if (force) {
  190. if (playing.value && streamId.value) {
  191. closeStream(deviceId.value, channelId.value, streamId.value).then((res) => {
  192. streamId.value = ''
  193. ssrc.value = ''
  194. playurl.value = ''
  195. pushStream.value = false
  196. })
  197. playing.value = false
  198. playrecording.value = false
  199. }
  200. } else {
  201. if (playrecording.value === true) {
  202. return
  203. }
  204. if (playing.value && streamId.value) {
  205. console.log('关闭推流: [' + streamId.value + ']')
  206. closeStream(deviceId.value, channelId.value, streamId.value).then((res) => {
  207. streamId.value = ''
  208. ssrc.value = ''
  209. playurl.value = ''
  210. pushStream.value = false
  211. })
  212. playing.value = false
  213. playrecording.value = false
  214. }
  215. }
  216. }
  217. // 关闭并销毁
  218. const closeDestroy = (force) => {
  219. handleCloseStream(force)
  220. playerRef.value?.destroy()
  221. }
  222. // 销毁播放器
  223. const destroy = () => {
  224. playerRef.value?.destroy()
  225. }
  226. // 暴露给父组件的方法
  227. defineExpose({
  228. destroy,
  229. channelId,
  230. closeDestroy,
  231. playing,
  232. changeChannel
  233. })
  234. </script>