Commit 445cfec6d1aac75bfc1127a720030c8777b3f628

Authored by xp.Huang
2 parents 16a6e0fc dff69cdb

Merge branch 'feat/video-component-support-gbt' into 'main_dev'

feat: 视频组件支持gbt

See merge request yunteng/thingskit-scada!220
... ... @@ -68,6 +68,8 @@ export interface VideoOptionType {
68 68 orgId: string
69 69 videoComponentFlag: boolean
70 70 videoUrl: string
  71 + channelId?: string
  72 + deviceId?: string
71 73 }
72 74
73 75 export interface AlarmListOptionType {
... ...
  1 +import type { VideoChannelPlayAddressType, VideoItemRecordType } from './model'
1 2 import { defHttp } from '@/utils/http'
2 3
3 4 enum Api {
4 5 GET_VIDEO_LIST = '/video',
5 6 STREAMING_PLAY_GET_URL = '/video/url',
  7 + GET_VIDEO_ALL_LIST = '/video/list',
6 8 RTSP_CLOSEFLV = '/rtsp.closeFlv',
  9 + GET_VIDEO_CONTROL_START = '/video/control/start',
  10 + GET_VIDEO_CONTROL_STOP = '/video/control/stop',
7 11 }
8 12
9 13 // 获取视频组件--->频流
10 14 export const getVideoList = (organizationId?: string) => {
11   - return defHttp.get({
  15 + return defHttp.get<{ items: VideoItemRecordType[] }>({
12 16 url: Api.GET_VIDEO_LIST,
13 17 params: {
14 18 organizationId,
... ... @@ -35,3 +39,21 @@ export const closeFlvPlay = (url: string, browserId: string) => {
35 39 })
36 40 }
37 41
  42 +export const getVideoControlStart = ({
  43 + deviceId,
  44 + channelId,
  45 +}: Record<'deviceId' | 'channelId', string>) => {
  46 + return defHttp.get<VideoChannelPlayAddressType>(
  47 + {
  48 + url: `${Api.GET_VIDEO_CONTROL_START}/${deviceId}/${channelId}`,
  49 + timeout: 30 * 1000,
  50 + },
  51 + {},
  52 + )
  53 +}
  54 +export const getCameraList = (params: Record<'organizationId', string>) => {
  55 + return defHttp.get<{ data: VideoItemRecordType[] }>({
  56 + url: Api.GET_VIDEO_ALL_LIST,
  57 + params,
  58 + })
  59 +}
... ...
  1 +export interface VideoItemRecordType {
  2 + id: string
  3 + creator: string
  4 + createTime: string
  5 + name: string
  6 + enabled: boolean
  7 + tenantId: string
  8 + sn: string
  9 + organizationId: string
  10 + organizationName: string
  11 + status: boolean
  12 + accessMode: number
  13 + playProtocol: number
  14 + params: Params
  15 + videoUrl: string
  16 +}
  17 +
  18 +export interface Params {
  19 + channelNo: string
  20 + deviceId: string
  21 +}
  22 +
  23 +export interface VideoChannelPlayAddressType {
  24 + code: number
  25 + message: string
  26 + data: Data
  27 +}
  28 +
  29 +export interface Data {
  30 + app: string
  31 + stream: string
  32 + ip: any
  33 + flv: string
  34 + https_flv: string
  35 + ws_flv: string
  36 + wss_flv: string
  37 + fmp4: string
  38 + https_fmp4: string
  39 + ws_fmp4: string
  40 + wss_fmp4: string
  41 + hls: string
  42 + https_hls: string
  43 + ws_hls: string
  44 + wss_hls: string
  45 + ts: string
  46 + https_ts: string
  47 + ws_ts: string
  48 + wss_ts: any
  49 + rtmp: string
  50 + rtmps: string
  51 + rtsp: string
  52 + rtsps: string
  53 + rtc: string
  54 + rtcs: string
  55 + mediaServerId: string
  56 + tracks: Track[]
  57 + startTime: any
  58 + endTime: any
  59 + progress: number
  60 +}
  61 +
  62 +export interface Track {
  63 + channels: number
  64 + codecId: number
  65 + codecIdName: any
  66 + codecType: number
  67 + ready: boolean
  68 + sampleBit: number
  69 + sampleRate: number
  70 + fps: number
  71 + height: number
  72 + width: number
  73 +}
... ...
... ... @@ -2,7 +2,7 @@
2 2 import { Button, Divider } from 'ant-design-vue'
3 3 import { nextTick, onMounted, ref, unref } from 'vue'
4 4 import { getOrganization } from '@/api/device'
5   -import { getVideoList } from '@/api/video'
  5 +import { getCameraList } from '@/api/video'
6 6 import { BasicForm, useForm } from '@/components/Form'
7 7 import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'
8 8 import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource'
... ... @@ -13,6 +13,8 @@ import { createPublicFormContext } from '@/core/Library/components/PublicForm/us
13 13 import type { NodeDataDataSourceJsonType, VideoOptionType } from '@/api/node/model'
14 14 import { useMessage } from '@/hooks/web/useMessage'
15 15 import { useSavePageContent } from '@/core/Library/hook/useSavePageContent'
  16 +import { VideoAccessModeEnum } from '@/enums/videoEnum'
  17 +import type { VideoItemRecordType } from '@/api/video/model'
16 18
17 19 const props = defineProps<ConfigComponentProps>()
18 20 const { organizationId } = useParseParams()
... ... @@ -20,6 +22,23 @@ const nodeDataActinType = useNodeData({ cell: props.cell!, immediate: false })
20 22
21 23 const { getNodeData, getNodeAllData, saveNodeAllData } = nodeDataActinType
22 24
  25 +enum VideoFormFieldsEnum {
  26 + VIDEO_ID = 'id',
  27 + VIDEO_URL = 'videoUrl',
  28 + ACCESS_MODE = 'accessMode',
  29 + VIDEO_FLAG = 'videoComponentFlag',
  30 + CHANNEL_ID = 'channelId',
  31 + DEVICE_ID = 'deviceId',
  32 +}
  33 +
  34 +enum VideoFormFieldsNameEnum {
  35 + VIDEO_ID = '视频流',
  36 + ACCESS_MODE = 'ACCESS_MODE',
  37 + VIDEO_URL = '视频地址',
  38 + CHANNEL_ID = '通道号',
  39 + DEVICE_ID = '设备id',
  40 +}
  41 +
23 42 const loading = ref(false)
24 43
25 44 const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({
... ... @@ -36,45 +55,63 @@ const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({
36 55 labelField: 'name',
37 56 valueField: 'id',
38 57 onChange() {
39   - formModel[ContentDataFieldsEnum.VIDEO_ID] = null
  58 + formModel[VideoFormFieldsEnum.VIDEO_ID] = null
  59 + formModel[VideoFormFieldsEnum.CHANNEL_ID] = null
  60 + formModel[VideoFormFieldsEnum.DEVICE_ID] = null
40 61 },
41 62 }
42 63 },
43 64 },
44 65 {
45   - field: ContentDataFieldsEnum.VIDEO_ID,
46   - label: ContentDataFieldsNameEnum.VIDEO_ID,
  66 + field: VideoFormFieldsEnum.VIDEO_ID,
  67 + label: VideoFormFieldsNameEnum.VIDEO_ID,
47 68 component: ComponentEnum.API_SELECT,
48 69 componentProps: ({ formModel }) => {
49 70 return {
50   - api: getVideoList,
51   - params: formModel[ContentDataFieldsEnum.ORG_ID],
52   -
  71 + api: getCameraList,
  72 + params: {
  73 + organizationId: formModel[ContentDataFieldsEnum.ORG_ID],
  74 + },
53 75 fieldNames: { label: 'name', value: 'id' },
54   - resultField: 'items',
55   - onSelect(value: string, option: any) {
56   - formModel[ContentDataFieldsEnum.ACCESS_MODE] = value && option ? option.accessMode : null
57   - formModel[ContentDataFieldsEnum.VIDEO_URL] = value && option ? option.videoUrl : null
58   - formModel[ContentDataFieldsEnum.VIDEO_FLAG] = true
  76 + resultField: 'data',
  77 + onSelect(_: string, option: VideoItemRecordType) {
  78 + const accessMode = option?.accessMode
  79 + formModel[VideoFormFieldsEnum.ACCESS_MODE] = accessMode
  80 + formModel[VideoFormFieldsEnum.VIDEO_URL] = option?.videoUrl
  81 + formModel[VideoFormFieldsEnum.CHANNEL_ID] = accessMode === VideoAccessModeEnum.GBT28181 ? option?.params?.channelNo : null
  82 + formModel[VideoFormFieldsEnum.DEVICE_ID] = accessMode === VideoAccessModeEnum.GBT28181 ? option?.params?.deviceId : null
  83 + formModel[VideoFormFieldsEnum.VIDEO_FLAG] = true
59 84 },
60 85 }
61 86 },
62 87 },
63 88 {
64   - field: ContentDataFieldsEnum.ACCESS_MODE,
65   - label: ContentDataFieldsNameEnum.ACCESS_MODE,
  89 + field: VideoFormFieldsEnum.ACCESS_MODE,
  90 + label: VideoFormFieldsNameEnum.ACCESS_MODE,
  91 + component: ComponentEnum.INPUT,
  92 + ifShow: false,
  93 + },
  94 + {
  95 + field: VideoFormFieldsEnum.DEVICE_ID,
  96 + label: VideoFormFieldsNameEnum.DEVICE_ID,
  97 + component: ComponentEnum.INPUT,
  98 + ifShow: false,
  99 + },
  100 + {
  101 + field: VideoFormFieldsEnum.CHANNEL_ID,
  102 + label: VideoFormFieldsNameEnum.CHANNEL_ID,
66 103 component: ComponentEnum.INPUT,
67 104 ifShow: false,
68 105 },
69 106 {
70   - field: ContentDataFieldsEnum.VIDEO_URL,
71   - label: ContentDataFieldsNameEnum.VIDEO_URL,
  107 + field: VideoFormFieldsEnum.VIDEO_URL,
  108 + label: VideoFormFieldsNameEnum.VIDEO_URL,
72 109 component: ComponentEnum.INPUT,
73 110 ifShow: false,
74 111 },
75 112 {
76   - field: ContentDataFieldsEnum.VIDEO_FLAG,
77   - label: ContentDataFieldsEnum.VIDEO_FLAG,
  113 + field: VideoFormFieldsEnum.VIDEO_FLAG,
  114 + label: VideoFormFieldsEnum.VIDEO_FLAG,
78 115 component: ComponentEnum.INPUT,
79 116 ifShow: false,
80 117 },
... ...
... ... @@ -4,13 +4,14 @@ import 'video.js/dist/video-js.css'
4 4
5 5 import type { VideoJsPlayerOptions } from 'video.js'
6 6 import { BasicVideoPlay } from './component/index.ts'
7   -import { getVideoTypeByUrl, isRtspProtocol, useFingerprint } from './component/config'
  7 +import { VideoPlayerType, getVideoTypeByUrl, isRtspProtocol, useFingerprint } from './component/config'
8 8
9 9 import { getJwtToken, getShareJwtToken } from '@/utils/auth'
10 10 import { isLightboxMode, isShareMode } from '@/utils/env'
11 11 import type { CreateComponentType } from '@/core/Library/types'
12   -import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl } from '@/api/video'
  12 +import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl, getVideoControlStart } from '@/api/video'
13 13 import { useContentDataStore } from '@/store/modules/contentData'
  14 +import { VideoAccessModeEnum } from '@/enums/videoEnum'
14 15
15 16 const props = defineProps<{
16 17 config: CreateComponentType
... ... @@ -48,16 +49,24 @@ const playSource = ref<Record<'src' | 'type', string>>(
48 49 const handleGetVideoPlay = async () => {
49 50 const { dataSourceJson } = unref(videoConfig)[0] || {}
50 51 const { videoOption } = dataSourceJson || {}
51   - const { id, accessMode, videoUrl } = videoOption || {}
  52 + const { id, accessMode, videoUrl, deviceId, channelId } = videoOption || {}
52 53 let type = getVideoTypeByUrl(videoUrl!)
53 54 let playUrl = videoUrl
54 55
55 56 // 判断是否是流媒体播放
56   - if (accessMode === 1 && id) {
  57 + if (accessMode === VideoAccessModeEnum.Streaming && id) {
57 58 const { data: { url } = { url: '' } } = await getStreamingPlayUrl(id!)
58 59 playUrl = url
59 60 playUrl && (type = getVideoTypeByUrl(playUrl!))
60 61 }
  62 + else if (accessMode === VideoAccessModeEnum.GBT28181 && deviceId && channelId) {
  63 + const {
  64 + data: { flv },
  65 + } = await getVideoControlStart({ channelId, deviceId })
  66 +
  67 + playUrl = flv
  68 + type = VideoPlayerType.flv
  69 + }
61 70
62 71 // 判断是否是rtsp播放
63 72 if (isRtspProtocol(videoUrl!)) {
... ... @@ -128,9 +137,7 @@ onUnmounted(async () => {
128 137 <div class="w-full h-full flex justify-center items-center">
129 138 <!-- <Spin :spinning="loading" wrapper-class-name="video-spin" class="w-full h-full"> -->
130 139 <BasicVideoPlay
131   - ref="basicVideoPlayEl"
132   - :options="getOptions"
133   - :with-token="withToken"
  140 + ref="basicVideoPlayEl" :options="getOptions" :with-token="withToken"
134 141 :immediate-init-on-mounted="false"
135 142 />
136 143 <!-- </Spin> -->
... ... @@ -139,10 +146,10 @@ onUnmounted(async () => {
139 146
140 147 <style lang="less" scoped>
141 148 .video-spin {
142   - @apply w-full h-full;
  149 + @apply w-full h-full;
143 150
144   - :deep(.ant-spin-container) {
145   - @apply w-full h-full;
146   - }
  151 + :deep(.ant-spin-container) {
  152 + @apply w-full h-full;
147 153 }
  154 +}
148 155 </style>
... ...
... ... @@ -195,11 +195,6 @@ export enum ContentDataFieldsEnum {
195 195 ORG_ID = 'orgId',
196 196 ATTR = 'attr',
197 197 DEVICE_NAME = 'deviceName',
198   - VIDEO_FILTER = 'videoFilter',
199   - VIDEO_ID = 'id',
200   - VIDEO_URL = 'videoUrl',
201   - ACCESS_MODE = 'accessMode',
202   - VIDEO_FLAG = 'videoComponentFlag',
203 198
204 199 CODE_TYPE = 'codeType',
205 200 }
... ... @@ -211,10 +206,6 @@ export enum ContentDataFieldsNameEnum {
211 206 DEVICE_ID = '设备',
212 207 ATTR = '属性',
213 208 deviceName = '设备名称',
214   - VIDEO_ID = '视频流',
215   - ACCESS_MODE = 'ACCESS_MODE',
216   - VIDEO_URL = '视频地址',
217   -
218 209 }
219 210
220 211 export enum VariableImageSourceEnum {
... ...
  1 +export enum VideoAccessModeEnum {
  2 + ManuallyEnter = 0,
  3 + Streaming = 1,
  4 + GBT28181 = 2,
  5 +}
... ...