mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
feat(video): 事务化切换与前端统一编排,增强视频输入格式支持
- 后端:切换事务+transition_id,/stream/mode 返回 switching/transition_id 与实际 codec - 事件:新增 mode_switching/mode_ready,config/webrtc_ready/mode_changed 关联事务 - 编码/格式:扩展 NV21/NV16/NV24/RGB/BGR 输入与转换链路,RKMPP direct input 优化 - 前端:useVideoSession 统一切换,失败回退真实切回 MJPEG,菜单格式同步修复 - 清理:useVideoStream 降级为 MJPEG-only
This commit is contained in:
@@ -189,6 +189,7 @@ const selectedFormat = ref<string>('')
|
||||
const selectedResolution = ref<string>('')
|
||||
const selectedFps = ref<number>(30)
|
||||
const selectedBitratePreset = ref<'Speed' | 'Balanced' | 'Quality'>('Balanced')
|
||||
const isDirty = ref(false)
|
||||
|
||||
// UI state
|
||||
const applying = ref(false)
|
||||
@@ -327,6 +328,25 @@ function initializeFromCurrent() {
|
||||
selectedFormat.value = config.format
|
||||
selectedResolution.value = `${config.width}x${config.height}`
|
||||
selectedFps.value = config.fps
|
||||
isDirty.value = false
|
||||
}
|
||||
|
||||
function syncFromCurrentIfChanged() {
|
||||
const config = currentConfig.value
|
||||
const nextResolution = `${config.width}x${config.height}`
|
||||
|
||||
if (selectedDevice.value === config.device
|
||||
&& selectedFormat.value === config.format
|
||||
&& selectedResolution.value === nextResolution
|
||||
&& selectedFps.value === config.fps) {
|
||||
return
|
||||
}
|
||||
|
||||
selectedDevice.value = config.device
|
||||
selectedFormat.value = config.format
|
||||
selectedResolution.value = nextResolution
|
||||
selectedFps.value = config.fps
|
||||
isDirty.value = false
|
||||
}
|
||||
|
||||
// Handle video mode change
|
||||
@@ -339,6 +359,7 @@ function handleVideoModeChange(mode: unknown) {
|
||||
function handleDeviceChange(devicePath: unknown) {
|
||||
if (typeof devicePath !== 'string') return
|
||||
selectedDevice.value = devicePath
|
||||
isDirty.value = true
|
||||
|
||||
// Auto-select first format
|
||||
const device = devices.value.find(d => d.path === devicePath)
|
||||
@@ -358,6 +379,7 @@ function handleDeviceChange(devicePath: unknown) {
|
||||
function handleFormatChange(format: unknown) {
|
||||
if (typeof format !== 'string') return
|
||||
selectedFormat.value = format
|
||||
isDirty.value = true
|
||||
|
||||
// Auto-select first resolution for this format
|
||||
const formatData = availableFormats.value.find(f => f.format === format)
|
||||
@@ -372,6 +394,7 @@ function handleFormatChange(format: unknown) {
|
||||
function handleResolutionChange(resolution: unknown) {
|
||||
if (typeof resolution !== 'string') return
|
||||
selectedResolution.value = resolution
|
||||
isDirty.value = true
|
||||
|
||||
// Auto-select first FPS for this resolution
|
||||
const resolutionData = availableResolutions.value.find(
|
||||
@@ -386,6 +409,7 @@ function handleResolutionChange(resolution: unknown) {
|
||||
function handleFpsChange(fps: unknown) {
|
||||
if (typeof fps !== 'string' && typeof fps !== 'number') return
|
||||
selectedFps.value = typeof fps === 'string' ? Number(fps) : fps
|
||||
isDirty.value = true
|
||||
}
|
||||
|
||||
// Apply bitrate preset change
|
||||
@@ -427,6 +451,7 @@ async function applyVideoConfig() {
|
||||
})
|
||||
|
||||
toast.success(t('config.applied'))
|
||||
isDirty.value = false
|
||||
// Stream state will be updated via WebSocket system.device_info event
|
||||
} catch (e) {
|
||||
console.info('[VideoConfig] Failed to apply config:', e)
|
||||
@@ -455,8 +480,17 @@ watch(() => props.open, (isOpen) => {
|
||||
loadEncoderBackend()
|
||||
// Initialize from current config
|
||||
initializeFromCurrent()
|
||||
} else {
|
||||
isDirty.value = false
|
||||
}
|
||||
})
|
||||
|
||||
// Sync selected values when backend config changes (e.g., auto format switch on mode change)
|
||||
watch(currentConfig, () => {
|
||||
if (applying.value) return
|
||||
if (props.open && isDirty.value) return
|
||||
syncFromCurrentIfChanged()
|
||||
}, { deep: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user