|
|
|
|
@ -35,7 +35,6 @@
|
|
|
|
|
:webrtc-url="webrtcUrl"
|
|
|
|
|
:offline-video-url="offlineVideoUrl"
|
|
|
|
|
:video-autoplay-trigger="videoAutoplayTrigger"
|
|
|
|
|
@connection-change="onConnectionChange"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<DetectionProgress
|
|
|
|
|
@ -58,6 +57,21 @@
|
|
|
|
|
<div class="right-section">
|
|
|
|
|
<ChartsPanel :process-stats="processStats" :counts="detectCounts" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- SOP 未完成弹窗 -->
|
|
|
|
|
<el-dialog
|
|
|
|
|
v-model="sopAlert.visible"
|
|
|
|
|
:title="sopAlert.title"
|
|
|
|
|
width="500px"
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
:close-on-press-escape="false"
|
|
|
|
|
draggable
|
|
|
|
|
>
|
|
|
|
|
<div style="white-space: pre-line; line-height: 1.8;">{{ sopAlert.content }}</div>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button type="primary" @click="sopAlert.visible = false">知道了</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
@ -78,26 +92,328 @@ import { downloadAsZip } from '@/utils/zip.js'
|
|
|
|
|
|
|
|
|
|
// ============ 视频流相关 ============
|
|
|
|
|
const videoMode = ref('idle')
|
|
|
|
|
const streamAddress = ref('')
|
|
|
|
|
const webrtcUrl = computed(() => {
|
|
|
|
|
return import.meta.env.VITE_WEBRTC_URL || `http://${streamAddress.value}/stream`
|
|
|
|
|
})
|
|
|
|
|
const streamAddress = ref('rtsp://10.23.22.97:8556/camera2')
|
|
|
|
|
const webrtcUrl = ref('')
|
|
|
|
|
const streamRunning = ref(false)
|
|
|
|
|
const isLiveMode = computed(() => videoMode.value === 'live')
|
|
|
|
|
const RAW_WS_URL = 'ws://10.23.22.43:8001/ws/raw_rtsp'
|
|
|
|
|
const DETECT_WS_URL = 'ws://10.23.22.43:8001/ws/detect_rtsp'
|
|
|
|
|
let rawWsClient = null
|
|
|
|
|
let detectWsClient = null
|
|
|
|
|
|
|
|
|
|
function destroyRawWs() {
|
|
|
|
|
if (rawWsClient) {
|
|
|
|
|
rawWsClient.destroy()
|
|
|
|
|
rawWsClient = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function destroyDetectWs() {
|
|
|
|
|
if (detectWsClient) {
|
|
|
|
|
detectWsClient.destroy()
|
|
|
|
|
detectWsClient = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stopRawStream() {
|
|
|
|
|
if (rawWsClient && rawWsClient.getReadyState() === WebSocket.OPEN) {
|
|
|
|
|
rawWsClient.send({ type: 'stop' })
|
|
|
|
|
}
|
|
|
|
|
destroyRawWs()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let detectStopCallback = null
|
|
|
|
|
|
|
|
|
|
function stopDetectStream(onDone) {
|
|
|
|
|
if (detectWsClient && detectWsClient.getReadyState() === WebSocket.OPEN) {
|
|
|
|
|
detectStopCallback = onDone || null
|
|
|
|
|
detectWsClient.send({ type: 'stop' })
|
|
|
|
|
addLog('已发送停止请求,等待检测结果...', 'info')
|
|
|
|
|
} else {
|
|
|
|
|
// 没有活跃的检测连接,立即回调
|
|
|
|
|
if (onDone) onDone()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildRtspUrl(input) {
|
|
|
|
|
return input.startsWith('rtsp://') ? input : `rtsp://${input}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function connectRawStream() {
|
|
|
|
|
destroyRawWs()
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
addLog('正在连接原始流服务...', 'info')
|
|
|
|
|
|
|
|
|
|
rawWsClient = new WebSocketClient({
|
|
|
|
|
url: RAW_WS_URL,
|
|
|
|
|
autoReconnect: false,
|
|
|
|
|
heartbeatInterval: 0,
|
|
|
|
|
heartbeatTimeout: 0,
|
|
|
|
|
debug: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rawWsClient.on('open', () => {
|
|
|
|
|
addLog('原始流服务已连接,请求流地址', 'info')
|
|
|
|
|
rawWsClient.send({ type: 'start_raw' })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rawWsClient.on('message', (msg) => {
|
|
|
|
|
switch (msg.type) {
|
|
|
|
|
case 'ready':
|
|
|
|
|
addLog(msg.message || '原始流服务就绪', 'info')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'processing_started':
|
|
|
|
|
addLog(msg.message || '原始流发布已启动', 'info')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'webrtc_stream':
|
|
|
|
|
webrtcUrl.value = msg.webrtc_url
|
|
|
|
|
addLog('已获取原始 WebRTC 流,开始播放', 'success')
|
|
|
|
|
ElMessage.success('原始视频流已连接')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'stopping':
|
|
|
|
|
addLog(msg.message || '原始流正在关闭', 'warning')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'error':
|
|
|
|
|
addLog('流服务错误: ' + (msg.message || ''), 'error')
|
|
|
|
|
ElMessage.error(msg.message || '流服务出错')
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rawWsClient.on('close', () => {
|
|
|
|
|
addLog('原始流连接已关闭', 'warning')
|
|
|
|
|
if (videoMode.value === 'live' && !algorithmRunning.value) {
|
|
|
|
|
streamRunning.value = false
|
|
|
|
|
videoMode.value = 'idle'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rawWsClient.on('error', () => {
|
|
|
|
|
addLog('原始流连接失败', 'error')
|
|
|
|
|
ElMessage.error('连接原始流服务失败')
|
|
|
|
|
if (!algorithmRunning.value) {
|
|
|
|
|
videoMode.value = 'idle'
|
|
|
|
|
streamRunning.value = false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
rawWsClient.connect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function connectDetectStream() {
|
|
|
|
|
destroyDetectWs()
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
addLog('正在连接检测流服务...', 'info')
|
|
|
|
|
|
|
|
|
|
// 两步握手状态: 0-初始, 1-已发送switch, 2-已发送start_rtsp
|
|
|
|
|
let handshakeStep = 0
|
|
|
|
|
|
|
|
|
|
detectWsClient = new WebSocketClient({
|
|
|
|
|
url: DETECT_WS_URL,
|
|
|
|
|
autoReconnect: false,
|
|
|
|
|
heartbeatInterval: 0,
|
|
|
|
|
heartbeatTimeout: 0,
|
|
|
|
|
debug: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
detectWsClient.on('open', () => {
|
|
|
|
|
addLog('检测流服务已连接,开启算法开关...', 'info')
|
|
|
|
|
handshakeStep = 1
|
|
|
|
|
detectWsClient.send({ type: 'switch', enabled: true })
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
detectWsClient.on('message', (msg) => {
|
|
|
|
|
switch (msg.type) {
|
|
|
|
|
case 'ready':
|
|
|
|
|
addLog(msg.message || '检测流服务就绪', 'info')
|
|
|
|
|
// 如果 open 已发 switch 但 ready 晚到, 忽略
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'switch':
|
|
|
|
|
if (handshakeStep === 1) {
|
|
|
|
|
// switch 响应收到,发送 start_rtsp 开始检测
|
|
|
|
|
addLog('算法开关已开启,发送 RTSP 地址', 'info')
|
|
|
|
|
handshakeStep = 2
|
|
|
|
|
detectWsClient.send({
|
|
|
|
|
type: 'start_rtsp',
|
|
|
|
|
rtsp_url: buildRtspUrl(streamAddress.value),
|
|
|
|
|
options: {
|
|
|
|
|
conf: 0.6,
|
|
|
|
|
iou: 0.45,
|
|
|
|
|
skip: 5,
|
|
|
|
|
imgsz: 640,
|
|
|
|
|
save_video: true,
|
|
|
|
|
publish_webrtc: true,
|
|
|
|
|
publish_fps: 25
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'processing_started':
|
|
|
|
|
addLog(msg.message || 'RTSP 检测已启动', 'success')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'webrtc_stream':
|
|
|
|
|
webrtcUrl.value = msg.webrtc_url
|
|
|
|
|
addLog('已获取检测 WebRTC 流,开始播放', 'success')
|
|
|
|
|
ElMessage.success('检测流已连接')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'pause':
|
|
|
|
|
paused.value = Boolean(msg.paused)
|
|
|
|
|
addLog(msg.message || (paused.value ? '检测流已暂停' : '检测流已恢复'), 'warning')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'counts':
|
|
|
|
|
mergeCounts(msg.counts || [])
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'progress':
|
|
|
|
|
if (msg.frame) {
|
|
|
|
|
const progress = msg.total_frames > 0
|
|
|
|
|
? (msg.frame / msg.total_frames * 100)
|
|
|
|
|
: 0
|
|
|
|
|
detectProgress.value = Math.round(progress)
|
|
|
|
|
detectStatusText.value = msg.is_rtsp
|
|
|
|
|
? `处理中 (第 ${msg.frame} 帧)`
|
|
|
|
|
: `处理中 (${msg.frame}/${msg.total_frames || '?'})`
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'log':
|
|
|
|
|
addLog(msg.message || '', 'info')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'process_status':
|
|
|
|
|
addLog(msg.message || '', 'warning')
|
|
|
|
|
// 更新流程统计
|
|
|
|
|
processStats.completed += msg.is_complete ? 1 : 0
|
|
|
|
|
processStats.incomplete += msg.is_complete ? 0 : 1
|
|
|
|
|
if (msg.should_alert || !msg.is_complete) {
|
|
|
|
|
const detail = (msg.missing_parts || [])
|
|
|
|
|
.map(item => `步骤${item.step_num} ${item.step_name}:${item.part_name}`)
|
|
|
|
|
.join('\n')
|
|
|
|
|
Object.assign(sopAlert, {
|
|
|
|
|
visible: true,
|
|
|
|
|
title: `SOP 流程 #${msg.process_id} 未完成`,
|
|
|
|
|
content: detail
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'stopping':
|
|
|
|
|
addLog(msg.message || '检测流正在关闭', 'warning')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'done':
|
|
|
|
|
addLog(msg.message || '检测完成', 'success')
|
|
|
|
|
// 切换为离线视频模式,显示处理后的结果视频(类似离线视频逻辑)
|
|
|
|
|
if (msg.output_video_url) {
|
|
|
|
|
const videoUrl = HTTP_BASE.replace(/\/$/, '') + msg.output_video_url
|
|
|
|
|
if (offlineVideoUrl.value) {
|
|
|
|
|
URL.revokeObjectURL(offlineVideoUrl.value)
|
|
|
|
|
}
|
|
|
|
|
offlineVideoUrl.value = videoUrl
|
|
|
|
|
videoMode.value = 'offline'
|
|
|
|
|
detectStatusText.value = '检测完成'
|
|
|
|
|
detectProgress.value = 100
|
|
|
|
|
addLog('检测完成,视频已更新', 'success')
|
|
|
|
|
// 流程统计(类似离线视频 done 处理)
|
|
|
|
|
const processes = msg.processes || []
|
|
|
|
|
processStats.completed = processes.filter(p => p.is_complete).length
|
|
|
|
|
processStats.incomplete = processes.filter(p => !p.is_complete).length
|
|
|
|
|
videoAutoplayTrigger.value++
|
|
|
|
|
exportVideoUrl.value = videoUrl
|
|
|
|
|
}
|
|
|
|
|
if (msg.report_url) {
|
|
|
|
|
exportReportUrl.value = HTTP_BASE.replace(/\/$/, '') + msg.report_url
|
|
|
|
|
}
|
|
|
|
|
if (msg.counts) mergeCounts(msg.counts)
|
|
|
|
|
if (exportVideoUrl.value || exportReportUrl.value) {
|
|
|
|
|
canExport.value = true
|
|
|
|
|
}
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
paused.value = false
|
|
|
|
|
destroyDetectWs()
|
|
|
|
|
// 执行回调
|
|
|
|
|
if (detectStopCallback) {
|
|
|
|
|
const cb = detectStopCallback
|
|
|
|
|
detectStopCallback = null
|
|
|
|
|
cb()
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
case 'error':
|
|
|
|
|
addLog('检测流错误: ' + (msg.message || ''), 'error')
|
|
|
|
|
ElMessage.error(msg.message || '检测流出错')
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
destroyDetectWs()
|
|
|
|
|
connectRawStream()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
detectWsClient.on('close', () => {
|
|
|
|
|
addLog('检测流连接已关闭', 'warning')
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
detectWsClient.on('error', () => {
|
|
|
|
|
addLog('检测流连接失败', 'error')
|
|
|
|
|
ElMessage.error('连接检测流服务失败')
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
detectWsClient.connect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function switchStream() {
|
|
|
|
|
if (!streamAddress.value) {
|
|
|
|
|
ElMessage.warning('请输入视频流地址')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
stopRawStream()
|
|
|
|
|
videoMode.value = 'live'
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
paused.value = false
|
|
|
|
|
clearAllLiveData()
|
|
|
|
|
stopDetectStream(() => connectRawStream())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleStream() {
|
|
|
|
|
streamRunning.value = !streamRunning.value
|
|
|
|
|
if (streamRunning.value) {
|
|
|
|
|
// 防呆:如果已有上传的离线视频,清空它,避免启动算法按钮未禁用
|
|
|
|
|
// 关闭视频流
|
|
|
|
|
stopRawStream()
|
|
|
|
|
if (algorithmRunning.value) {
|
|
|
|
|
// 算法正在运行,等待 done 后自动切换到结果视频
|
|
|
|
|
stopDetectStream(() => {
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
streamRunning.value = false
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
paused.value = false
|
|
|
|
|
ending.value = false
|
|
|
|
|
ElMessage.info('视频流已关闭')
|
|
|
|
|
addLog('视频检测完成,已切换到结果视频', 'info')
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
stopDetectStream()
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
streamRunning.value = false
|
|
|
|
|
videoMode.value = 'idle'
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
paused.value = false
|
|
|
|
|
ending.value = false
|
|
|
|
|
ElMessage.info('视频流已关闭')
|
|
|
|
|
addLog('视频流已关闭', 'info')
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 启动视频流
|
|
|
|
|
if (offlineVideoUrl.value) {
|
|
|
|
|
URL.revokeObjectURL(offlineVideoUrl.value)
|
|
|
|
|
offlineVideoUrl.value = ''
|
|
|
|
|
@ -105,18 +421,23 @@ function toggleStream() {
|
|
|
|
|
resetDetectionState()
|
|
|
|
|
addLog('已清空离线视频文件,如需检测请重新上传', 'warning')
|
|
|
|
|
}
|
|
|
|
|
if (!streamAddress.value) {
|
|
|
|
|
ElMessage.warning('请先输入视频流地址')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
streamRunning.value = true
|
|
|
|
|
videoMode.value = 'live'
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
clearAllLiveData()
|
|
|
|
|
} else {
|
|
|
|
|
videoMode.value = 'idle'
|
|
|
|
|
ElMessage.info('视频流已关闭')
|
|
|
|
|
// 如果原始流还在运行(由"切换"启动),先关闭
|
|
|
|
|
if (rawWsClient) {
|
|
|
|
|
addLog('正在关闭原始流,切换到推算流...', 'info')
|
|
|
|
|
stopRawStream()
|
|
|
|
|
}
|
|
|
|
|
connectDetectStream()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onConnectionChange(status) {
|
|
|
|
|
streamRunning.value = status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============ 离线视频相关 ============
|
|
|
|
|
const offlineVideoUrl = ref('')
|
|
|
|
|
const detectionFile = ref(null)
|
|
|
|
|
@ -153,6 +474,7 @@ const DEFAULT_COUNTS = [
|
|
|
|
|
]
|
|
|
|
|
const detectCounts = reactive(DEFAULT_COUNTS.map(c => ({ ...c })))
|
|
|
|
|
const processStats = reactive({ completed: 0, incomplete: 0 })
|
|
|
|
|
const sopAlert = reactive({ visible: false, title: '', content: '' })
|
|
|
|
|
|
|
|
|
|
function mergeCounts(newCounts) {
|
|
|
|
|
newCounts.forEach(newItem => {
|
|
|
|
|
@ -232,6 +554,18 @@ function toggleAlgorithm() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 直播模式下关闭算法 → 停止检测流,等待 done 后切换到结果视频
|
|
|
|
|
if (algorithmRunning.value && videoMode.value === 'live') {
|
|
|
|
|
addLog('算法已关闭,等待检测结果...', 'info')
|
|
|
|
|
stopDetectStream(() => {
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
paused.value = false
|
|
|
|
|
ElMessage.info('算法已关闭')
|
|
|
|
|
addLog('检测结果已保存,切换到结果视频', 'info')
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
algorithmRunning.value = !algorithmRunning.value
|
|
|
|
|
|
|
|
|
|
if (algorithmRunning.value) {
|
|
|
|
|
@ -243,7 +577,6 @@ function toggleAlgorithm() {
|
|
|
|
|
resetDetectionState()
|
|
|
|
|
detectStatusText.value = '正在连接 WebSocket...'
|
|
|
|
|
addLog('开始连接算法检测服务', 'info')
|
|
|
|
|
// 触发视频自动播放
|
|
|
|
|
videoAutoplayTrigger.value++
|
|
|
|
|
|
|
|
|
|
wsClient = new WebSocketClient({
|
|
|
|
|
@ -331,11 +664,9 @@ function toggleAlgorithm() {
|
|
|
|
|
detectStatusText.value = '检测完成'
|
|
|
|
|
detectProgress.value = 100
|
|
|
|
|
addLog('检测完成,视频已更新', 'success')
|
|
|
|
|
// 更新流程统计(饼图数据)
|
|
|
|
|
const processes = msg.processes || []
|
|
|
|
|
processStats.completed = processes.filter(p => p.is_complete).length
|
|
|
|
|
processStats.incomplete = processes.filter(p => !p.is_complete).length
|
|
|
|
|
// 更新检测计数(柱状图数据)
|
|
|
|
|
mergeCounts(msg.counts || [])
|
|
|
|
|
ElMessage.success('视频检测完成')
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
@ -343,7 +674,6 @@ function toggleAlgorithm() {
|
|
|
|
|
ending.value = false
|
|
|
|
|
wsClient.close()
|
|
|
|
|
videoAutoplayTrigger.value++
|
|
|
|
|
// 保存导出数据
|
|
|
|
|
exportVideoUrl.value = videoUrl
|
|
|
|
|
exportReportUrl.value = msg.report_url ? (HTTP_BASE.replace(/\/$/, '') + msg.report_url) : ''
|
|
|
|
|
canExport.value = true
|
|
|
|
|
@ -379,6 +709,23 @@ function toggleAlgorithm() {
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
wsClient.connect()
|
|
|
|
|
} else if (videoMode.value === 'live') {
|
|
|
|
|
// 直播模式:检测流已在"启动视频流"中连接,仅更新状态
|
|
|
|
|
algorithmRunning.value = true
|
|
|
|
|
paused.value = false
|
|
|
|
|
ending.value = false
|
|
|
|
|
flowLogs.value = []
|
|
|
|
|
resetDetectionState()
|
|
|
|
|
addLog('算法检测已启用', 'success')
|
|
|
|
|
if (!detectWsClient && !rawWsClient) {
|
|
|
|
|
// 如果还没有任何流连接,主动连接检测流
|
|
|
|
|
connectDetectStream()
|
|
|
|
|
} else if (rawWsClient) {
|
|
|
|
|
// 如果当前是原始流,切换到检测流
|
|
|
|
|
addLog('正在关闭原始流,切换到检测流...', 'info')
|
|
|
|
|
stopRawStream()
|
|
|
|
|
connectDetectStream()
|
|
|
|
|
}
|
|
|
|
|
} else if (videoMode.value === 'offline' && !detectionFile.value) {
|
|
|
|
|
ElMessage.warning('请先上传离线视频')
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
@ -408,6 +755,22 @@ function toggleAlgorithm() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function togglePause() {
|
|
|
|
|
if (isLiveMode.value && algorithmRunning.value) {
|
|
|
|
|
// 直播检测模式 → 用检测流 WS
|
|
|
|
|
if (!detectWsClient || detectWsClient.getReadyState() !== WebSocket.OPEN) return
|
|
|
|
|
paused.value = !paused.value
|
|
|
|
|
detectWsClient.send({ type: 'pause', paused: paused.value })
|
|
|
|
|
addLog(paused.value ? '发送暂停请求...' : '发送恢复请求...', 'info')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (isLiveMode.value) {
|
|
|
|
|
// 原始流模式 → 用原始流 WS
|
|
|
|
|
if (!rawWsClient || rawWsClient.getReadyState() !== WebSocket.OPEN) return
|
|
|
|
|
paused.value = !paused.value
|
|
|
|
|
rawWsClient.send({ type: 'pause', paused: paused.value })
|
|
|
|
|
addLog(paused.value ? '发送暂停请求...' : '发送恢复请求...', 'info')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!wsClient || wsClient.getReadyState() !== WebSocket.OPEN) return
|
|
|
|
|
paused.value = !paused.value
|
|
|
|
|
wsClient.send({ type: 'pause', paused: paused.value })
|
|
|
|
|
@ -444,9 +807,12 @@ onUnmounted(() => {
|
|
|
|
|
URL.revokeObjectURL(offlineVideoUrl.value)
|
|
|
|
|
}
|
|
|
|
|
if (wsClient) {
|
|
|
|
|
wsClient.send({ type: 'stop' })
|
|
|
|
|
wsClient.destroy()
|
|
|
|
|
wsClient = null
|
|
|
|
|
}
|
|
|
|
|
stopRawStream()
|
|
|
|
|
stopDetectStream()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|