Ver código fonte

feat: device record config

Casper Dai 3 anos atrás
pai
commit
fe8523bd40

+ 116 - 3
src/components/external/DevicePlayer/index.vue

@@ -22,6 +22,44 @@
         </div>
       </div>
     </template>
+    <div
+      v-if="controls"
+      class="l-flex--row c-footer"
+    >
+      <div class="l-flex__fill c-sibling-item u-ellipsis">{{ device.name }}</div>
+      <div
+        v-if="online"
+        class="l-flex__none c-sibling-item o-quality-menu"
+        @click.stop="toggleQualityMenu"
+      >
+        <div
+          v-if="showQualityMenu"
+          class="o-quality-menu__list"
+        >
+          <div
+            class="o-quality-menu__item u-pointer"
+            @click="changeQuality('fff')"
+          >
+            标清
+          </div>
+          <div
+            class="o-quality-menu__item u-pointer"
+            @click="changeQuality('ff')"
+          >
+            高清
+          </div>
+          <div
+            class="o-quality-menu__item u-pointer"
+            @click="changeQuality('')"
+          >
+            原画
+          </div>
+        </div>
+        <div class="u-pointer">
+          {{ qualityValue }}
+        </div>
+      </div>
+    </div>
     <slot />
   </div>
 </template>
@@ -42,6 +80,10 @@ export default {
     autoplay: {
       type: [Boolean, String],
       default: false
+    },
+    controls: {
+      type: [Boolean, String],
+      default: false
     }
   },
   data () {
@@ -49,7 +91,9 @@ export default {
       poster: videoPoster,
       loading: false,
       refreshShow: false,
-      paused: false
+      paused: false,
+      showQualityMenu: false,
+      quality: ''
     }
   },
   computed: {
@@ -67,19 +111,50 @@ export default {
     },
     online () {
       return this.device.activate === 2 && this.device.onlineStatus === 1
+    },
+    qualityValue () {
+      switch (this.quality) {
+        case 'fff':
+          return '标清'
+        case 'ff':
+          return '高清'
+        default:
+          return '原画'
+      }
     }
   },
   created () {
     this.$timer = -1
     if (this.online) {
       this.getAuthCode()
+      if (this.controls) {
+        document.addEventListener('click', this.hideQualityMenu)
+      }
     }
   },
   beforeDestroy () {
+    if (this.online && this.controls) {
+      document.removeEventListener('click', this.hideQualityMenu)
+    }
     this.destroyPlayer()
     this.$timer = null
   },
   methods: {
+    hideQualityMenu () {
+      this.showQualityMenu = false
+    },
+    toggleQualityMenu () {
+      this.showQualityMenu = !this.showQualityMenu
+    },
+    changeQuality (quality) {
+      if (this.quality !== quality) {
+        this.quality = quality
+        if (this.$playerInfo && this.$player) {
+          this.destroyPlayer()
+          this.createPlayer(this.$playerInfo)
+        }
+      }
+    },
     onPause () {
       this.paused = true
     },
@@ -115,10 +190,12 @@ export default {
         return
       }
       this.loading = true
+      this.$playerInfo = null
       authCode({ deviceId: this.device.id }).then(
         ({ data }) => {
           if (this.$timer !== null) {
-            this.createPlayer(data)
+            this.$playerInfo = data
+            this.createPlayer(this.$playerInfo)
           }
         },
         () => {
@@ -128,11 +205,12 @@ export default {
     },
     createPlayer ({ timestamp, token, expire }) {
       if (flvjs.isSupported()) {
+        const quality = this.quality ? `_${this.quality}` : ''
         const player = flvjs.createPlayer({
           type: 'flv',
           isLive: true,
           hasAudio: false,
-          url: `${GATEWAY}/live/${this.device.id}.flv?authorization=${token}&timestamp=${timestamp}&expire=${expire}`
+          url: `${GATEWAY}/live/${this.device.id}${quality}.flv?authorization=${token}&timestamp=${timestamp}&expire=${expire}`
         })
         player.on(flvjs.Events.ERROR, e => {
           console.log('flvjs', this.device.name, flvjs.Events.ERROR, e)
@@ -190,4 +268,39 @@ export default {
     display: inline-block;
   }
 }
+
+.o-quality-menu {
+  display: inline-block;
+  position: relative;
+  padding: 0 6px;
+  z-index: 1;
+
+  &:hover {
+    .o-quality-menu__list {
+      display: inline-block;
+    }
+  }
+
+  &__list {
+    display: none;
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    width: 100%;
+    padding-bottom: 24px;
+    font-size: 14px;
+    text-align: center;
+    border-radius: $radius $radius 0 0;
+    background-color: rgba(#000, 0.3);
+    z-index: -1;
+  }
+
+  &__item {
+    padding: 4px 0;
+
+    &:hover {
+      color: $primary;
+    }
+  }
+}
 </style>

+ 4 - 4
src/router/index.js

@@ -220,10 +220,10 @@ export const asyncRoutes = [
         meta: { title: '分组管理' }
       },
       {
-        name: 'back',
-        path: 'back',
-        component: () => import('@/views/device/back/index'),
-        meta: { title: '视频回' }
+        name: 'record',
+        path: 'record',
+        component: () => import('@/views/device/record/index'),
+        meta: { title: '视频回' }
       }
     ]
   },

+ 3 - 2
src/views/device/back/index.vue → src/views/device/record/index.vue

@@ -41,9 +41,10 @@
             v-for="item in options.list"
             :key="item.identifier"
             :device="item"
+            controls
+            quality="ff"
           >
             <div class="o-video__tag" />
-            <div class="c-footer u-ellipsis">{{ item.name }}</div>
           </device-player>
         </div>
         <pagination
@@ -67,7 +68,7 @@ import { getDevices } from '@/api/device'
 import { createListOptions } from '@/utils'
 
 export default {
-  name: 'Back',
+  name: 'DeviceRecord',
   data () {
     return {
       options: createListOptions({ activate: 2, pageSize: 4 })

+ 10 - 1
src/views/realm/device/Device.vue

@@ -72,6 +72,7 @@
         />
       </div>
     </confirm-dialog>
+    <record-config-dialog ref="recordConfigDialog" />
   </schema-table>
 </template>
 
@@ -91,9 +92,13 @@ import {
   validLongitude,
   validLatitude
 } from '@/utils/validate'
+import RecordConfigDialog from './components/RecordConfigDialog'
 
 export default {
   name: 'Device',
+  components: {
+    RecordConfigDialog
+  },
   props: {
     group: {
       type: Object,
@@ -161,8 +166,9 @@ export default {
                   : '未激活'
               }
           }, on: this.onTagClick },
-          { type: 'invoke', width: 180, render: [
+          { type: 'invoke', width: 240, render: [
             { label: '查看', render ({ empty }) { return !empty }, on: this.onViewDevice },
+            { label: '推流配置', render ({ isMaster }) { return isMaster }, on: this.onRecordConfig },
             { label: '添加备份', render ({ isMaster }) { return isMaster }, on: this.onAddSubDevice },
             { label: '删除', render ({ empty }) { return !empty }, on: this.onDelDevice }
           ] }
@@ -367,6 +373,9 @@ export default {
           this.reloadSubDevices(data.parent)
         }
       })
+    },
+    onRecordConfig (device) {
+      this.$refs.recordConfigDialog.show(device)
     }
   }
 }

+ 11 - 2
src/views/realm/device/Group.vue

@@ -11,6 +11,7 @@
       :schema="schema"
       @row-click="onRowClick"
     />
+    <record-config-dialog ref="recordConfigDialog" />
   </wrapper>
 </template>
 
@@ -21,9 +22,13 @@ import {
   activateDevice,
   deactivateDevice
 } from '@/api/device'
+import RecordConfigDialog from './components/RecordConfigDialog'
 
 export default {
   name: 'GroupDeviceManage',
+  components: {
+    RecordConfigDialog
+  },
   data () {
     return {
       schema: {
@@ -69,8 +74,9 @@ export default {
                   : '未激活'
               }
           }, on: this.onTagClick },
-          { type: 'invoke', render: [
-            { label: '查看', render ({ empty }) { return !empty }, on: this.onViewDevice }
+          { type: 'invoke', width: 160, render: [
+            { label: '查看', render ({ empty }) { return !empty }, on: this.onViewDevice },
+            { label: '推流配置', render ({ isMaster }) { return isMaster }, on: this.onRecordConfig }
           ] }
         ]
       }
@@ -157,6 +163,9 @@ export default {
           this.reloadSubDevices(data.parent)
         }
       })
+    },
+    onRecordConfig (device) {
+      this.$refs.recordConfigDialog.show(device)
     }
   }
 }

+ 29 - 0
src/views/realm/device/api.js

@@ -0,0 +1,29 @@
+import {
+  add,
+  update,
+  send
+} from '@/api/base'
+
+export function getRecordConfig (deviceId) {
+  return send({
+    url: '/deviceStream/config',
+    method: 'GET',
+    params: { deviceId }
+  })
+}
+
+export function addRecordConfig (data) {
+  return add({
+    url: '/deviceStream/config',
+    method: 'POST',
+    data
+  })
+}
+
+export function updateRecordConfig (data) {
+  return update({
+    url: '/deviceStream/config',
+    method: 'PUT',
+    data
+  })
+}

+ 100 - 0
src/views/realm/device/components/RecordConfigDialog.vue

@@ -0,0 +1,100 @@
+<template>
+  <confirm-dialog
+    ref="configDialog"
+    :title="title"
+    @confirm="onSave"
+  >
+    <div class="c-grid-form mini u-align-self--center">
+      <span class="c-grid-form__label">宽度:</span>
+      <el-input-number
+        v-model="recordConfig.videoWidth"
+        :min="240"
+        :max="999999"
+        step-strictly
+      />
+      <span class="c-grid-form__label">高度:</span>
+      <el-input-number
+        v-model="recordConfig.videoHeight"
+        :min="240"
+        :max="999999"
+        step-strictly
+      />
+      <span class="c-grid-form__label">码率:</span>
+      <div class="l-flex--row c-grid-form__option">
+        <el-input-number
+          v-model="recordConfig.videoBitRate"
+          :min="100"
+          :max="999999"
+          step-strictly
+        />
+        &nbsp;Kbps(最低100Kbps)
+      </div>
+      <span class="c-grid-form__label">帧率:</span>
+      <div class="l-flex--row c-grid-form__option">
+        <el-input-number
+          v-model="recordConfig.frameRate"
+          :min="10"
+          :max="60"
+          step-strictly
+        />
+        (10~60)
+      </div>
+    </div>
+  </confirm-dialog>
+</template>
+
+<script>
+import {
+  getRecordConfig,
+  addRecordConfig,
+  updateRecordConfig
+} from '../api'
+
+export default {
+  name: 'RecordConfigDialog',
+  data () {
+    return {
+      title: '',
+      recordConfig: {}
+    }
+  },
+  methods: {
+    show ({ id, name }) {
+      this.title = `${name}推流配置`
+      getRecordConfig(id).then(({ data }) => {
+        if (data) {
+          this.$has = true
+          const { videoWidth, videoHeight, videoBitRate, frameRate } = data
+          this.recordConfig = {
+            deviceId: id,
+            videoWidth,
+            videoHeight,
+            videoBitRate: videoBitRate / 1000,
+            frameRate
+          }
+        } else {
+          this.$has = false
+          this.recordConfig = {
+            deviceId: id,
+            videoWidth: 1920,
+            videoHeight: 1080,
+            videoBitRate: 2048,
+            frameRate: 30
+          }
+        }
+        this.$refs.configDialog.show()
+      })
+    },
+    onSave (done) {
+      const { deviceId, videoWidth, videoHeight, videoBitRate, frameRate } = this.recordConfig
+      ;(this.$has ? updateRecordConfig : addRecordConfig)({
+        deviceId,
+        videoWidth,
+        videoHeight,
+        videoBitRate: videoBitRate * 1000,
+        frameRate
+      }).then(done)
+    }
+  }
+}
+</script>