pilot-liveshare.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <template>
  2. <div class="width100 flex-column flex-justify-start flex-align-start" style="background-color: white;">
  3. <p class="fz16 ml10 mt15 mb10 color-text-title color-font-bold" style="color: #939393">
  4. 在手动开始之前,请选择发布模式和直播类型
  5. </p>
  6. <div class="mt15 flex-row flex-align-center flex-justify-between" style="width: 100%;">
  7. <p class="ml10 mb0 fz16" style="color: black">
  8. 选择视频发布模式:
  9. </p>
  10. <a-select style="width: 200px; margin-right: 20px;" placeholder="发布模式" @select="onPublishModeSelect">
  11. <a-select-option v-for="item in publishModeList" :key="item.label" :value="item.value">
  12. {{ item.label }}
  13. </a-select-option>
  14. </a-select>
  15. </div>
  16. <div class="ml10 mr10" style="width: 96%; margin-top: -10px;">
  17. <a-divider />
  18. </div>
  19. <div class="flex-row flex-align-center flex-justify-between" style="width: 100%; margin-top: -10px;">
  20. <p class="ml10 mb0 fz16">
  21. 选择直播类型:
  22. </p>
  23. <a-select style="width: 200px; margin-right: 20px;" placeholder="直播类型" :value="liveStreamStatus.type"
  24. @select="onLiveTypeSelect">
  25. <a-select-option v-for="item in liveTypeList" :key="item.label" :value="item.value">
  26. {{ item.label }}
  27. </a-select-option>
  28. </a-select>
  29. </div>
  30. <div class="ml10 mr10" style="width: 96%; margin-top: -10px;">
  31. <a-divider />
  32. </div>
  33. <div class="width-100" style="margin-top: -10px;">
  34. <div class="ml10" style="width: 97%;">
  35. <span class="fz16">参数: </span>
  36. <span v-if="liveStreamStatus.type === ELiveTypeValue.Agora" style="word-break: break-all; color: #75c5f6;">
  37. <div class="flex-col flex-justify-center flex-align-center">
  38. <div>
  39. <span class="ml10">Token:</span>
  40. <a-input class="ml10" v-model:value="agoraParam.token" placeholder="Token"></a-input>
  41. </div>
  42. <div>
  43. <span class="ml10">Channel:</span>
  44. <a-input class="ml10" v-model:value="agoraParam.channelId" placeholder="Channel"></a-input>
  45. </div>
  46. </div>
  47. </span>
  48. <span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTMP"
  49. style="word-break: break-all; color: #75c5f6;">{{ rtmpParam }}</span>
  50. <span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTSP"
  51. style="word-break: break-all; color: #75c5f6;">{{ rtspParam }}</span>
  52. <span v-else-if="liveStreamStatus.type === ELiveTypeValue.GB28181"
  53. style="word-break: break-all; color: #75c5f6;">{{ gb28181Param }}</span>
  54. <span v-else></span>
  55. </div>
  56. </div>
  57. <div class="ml10 mr10" style="width: 96%; margin-top: -10px;">
  58. <a-divider />
  59. </div>
  60. <div class="mb20 flex-row flex-align-center flex-justify-center" style="width: 100%; ">
  61. <a-button class="flex-column fz20 flex-align-center flex-justify-center" style="width: 100px;" type="ghost"
  62. @click="onPlay">播放</a-button>
  63. <a-button class="flex-column fz20 flex-align-center flex-justify-center ml40" style="width: 100px;" type="ghost"
  64. @click="onStop">停止</a-button>
  65. </div>
  66. <a-button v-if="playVisiable" class="flex-column flex-align-center" shape="circle" @click="showLivingStatus"
  67. style="position: fixed; top: 13vh; left: 5vw; opacity: 0.8; background-color: rgb(0,0,0,0)">
  68. <template #icon>
  69. <CaretRightFilled style="font-size: 26px; color: " />
  70. </template>
  71. </a-button>
  72. <a-drawer placement="right" v-model:visible="drawerVisible" width="280px" :mask="false" @close="closeDrawer">
  73. <div class="fz16 width-100">
  74. <div class="mt20" style=" margin-bottom: -10px;">
  75. <span class="fz20 flex-row flex-align-center flex-justify-center">
  76. <font
  77. :color="liveState === EStatusValue.LIVING ? 'green' : liveState === EStatusValue.CONNECTED ? 'blue' : 'red'">
  78. {{ liveState }}</font>
  79. </span>
  80. </div>
  81. <a-divider />
  82. <div style=" margin-top: -10px; margin-bottom: -15px;">
  83. <span>Frame Rate:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.fps }}<span
  84. v-if="liveStreamStatus.fps != -1"> fps</span></span><br />
  85. </div>
  86. <a-divider />
  87. <div style=" margin-top: -10px; margin-bottom: -10px;">
  88. <span>Video Bit Rate:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.videoBitRate
  89. }}<span v-if="liveStreamStatus.videoBitRate != -1"> kbps</span></span><br />
  90. </div>
  91. <a-divider />
  92. <div style=" margin-top: -10px; margin-bottom: -10px;">
  93. <span>Audio Bit Rate:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.audioBitRate
  94. }}<span v-if="liveStreamStatus.audioBitRate != -1"> kbps</span></span><br />
  95. </div>
  96. <a-divider />
  97. <div style=" margin-top: -10px; margin-bottom: -10px;">
  98. <span>Packet Loss Rate:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.dropRate }}<span
  99. v-if="liveStreamStatus.dropRate != -1"> %</span></span><br />
  100. </div>
  101. <a-divider />
  102. <div style=" margin-top: -10px; margin-bottom: -10px;">
  103. <span>RTT:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.rtt }}<span
  104. v-if="liveStreamStatus.rtt != -1"> ms</span></span><br />
  105. </div>
  106. <a-divider />
  107. <div style=" margin-top: -10px;">
  108. <span>Jitter:</span><span style="float: right; color: #75c5f6;">{{ liveStreamStatus.jitter }}</span><br />
  109. </div>
  110. </div>
  111. </a-drawer>
  112. </div>
  113. </template>
  114. <script lang="ts" setup>
  115. import { message } from 'ant-design-vue'
  116. import { onMounted, reactive, ref, UnwrapRef } from 'vue'
  117. import { CURRENT_CONFIG as config, CURRENT_CONFIG } from '/@/api/http/config'
  118. import { ELiveTypeName, ELiveTypeValue, GB28181Param, LiveConfigParam, LiveStreamStatus, RTSPParam, EVideoPublishType } from '/@/types/live-stream'
  119. import apiPilot from '/@/api/pilot-bridge'
  120. import { getRoot } from '/@/root'
  121. import { ELiveStatusValue, EStatusValue } from '/@/types'
  122. import { CaretRightFilled } from '@ant-design/icons-vue'
  123. const root = getRoot()
  124. const publishModeList = [
  125. {
  126. value: EVideoPublishType.VideoOnDemand,
  127. label: EVideoPublishType.VideoOnDemand
  128. },
  129. {
  130. value: EVideoPublishType.VideoByManual,
  131. label: EVideoPublishType.VideoByManual
  132. },
  133. {
  134. value: EVideoPublishType.VideoDemandAuxManual,
  135. label: EVideoPublishType.VideoDemandAuxManual
  136. }
  137. ]
  138. const liveTypeList = [
  139. {
  140. value: ELiveTypeValue.Agora,
  141. label: ELiveTypeName.Agora
  142. },
  143. {
  144. value: ELiveTypeValue.RTMP,
  145. label: ELiveTypeName.RTMP
  146. },
  147. {
  148. value: ELiveTypeValue.RTSP,
  149. label: ELiveTypeName.RTSP
  150. },
  151. {
  152. value: ELiveTypeValue.GB28181,
  153. label: ELiveTypeName.GB28181
  154. }
  155. ]
  156. const agoraParam = reactive({
  157. uid: '2892130292',
  158. token: config.agoraToken,
  159. channelId: config.agoraChannel
  160. })
  161. const rtmpParam = {
  162. url: config.rtmpURL //+ new Date().getTime()
  163. }
  164. const rtspParam: RTSPParam = {
  165. userName: CURRENT_CONFIG.rtspUserName,
  166. password: CURRENT_CONFIG.rtspPassword,
  167. port: CURRENT_CONFIG.rtspPort
  168. }
  169. const gb28181Param: GB28181Param = {
  170. serverIp: CURRENT_CONFIG.gbServerIp,
  171. serverPort: CURRENT_CONFIG.gbServerPort,
  172. serverId: CURRENT_CONFIG.gbServerId,
  173. agentId: CURRENT_CONFIG.gbAgentId,
  174. password: CURRENT_CONFIG.gbPassword,
  175. agentPort: CURRENT_CONFIG.gbAgentPort,
  176. agentChannel: CURRENT_CONFIG.gbAgentChannel,
  177. }
  178. const playVisiable = ref(false)
  179. const drawerVisible = ref(false)
  180. const liveState = ref(EStatusValue.DISCONNECT)
  181. const liveTypeSelected = ref<string>()
  182. const publishModeSelected = ref<string>()
  183. const liveStreamStatus: LiveStreamStatus = reactive({
  184. audioBitRate: -1,
  185. dropRate: -1,
  186. fps: -1,
  187. jitter: -1,
  188. quality: -1,
  189. rtt: -1,
  190. status: -1,
  191. type: -1,
  192. videoBitRate: -1
  193. })
  194. onMounted(() => {
  195. const config: LiveConfigParam = JSON.parse(apiPilot.getLiveshareConfig())
  196. liveStreamStatus.type = config.type
  197. refreshLiveType()
  198. window.liveStatusCallback = arg => {
  199. liveStatusCallback(arg)
  200. }
  201. })
  202. const liveStatusCallback = async (arg: LiveStreamStatus) => {
  203. liveStreamStatus.fps = arg.fps
  204. liveStreamStatus.audioBitRate = arg.audioBitRate
  205. liveStreamStatus.dropRate = arg.dropRate
  206. liveStreamStatus.jitter = arg.jitter
  207. liveStreamStatus.rtt = arg.rtt
  208. liveStreamStatus.videoBitRate = arg.videoBitRate
  209. liveStreamStatus.quality = arg.quality
  210. liveStreamStatus.type = arg.type
  211. liveStreamStatus.status = arg.status
  212. switch (liveStreamStatus.status) {
  213. case ELiveStatusValue.LIVING:
  214. liveState.value = EStatusValue.LIVING
  215. break
  216. case ELiveStatusValue.CONNECTED:
  217. liveState.value = EStatusValue.CONNECTED
  218. break
  219. default:
  220. liveState.value = EStatusValue.DISCONNECT
  221. }
  222. }
  223. function refreshLiveType() {
  224. switch (liveStreamStatus.type) {
  225. case ELiveTypeValue.Agora:
  226. liveTypeSelected.value = ELiveTypeName.Agora
  227. break
  228. case ELiveTypeValue.RTMP:
  229. liveTypeSelected.value = ELiveTypeName.RTMP
  230. break
  231. case ELiveTypeValue.RTSP:
  232. liveTypeSelected.value = ELiveTypeName.RTSP
  233. break
  234. case ELiveTypeValue.GB28181:
  235. liveTypeSelected.value = ELiveTypeName.GB28181
  236. break
  237. default:
  238. liveTypeSelected.value = ELiveTypeName.Unknown
  239. }
  240. }
  241. const onLiveTypeSelect = (val: number) => {
  242. liveStreamStatus.type = val
  243. refreshLiveType()
  244. }
  245. const onPublishModeSelect = (val: string) => {
  246. publishModeSelected.value = val
  247. apiPilot.setVideoPublishType(publishModeSelected.value)
  248. }
  249. const onPlay = () => {
  250. console.info(JSON.stringify(agoraParam))
  251. if (!publishModeSelected.value) {
  252. message.warn('请选择发布模式!')
  253. return
  254. }
  255. if (liveTypeSelected.value === ELiveTypeName.Unknown) {
  256. message.warn('请选择直播类型!')
  257. return
  258. }
  259. switch (liveStreamStatus.type) {
  260. case 1: {
  261. apiPilot.setLiveshareConfig(ELiveTypeValue.Agora, JSON.stringify(agoraParam))
  262. break
  263. }
  264. case 2: {
  265. apiPilot.setLiveshareConfig(ELiveTypeValue.RTMP, JSON.stringify(rtmpParam))
  266. break
  267. }
  268. case 3: {
  269. apiPilot.setLiveshareConfig(ELiveTypeValue.RTSP, JSON.stringify(rtspParam))
  270. break
  271. }
  272. case 4: {
  273. apiPilot.setLiveshareConfig(ELiveTypeValue.GB28181, JSON.stringify(gb28181Param))
  274. break
  275. }
  276. }
  277. const status = apiPilot.startLiveshare()
  278. if (status) {
  279. playVisiable.value = true
  280. drawerVisible.value = true
  281. message.success('成功')
  282. }
  283. }
  284. const showLivingStatus = () => {
  285. drawerVisible.value = !drawerVisible.value
  286. }
  287. const onStop = () => {
  288. const status = apiPilot.stopLiveshare()
  289. if (status) {
  290. message.success('成功')
  291. playVisiable.value = false
  292. drawerVisible.value = false
  293. setTimeout(() => {
  294. let key: (keyof LiveStreamStatus)
  295. for (key in liveStreamStatus) {
  296. if (key === 'type') {
  297. continue
  298. }
  299. liveStreamStatus[key] = -1
  300. }
  301. }, 2000)
  302. }
  303. }
  304. </script>
  305. <style lang="scss" scoped>
  306. // @import '/@/styles/index.scss';</style>