Showing
7 changed files
with
176 additions
and
39 deletions
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 { | ... | ... |