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 { | ... | ... |