Commit ce47b172 authored by 柳 佳乐's avatar 柳 佳乐
Browse files

静态相机和动态相机使用worker

parent 3845cf1e
......@@ -17,7 +17,8 @@
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"vue-template-compiler": "^2.6.14"
"vue-template-compiler": "^2.6.14",
"worker-loader": "^3.0.8"
}
},
"node_modules/@achrinza/node-ipc": {
......@@ -10925,6 +10926,61 @@
"dev": true,
"license": "MIT"
},
"node_modules/worker-loader": {
"version": "3.0.8",
"resolved": "https://registry.npmmirror.com/worker-loader/-/worker-loader-3.0.8.tgz",
"integrity": "sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g==",
"dev": true,
"license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^4.0.0 || ^5.0.0"
}
},
"node_modules/worker-loader/node_modules/loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/worker-loader/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
......
......@@ -16,7 +16,8 @@
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"vue-template-compiler": "^2.6.14"
"vue-template-compiler": "^2.6.14",
"worker-loader": "^3.0.8"
},
"browserslist": [
"> 1%",
......
......@@ -44,7 +44,6 @@ export default {
isVisible: true
}
},
methods: {
togglePanel() {
this.isVisible = !this.isVisible
......
import request from '@/utils/request'
import { baseURLConsole } from '@/utils/url'
import { baseURLConsole1 } from '@/utils/url'
//获取双目相机列表
export function getStereoCameraList() {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: '/api/cameras/rgbd/rgbd',
method: 'GET',
})
......@@ -12,7 +12,7 @@ export function getStereoCameraList() {
//初始化双目相机
export function initStereoCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: '/api/cameras/rgbd/rgbd/initialize',
method: 'POST',
data
......@@ -22,7 +22,7 @@ export function initStereoCamera(data) {
//关闭指定的双目相机
export function closeStereoCamera(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/${camera_id}/close`,
method: 'POST',
})
......@@ -31,7 +31,7 @@ export function closeStereoCamera(camera_id) {
//获取双目相机当前帧
export function getStereoCameraFrame(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/${camera_id}/current-frame`,
method: 'GET',
})
......@@ -40,7 +40,7 @@ export function getStereoCameraFrame(camera_id) {
//获取指定双目相机的录制状态
export function getStereoCameraRecordStatus(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/${camera_id}/recording-status`,
method: 'GET',
})
......@@ -49,7 +49,7 @@ export function getStereoCameraRecordStatus(camera_id) {
//开始双目相机录制
export function startStereoCameraRecording(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/start-recording`,
method: 'POST',
data
......@@ -58,7 +58,7 @@ export function startStereoCameraRecording(data) {
//停止双目相机录制
export function stopStereoCameraRecording(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/${camera_id}/stop-recording`,
method: 'POST',
})
......@@ -67,7 +67,7 @@ export function stopStereoCameraRecording(camera_id) {
//设置相机曝光和增益参数
export function setStereoCameraExposureGain(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/set_rgbd_exposure_gain`,
method: 'PUT',
data
......@@ -77,7 +77,7 @@ export function setStereoCameraExposureGain(data) {
//设置相机亮度
export function setStereoCameraBrightness(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/brightness`,
method: 'PUT',
data
......@@ -87,7 +87,7 @@ export function setStereoCameraBrightness(data) {
//设置相机对比度
export function setStereoCameraContrast(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/contrast`,
method: 'PUT',
data
......@@ -97,7 +97,7 @@ export function setStereoCameraContrast(data) {
//设置相机色调
export function setStereoCameraHue(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/hue`,
method: 'PUT',
data
......@@ -107,7 +107,7 @@ export function setStereoCameraHue(data) {
//设置相机饱和度
export function setStereoCameraSaturation(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/saturation`,
method: 'PUT',
data
......@@ -117,7 +117,7 @@ export function setStereoCameraSaturation(data) {
//设置相机锐度
export function setStereoCameraSharpness(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/sharpness`,
method: 'PUT',
data
......@@ -127,7 +127,7 @@ export function setStereoCameraSharpness(data) {
//设置相机伽马
export function setStereoCameraGamma(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/gamma`,
method: 'PUT',
data
......@@ -137,7 +137,7 @@ export function setStereoCameraGamma(data) {
//设置相机白平衡
export function setStereoCameraWhiteBalance(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/whitebalance`,
method: 'PUT',
data
......@@ -147,7 +147,7 @@ export function setStereoCameraWhiteBalance(data) {
//设置相机分辨率
export function setStereoCameraResolution(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/resolution`,
method: 'PUT',
data
......@@ -157,7 +157,7 @@ export function setStereoCameraResolution(data) {
//设置相机帧率
export function setStereoCameraFps(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/frame-rate`,
method: 'PUT',
data
......@@ -167,7 +167,7 @@ export function setStereoCameraFps(data) {
//设置相机深度开关
export function setStereoCameraDepthEnabled(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/depth-enabled`,
method: 'PUT',
data
......@@ -177,7 +177,7 @@ export function setStereoCameraDepthEnabled(data) {
//设置相机深度模式
export function setStereoCameraDepthMode(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/rgbd/rgbd/depth-mode`,
method: 'PUT',
data
......
import request from '@/utils/request'
import { baseURLConsole } from '@/utils/url'
import { baseURLConsole2 } from '@/utils/url'
//获取测量数据
export function quickMMeasure(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: '/api/bluetooth_scale/quick-measure',
method: 'POST',
data
......
import request from '@/utils/request'
import { baseURLConsole } from '@/utils/url'
import { baseURLConsole1 } from '@/utils/url'
//获取动态拍摄高清相机列表
export function getDynamiCameraList() {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: '/api/cameras/hd_video/get_list',
method: 'GET',
})
......@@ -12,7 +12,7 @@ export function getDynamiCameraList() {
//初始化指定的动态高清相机
export function initDynamiCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: '/api/cameras/hd_video/initialize',
method: 'POST',
data,
......@@ -22,7 +22,7 @@ export function initDynamiCamera(data) {
//配置高清-视频采集相机参数
export function configDynamiCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/${data.camera_id}/set_orientation`,
method: 'POST',
data,
......@@ -32,7 +32,7 @@ export function configDynamiCamera(data) {
//关闭指定的静态采集高清相机
export function closeDynamiCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: '/api/cameras/hd_video/close',
method: 'POST',
data
......@@ -42,7 +42,7 @@ export function closeDynamiCamera(data) {
//断开视频相机WebSocket连接
export function disconnectDynamiCamera(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/ws/disconnect/${camera_id}`,
method: 'POST',
})
......@@ -51,7 +51,7 @@ export function disconnectDynamiCamera(camera_id) {
//设置视频相机低帧率模式
export function lowDynamiCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/ws/fps/batch/set_ws_fps`,
method: 'POST',
data
......@@ -61,7 +61,7 @@ export function lowDynamiCamera(data) {
//动态视频拍摄相机同步视频采集
export function startDynamiCameraStart(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/multi_hd_video_recode/start`,
method: 'POST',
data
......@@ -72,7 +72,7 @@ export function startDynamiCameraStart(data) {
export function stopDynamiCameraStart(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/multi_hd_video_recode/stop`,
method: 'POST',
data
......@@ -81,7 +81,7 @@ export function stopDynamiCameraStart(data) {
//获取动态相机的链接状态
export function getWebSocketLink(){
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/hd_video/ws/status`,
method: 'GET',
})
......@@ -90,7 +90,7 @@ export function getWebSocketLink(){
//双目相机和动态相机一键录制
export function startMultiCameraRecode(data){
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/multi_control/multi_camera_recode/start`,
method: 'POST',
data
......@@ -99,7 +99,7 @@ export function startMultiCameraRecode(data){
//双目相机和动态相机一键停止
export function stopMultiCameraRecode(data){
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole1}`,
url: `/api/cameras/multi_control/multi_camera_recode/stop`,
method: 'POST',
data
......
import request from '@/utils/request'
import { baseURLConsole } from '@/utils/url'
import { baseURLConsole2 } from '@/utils/url'
//获取静态拍摄高清相机列表
export function getStillCameraList() {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: '/api/cameras/hd_static/get_list',
method: 'GET',
})
......@@ -14,7 +14,7 @@ export function getStillCameraList() {
//初始化指定的静态高清相机
export function initializeStillCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: '/api/cameras/hd_static/initialize',
method: 'POST',
data
......@@ -23,7 +23,7 @@ export function initializeStillCamera(data) {
//配置静态采集高清相机参数
export function setStillCameraOrientation(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: '/api/cameras/hd_static/set_orientation',
method: 'POST',
data
......@@ -33,7 +33,7 @@ export function setStillCameraOrientation(data) {
//批量设置低帧率模式
export function lowStillCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: `/api/cameras/hd_static/ws/fps/batch/set_ws_rate`,
method: 'POST',
data
......@@ -43,7 +43,7 @@ export function lowStillCamera(data) {
//关闭指定的静态采集高清相机
export function closeStillCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: `/api/cameras/hd_static/close`,
method: 'POST',
data
......@@ -53,7 +53,7 @@ export function closeStillCamera(data) {
//捕获高清相机的单帧图像并保存
export function captureStillCamera(data) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: `/api/cameras/hd_static/capture`,
method: 'POST',
data
......@@ -64,7 +64,7 @@ export function captureStillCamera(data) {
//websocket断开链接清理后端状态
export function disconnectStillCamera(camera_id) {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: `/api/cameras/hd_static/ws/disconnect/${camera_id}`,
method: 'POST',
})
......@@ -74,7 +74,7 @@ export function disconnectStillCamera(camera_id) {
//查询webSocket链接
export function getWebSocketLink() {
return request({
baseURL:`http://${baseURLConsole}`,
baseURL:`http://${baseURLConsole2}`,
url: '/api/cameras/hd_static/ws/status',
method: 'GET',
})
......
......@@ -63,8 +63,9 @@ export const binocularCameraMixins = {
'NEURAL_PLUS'
],
autoRefresh: false,
refreshInterval: 15,
refreshInterval: 5,
interval: null,
animationFrameId:null,
}
},
methods: {
......@@ -201,86 +202,59 @@ export const binocularCameraMixins = {
},
//获取帧
getStereoFrame() {
// 取消任何待处理的帧请求
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
}
getStereoCameraFrame(this.stereoCameras[0].camera_id).then(res => {
this.stereoCamerasImage.leftImage = res.data.frame_data.left_image
this.stereoCamerasImage.rightImage = res.data.frame_data.right_image
this.stereoCamerasImage.depthImage = res.data.frame_data.depth_image
this.$nextTick(() => {
this.canvasLeftImage();
this.canvasRightImage();
this.canvasDepthImage();
// 使用requestAnimationFrame批量更新所有画布
this.animationFrameId = requestAnimationFrame(() => {
this.$nextTick(() => {
this.renderAllCanvases();
});
});
})
},
canvasLeftImage() {
if (!this.stereoCamerasImage.leftImage) return;
const canvas = this.$refs['canvas-leftImage'];
if (!canvas) return;
const img = new Image();
img.onload = () => {
// 计算保持比例的尺寸
const containerWidth = canvas.parentElement.clientWidth;
const ratio = img.width / img.height;
const height = containerWidth / ratio;
// 设置canvas尺寸
canvas.width = containerWidth;
canvas.height = height;
// 绘制图像
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
img.src = '';
};
img.src = this.stereoCamerasImage.leftImage;
// 合并的画布渲染方法
renderAllCanvases() {
this.renderCanvas('canvas-leftImage', this.stereoCamerasImage.leftImage);
this.renderCanvas('canvas-rightImage', this.stereoCamerasImage.rightImage);
this.renderCanvas('canvas-depthImage', this.stereoCamerasImage.depthImage);
},
canvasRightImage() {
if (!this.stereoCamerasImage.rightImage) return;
const canvas = this.$refs['canvas-rightImage'];
// 通用的画布渲染方法
renderCanvas(refName, imageData) {
if (!imageData) return;
const canvas = this.$refs[refName];
if (!canvas) return;
const img = new Image();
img.onload = () => {
// 计算保持比例的尺寸
const containerWidth = canvas.parentElement.clientWidth;
const ratio = img.width / img.height;
const height = containerWidth / ratio;
// 设置canvas尺寸
canvas.width = containerWidth;
canvas.height = height;
// 绘制图像
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
img.src = '';
};
img.src = this.stereoCamerasImage.rightImage;
},
canvasDepthImage() {
if (!this.stereoCamerasImage.depthImage) return;
const canvas = this.$refs['canvas-depthImage'];
if (!canvas) return;
const img = new Image();
img.onload = () => {
// 计算保持比例的尺寸
const containerWidth = canvas.parentElement.clientWidth;
const ratio = img.width / img.height;
const height = containerWidth / ratio;
// 创建离屏Canvas进行绘制
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = containerWidth;
offscreenCanvas.height = height;
const offCtx = offscreenCanvas.getContext('2d');
offCtx.drawImage(img, 0, 0, offscreenCanvas.width, offscreenCanvas.height);
// 设置canvas尺寸
// 将离屏Canvas绘制到目标Canvas
canvas.width = containerWidth;
canvas.height = height;
// 绘制图像
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.drawImage(offscreenCanvas, 0, 0, canvas.width, canvas.height);
img.src = '';
};
img.src = this.stereoCamerasImage.depthImage;
img.src = imageData;
},
//自动获取
toggleAutoRefresh() {
this.autoRefresh = !this.autoRefresh
if (this.autoRefresh) {
......@@ -291,6 +265,10 @@ export const binocularCameraMixins = {
} else {
clearInterval(this.interval)
this.interval = null;
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
}
},
//修改曝光和增益
......
......@@ -6,7 +6,8 @@
<div class="header-title">相机2、3模块</div>
<div class="header-buttons">
<el-button type="primary" @click="getVideoList" :disabled="dynamiCameraisInitialized">获取相机2列表</el-button>
<el-button type="primary" @click="getStereoCameraList" :disabled="stereoCameraisInitialized">获取相机3列表</el-button>
<el-button type="primary" @click="getStereoCameraList"
:disabled="stereoCameraisInitialized">获取相机3列表</el-button>
</div>
</div>
......@@ -37,17 +38,18 @@
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="initDynamiCameras" icon="el-icon-refresh" :disabled="dynamiCameraisInitialized">初始化</el-button>
<el-button type="danger" @click="closeAllCameras" icon="el-icon-close" >关闭</el-button>
<el-button type="primary" @click="initDynamiCameras" icon="el-icon-refresh"
:disabled="dynamiCameraisInitialized">初始化</el-button>
<el-button type="danger" @click="closeAllCameras" icon="el-icon-close">关闭</el-button>
</el-button-group>
</el-form-item>
<el-form-item>
<el-switch v-model="dynamiSettings.lowFpsMode" active-text="低帧率" inactive-text="原始帧"
@change="toggleLowFpsMode" :disabled="!dynamiCameraisInitialized"/>
@change="toggleLowFpsMode" :disabled="!dynamiCameraisInitialized" />
</el-form-item>
<el-form-item>
<el-tag :type="dynamiCameraisInitialized?'success':'info'" >
{{ dynamiCameraisInitialized?'已初始化':'未初始化' }}
<el-tag :type="dynamiCameraisInitialized ? 'success' : 'info'">
{{ dynamiCameraisInitialized ? '已初始化' : '未初始化' }}
</el-tag>
</el-form-item>
......@@ -169,13 +171,14 @@
</el-row>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="initStereoCameras" :disabled="stereoCameraisInitialized">初始化</el-button>
<el-button type="primary" @click="initStereoCameras"
:disabled="stereoCameraisInitialized">初始化</el-button>
<el-button type="danger" @click="closeStereoCameras">关闭</el-button>
</el-button-group>
</el-form-item>
<el-form-item>
<el-tag :type="stereoCameraisInitialized?'success':'info'" >
{{ stereoCameraisInitialized?'已初始化':'未初始化' }}
<el-tag :type="stereoCameraisInitialized ? 'success' : 'info'">
{{ stereoCameraisInitialized ? '已初始化' : '未初始化' }}
</el-tag>
</el-form-item>
</el-form>
......@@ -201,7 +204,8 @@
</div>
</div>
<div class="frame-header" style="padding: 5px; text-align: center;">
<el-select v-model="camera.cameraOrientation" size="mini" style="width: 80px; margin-right: 10px;" @change="configCamera(camera)">
<el-select v-model="camera.cameraOrientation" size="mini" style="width: 80px; margin-right: 10px;"
@change="configCamera(camera)">
<el-option label="不指定" value="不指定"></el-option>
<el-option label="右中" value="右中"></el-option>
<el-option label="右下" value="右下"></el-option>
......@@ -214,10 +218,9 @@
<el-button size="mini" type="primary" plain @click="startStream(camera)">链接</el-button>
<el-button size="mini" type="warning" plain @click="stopStream(camera)">断链</el-button>
</div>
<div v-if="!camera.imageData"
style="width: 100%; height: 180px; background: #000;">
<div v-if="!camera.imageData" style="width: 100%; height: 180px; background: #000;">
</div>
<canvas v-else :ref="`canvas-${camera.id}`" style="width: 100%; background: #000;"></canvas>
<canvas v-else :ref="`canvas_${camera.id}`" style="width: 100%; background: #000;"></canvas>
<div style="padding: 5px; font-size: 12px; color: #666; min-height: 40px;">
<div v-if="camera.statusInfo">{{ camera.statusInfo }}</div>
<div v-if="camera.captureLog">{{ camera.captureLog }}</div>
......@@ -265,7 +268,7 @@
</el-button>
<div class="interval-control">
<span class="control-text">刷新间隔:</span>
<el-input-number v-model="refreshInterval" size="mini" :min="1" :max="15" :step="1"
<el-input-number v-model="refreshInterval" size="mini" :min="1" :max="5" :step="1"
:disabled="autoRefresh"></el-input-number>
<span class="control-text">次/秒</span>
</div>
......@@ -310,7 +313,7 @@
<script>
import {
baseURLConsole
baseURLConsole1
} from '@/utils/url'
import {
getDynamiCameraList,
......@@ -324,6 +327,7 @@ import {
stopMultiCameraRecode
} from '@/api/dynamiCamera.js'
import { binocularCameraMixins } from '@/components/binocularCamera'
import Worker from '@/components/workers/dynamiCamera.worker.js'
export default {
name: 'DynamiCamera',
mixins: [binocularCameraMixins],
......@@ -332,7 +336,7 @@ export default {
activeTab: 'dynami',
dynamiCameras: [],
frameCanvas: null,
dynamiCameraisInitialized:false,
dynamiCameraisInitialized: false,
// 动态相机设置
dynamiSettings: {
resolution: '1920x1080',
......@@ -344,36 +348,10 @@ export default {
isRecording: false,
recordingStartTime: null,
recordingTimer: null,
workers: {} // 存储每个相机对应的worker
}
},
methods: {
// 渲染图像到canvas
renderImageToCanvas(camera) {
if (!camera.imageData) return;
const canvas = this.$refs[`canvas-${camera.id}`];
if (!canvas || !canvas[0]) return;
const img = new Image();
img.onload = () => {
// 计算保持比例的尺寸
const containerWidth = canvas[0].parentElement.clientWidth;
const ratio = img.width / img.height;
const height = containerWidth / ratio;
// 设置canvas尺寸
canvas[0].width = containerWidth;
canvas[0].height = height;
// 绘制图像
const ctx = canvas[0].getContext('2d');
ctx.clearRect(0, 0, canvas[0].width, canvas[0].height);
ctx.drawImage(img, 0, 0, canvas[0].width, canvas[0].height);
img.src = '';
};
img.src = camera.imageData;
},
// 获取动态相机列表
getVideoList() {
getDynamiCameraList().then(res => {
......@@ -381,6 +359,8 @@ export default {
if (res.data.length > 0) {
let list = []
res.data.forEach(item => {
const worker = new Worker();
worker.onmessage = (e) => this.handleWorkerMessage(item.camera_id, e.data);
list.push({
id: item.camera_id,
name: item.name,
......@@ -394,7 +374,9 @@ export default {
objectURL: null,
statusInfo: null,
captureLog: '等待连接',
worker, // 存储worker引用
})
this.workers[item.camera_id] = worker;
})
this.dynamiCameras = list
this.$message.success(`获取到 ${this.dynamiCameras.length} 个相机2`)
......@@ -414,7 +396,7 @@ export default {
},
//初始化动态相机
initDynamiCameras() {
let cameraIds = [];
const [width, height] = this.dynamiSettings.resolution.split('x')
//
......@@ -446,7 +428,7 @@ export default {
},
// 关闭所有相机
closeAllCameras() {
let cameraIds = [];
this.dynamiCameras.forEach(camera => {
cameraIds.push(camera.id);
......@@ -459,6 +441,8 @@ export default {
camera_ids: cameraIds.join(',')
};
closeDynamiCamera(param).then(res => {
Object.values(this.workers).forEach(worker => worker.terminate());
this.workers = {};
this.dynamiCameras = []
this.dynamiSettings = {
resolution: '1920x1080',
......@@ -466,13 +450,13 @@ export default {
savePath: 'images/captures',
lowFpsMode: false
},
this.dynamiCameraisInitialized = false
this.dynamiCameraisInitialized = false
this.$message.success(`关闭所有相机成功`)
})
},
// 切换低帧率模式
toggleLowFpsMode(isLowFps) {
// 调用接口设置帧率模式
let cameraIds = [];
this.dynamiCameras.forEach(camera => {
......@@ -489,7 +473,7 @@ export default {
},
//配置相机
configCamera(camera) {
let param = {
camera_id: camera.id,
orientation: camera.cameraOrientation
......@@ -500,170 +484,116 @@ export default {
},
//开启WebStock连接
startStream(camera) {
// 创建canvas用于渲染帧数据
if (camera.websocket) {
camera.websocket.close();
camera.websocket = null;
// 等待一小段时间确保连接完全关闭
setTimeout(() => {
this.establishWebSocketConnection(camera);
}, 100);
} else {
this.establishWebSocketConnection(camera);
}
camera.worker.postMessage({
action: 'connect',
id: camera.id,
baseURL: baseURLConsole1
});
},
establishWebSocketConnection(camera) {
const wsUrl = `ws://${baseURLConsole}/api/cameras/hd_video/ws/connect/${camera.id}`;
camera.websocket = new WebSocket(wsUrl);
camera.reconnectAttempts = 0;
camera.websocket.onopen = (event) => {
camera.captureLog = 'WebSocket已连接,等待相机响应...'
setInterval(() => {
if (camera.websocket.readyState === WebSocket.OPEN) {
camera.websocket.send('ping');
}
}, 30000);
};
camera.websocket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 处理连接状态消息
if (data.status) {
camera.captureLog = `连接成功: ${data.message}`
return;
}
// 处理错误消息
if (data.error) {
// 如果是相机未初始化的错误,提示用户
if (data.error.includes('未初始化')) {
camera.captureLog = '请先在"相机控制"部分初始化相机,然后再尝试连接视频流'
}
// 如果是相机已在使用的错误,提示用户稍后重试
if (data.error.includes('已在流式传输中')) {
camera.captureLog = '相机正在使用中,请等待一会儿后再尝试连接'
}
camera.captureLog = `错误: ${data.error}`
return;
}
// 处理视频帧数据
if (data.data) {
// 显示图像
camera.imageData = `data:image/jpeg;base64,${data.data}`;
this.$nextTick(() => {
this.renderImageToCanvas(camera);
});
// 更新状态信息
let statusInfo = `分辨率: ${data.width}x${data.height}`;
if (data.performance) {
statusInfo += ` | 实际FPS: ${data.performance.actual_fps} | 目标FPS: ${data.performance.target_fps}`;
}
if (data.encode_time_ms) {
statusInfo += ` | 编码耗时: ${data.encode_time_ms}ms`;
}
if (data.data_size_kb) {
statusInfo += ` | 数据大小: ${data.data_size_kb}KB`;
}
camera.statusInfo = statusInfo
}
} catch (error) {
camera.captureLog = `解析消息数据时出错: ${error}`
}
}
camera.websocket.onerror = (error) => {
console.log(error)
camera.captureLog = `WebSocket连接错误`
};
camera.websocket.onclose = (event) => {
console.log(`WebSocket连接已关闭 (代码: ${event.code}, 原因: ${event.reason || '未知'})`);
// 不同的关闭代码有不同的含义
let closeReason = '';
switch (event.code) {
case 1000:
closeReason = '正常关闭';
break;
case 1001:
closeReason = '端点离开';
break;
case 1002:
closeReason = '协议错误';
break;
case 1003:
closeReason = '不支持的数据类型';
break;
case 1006:
closeReason = '异常关闭';
break;
case 1011:
closeReason = '服务器错误';
break;
default:
closeReason = `未知错误 (${event.code})`;
}
// 渲染图像到canvas
renderImageToCanvas(camera) {
if (!camera.imageData) return;
const canvas = this.$refs[`canvas_${camera.id}`];
if (!canvas || !canvas[0]) return;
// 计算保持比例的尺寸
const containerWidth = canvas[0].parentElement.clientWidth;
const ratio = camera.imageData.width / camera.imageData.height;
const height = containerWidth / ratio;
camera.captureLog = `WebSocket连接已关闭: ${closeReason}`
// 设置canvas尺寸
canvas[0].width = containerWidth;
canvas[0].height = height;
// 只有在非正常关闭的情况下才自动重连
if (event.code !== 1000 && camera.reconnectAttempts < 5) {
camera.reconnectAttempts++;
const retryDelay = Math.min(1000 * Math.pow(2, camera.reconnectAttempts - 1), 10000); // 指数退避,最大10秒
console.log(`连接异常关闭,${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`);
camera.captureLog = `连接异常关闭,${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`
setTimeout(() => this.startStream(camera), retryDelay);
} else if (event.code === 1000) {
console.log('WebSocket正常关闭,不进行重连');
} else {
console.log('重连次数已达上限,停止重连');
camera.captureLog = '重连失败,请检查相机状态后手动重连'
}
};
// 绘制图像
const ctx = canvas[0].getContext('2d');
ctx.clearRect(0, 0, canvas[0].width, canvas[0].height);
ctx.drawImage(camera.imageData, 0, 0, canvas[0].width, canvas[0].height);
},
stopStream(camera) {
//调用接口断开链接
disconnectDynamiCamera(camera.id).then(res => {
this.$message.success(`断开链接成功`)
if (camera.websocket) {
camera.websocket.close()
this.$nextTick(() => {
camera.websocket = null
camera.reconnectAttempts = 0
camera.imageData = null
camera.objectURL = null
camera.statusInfo = null
camera.captureLog = '等待链接'
})
}
})
this.$message.success(`断开链接成功`);
camera.worker.postMessage({
action: 'disconnect'
});
this.$nextTick(() => {
camera.imageData = null;
camera.statusInfo = null;
camera.captureLog = '等待链接';
});
});
},
//开始录制
startAllRecordings() {
let hdCameras = [];
this.dynamiCameras.forEach(camera => {
hdCameras.push(camera.id);
});
let param = {
hd_cameras: hdCameras.join(','),
rgbd_cameras: this.stereoCameras[0].camera_id + '',
user_name: this.$store.getters.username,
save_dir: this.dynamiSettings.savePath
async startAllRecordings() {
if (!this.autoRefresh) {
let hdCameras = [];
this.dynamiCameras.forEach(camera => {
hdCameras.push(camera.id);
});
let param = {
hd_cameras: hdCameras.join(','),
rgbd_cameras: this.stereoCameras[0].camera_id + '',
user_name: this.$store.getters.username,
save_dir: this.dynamiSettings.savePath
}
console.log(param)
//低帧率
this.dynamiSettings.lowFpsMode = true
await this.toggleLowFpsMode(this.dynamiSettings.lowFpsMode)
setTimeout()
startMultiCameraRecode(param).then(res => {
this.isRecording = true;
this.recordingStartTime = new Date();
this.startRecordingTimer();
this.$message.success(`开始录制`);
})
}else{
this.$message.warning("请先停止双目相机的获取")
}
console.log(param)
startMultiCameraRecode(param).then(res => {
this.isRecording = true;
this.recordingStartTime = new Date();
this.startRecordingTimer();
this.$message.success(`开始录制`);
})
},
// 处理worker消息
handleWorkerMessage(cameraId, data) {
const camera = this.dynamiCameras.find(c => c.id === cameraId);
if (!camera) return;
const { type, message, blob, width, height, performance, encode_time_ms, data_size_kb } = data;
switch (type) {
case 'status':
camera.captureLog = message;
break;
case 'error':
camera.captureLog = message;
break;
case 'frame':
// 使用 createImageBitmap 处理 Blob 数据
createImageBitmap(blob).then(bitmap => {
camera.imageData = bitmap;
this.$nextTick(() => {
this.renderImageToCanvas(camera);
});
// 更新状态信息
let statusInfo = `分辨率: ${width}x${height}`;
if (performance) {
statusInfo += ` | 实际FPS: ${performance.actual_fps} | 目标FPS: ${performance.target_fps}`;
}
if (encode_time_ms) {
statusInfo += ` | 编码耗时: ${encode_time_ms}ms`;
}
if (data_size_kb) {
statusInfo += ` | 数据大小: ${data_size_kb}KB`;
}
camera.statusInfo = statusInfo;
});
break;
}
},
// 启动录制计时器
startRecordingTimer() {
this.recordingTimer = setInterval(() => {
......@@ -710,6 +640,17 @@ export default {
this.$message.success(`停止录制`);
})
},
// 组件销毁前清理
beforeDestroy() {
// 关闭所有相机连接
this.dynamiCameras.forEach(camera => {
this.stopStream(camera);
});
// 终止所有worker
Object.values(this.workers).forEach(worker => worker.terminate());
this.workers = {};
}
}
}
</script>
......
......@@ -86,8 +86,9 @@ export default {
}
let param = {
isBooking: this.patientInfo.bookingId ? true : false,
bookingId: this.patientInfo.bookingId || 1,//预约ID
// isBooking: this.patientInfo.bookingId ? true : false,
is_booking: false,
bookingId: this.patientInfo.bookingId || null,//预约ID
gender: this.patientInfo.gender,//性别
age: this.patientInfo.age,//年龄
deviceAddress: null,//设备地址
......
......@@ -36,31 +36,16 @@
<el-form-item>
<el-button-group>
<el-button
type="primary"
@click="initAllCameras"
icon="el-icon-refresh"
:disabled="isInitialized">
<el-button type="primary" @click="initAllCameras" icon="el-icon-refresh" :disabled="isInitialized">
{{ isInitialized ? '已初始化' : '初始化' }}
</el-button>
<el-switch
v-model="globalParams.lowFpsMode"
active-text="低帧率"
inactive-text="原始帧"
@change="toggleFpsMode"
style="margin-left: 10px;"
:disabled="!isInitialized" />
<el-button
@click="closeAllCameras"
size="mini"
icon="el-icon-close"
style="margin-left: 10px;">
关闭所有
</el-button>
<el-switch v-model="globalParams.lowFpsMode" active-text="低帧率" inactive-text="原始帧"
@change="toggleFpsMode" style="margin-left: 10px;" :disabled="!isInitialized" />
<el-button type="danger" @click="closeAllCameras" icon="el-icon-close">关闭</el-button>
</el-button-group>
<el-tag :type="isInitialized?'success':'info'" >
{{ isInitialized?'已初始化':'未初始化' }}
</el-tag>
<el-tag :type="isInitialized ? 'success' : 'info'">
{{ isInitialized ? '已初始化' : '未初始化' }}
</el-tag>
</el-form-item>
</el-form>
</div>
......@@ -77,14 +62,13 @@
</div>
</div>
<div class="frame-header">
<el-select v-model="camera.cameraOrientation" style="width: 120px">
<el-select v-model="camera.cameraOrientation" style="width: 120px" @change="configCamera(camera)">
<el-option label="不指定" value="不指定"></el-option>
<el-option label="后侧" value="后侧"></el-option>
<el-option label="左侧" value="左侧"></el-option>
<el-option label="右侧" value="右侧"></el-option>
</el-select>
<el-button-group style="margin-left: 10px;">
<el-button @click="configCamera(camera)" size="mini">配置</el-button>
<el-button type="success" @click="startStream(camera)" size="mini"
icon="el-icon-video-play">链接</el-button>
<el-button type="danger" @click="stopStream(camera)" size="mini"
......@@ -122,21 +106,23 @@
<script>
import {
baseURLConsole
baseURLConsole2
} from '@/utils/url'
import { getStillCameraList, initializeStillCamera, setStillCameraOrientation, lowStillCamera, getWebSocketLink, closeStillCamera, captureStillCamera, disconnectStillCamera } from '@/api/stillCamera'
import Worker from '@/components/workers/stillCamera.worker.js'
export default {
name:'StillCamera',
name: 'StillCamera',
data() {
return {
globalParams: {
fps: 30,//帧率
resolution: '2560x1440',//分别率
savePath: '/images/captures',//存储路径
savePath: 'images/captures',//存储路径
lowFpsMode: false,//低帧率模式开关
},
cameraList: [],//相机列表
isInitialized: false, // 初始化状态
workers: {} // 存储每个相机对应的worker
}
},
methods: {
......@@ -148,6 +134,8 @@ export default {
if (res.data.length > 0) {
let list = [];
res.data.forEach(item => {
const worker = new Worker();
worker.onmessage = (e) => this.handleWorkerMessage(item.camera_id, e.data);
list.push({
id: item.camera_id,
name: item.name,
......@@ -161,7 +149,9 @@ export default {
objectURL: null,
statusInfo: null,
captureLog: '等待连接',
worker, // 存储worker引用
})
this.workers[item.camera_id] = worker;
})
this.cameraList = list;
this.$message.success(`获取到 ${this.cameraList.length} 个相机`)
......@@ -240,179 +230,87 @@ export default {
},
//开始链接webScoke
startStream(camera) {
// 创建canvas用于渲染帧数据
if (camera.websocket) {
camera.websocket.close();
camera.websocket = null;
// 等待一小段时间确保连接完全关闭
setTimeout(() => {
this.establishWebSocketConnection(camera);
}, 100);
} else {
this.establishWebSocketConnection(camera);
}
camera.worker.postMessage({
action: 'connect',
id: camera.id,
baseURL: baseURLConsole2
});
},
establishWebSocketConnection(camera) {
const wsUrl = `ws://${baseURLConsole}/api/cameras/hd_static/ws/connect/${camera.id}`;
camera.websocket = new WebSocket(wsUrl);
camera.reconnectAttempts = 0;
camera.websocket.onopen = (event) => {
camera.captureLog = 'WebSocket已连接,等待相机响应...'
//心跳检测
setInterval(() => {
if (camera.websocket.readyState === WebSocket.OPEN) {
camera.websocket.send('ping');
}
}, 30000);
};
camera.websocket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 处理连接状态消息
if (data.status) {
camera.captureLog = `连接成功: ${data.message}`
return;
}
// 处理错误消息
if (data.error) {
if (data.error.includes('未初始化')) {
camera.captureLog = '请先在"相机控制"部分初始化相机,然后再尝试连接视频流'
// 处理worker消息
handleWorkerMessage(cameraId, data) {
const camera = this.cameraList.find(c => c.id === cameraId);
if (!camera) return;
const { type, message, blob, width, height, performance, encode_time_ms, data_size_kb } = data;
switch (type) {
case 'status':
camera.captureLog = message;
break;
case 'error':
camera.captureLog = message;
break;
case 'frame':
// 使用 createImageBitmap 处理 Blob 数据
createImageBitmap(blob).then(bitmap => {
camera.imageData = bitmap;
this.$nextTick(() => {
this.renderImageToCanvas(camera);
});
// 更新状态信息
let statusInfo = `分辨率: ${width}x${height}`;
if (performance) {
statusInfo += ` | 实际FPS: ${performance.actual_fps} | 目标FPS: ${performance.target_fps}`;
}
if (data.error.includes('已在流式传输中')) {
camera.captureLog = '相机正在使用中,请等待一会儿后再尝试连接'
if (encode_time_ms) {
statusInfo += ` | 编码耗时: ${encode_time_ms}ms`;
}
camera.captureLog = `错误: ${data.error}`
return;
}
// 处理视频帧数据
if (data.data) {
// 创建Image对象加载图像
const img = new Image();
img.onload = () => {
// 获取对应相机的canvas元素
const canvasRef = `canvas_${camera.id}`;
const canvas = this.$refs[canvasRef]?.[0];
if (!canvas) return;
// 获取容器尺寸
const containerWidth = canvas.parentElement.clientWidth;
const containerHeight = canvas.parentElement.clientHeight;
// 计算保持比例的尺寸
let width = img.width;
let height = img.height;
const ratio = width / height;
if (width > containerWidth) {
width = containerWidth;
height = width / ratio;
}
if (height > containerHeight) {
height = containerHeight;
width = height * ratio;
}
// 设置canvas尺寸
canvas.width = width;
canvas.height = height;
// 绘制图像
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, width, height);
// 更新状态信息
let statusInfo = `分辨率: ${img.width}x${img.height} | 显示尺寸: ${Math.round(width)}x${Math.round(height)}`;
if (data.performance) {
statusInfo += ` | 实际FPS: ${data.performance.actual_fps} | 目标FPS: ${data.performance.target_fps}`;
}
if (data.encode_time_ms) {
statusInfo += ` | 编码耗时: ${data.encode_time_ms}ms`;
}
if (data.data_size_kb) {
statusInfo += ` | 数据大小: ${data.data_size_kb}KB`;
}
camera.statusInfo = statusInfo;
img.src = '';
};
img.src = `data:image/jpeg;base64,${data.data}`;
camera.imageData = `data:image/jpeg;base64,${data.data}`;
}
} catch (error) {
camera.captureLog = `解析消息数据时出错: ${error}`
}
if (data_size_kb) {
statusInfo += ` | 数据大小: ${data_size_kb}KB`;
}
camera.statusInfo = statusInfo;
});
break;
}
camera.websocket.onerror = (error) => {
console.log(error)
camera.captureLog = `WebSocket连接错误`
};
camera.websocket.onclose = (event) => {
console.log(`WebSocket连接已关闭 (代码: ${event.code}, 原因: ${event.reason || '未知'})`);
// 不同的关闭代码有不同的含义
let closeReason = '';
switch (event.code) {
case 1000:
closeReason = '正常关闭';
break;
case 1001:
closeReason = '端点离开';
break;
case 1002:
closeReason = '协议错误';
break;
case 1003:
closeReason = '不支持的数据类型';
break;
case 1006:
closeReason = '异常关闭';
break;
case 1011:
closeReason = '服务器错误';
break;
default:
closeReason = `未知错误 (${event.code})`;
}
camera.captureLog = `WebSocket连接已关闭: ${closeReason}`
// 只有在非正常关闭的情况下才自动重连
if (event.code !== 1000 && camera.reconnectAttempts < 5) {
camera.reconnectAttempts++;
const retryDelay = Math.min(1000 * Math.pow(2, camera.reconnectAttempts - 1), 10000); // 指数退避,最大10秒
console.log(`连接异常关闭,${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`);
camera.captureLog = `连接异常关闭,${retryDelay / 1000}秒后尝试重连 (${camera.reconnectAttempts}/5)...`
setTimeout(() => this.startStream(camera), retryDelay);
} else if (event.code === 1000) {
console.log('WebSocket正常关闭,不进行重连');
} else {
console.log('重连次数已达上限,停止重连');
camera.captureLog = '重连失败,请检查相机状态后手动重连'
}
};
},
// 渲染图像到canvas
renderImageToCanvas(camera) {
if (!camera.imageData) return;
const canvas = this.$refs[`canvas_${camera.id}`];
if (!canvas || !canvas[0]) return;
// 计算保持比例的尺寸
const containerWidth = canvas[0].parentElement.clientWidth;
const ratio = camera.imageData.width / camera.imageData.height;
const height = containerWidth / ratio;
// 设置canvas尺寸
canvas[0].width = containerWidth;
canvas[0].height = height;
// 绘制图像
const ctx = canvas[0].getContext('2d');
ctx.clearRect(0, 0, canvas[0].width, canvas[0].height);
ctx.drawImage(camera.imageData, 0, 0, canvas[0].width, canvas[0].height);
},
//断开链接
stopStream(camera) {
//调用接口断开链接
disconnectStillCamera(camera.id).then(res => {
this.$message.success(`断开链接成功`)
if (camera.websocket) {
camera.websocket.close()
this.$nextTick(() => {
camera.websocket = null
camera.reconnectAttempts = 0
camera.imageData = null
camera.objectURL = null
camera.statusInfo = null
camera.captureLog = '等待链接'
})
}
})
this.$message.success(`断开链接成功`);
camera.worker.postMessage({
action: 'disconnect'
});
this.$nextTick(() => {
camera.imageData = null;
camera.statusInfo = null;
camera.captureLog = '等待链接';
});
});
},
//关闭所有相机
......@@ -422,8 +320,7 @@ export default {
this.cameraList.forEach(camera => {
cameraIds.push(camera.id);
if (camera.websocket) {
camera.websocket.close();
camera.websocket = null;
this.stopStream(camera)
}
});
......@@ -434,16 +331,19 @@ export default {
closeStillCamera(param).then(res => {
console.log(res);
this.cameraList = [];
Object.values(this.workers).forEach(worker => worker.terminate());
this.workers = {};
this.isInitialized = false
this.globalParams = {
fps: 30,//帧率
resolution: '2560x1440',//分别率
savePath: '/images/captures',//存储路径
savePath: 'images/captures',//存储路径
lowFpsMode: false,//低帧率模式开关
},
this.$message.success('已关闭所有相机');
})
},
//捕捉帧
captureImage(camera) {
let cameraIds = [];
......@@ -460,26 +360,18 @@ export default {
this.$message.success(`捕获图像成功`)
})
},
base64ToBlob(base64) {
const byteString = atob(base64.split(',')[1]);
const mimeType = base64.match(/:(.*?);/)[1];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
}
},
// 2. 组件销毁时清理
beforeDestroy() {
// 关闭所有相机连接
this.cameraList.forEach(camera => {
if (camera.objectURL) URL.revokeObjectURL(camera.objectURL);
if (camera.websocket) {
camera.websocket.close()
disconnectStillCamera(camera.id)
}
this.disconnectStillCamera(camera.id);
});
// 终止所有worker
Object.values(this.workers).forEach(worker => worker.terminate());
this.workers = {};
}
}
</script>
......
// cameraWorker.js
let socket = null;
let cameraId = null;
function connect(baseURL) {
if (socket) {
socket.close();
}
const wsUrl = `ws://${baseURL}/api/cameras/hd_video/ws/connect/${cameraId}`;
socket = new WebSocket(wsUrl);
socket.onopen = (event) => {
postMessage({
type: 'status',
message: 'WebSocket已连接,等待相机响应...'
});
};
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.status) {
postMessage({
type: 'status',
message: `连接成功: ${data.message}`
});
return;
}
if (data.error) {
postMessage({
type: 'error',
message: data.error.includes('未初始化')
? '请先在"相机控制"部分初始化相机,然后再尝试连接视频流'
: data.error.includes('已在流式传输中')
? '相机正在使用中,请等待一会儿后再尝试连接'
: `错误: ${data.error}`
});
return;
}
if (data.data) {
// 转换base64为Blob
const byteString = atob(data.data);
const mimeType = 'image/jpeg';
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeType });
postMessage({
type: 'frame',
blob,
width: data.width,
height: data.height,
performance: data.performance,
encode_time_ms: data.encode_time_ms,
data_size_kb: data.data_size_kb
});
}
} catch (error) {
postMessage({
type: 'error',
message: `解析消息数据时出错: ${error}`
});
}
};
socket.onerror = (error) => {
postMessage({
type: 'error',
message: 'WebSocket连接错误'
});
};
socket.onclose = (event) => {
let closeReason = '';
switch (event.code) {
case 1000: closeReason = '正常关闭'; break;
case 1001: closeReason = '端点离开'; break;
case 1002: closeReason = '协议错误'; break;
case 1003: closeReason = '不支持的数据类型'; break;
case 1006: closeReason = '异常关闭'; break;
case 1011: closeReason = '服务器错误'; break;
default: closeReason = `未知错误 (${event.code})`;
}
postMessage({
type: 'status',
message: `WebSocket连接已关闭: ${closeReason}`
});
};
}
function disconnect() {
if (socket) {
socket.close();
socket = null;
}
}
self.addEventListener('message', function(e) {
const { action, id, baseURL } = e.data;
// 设置相机ID
cameraId = id;
switch (action) {
case 'connect':
connect(baseURL);
break;
case 'disconnect':
disconnect();
break;
}
});
\ No newline at end of file
// cameraWorker.js
let socket = null;
let cameraId = null;
function connect(baseURL) {
if (socket) {
socket.close();
}
console.log("321",baseURL,cameraId)
const wsUrl = `ws://${baseURL}/api/cameras/hd_static/ws/connect/${cameraId}`;
socket = new WebSocket(wsUrl);
socket.onopen = (event) => {
postMessage({
type: 'status',
message: 'WebSocket已连接,等待相机响应...'
});
};
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.status) {
postMessage({
type: 'status',
message: `连接成功: ${data.message}`
});
return;
}
if (data.error) {
postMessage({
type: 'error',
message: data.error.includes('未初始化')
? '请先在"相机控制"部分初始化相机,然后再尝试连接视频流'
: data.error.includes('已在流式传输中')
? '相机正在使用中,请等待一会儿后再尝试连接'
: `错误: ${data.error}`
});
return;
}
if (data.data) {
// 转换base64为Blob
const byteString = atob(data.data);
const mimeType = 'image/jpeg';
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeType });
postMessage({
type: 'frame',
blob,
width: data.width,
height: data.height,
performance: data.performance,
encode_time_ms: data.encode_time_ms,
data_size_kb: data.data_size_kb
});
}
} catch (error) {
postMessage({
type: 'error',
message: `解析消息数据时出错: ${error}`
});
}
};
socket.onerror = (error) => {
postMessage({
type: 'error',
message: `WebSocket连接错误:${error}`
});
};
socket.onclose = (event) => {
let closeReason = '';
console.log(`websocket关闭:${event.code}`)
switch (event.code) {
case 1000: closeReason = '正常关闭'; break;
case 1001: closeReason = '端点离开'; break;
case 1002: closeReason = '协议错误'; break;
case 1003: closeReason = '不支持的数据类型'; break;
case 1006: closeReason = '异常关闭'; break;
case 1011: closeReason = '服务器错误'; break;
default: closeReason = `未知错误 (${event.code})`;
}
postMessage({
type: 'status',
message: `WebSocket连接已关闭: ${closeReason}`
});
};
}
function disconnect() {
if (socket) {
socket.close();
socket = null;
}
}
self.addEventListener('message', function(e) {
const { action, id, baseURL } = e.data;
console.log("123",action, id, baseURL)
// 设置相机ID
cameraId = id;
switch (action) {
case 'connect':
connect(baseURL);
break;
case 'disconnect':
disconnect();
break;
}
});
\ No newline at end of file
......@@ -14,7 +14,7 @@ export default new Vuex.Store({
nativePlace: 'null',//籍贯
height: 165,//身高
weight: 55,//体重
bookingId: 1,//预约ID
bookingId: null,//预约ID
bkBkTime:'2023-08-10 10:00:00',//预约时间
},
},
......
export const baseURL = '/api'
export const baseURLConsole ='192.168.0.104:8000'
\ No newline at end of file
export const baseURLConsole1 ='192.168.0.102:8000'
export const baseURLConsole2 ='192.168.0.106:8000'
\ No newline at end of file
......@@ -5,5 +5,23 @@ module.exports = defineConfig({
client:{
overlay: false
}
}
},
chainWebpack(config) {
// set worker-loader
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.options({
inline: 'fallback' // 添加这行
})
.end();
// 解决:worker 热更新问题
config.module.rule('js').exclude.add(/\.worker\.js$/);
// 解决:"window is undefined"报错
config.output.globalObject('this');
},
parallel: false,
})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment