mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-09-12 10:34:24 +08:00
fix: 为 RKCIF/RK628 选择受支持的视频格式 (#292)
RK628 通过 RKCIF 接入时,如果 One-KVM 启动时 HDMI 信号尚未锁定, SourceFollowing 路径可能回退到默认 MJPEG。由于 RKCIF 不支持 MJPEG, 后续 S_FMT 无法建立有效的采集链路。 解决方案: 在解析 SourceFollowing 配置后,根据设备的 VIDIOC_ENUM_FMT 枚举结果 校验最终格式。如果该格式不受支持且格式列表非空,则选择按优先级排序 的首个可用格式,对于 RKCIF/RK628 通常为 NV12。 该方法只替换 FourCC,保留 HDMI DV timings 提供的分辨率和帧率。 同时覆盖无信号启动和当前格式已失效两种场景。
This commit is contained in:
@@ -125,6 +125,12 @@ pub fn resolve_video_input_config(
|
|||||||
requested_resolution: Resolution,
|
requested_resolution: Resolution,
|
||||||
requested_fps: u32,
|
requested_fps: u32,
|
||||||
) -> ResolvedVideoInputConfig {
|
) -> ResolvedVideoInputConfig {
|
||||||
|
let mut resolved = ResolvedVideoInputConfig {
|
||||||
|
format: requested_format,
|
||||||
|
resolution: requested_resolution,
|
||||||
|
fps: requested_fps,
|
||||||
|
};
|
||||||
|
|
||||||
if device.control_mode == VideoControlMode::SourceFollowing {
|
if device.control_mode == VideoControlMode::SourceFollowing {
|
||||||
if let VideoInputStatus {
|
if let VideoInputStatus {
|
||||||
state: VideoInputState::Locked,
|
state: VideoInputState::Locked,
|
||||||
@@ -135,26 +141,39 @@ pub fn resolve_video_input_config(
|
|||||||
} = &device.input_status
|
} = &device.input_status
|
||||||
{
|
{
|
||||||
if let Ok(format) = format.parse::<PixelFormat>() {
|
if let Ok(format) = format.parse::<PixelFormat>() {
|
||||||
return ResolvedVideoInputConfig {
|
resolved = ResolvedVideoInputConfig {
|
||||||
format,
|
format,
|
||||||
resolution: Resolution::new(*width, *height),
|
resolution: Resolution::new(*width, *height),
|
||||||
fps: fps.round().clamp(1.0, 120.0) as u32,
|
fps: fps.round().clamp(1.0, 120.0) as u32,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source-following devices do not allow One-KVM to choose the HDMI
|
||||||
|
// resolution or frame rate, but their pixel format still has to be one
|
||||||
|
// of the formats enumerated by the capture node. In particular, rkcif
|
||||||
|
// commonly exposes NV12 but One-KVM's default is MJPEG. Passing that
|
||||||
|
// unsupported default to S_FMT leaves the pipeline in an invalid state.
|
||||||
|
if !device.formats.is_empty()
|
||||||
|
&& !device
|
||||||
|
.formats
|
||||||
|
.iter()
|
||||||
|
.any(|format| format.format == resolved.format)
|
||||||
|
{
|
||||||
|
resolved.format = device.formats[0].format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolvedVideoInputConfig {
|
resolved
|
||||||
format: requested_format,
|
|
||||||
resolution: requested_resolution,
|
|
||||||
fps: requested_fps,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use super::linux::FormatInfo;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn device(control_mode: VideoControlMode, input_status: VideoInputStatus) -> VideoDeviceInfo {
|
fn device(control_mode: VideoControlMode, input_status: VideoInputStatus) -> VideoDeviceInfo {
|
||||||
VideoDeviceInfo {
|
VideoDeviceInfo {
|
||||||
@@ -175,6 +194,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn format(format: PixelFormat) -> FormatInfo {
|
||||||
|
FormatInfo {
|
||||||
|
format,
|
||||||
|
resolutions: Vec::new(),
|
||||||
|
description: format.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn recognizes_vendor_and_upstream_native_hdmirx_names() {
|
fn recognizes_vendor_and_upstream_native_hdmirx_names() {
|
||||||
assert!(is_rk_hdmirx_driver("rk_hdmirx", "rk_hdmirx"));
|
assert!(is_rk_hdmirx_driver("rk_hdmirx", "rk_hdmirx"));
|
||||||
@@ -246,6 +274,48 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn source_following_replaces_unenumerated_default_format_without_signal() {
|
||||||
|
let mut device = device(
|
||||||
|
VideoControlMode::SourceFollowing,
|
||||||
|
VideoInputStatus::no_signal(),
|
||||||
|
);
|
||||||
|
device.formats = vec![format(PixelFormat::Nv12), format(PixelFormat::Yuyv)];
|
||||||
|
|
||||||
|
let resolved = resolve_video_input_config(
|
||||||
|
&device,
|
||||||
|
PixelFormat::Mjpeg,
|
||||||
|
Resolution::new(1920, 1080),
|
||||||
|
30,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(resolved.format, PixelFormat::Nv12);
|
||||||
|
assert_eq!(resolved.resolution, Resolution::new(1920, 1080));
|
||||||
|
assert_eq!(resolved.fps, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn source_following_replaces_stale_active_format_but_keeps_input_mode() {
|
||||||
|
let mut device = device(
|
||||||
|
VideoControlMode::SourceFollowing,
|
||||||
|
VideoInputStatus::locked(PixelFormat::Mjpeg, 1280, 720, 59.94),
|
||||||
|
);
|
||||||
|
device.formats = vec![format(PixelFormat::Nv12), format(PixelFormat::Yuyv)];
|
||||||
|
|
||||||
|
let resolved = resolve_video_input_config(
|
||||||
|
&device,
|
||||||
|
PixelFormat::Mjpeg,
|
||||||
|
Resolution::new(1920, 1080),
|
||||||
|
30,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(resolved.format, PixelFormat::Nv12);
|
||||||
|
assert_eq!(resolved.resolution, Resolution::new(1280, 720));
|
||||||
|
assert_eq!(resolved.fps, 60);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_signal_and_unavailable_never_expose_stale_mode_fields() {
|
fn no_signal_and_unavailable_never_expose_stale_mode_fields() {
|
||||||
for status in [
|
for status in [
|
||||||
|
|||||||
Reference in New Issue
Block a user