mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-04-29 17:36:35 +08:00
fix: 修复 MJPEG模式可用但状态显示离线的问题
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -42,3 +42,4 @@ CLAUDE.md
|
|||||||
secrets.toml
|
secrets.toml
|
||||||
.env
|
.env
|
||||||
/docs/
|
/docs/
|
||||||
|
web/package-lock.json
|
||||||
|
|||||||
@@ -259,6 +259,30 @@ export const useSystemStore = defineStore('system', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchStreamState() {
|
||||||
|
try {
|
||||||
|
const [status, modeResp] = await Promise.all([
|
||||||
|
streamApi.status(),
|
||||||
|
streamApi.getMode().catch(() => ({ mode: 'mjpeg' }))
|
||||||
|
])
|
||||||
|
stream.value = {
|
||||||
|
online: status.state === 'streaming',
|
||||||
|
active: status.state !== 'uninitialized',
|
||||||
|
device: status.device,
|
||||||
|
format: status.format,
|
||||||
|
resolution: status.resolution,
|
||||||
|
targetFps: status.target_fps,
|
||||||
|
clients: status.clients,
|
||||||
|
streamMode: modeResp.mode || 'mjpeg',
|
||||||
|
error: status.state === 'error' ? 'Stream error' : null,
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to fetch stream state:', e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchAllStates() {
|
async function fetchAllStates() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
@@ -266,7 +290,8 @@ export const useSystemStore = defineStore('system', () => {
|
|||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchSystemInfo(),
|
fetchSystemInfo(),
|
||||||
// HID state is updated via WebSocket device_info event
|
fetchStreamState().catch(() => null),
|
||||||
|
fetchHidState().catch(() => null),
|
||||||
fetchAtxState().catch(() => null),
|
fetchAtxState().catch(() => null),
|
||||||
fetchMsdState().catch(() => null),
|
fetchMsdState().catch(() => null),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ const videoLoading = ref(true)
|
|||||||
const videoError = ref(false)
|
const videoError = ref(false)
|
||||||
const videoErrorMessage = ref('')
|
const videoErrorMessage = ref('')
|
||||||
const videoRestarting = ref(false) // Track if video is restarting due to config change
|
const videoRestarting = ref(false) // Track if video is restarting due to config change
|
||||||
|
const mjpegFrameReceived = ref(false) // Whether MJPEG stream has received at least one frame
|
||||||
|
|
||||||
// Video aspect ratio (dynamically updated from actual video dimensions)
|
// Video aspect ratio (dynamically updated from actual video dimensions)
|
||||||
// Using string format "width/height" to let browser handle the ratio calculation
|
// Using string format "width/height" to let browser handle the ratio calculation
|
||||||
@@ -188,6 +189,11 @@ const videoStatus = computed<'connected' | 'connecting' | 'disconnected' | 'erro
|
|||||||
if (webrtc.isConnecting.value) return 'connecting'
|
if (webrtc.isConnecting.value) return 'connecting'
|
||||||
if (webrtc.isConnected.value) return 'connected'
|
if (webrtc.isConnected.value) return 'connected'
|
||||||
}
|
}
|
||||||
|
// MJPEG: check if frames have actually arrived (frontend-side detection)
|
||||||
|
// This is more reliable than relying on stream.online from backend,
|
||||||
|
// which can be stale due to the debounce delay in device_info broadcaster.
|
||||||
|
// Also handles browsers that don't fire img.onload for multipart MJPEG streams.
|
||||||
|
if (videoMode.value === 'mjpeg' && mjpegFrameReceived.value) return 'connected'
|
||||||
if (systemStore.stream?.online) return 'connected'
|
if (systemStore.stream?.online) return 'connected'
|
||||||
return 'disconnected'
|
return 'disconnected'
|
||||||
})
|
})
|
||||||
@@ -680,6 +686,7 @@ function handleVideoLoad() {
|
|||||||
// MJPEG video frame loaded successfully - update stream online status
|
// MJPEG video frame loaded successfully - update stream online status
|
||||||
// This fixes the timing issue where device_info event may arrive before stream is fully active
|
// This fixes the timing issue where device_info event may arrive before stream is fully active
|
||||||
if (videoMode.value === 'mjpeg') {
|
if (videoMode.value === 'mjpeg') {
|
||||||
|
mjpegFrameReceived.value = true
|
||||||
systemStore.setStreamOnline(true)
|
systemStore.setStreamOnline(true)
|
||||||
// Update aspect ratio from MJPEG image dimensions
|
// Update aspect ratio from MJPEG image dimensions
|
||||||
const img = videoRef.value
|
const img = videoRef.value
|
||||||
@@ -758,6 +765,7 @@ function handleVideoError() {
|
|||||||
|
|
||||||
// Show loading state immediately
|
// Show loading state immediately
|
||||||
videoLoading.value = true
|
videoLoading.value = true
|
||||||
|
mjpegFrameReceived.value = false
|
||||||
|
|
||||||
// Auto-retry with exponential backoff (infinite retry, capped delay)
|
// Auto-retry with exponential backoff (infinite retry, capped delay)
|
||||||
retryCount++
|
retryCount++
|
||||||
@@ -1062,6 +1070,7 @@ function refreshVideo() {
|
|||||||
backendFps.value = 0
|
backendFps.value = 0
|
||||||
videoError.value = false
|
videoError.value = false
|
||||||
videoErrorMessage.value = ''
|
videoErrorMessage.value = ''
|
||||||
|
mjpegFrameReceived.value = false
|
||||||
|
|
||||||
// Update timestamp to force MJPEG reconnection via reactive URL
|
// Update timestamp to force MJPEG reconnection via reactive URL
|
||||||
isRefreshingVideo = true
|
isRefreshingVideo = true
|
||||||
@@ -2061,6 +2070,11 @@ async function activateConsoleView() {
|
|||||||
isConsoleActive.value = true
|
isConsoleActive.value = true
|
||||||
registerInteractionListeners()
|
registerInteractionListeners()
|
||||||
|
|
||||||
|
// Ensure HID WebSocket is connected when console becomes active
|
||||||
|
if (!hidWs.connected.value) {
|
||||||
|
hidWs.connect().catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
if (videoMode.value !== 'mjpeg' && webrtc.videoTrack.value) {
|
if (videoMode.value !== 'mjpeg' && webrtc.videoTrack.value) {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await rebindWebRTCVideo()
|
await rebindWebRTCVideo()
|
||||||
|
|||||||
Reference in New Issue
Block a user