index.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <template>
  2. <div
  3. class="o-video"
  4. :class="{ offline: !online, mask: !isPlaying, controls, floating, fullscreen: isFullscreen }"
  5. @mousemove="onMouseMove"
  6. @click.stop="onClick"
  7. >
  8. <template v-if="online">
  9. <video
  10. ref="video"
  11. class="o-video__player o-simple-video"
  12. :poster="poster"
  13. autoplay
  14. muted
  15. @play="onVideoPlay"
  16. @pause="onVideoPause"
  17. @waiting="onVideoWaiting"
  18. @playing="onVideoPlaying"
  19. @error="onVideoError"
  20. @ended="onVideoEnded"
  21. />
  22. <div class="o-video__mask">
  23. <div
  24. class="l-flex--row center o-video__btn u-pointer"
  25. @click.stop="onPlayOrPause"
  26. >
  27. <i :class="statusIconClass" />
  28. </div>
  29. </div>
  30. </template>
  31. <div
  32. v-if="controls"
  33. class="l-flex--row c-footer"
  34. >
  35. <div class="l-flex__fill c-sibling-item u-ellipsis">{{ device.name }}</div>
  36. <slot
  37. name="controls"
  38. :can-play="canPlay"
  39. :on-full-screen="onFullScreen"
  40. :waiting-or-loading="loading || waiting"
  41. :online="online"
  42. :is-playing="isPlaying"
  43. >
  44. <template v-if="online">
  45. <i
  46. v-if="loadingQuality"
  47. class="c-sibling-item el-icon-loading"
  48. />
  49. <div
  50. v-else
  51. class="l-flex__none c-sibling-item o-quality-menu u-pointer"
  52. @click.stop="toggleQualityMenu"
  53. >
  54. <div
  55. v-if="showQualityMenu"
  56. class="o-quality-menu__list u-font-size--sm u-text--center"
  57. >
  58. <div
  59. v-for="item in qualities"
  60. :key="item.value"
  61. class="o-quality-menu__item has-active"
  62. :class="{ 'u-color--success': quality.value === item.value }"
  63. @click="changeQuality(item)"
  64. >
  65. {{ item.label }}
  66. </div>
  67. </div>
  68. <span class="has-active">{{ quality.label }}</span>
  69. </div>
  70. <i
  71. class="c-sibling-item el-icon-full-screen has-active"
  72. @click.stop="onFullScreen"
  73. />
  74. </template>
  75. </slot>
  76. </div>
  77. </div>
  78. </template>
  79. <script>
  80. import {
  81. authCode,
  82. getRecordConfig,
  83. addRecordConfig,
  84. updateRecordConfig
  85. } from '@/api/device'
  86. import { GATEWAY } from '@/constant'
  87. import playerMixin from '../player'
  88. const Quality = {
  89. fff: {
  90. videoWidth: 640,
  91. videoHeight: 360,
  92. videoBitRate: 36 * 1024,
  93. frameRate: 1
  94. },
  95. ff: {
  96. videoWidth: 1280,
  97. videoHeight: 720,
  98. videoBitRate: 100 * 1024,
  99. frameRate: 10
  100. },
  101. f: {
  102. videoWidth: 1920,
  103. videoHeight: 1080,
  104. videoBitRate: 1024 * 1024,
  105. frameRate: 20
  106. }
  107. }
  108. export default {
  109. name: 'DevicePlayer',
  110. mixins: [playerMixin],
  111. props: {
  112. device: {
  113. type: Object,
  114. required: true
  115. }
  116. },
  117. data () {
  118. return {
  119. floating: true,
  120. qualities: [
  121. { value: 'fff', label: '标清' },
  122. { value: 'ff', label: '高清' },
  123. { value: 'f', label: '超清' }
  124. ],
  125. loadingQuality: true,
  126. showQualityMenu: false,
  127. quality: null,
  128. recordConfig: null
  129. }
  130. },
  131. computed: {
  132. online () {
  133. return this.device.onlineStatus === 1
  134. },
  135. canPlay () {
  136. return !this.loading && !this.needReset
  137. }
  138. },
  139. watch: {
  140. online (val) {
  141. if (val) {
  142. this.$nextTick(this.createPlayer)
  143. } else {
  144. this.hideQualityMenu()
  145. this.onVideoReset()
  146. this.destroyPlayer()
  147. }
  148. },
  149. isFullscreen () {
  150. this.onMouseMove()
  151. }
  152. },
  153. created () {
  154. this.$delayMoveTimer = -1
  155. },
  156. mounted () {
  157. if (this.online) {
  158. this.createPlayer()
  159. }
  160. },
  161. beforeDestroy () {
  162. clearTimeout(this.$delayMoveTimer)
  163. this.hideQualityMenu()
  164. },
  165. methods: {
  166. onClick () {
  167. if (this.online) {
  168. this.$emit('click', this.device)
  169. }
  170. },
  171. onMouseMove () {
  172. if (this.keep) {
  173. return
  174. }
  175. clearTimeout(this.$delayMoveTimer)
  176. this.floating = false
  177. if (!this.isFullscreen) {
  178. this.$delayMoveTimer = setTimeout(() => {
  179. this.floating = true
  180. }, 3000)
  181. }
  182. },
  183. onVideoReset () {
  184. this.recordConfig = null
  185. this.$playerInfo = null
  186. },
  187. getRecordConfig () {
  188. this.loadingQuality = true
  189. getRecordConfig(this.device.id, { custom: true }).then(
  190. ({ data }) => {
  191. if (data) {
  192. this.loadingQuality = false
  193. const { frameRate } = data
  194. const key = Object.keys(Quality).find(key => Quality[key].frameRate === frameRate) || 'fff'
  195. this.quality = this.qualities.find(({ value }) => value === key) || this.qualities[0]
  196. this.recordConfig = data
  197. this.createPlayer()
  198. } else {
  199. this.setRecordConfig(this.qualities[0])
  200. }
  201. },
  202. () => {
  203. this.loadingQuality = false
  204. this.onVideoDestroyByError()
  205. }
  206. )
  207. },
  208. setRecordConfig (quality) {
  209. this.loadingQuality = true
  210. ;(this.recordConfig ? updateRecordConfig : addRecordConfig)({
  211. deviceId: this.device.id,
  212. ...Quality[quality.value]
  213. }, { custom: true }).then(
  214. ({ data }) => {
  215. this.loadingQuality = false
  216. this.quality = quality
  217. this.recordConfig = data
  218. this.createPlayer()
  219. },
  220. () => {
  221. this.onVideoDestroyByError()
  222. }
  223. )
  224. },
  225. hideQualityMenu () {
  226. if (this.showQualityMenu) {
  227. this.showQualityMenu = false
  228. document.removeEventListener('click', this.hideQualityMenu)
  229. }
  230. },
  231. toggleQualityMenu () {
  232. if (this.showQualityMenu) {
  233. this.hideQualityMenu()
  234. } else {
  235. document.addEventListener('click', this.hideQualityMenu)
  236. this.showQualityMenu = true
  237. }
  238. },
  239. changeQuality (quality) {
  240. if (this.quality.value !== quality.value) {
  241. this.$playerInfo = null
  242. this.setRecordConfig(quality)
  243. } else if (this.needReset) {
  244. this.createPlayer()
  245. }
  246. },
  247. getAuthCode () {
  248. this.$playerInfo = null
  249. authCode(this.recordConfig.stream).then(
  250. ({ data }) => {
  251. this.$playerInfo = data
  252. this.createPlayer()
  253. },
  254. () => {
  255. this.onVideoDestroyByError()
  256. }
  257. )
  258. },
  259. createPlayer () {
  260. if (this.$timer === null) {
  261. return
  262. }
  263. this.loading = true
  264. clearTimeout(this.$timer)
  265. if (!this.recordConfig) {
  266. this.getRecordConfig()
  267. return
  268. }
  269. // if (!this.$playerInfo) {
  270. // this.getAuthCode()
  271. // return
  272. // }
  273. // const { vhost = '__defaultVhost__', token, timestamp, expire } = this.$playerInfo
  274. // if (Number(timestamp) + Number(expire) * 1000 <= Date.now()) {
  275. // this.getAuthCode()
  276. // return
  277. // }
  278. // this.playUrl(`${GATEWAY}/live/${this.recordConfig.stream}.flv?vhost=${vhost}&authorization=${token}&timestamp=${timestamp}&expire=${expire}`)
  279. this.playUrl(`${GATEWAY}/live/${this.recordConfig.stream}.flv?vhost=__defaultVhost__`)
  280. }
  281. }
  282. }
  283. </script>
  284. <style lang="scss" scoped>
  285. .o-quality-menu {
  286. display: inline-block;
  287. position: relative;
  288. padding: 0 $padding--xs;
  289. z-index: 1;
  290. &__list {
  291. position: absolute;
  292. right: 0;
  293. bottom: 100%;
  294. width: 100%;
  295. margin-bottom: $spacing--2xs;
  296. border-radius: $radius;
  297. background-color: rgba(#000, 0.85);
  298. z-index: -1;
  299. }
  300. &__item {
  301. padding: $padding--2xs 0;
  302. }
  303. }
  304. </style>