Browse Source

增加营石SDK

caner 1 year ago
parent
commit
acc73224e6

+ 3 - 1
README.md

@@ -18,4 +18,6 @@
 9. '/three' threejs 3D
 ![image](./public/imgs/9.png)
 10. '/process' svg-进度
-![image](./public/imgs/10.png)
+![image](./public/imgs/10.png)
+11. '/video' 荧石视频SDK-Demo
+![image](./public/imgs/11.png)

+ 2 - 0
package.json

@@ -13,6 +13,7 @@
     "@wangeditor/editor-for-vue": "^5.1.12",
     "date-fns": "^4.1.0",
     "echarts": "^5.5.1",
+    "ezuikit-js": "^8.1.1-alpha.3",
     "html2canvas": "^1.4.1",
     "jspdf": "^2.5.1",
     "leaflet": "^1.9.4",
@@ -28,6 +29,7 @@
     "xlsx": "^0.18.5"
   },
   "devDependencies": {
+    "@types/ezuikit-js": "^8.0.0",
     "@types/leaflet": "^1.9.12",
     "@types/node": "^20.10.4",
     "@types/three": "^0.144.0",

BIN
public/imgs/11.png


+ 6 - 0
src/assets/naive-theme.ts

@@ -41,6 +41,12 @@ const themeOverrides: GlobalThemeOverrides = {
   },
   Icon: {
     color: 'white'
+  },
+  Tree: {
+    nodeTextColor: '#FFFFFFFF',
+    nodeColorHover: 'rgba(0,0,0,0)',
+    nodeColorPressed: 'rgba(0,0,0,0)',
+    nodeColorActive: 'rgba(0,0,0,0)'
   }
 }
 export default themeOverrides

+ 4 - 2
src/assets/native-plugin.ts

@@ -12,7 +12,8 @@ import {
   NInput,
   NSwitch,
   NFormItem,
-  NForm
+  NForm,
+  NTree
 } from 'naive-ui'
 
 const naive = create({
@@ -29,7 +30,8 @@ const naive = create({
     NInput,
     NSwitch,
     NFormItem,
-    NForm
+    NForm,
+    NTree
   ]
 })
 

+ 2 - 1
src/pages/App.vue

@@ -35,7 +35,8 @@ console.log(
 7. '/face' face SVG画图
 8. '/leaflet' leaflet画图
 9. '/three' threejs 3D画图
-10. '/process' process SVG画图`
+10. '/process' process SVG画图
+11. '/video' 荧石视频SDK-Demo`
 )
 </script>
 <style>

+ 27 - 0
src/pages/views/video/ezVideo.service.ts

@@ -0,0 +1,27 @@
+import EZUIKit from 'ezuikit-js'
+export default class EzVideoService {
+  player = null as unknown as EZUIKit.EZUIKitPlayer | null
+
+  init(option: EZUIKit.EZUIKitPlayerOptions, callBack?: (e: any) => void) {
+    if (this.player) this.destory()
+    this.player = new EZUIKit.EZUIKitPlayer({
+      ...option,
+      template: 'simple',
+      handleError: (err) => {
+        console.error('handleError', err)
+        if (callBack) callBack(err)
+      },
+      handleSuccess: (res) => {
+        console.info('handleSuccess', res)
+        if (callBack) callBack(res)
+      }
+    })
+    this.player.play()
+    return this.player
+  }
+
+  destory() {
+    if (this.player) this.player.stop()
+    this.player = null
+  }
+}

+ 263 - 0
src/pages/views/video/index.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="video">
+    <div class="video-left">
+      <div class="video-left-top">
+        <n-input
+          placeholder="搜索"
+          clearable
+          style=" width: 150px;height:28px;"
+          size="tiny"
+        />
+        <n-select
+          v-model:value="selectRow"
+          :options="option"
+          size="small"
+          @update:value="onChange"
+        />
+        <n-button
+          secondary
+          size="small"
+          style="margin-left: 10px;"
+          @click="onChange(selectRow)"
+        >
+          清空
+        </n-button>
+      </div>
+      <div class="video-left-tree">
+        <n-scrollbar>
+          <n-tree
+            :data="treeData"
+            block-line
+            expand-on-click
+            draggable
+            cascade
+            accordion
+            @dragend="onDrop"
+          />
+        </n-scrollbar>
+      </div>
+    </div>
+    <div class="video-right">
+      <template
+        v-for="(item, index) in videoItem"
+        :key="index"
+      >
+        <div
+          id="videoBoxs"
+          class="video-right-item"
+          :style="`width:${(100 / selectRow) - 0.05}%;height:${(100 / selectRow) - 0.15}%;`"
+        >
+          <div :id="item.id" />
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang='ts'>
+import {
+  onMounted, onUnmounted, ref, watch
+} from 'vue'
+import EzVideoService from './ezVideo.service'
+import { useNotification } from 'naive-ui'
+
+interface VideoItem {
+  id: string,
+  liveUrl: string,
+  accessToken: string,
+  video: undefined | any,
+  play: (a: number, b: number) => void,
+  destory: () => void,
+  resize: (a: number, b: number) => void
+}
+
+const notification = useNotification()
+const option = [
+  { label: '1分屏', value: 1 },
+  { label: '4分屏', value: 2 },
+  { label: '9分屏', value: 3 }
+]
+const selectRow = ref(2)
+const videoItem = ref([] as VideoItem[])
+const treeData = ref([
+  {
+    key: '1',
+    label: '1',
+    children: [
+      {
+        key: '1-2',
+        label: '2',
+        children: [
+          {
+            accessToken: 'at.a82kqljj96ja8ony6e57nrqi4aigp963-7sn8ji0ji3-16njxp9-rmioevhxz',
+            label: 'test',
+            key: '1-2-1',
+            liveUrl: 'ezopen://aczn1688@open.ys7.com/FH2861570/2.live'
+          }
+        ]
+      }
+    ]
+  }
+])
+
+function onChange(v: number) {
+  // 清空一次
+  if (videoItem.value.length) {
+    for (let k = 0; k < videoItem.value.length; k++) {
+      const el = videoItem.value[k]
+      el.destory()
+    }
+  }
+  // 重新组装分屏数据
+  const arr = [] as VideoItem[]
+  const n = v * v
+  for (let k = 0; k < n; k++) {
+    const ezVideoService = new EzVideoService()
+    arr.push({
+      id: `video${k}`,
+      liveUrl: '',
+      accessToken: '',
+      video: undefined as any,
+      play(width: number, height: number) {
+        if (this.video) this.destory()
+        if (!width || !height) return
+        this.video = ezVideoService.init({
+          id: this.id, url: this.liveUrl, accessToken: this.accessToken, width, height
+        })
+      },
+      destory() {
+        if (this.video) {
+          this.video.stop()
+          this.video = undefined
+          document.getElementById(this.id)?.remove()
+        }
+      },
+      resize(width: number, height: number) {
+        if (!width || !height) return
+        if (this.video) this.video.reSize(width, height)
+      }
+    })
+  }
+  videoItem.value = arr
+}
+
+function onDrop(data: any) {
+  // 查找节点下的摄像头
+  const recursion = (list: any, arr = [] as VideoItem[]) => {
+    for (let k = 0; k < list.length; k++) {
+      const el = list[k]
+      if (el.accessToken) arr.push(el)
+      if (el.children) recursion(el.children, arr)
+    }
+  }
+  const arr: VideoItem[] = []
+  recursion([ data.node ], arr)
+  if (!arr.length) {
+    notification.warning({ title: `当前${data.node.label}无视频设备`, duration: 2000 })
+    return
+  }
+  // 查找没有播放的分屏
+  const index = videoItem.value.findIndex((el) => !el.accessToken && !el.liveUrl)
+  console.log(7777, arr, videoItem.value, index)
+  if (index < 0) return
+  for (let k = 0; k < arr.length; k++) {
+    const el = arr[k]
+    const num = index + k
+    if (num > (videoItem.value.length - 1)) continue
+    videoItem.value[num].accessToken = el.accessToken
+    videoItem.value[num].liveUrl = el.liveUrl
+    setTimeout(() => {
+      const dom = document.getElementById('videoBoxs')
+      videoItem.value[num].play(dom?.clientWidth || 0, dom?.clientHeight || 0)
+    }, 200)
+  }
+  console.log(888, videoItem.value)
+}
+
+onMounted(() => onChange(selectRow.value))
+
+onUnmounted(() => onChange(selectRow.value))
+
+</script>
+<style lang="scss" scoped>
+.video {
+  display: flex;
+  width: 100vw;
+  height: 100vh;
+  box-sizing: border-box;
+  padding: 5px;
+
+  &-left {
+    width: 320px;
+    background: #0c1f3ad2;
+    margin-right: 10px;
+    padding: 10px;
+    box-sizing: border-box;
+
+    &-top {
+      display: flex;
+      margin-bottom: 10px;
+
+      :deep(.n-select) {
+        width: auto;
+      }
+
+      :deep(.n-base-selection) {
+        --n-color-active: rgba(21, 50, 90, 0.6);
+        --n-color-active-error: rgba(21, 50, 90, 0.6);
+        --n-text-color: white;
+        width: 90px;
+        margin-left: 10px;
+
+        .n-base-selection-label {
+          background: rgba(21, 50, 90, 0.6);
+        }
+
+        .n-base-selection__border {
+          border: none;
+        }
+
+        .n-base-selection__state-border {
+          border-radius: 2px;
+          border: 1px solid rgba(27, 175, 255, 0.50);
+        }
+      }
+    }
+
+    &-tree {
+      height: calc(100% - 38px);
+
+      :deep(.n-tree) {
+        --n-drop-mark-color: rgba(0, 0, 0, 0);
+      }
+    }
+  }
+
+  &-right {
+    width: calc(100% - 330px);
+    height: 100%;
+    display: flex;
+    flex-wrap: wrap;
+    background: #646464;
+    justify-content: space-between;
+    align-items: center;
+    box-sizing: border-box;
+    padding: 0 1px;
+
+    &-item {
+      background: black;
+      overflow: hidden;
+
+      &>div {
+        overflow: hidden;
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .active {
+      border: solid 1px #FCFF00;
+    }
+  }
+}
+</style>

+ 9 - 0
src/pages/views/video/route.ts

@@ -0,0 +1,9 @@
+import { RouteRecordRaw } from 'vue-router'
+import video from './index.vue'
+export default {
+  path: '/video',
+  meta: {
+    authorize: true
+  },
+  component: video
+} as RouteRecordRaw