摄像头预览优化

This commit is contained in:
曹磊 2025-08-14 17:40:17 +08:00
parent 9c1076e64c
commit d67d7b955c
7 changed files with 236 additions and 130 deletions

View File

@ -27,5 +27,6 @@
<div id="app">
</div>
<script type="module" src="/src/main.ts"></script>
<script src="<%= basePublicPath %>/static/tums-player/tums-player.umd.min.js"></script>
</body>
</html>

View File

@ -1,6 +1,6 @@
<template>
<a-spin :spinning="confirmLoading">
<JFormContainer :disabled="disabled">
<JFormContainer>
<template #detail>
<a-row>
<a-col :span="12">
@ -12,7 +12,7 @@
<a-row>
<a-col :span="2"></a-col>
<a-col :span="20">
<div id="video-container" class="video-container"></div>
<div id="video-container-common" class="video-container"></div>
</a-col>
</a-row>
<a-row style="margin-top: 14px">
@ -225,24 +225,24 @@ import {
const useForm = Form.useForm;
const formData = reactive<Record<string, any>>({
deviceIndex: '',//
streamType: 0,// 0 1
streamType: 1,// 0 1
//
url: '',//URL
backupUrl: '',//URLIPnull
wsUrl: '',//ws
wssUrl: '',//wss
chroma: "0", //
luma: "0", //
sharpness: "0", //
saturation: "0", //
contrast: "0", //
wd_gain: "0", //
smartir_level: "0",//
chroma: 0, //
luma: 0, //
sharpness: 0, //
saturation: 0, //
contrast: 0, //
wd_gain: 0, //
smartir_level: 0,//
wide_dynamic: "off", // "off"// "on"//
smartir: "auto_ir",//"auto_ir_ae" //- "auto_ir" //- "manual"//
smartwtl: "auto_wtl",//"auto_wtl_ae" //- "auto_wtl" //- "manual" //
smartwtl_digital_level: "0",//
smartwtl_digital_level: 0,//
flip_type: "center",// "off"// "left_and_right"// "up_and_down"// "center"//
night_vision_mode: "md_night_vision",//"wtl_night_vision"// "inf_night_vision"// "md_night_vision"//
@ -286,13 +286,20 @@ import {
formData.wssUrl = res.wssUrl;
confirmLoading.value=false;
});
if (player.value){
player.value.destroy().then(() => {
}); //
player.value = null;
}
const TumsPlayer = window['tums-player'].default;
player.value = new TumsPlayer('video-container', {
player.value = new TumsPlayer('video-container-common', {
type: "rtsp", // rtsp
url: formData.url, // , getPreviewUrl
// url: formData.backupUrl, // , getPreviewUrl
socket: formData.wssUrl, // websocket, getPreviewUrl
pluginPath: '/static', // sdkpluginPath
talkEnable: true,
useMultitrans: true,
});
let isPlaying = player.value.isPlaying();
if (!isPlaying) {
@ -311,6 +318,7 @@ import {
if (player){
player.value.destroy().then(() => {
}); //
player.value = null;
}
}
@ -325,20 +333,28 @@ import {
"deviceIndex": deviceIndex,
"type": "common"
}).then(res=>{
formData.chroma = res.chroma; //
formData.luma = res.luma; //
formData.sharpness = res.sharpness; //
formData.saturation = res.saturation; //
formData.contrast = res.contrast; //
formData.wd_gain = res.wd_gain; //
formData.smartir_level = res.smartir_level; //
formData.chroma = strToInt(res.chroma); //
formData.luma = strToInt(res.luma); //
formData.sharpness = strToInt(res.sharpness); //
formData.saturation = strToInt(res.saturation); //
formData.contrast = strToInt(res.contrast); //
formData.wd_gain = strToInt(res.wd_gain); //
formData.smartir_level = strToInt(res.smartir_level); //
formData.wide_dynamic = res.wide_dynamic; // "off"// "on"//
formData.smartir = res.smartir; //"auto_ir_ae" //- "auto_ir" //- "manual"//
formData.smartwtl = res.smartwtl; //"auto_wtl_ae" //- "auto_wtl" //- "manual" //
formData.smartwtl_digital_level = res.smartwtl_digital_level; //
formData.smartwtl_digital_level = strToInt(res.smartwtl_digital_level); //
});
}
function strToInt(val){
if(val!=null&&isFinite(val)){
return Number(val);
}else{
return val;
}
}
/**
* 获取照明设置信息
*/
@ -415,6 +431,20 @@ import {
{ deep: true, immediate: true }
);
});
function destroyPlayer(){
if (player){
player.value.destroy().then(() => {
}); //
player.value = null;
}
}
//
defineExpose({
destroyPlayer
});
</script>
<style lang="less" scoped>

View File

@ -1,6 +1,6 @@
<template>
<a-spin :spinning="confirmLoading">
<JFormContainer :disabled="disabled">
<JFormContainer>
<template #detail>
<a-row>
<a-col :span="24">
@ -13,7 +13,7 @@
<a-row>
<a-col :span="1"></a-col>
<a-col :span="20">
<div id="video-container" class="video-container">
<div id="video-container-osd" class="video-container">
<div class="osd-text-top">
<span v-show="formData.dateEnabled">{{formData.dateData}}&nbsp;</span>
<span v-show="formData.weekEnabled">{{formData.weekData}}&nbsp;</span>
@ -108,7 +108,7 @@
weekEnabled: true,
labelEnabled: true,
streamType: 0,// 0 1
streamType: 1,// 0 1
//
url: '',//URL
backupUrl: '',//URLIPnull
@ -220,13 +220,20 @@
formData.wssUrl = res.wssUrl;
confirmLoading.value=false;
});
if (player.value){
player.value.destroy().then(() => {
}); //
player.value = null;
}
const TumsPlayer = window['tums-player'].default;
player.value = new TumsPlayer('video-container', {
player.value = new TumsPlayer('video-container-osd', {
type: "rtsp", // rtsp
url: formData.url, // , getPreviewUrl
// url: formData.backupUrl, // , getPreviewUrl
socket: formData.wssUrl, // websocket, getPreviewUrl
pluginPath: '/static', // sdkpluginPath
talkEnable: true,
useMultitrans: true,
});
let isPlaying = player.value.isPlaying();
if (!isPlaying) {
@ -245,6 +252,7 @@
if (player){
player.value.destroy().then(() => {
}); //
player.value = null;
}
}
@ -261,6 +269,20 @@
{ deep: true, immediate: true }
);
});
function destroyPlayer(){
if (player){
player.value.destroy().then(() => {
}); //
player.value = null;
}
}
//
defineExpose({
destroyPlayer
});
</script>
<style lang="less" scoped>

View File

@ -4,42 +4,39 @@
v-model:activeKey="activeKey"
tab-position="left"
:style="{ height: '100%' }"
@tabScroll="callback"
>
<a-tab-pane :key="1" tab="画面显示">
<a-tab-pane key="A" tab="画面显示">
<a-tabs
v-model:activeKey="oneChildActiveKey"
tab-position="top"
:style="{ height: '100%' }"
@tabScroll="callback"
type="card"
>
<a-tab-pane :key="1" tab="视频显示">
<div class="scrollable" v-if="oneChildActiveKey==1">
<CameraCommonForm :data="cameraData"></CameraCommonForm>
<a-tab-pane key="A1" tab="视频显示">
<div class="scrollable" v-if="oneChildActiveKey=='A1'">
<CameraCommonForm ref="commonFormRef" :data="cameraData"></CameraCommonForm>
</div>
</a-tab-pane>
<a-tab-pane :key="2" tab="OSD">
<div class="scrollable" v-if="oneChildActiveKey==2">
<CameraOsdForm :data="cameraData"></CameraOsdForm>
<a-tab-pane key="A2" tab="OSD">
<div class="scrollable" v-if="oneChildActiveKey=='A2'">
<CameraOsdForm ref="osdFormRef" :data="cameraData"></CameraOsdForm>
</div>
</a-tab-pane>
<a-tab-pane :key="3" tab="码流参数">
<div class="scrollable" v-if="oneChildActiveKey==3">
<a-tab-pane key="A3" tab="码流参数">
<div class="scrollable" v-if="oneChildActiveKey=='A3'">
<CameraBitrateForm :data="cameraData"></CameraBitrateForm>
</div>
</a-tab-pane>
</a-tabs>
</a-tab-pane>
<a-tab-pane :key="2" tab="事件侦测">
<a-tab-pane key="B" tab="事件侦测">
<a-tabs
v-model:activeKey="twoChildActiveKey"
tab-position="top"
:style="{ height: '100%' }"
@tabScroll="callback"
type="card"
>
<a-tab-pane :key="1" tab="镜头遮挡">
<a-tab-pane key="B1" tab="镜头遮挡">
<div class="scrollable">
<CameraBlockForm :data="cameraData"></CameraBlockForm>
</div>
@ -67,40 +64,38 @@
<!-- </a-tab-pane>-->
</a-tabs>
</a-tab-pane>
<a-tab-pane :key="3" tab="报警设备">
<a-tab-pane key="C" tab="报警设备">
<a-tabs
v-model:activeKey="threeChildActiveKey"
tab-position="top"
:style="{ height: '100%' }"
@tabScroll="callback"
type="card"
>
<a-tab-pane :key="1" tab="白光报警">
<a-tab-pane key="C1" tab="白光报警">
<div class="scrollable" v-if="threeChildActiveKey==1">
<CameraLightAlarmForm :data="cameraData"></CameraLightAlarmForm>
</div>
</a-tab-pane>
<a-tab-pane :key="2" tab="声音报警">
<a-tab-pane key="C2" tab="声音报警">
<div class="scrollable" v-if="threeChildActiveKey==2">
<CameraSoundAlarmForm :data="cameraData"></CameraSoundAlarmForm>
</div>
</a-tab-pane>
</a-tabs>
</a-tab-pane>
<a-tab-pane :key="4" tab="录像管理">
<a-tab-pane key="D" tab="录像管理">
<a-tabs
v-model:activeKey="fourChildActiveKey"
tab-position="top"
:style="{ height: '100%' }"
@tabScroll="callback"
type="card"
>
<a-tab-pane :key="1" tab="MP4转发FTP">
<a-tab-pane key="D1" tab="MP4转发FTP">
<div class="scrollable">
<CameraUploadForm :data="cameraData"></CameraUploadForm>
</div>
</a-tab-pane>
<a-tab-pane :key="2" tab="当天录像">
<a-tab-pane key="D2" tab="当天录像">
<div class="scrollable">
<CameraRecordList :data="cameraData" />
</div>
@ -148,10 +143,12 @@
ip: '' ,//IP
});
const cameraData = ref({});
const activeKey = ref(1);
const oneChildActiveKey = ref(1);
const twoChildActiveKey = ref(1);
const threeChildActiveKey = ref(1);
const activeKey = ref('A');
const oneChildActiveKey = ref('A1');
const twoChildActiveKey = ref('B1');
const threeChildActiveKey = ref('C1');
const commonFormRef = ref();
const osdFormRef = ref();
const imageData = reactive<Record<string, any>>({
//
@ -206,7 +203,7 @@
// cameraData.value.parentId = formData.parentId;
// })
function edit(record) {
function edit(record) {
formData.deviceIndex = record.deviceIndex;
formData.parentId = record.parentId;
formData.multitrans = record.multitrans;
@ -222,10 +219,18 @@
cameraData.value.parentId = formData.parentId;
}
function destroyPlayer(){
if(commonFormRef.value){
commonFormRef.value.destroyPlayer();
}
if(osdFormRef.value){
osdFormRef.value.destroyPlayer();
}
}
defineExpose({
edit,
destroyPlayer
});
</script>

View File

@ -1,6 +1,8 @@
<template>
<j-modal :title="title" :width="width" :fullscreen="true" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<CameraPictureConfig ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPictureConfig>
<div v-if="showCamera">
<CameraPictureConfig ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPictureConfig>
</div>
</j-modal>
</template>
@ -8,27 +10,29 @@
import { ref, nextTick, defineExpose } from 'vue';
import CameraPictureConfig from './CameraPictureConfig.vue'
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
const width = ref<string>('100%');
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const showCamera = ref<boolean>(false);
const emit = defineEmits(['register', 'success']);
/**
* 编辑
* @param record
*/
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
showCamera.value = true;
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
}
/**
* 确定按钮点击事件
*/
@ -49,6 +53,10 @@
*/
function handleCancel() {
visible.value = false;
nextTick(() => {
registerForm.value.destroyPlayer();
showCamera.value = false;
});
}
defineExpose({

View File

@ -2,75 +2,104 @@
<a-spin :spinning="confirmLoading">
<a-row>
<a-col :span="24">
<div id="video-container"></div>
<div id="video-container-preview"></div>
</a-col>
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;" v-show="!izPlaying">
<a-button preIcon="ant-design:play-circle-outlined" @click="play">播放</a-button>
</span>
<span style="margin-left: 5px;" v-show="izPlaying">
<a-button preIcon="ant-design:pause-circle-outlined" @click="pause">暂停</a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:swap-outlined" @click="switchResolution">{{ resolution }}</a-button>
</span>
<!-- <span style="margin-left: 5px;">
<a-button preIcon="ant-design:phone-outlined" @click="screenshot">巡航*</a-button>
</span>-->
<span style="margin-left: 5px;" v-show="!izPhone">
<a-button preIcon="ant-design:phone-outlined" @click="startPhone">电话</a-button>
</span>
<span style="margin-left: 5px;" v-show="izPhone">
<a-button type="primary" danger ghost preIcon="ant-design:phone-outlined" @click="stopPhone">电话</a-button>
</span>
<span style="margin-left: 15px;">分屏
<a-select
ref="select"
v-model:value="fishEyeDisplayMode"
style="width: 120px"
@focus="focus"
@change="setFishEyeDisplayMode"
>
<a-select-option value="ORIGIN">原图</a-select-option>
<a-select-option value="FISHEYE_360D">360全景</a-select-option>
<a-select-option value="FISHEYE_180D">180全景</a-select-option>
<a-select-option value="FISHEYE_WIN_PLANE_TOP_QUAD">四分屏</a-select-option>
<a-select-option value="FISHEYE_LONGITUDE">全景拉伸</a-select-option>
</a-select>
</span>
<a-col :span="20">
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;" v-show="!izPlaying">
<a-button preIcon="ant-design:play-circle-outlined" @click="play">播放</a-button>
</span>
<span style="margin-left: 5px;" v-show="izPlaying">
<a-button preIcon="ant-design:pause-circle-outlined" @click="pause">暂停</a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:swap-outlined" @click="switchResolution">{{ resolution }}</a-button>
</span>
<!-- <span style="margin-left: 5px;">
<a-button preIcon="ant-design:phone-outlined" @click="screenshot">巡航*</a-button>
</span>-->
<span style="margin-left: 5px;" v-show="!izPhone">
<a-button preIcon="ant-design:phone-outlined" @click="startPhone">电话</a-button>
</span>
<span style="margin-left: 5px;" v-show="izPhone">
<a-button type="primary" danger ghost preIcon="ant-design:phone-outlined" @click="stopPhone">电话</a-button>
</span>
<span style="margin-left: 15px;">分屏
<a-select
ref="select"
v-model:value="fishEyeDisplayMode"
style="width: 120px"
@focus="focus"
@change="setFishEyeDisplayMode"
>
<a-select-option value="ORIGIN">原图</a-select-option>
<a-select-option value="FISHEYE_360D">360全景</a-select-option>
<a-select-option value="FISHEYE_180D">180全景</a-select-option>
<a-select-option value="FISHEYE_WIN_PLANE_TOP_QUAD">四分屏</a-select-option>
<a-select-option value="FISHEYE_LONGITUDE">全景拉伸</a-select-option>
</a-select>
</span>
</a-col>
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:picture-outlined" @click="screenshot">截图</a-button>
</span>
<span style="margin-left: 5px;" v-show="!izRecording">
<a-button preIcon="ant-design:video-camera-outlined" @click="recordingStart">录制</a-button>
</span>
<span style="margin-left: 5px;" v-show="izRecording">
<a-button type="primary" danger ghost preIcon="ant-design:video-camera-outlined" @click="recordingEnd">录制</a-button>
</span>
<span style="margin-left: 15px;">画面翻转
<a-select v-model:value="formData.flip_type" @change="(value) => changeSwitch('flip_type', value)">
<a-select-option value="off">关闭</a-select-option>
<a-select-option value="left_and_right">左右</a-select-option>
<a-select-option value="up_and_down">上下</a-select-option>
<a-select-option value="center">中心</a-select-option>
</a-select>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:alert-outlined" @click="manualAlarm">报警</a-button>
</span>
</a-col>
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;">
变焦
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-out-outlined" @mousedown="moveCtrl(9,1,1)" @mouseup="moveCtrl(9,0,1)" title="缩小">缩小</a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-in-outlined" @mousedown="moveCtrl(10,1,1)" @mouseup="moveCtrl(10,0,1)" title="放大">放大</a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-out-outlined" @mousedown="moveCtrl(11,1,1)" @mouseup="moveCtrl(11,0,1)">对近焦</a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-in-outlined" @mousedown="moveCtrl(11,1,1)" @mouseup="moveCtrl(11,0,1)">对远焦</a-button>
</span>
</a-col>
</a-col>
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:picture-outlined" @click="screenshot">截图</a-button>
</span>
<span style="margin-left: 5px;" v-show="!izRecording">
<a-button preIcon="ant-design:video-camera-outlined" @click="recordingStart">录制</a-button>
</span>
<span style="margin-left: 5px;" v-show="izRecording">
<a-button type="primary" danger ghost preIcon="ant-design:video-camera-outlined" @click="recordingEnd">录制</a-button>
</span>
<span style="margin-left: 15px;">画面翻转
<a-select v-model:value="formData.flip_type" @change="(value) => changeSwitch('flip_type', value)">
<a-select-option value="off">关闭</a-select-option>
<a-select-option value="left_and_right">左右</a-select-option>
<a-select-option value="up_and_down">上下</a-select-option>
<a-select-option value="center">中心</a-select-option>
</a-select>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:alert-outlined" @click="manualAlarm">报警</a-button>
</span>
</a-col>
<a-col :span="24" style="padding: 5px;">
<span style="margin-left: 5px;">
变焦
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-out-outlined" @click="zoomInOut('out')" title="缩小"></a-button>
</span>
<span style="margin-left: 5px;">
<a-button preIcon="ant-design:zoom-in-outlined" @click="zoomInOut('in')" title="放大"></a-button>
</span>
<a-col :span="4" v-show="formData.ptz == '1'">
<table style="width: 96px;height:96px;margin: 5px 0px 5px 0px;">
<tbody>
<tr>
<td></td>
<td><a-button preIcon="ant-design:caret-up-outlined" @mousedown="moveCtrl(1,1,1)" @mouseup="moveCtrl(1,0,1)"></a-button></td>
<td></td>
</tr>
<tr>
<td><a-button preIcon="ant-design:caret-left-outlined" @mousedown="moveCtrl(3,1,1)" @mouseup="moveCtrl(3,0,1)"></a-button></td>
<td><a-button preIcon="ant-design:compress-outlined"></a-button></td>
<td><a-button preIcon="ant-design:caret-right-outlined" @mousedown="moveCtrl(5,1,1)" @mouseup="moveCtrl(5,0,1)"></a-button></td>
</tr>
<tr>
<td></td>
<td><a-button preIcon="ant-design:caret-down-outlined" @mousedown="moveCtrl(7,1,1)" @mouseup="moveCtrl(7,0,1)"></a-button></td>
<td></td>
</tr>
</tbody>
</table>
</a-col>
</a-row>
</a-spin>
@ -99,7 +128,7 @@
const formRef = ref();
const player = ref();
const resolution = ref<string>('超清');
const resolution = ref<string>('流畅');
const izPlaying = ref<boolean>(true);
const izRecording = ref<boolean>(false);
const izPhone = ref<boolean>(false);
@ -111,7 +140,7 @@
//
deviceIndex: '',//
streamType: 0,// 0 1
streamType: 1,// 0 1
//
url: '',//URL
@ -120,7 +149,10 @@
wssUrl: '',//wss
flip_type: '',// "off"// "left_and_right"// "up_and_down"// "center"//
zoom: 1, //
sliderValue : 1,//
ptz : 0,//
smartCode : 0,//
});
const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
@ -160,7 +192,6 @@
//
Object.assign(formData, tmpData);
});
createPreview();
getSwitch();
}
@ -179,9 +210,10 @@
if (player.value){
player.value.destroy().then(() => {
}); //
player.value = null;
}
const TumsPlayer = window['tums-player'].default;
player.value = new TumsPlayer('video-container', {
player.value = new TumsPlayer('video-container-preview', {
type: "rtsp", // rtsp
url: formData.url, // , getPreviewUrl
// url: formData.backupUrl, // , getPreviewUrl
@ -400,6 +432,7 @@
if (player){
player.value.destroy().then(() => {
}); //
player.value = null;
}
}

View File

@ -1,6 +1,8 @@
<template>
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<CameraPreviewForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPreviewForm>
<div v-if="showCamera">
<CameraPreviewForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPreviewForm>
</div>
</j-modal>
</template>
@ -12,6 +14,7 @@
const title = ref<string>('');
const width = ref<number>(600);
const visible = ref<boolean>(false);
const showCamera = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
@ -24,6 +27,7 @@
*/
function edit(record) {
title.value = record.deviceName;
showCamera.value = true;
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
@ -48,8 +52,11 @@
* 取消按钮回调事件
*/
function handleCancel() {
registerForm.value.destroy();
visible.value = false;
nextTick(() => {
registerForm.value.destroy();
showCamera.value = false;
});
}
defineExpose({