Pārlūkot izejas kodu

接入视频流,替换disabled的enabled值

louhangfei 2 gadi atpakaļ
vecāks
revīzija
d277918176

+ 1 - 0
package.json

@@ -50,6 +50,7 @@
     "element-resize-detector": "1.2.4",
     "fabric": "5.3.0",
     "html2canvas": "1.4.1",
+    "flv.js": "1.6.2",
     "konva": "9.3.0",
     "lodash-es": "4.17.21",
     "mockjs": "1.1.0",

+ 23 - 0
pnpm-lock.yaml

@@ -59,6 +59,9 @@ dependencies:
   fabric:
     specifier: 5.3.0
     version: 5.3.0
+  flv.js:
+    specifier: 1.6.2
+    version: 1.6.2
   html2canvas:
     specifier: 1.4.1
     version: 1.4.1
@@ -1253,6 +1256,7 @@ packages:
     resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
@@ -1261,6 +1265,7 @@ packages:
     resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
     requiresBuild: true
     dev: true
     optional: true
@@ -1269,6 +1274,7 @@ packages:
     resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
@@ -1277,6 +1283,7 @@ packages:
     resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
     requiresBuild: true
     dev: true
     optional: true
@@ -1285,6 +1292,7 @@ packages:
     resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
     requiresBuild: true
     dev: true
     optional: true
@@ -3572,6 +3580,10 @@ packages:
       es6-symbol: 3.1.3
     dev: false
 
+  /es6-promise@4.2.8:
+    resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
+    dev: false
+
   /es6-symbol@3.1.3:
     resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
     dependencies:
@@ -4365,6 +4377,13 @@ packages:
     resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==}
     dev: true
 
+  /flv.js@1.6.2:
+    resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==}
+    dependencies:
+      es6-promise: 4.2.8
+      webworkify-webpack: 2.1.5
+    dev: false
+
   /follow-redirects@1.15.1:
     resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
     engines: {node: '>=4.0'}
@@ -8322,6 +8341,10 @@ packages:
     dev: false
     optional: true
 
+  /webworkify-webpack@2.1.5:
+    resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
+    dev: false
+
   /whatwg-encoding@2.0.0:
     resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
     engines: {node: '>=12'}

+ 13 - 5
src/api/camera/camera-preview.ts

@@ -32,9 +32,17 @@ export const getCameraTree = () => {
   });
 };
 
-export enum AlgoStatus {
-  enabled = 1,
-  disabled = 0,
+/** 算法是否启用 */
+export enum ALGO_ENABLED_STATUS {
+  // 0代表启用
+  enabled = 0,
+  disabled = 1,
+}
+
+/** 电子围栏是否启用 */
+export enum FENCE_ENBALED_STATUS {
+  enabled = 0,
+  disabled = 1,
 }
 
 interface AlgoItem {
@@ -46,7 +54,7 @@ interface AlgoItem {
   url: string;
   pushStatement: string;
   pushLinkPrompt: string;
-  status: AlgoStatus;
+  status: ALGO_ENABLED_STATUS;
   createdAt: string;
   updatedAt: string;
 }
@@ -68,7 +76,7 @@ export interface CameraAlgoItem {
   detectionFrequency: number;
   detectionTime: string;
   electronicFence: number;
-  status: AlgoStatus;
+  status: ALGO_ENABLED_STATUS;
   algoInfo: AlgoItem;
 }
 

+ 76 - 0
src/components/LiveVideo/LiveVideo.vue

@@ -0,0 +1,76 @@
+<template>
+  <video id="video" autoplay muted loop class="video-js video-content">
+    <source :src="props.url" />
+  </video>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, onBeforeUnmount, watch } from 'vue';
+  import flvjs from 'flv.js';
+
+  const props = defineProps<{
+    url: string;
+  }>();
+
+  let player: flvjs.Player | null;
+
+  const initPlay = () => {
+    const videoElement = document.getElementById('video') as HTMLMediaElement;
+    player = flvjs.createPlayer({
+      type: 'flv',
+      isLive: true,
+      hasAudio: false,
+      url: props.url,
+    });
+    player.attachMediaElement(videoElement);
+    player.load();
+    player.on(flvjs.Events.METADATA_ARRIVED, () => {
+      // testStore.setVideoReady(true);
+    });
+    // player.play();
+    setTimeout(() => {
+      player?.play();
+    }, 50);
+  };
+
+  const destroyPlayer = () => {
+    // testStore.setVideoReady(false);
+    if (player) {
+      player.pause();
+      player.unload();
+      player.detachMediaElement();
+      player.destroy();
+      player = null;
+    }
+  };
+
+  onMounted(() => {
+    initPlay();
+  });
+
+  //切换播放url
+  watch(
+    () => props.url,
+    () => {
+      destroyPlayer();
+      if (props.url) {
+        initPlay();
+      }
+    },
+    {
+      deep: true,
+    },
+  );
+
+  onBeforeUnmount(() => {
+    destroyPlayer();
+  });
+</script>
+
+<style scoped>
+  .video-content {
+    width: 100%;
+    height: 100%;
+    background-color: transparent !important;
+  }
+</style>

+ 7 - 5
src/views/cameras/preview/components/AlgorithmsSetting/AlgoSettingCard.vue

@@ -79,8 +79,8 @@
   import dayjs, { Dayjs } from 'dayjs';
   import addTimeIcon from '@/assets/icons/addTimeIcon.png';
   import useCameraAlgoStore from '../../store/useCameraAlgoStore';
-  import { STATUS } from './types';
   import { createDefaultTime, frequencyOptions } from './utils';
+  import { ALGO_ENABLED_STATUS, FENCE_ENBALED_STATUS } from '@/api/camera/camera-preview';
 
   // const { data: algoList, loading } = useAllAlgos();
   const cameraAlgoStore = useCameraAlgoStore();
@@ -94,9 +94,9 @@
     /** 检测时间段,可以有多个,转化为字符串,格式为 09:00-10:00;11:00-12:00 */
     detectionTime: string;
     /** 电子围栏是否开启 */
-    electronicFence: STATUS;
+    electronicFence: FENCE_ENBALED_STATUS;
     /** 算法是否启用 */
-    status: STATUS;
+    status: ALGO_ENABLED_STATUS;
   }
 
   const emits = defineEmits<{
@@ -132,8 +132,10 @@
           return getTimeStrs(x.value);
         })
         .join(';'),
-      electronicFence: detail.electronicFenceBool ? STATUS.enabled : STATUS.disabled,
-      status: detail.enableCardBool ? STATUS.enabled : STATUS.disabled,
+      electronicFence: detail.electronicFenceBool
+        ? FENCE_ENBALED_STATUS.enabled
+        : FENCE_ENBALED_STATUS.disabled,
+      status: detail.enableCardBool ? ALGO_ENABLED_STATUS.enabled : ALGO_ENABLED_STATUS.disabled,
     };
     emits('onSubmit', param);
     console.log('param', param);

+ 10 - 5
src/views/cameras/preview/components/AlgorithmsSetting/AlgorithmsSetting.vue

@@ -11,7 +11,7 @@
           :label="item.algoInfo?.name"
           :is-active="item.algoId === selectedAlgoId"
           @click="handleSelectAlgo(item.algoId)"
-          :is-open="item.status === AlgoStatus.enabled"
+          :is-open="item.status === ALGO_ENABLED_STATUS.enabled"
         />
       </div>
       <div>
@@ -30,7 +30,11 @@
   import useCameraAlgoStore from '../../store/useCameraAlgoStore';
   import AlgoSettingCard from './AlgoSettingCard.vue';
   import { storeToRefs } from 'pinia';
-  import { deleteCameraAlgoApi, updateCameraAlgoApi } from '@/api/camera/camera-preview';
+  import {
+    deleteCameraAlgoApi,
+    updateCameraAlgoApi,
+    FENCE_ENBALED_STATUS,
+  } from '@/api/camera/camera-preview';
   import { ElMessage } from 'element-plus';
   import AlgoTag from './AlgoTag.vue';
   import useFenceStore from '../../store/useFenceStore';
@@ -38,7 +42,7 @@
   import usePresetListStore from '../../store/usePresetListStore';
   import AddAlgoDialog from './AddAlgoDialog.vue';
   import { createDefaultTime, getDetectionJSON, getDetectionTimeJSON } from './utils';
-  import { AlgoStatus } from '@/api/camera/camera-preview';
+  import { ALGO_ENABLED_STATUS } from '@/api/camera/camera-preview';
   import { watchEffect } from 'vue';
 
   const cameraAlgoStore = useCameraAlgoStore();
@@ -67,8 +71,9 @@
     if (!detail) return;
     console.log('detail change', detail);
     const detectionJSON = getDetectionJSON(detail?.detectionFrequency);
-    const enableCard = Boolean(detail?.status);
-    const electronicFenceBool = Boolean(detail?.electronicFence);
+    const enableCard = detail?.status === ALGO_ENABLED_STATUS.enabled ? true : false;
+    const electronicFenceBool =
+      detail?.electronicFence === FENCE_ENBALED_STATUS.enabled ? true : false;
 
     const timeRangeArr = getDetectionTimeJSON(detail?.detectionTime) || [createDefaultTime()];
 

+ 0 - 5
src/views/cameras/preview/components/AlgorithmsSetting/types.ts

@@ -1,10 +1,5 @@
 import { Dayjs } from 'dayjs';
 
-export enum STATUS {
-  enabled = 1,
-  disabled = 0,
-}
-
 export interface TimeRangeItem {
   id: string;
   value: [Dayjs, Dayjs];

+ 7 - 8
src/views/cameras/preview/components/CameraLiveVideo/CameraLiveVideo.vue

@@ -1,12 +1,11 @@
 <template>
-  <img :src="bg" alt="" class="videoLive" />
+  <LiveVideo :url="url" />
 </template>
 <script lang="ts" setup>
-  import bg from '@/assets/images/camera/video-live.png';
+  // import bg from '@/assets/images/camera/video-live.png';
+  import LiveVideo from '@/components/LiveVideo/LiveVideo.vue';
+  import { ref } from 'vue';
+  // const url = ref('http://10.94.4.184:8080/live/DHH150.flv');
+  // const url = ref('http://10.94.4.184:8080/live/JJ-GH-03.flv');
+  const url = ref('http://172.16.26.11:8080/live/FC-DM-01.flv');
 </script>
-<style scoped>
-  .videoLive {
-    /* width: 720px;
-    height: 405px; */
-  }
-</style>

+ 3 - 0
src/views/cameras/preview/components/CameraViewSetting/CameraViewSetting.vue

@@ -138,6 +138,9 @@
   };
 
   const drawable = computed(() => {
+    if (!presetStore.currentPresetToken) return false;
+    if (!cameraAlgoStore.selectedAlgoId) return false;
+    if (!cameraAlgoStore.selectedAlgoDetail?.electronicFenceBool) return false;
     return true;
   });
 

+ 1 - 1
src/views/cameras/preview/components/CameraViewSetting/constants.ts

@@ -5,6 +5,6 @@ export const canvasHeight = 1080;
 const videoRatio = canvasHeight / canvasWidth;
 
 /** dom展示出来的宽度,由于屏幕大小限制,完全按照1920来展示,一屏显示不下,所以要进行一定的缩放 */
-export const domWidth = 1344;
+export const domWidth = 960;
 export const domHeight = domWidth * videoRatio;
 export const scale = domWidth / canvasWidth;

+ 19 - 3
src/views/cameras/preview/components/PresetSelect/PresetSelect.vue

@@ -1,12 +1,13 @@
 <template>
   <div class="presetSetting">
-    <div style="margin-right: 10px">预置位</div>
+    <div style="margin-right: 10px">预置位 </div>
     <div>
       <ElSelect
         :model-value="currentPresetToken"
         size="small"
         filterable
         @update:model-value="handleChangeValue"
+        :loading="loading"
       >
         <ElOption
           v-for="item in presetOptions"
@@ -25,7 +26,7 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { ElSelect, ElOption, ElMessage, ElMessageBox } from 'element-plus';
+  import { ElSelect, ElOption, ElMessage, ElMessageBox, ElLoading } from 'element-plus';
   import { CircleCloseFilled } from '@element-plus/icons-vue';
   import usePresetListStore from '../../store/usePresetListStore';
   import { storeToRefs } from 'pinia';
@@ -39,7 +40,7 @@
 
   const cameraDetailStore = useCameraDetailStore();
 
-  const { data: presetOptions, currentPresetToken } = storeToRefs(presetListStore);
+  const { data: presetOptions, currentPresetToken, loading } = storeToRefs(presetListStore);
 
   const fenceStore = useFenceStore();
 
@@ -85,4 +86,19 @@
     display: flex;
     align-items: center;
   }
+
+  .circular {
+    display: inline;
+    height: 30px;
+    width: 30px;
+    animation: loading-rotate 2s linear infinite;
+  }
+  .path {
+    animation: loading-dash 1.5s ease-in-out infinite;
+    stroke-dasharray: 90, 150;
+    stroke-dashoffset: 0;
+    stroke-width: 2;
+    stroke: var(--el-color-primary);
+    stroke-linecap: round;
+  }
 </style>

+ 1 - 1
src/views/cameras/preview/store/usePresetListStore.ts

@@ -19,7 +19,7 @@ export const usePresetListStore = defineStore('presetListStore', () => {
     return data.value?.find((x) => x.name === name);
   };
 
-  return { data, currentPresetToken, getPresetList: runAsync, isPresetNameExist };
+  return { data, currentPresetToken, getPresetList: runAsync, isPresetNameExist, loading };
 });
 
 export default usePresetListStore;