diff --git a/examples/camera_demo.rs b/examples/camera_demo.rs index 1b0592c..182f48e 100644 --- a/examples/camera_demo.rs +++ b/examples/camera_demo.rs @@ -128,9 +128,9 @@ fn create_demo_config() -> Config { // 根据操作系统和设备类型自动选择合适的设备路径 let device = if is_raspberry_pi { - // 使用特定的GStreamer pipeline字符串在树莓派上适配摄像头 - // 这个pipeline使用v4l2src作为源,并确保正确初始化 - "v4l2src device=/dev/video0 ! video/x-raw,width=1280,height=720,framerate=30/1 ! videoconvert ! appsink".to_string() + // 使用更简单的GStreamer pipeline,尝试不同配置方式和格式 + // 此pipeline尝试放宽限制,避免严格的格式约束 + "v4l2src ! videoconvert ! appsink".to_string() } else if std::env::consts::OS == "linux" { "/dev/video0".to_string() } else if std::env::consts::OS == "macos" { diff --git a/src/camera/controller.rs b/src/camera/controller.rs index 966ca56..4d1f0b9 100644 --- a/src/camera/controller.rs +++ b/src/camera/controller.rs @@ -111,17 +111,10 @@ impl CameraController { // 在树莓派上但没有使用GStreamer pipeline,尝试使用pipeline重新初始化 warn!("Running on Raspberry Pi without GStreamer pipeline. Attempting to reinitialize with GStreamer..."); - // 创建GStreamer pipeline - let device_num = self.settings.device.strip_prefix("/dev/video") - .unwrap_or("0"); + // 创建更简单的GStreamer pipeline + let pipeline = "v4l2src ! videoconvert ! appsink".to_string(); - let pipeline = format!( - "v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink", - device_num, self.settings.resolution.dimensions().0, - self.settings.resolution.dimensions().1, self.settings.fps - ); - - info!("Trying GStreamer pipeline: {}", pipeline); + info!("Trying simple GStreamer pipeline: {}", pipeline); self.settings.device = pipeline; // 使用 Box::pin 来解决递归 async 调用问题 @@ -189,17 +182,10 @@ impl CameraController { // 关闭当前相机 self.camera = None; - // 创建GStreamer pipeline - let device_num = self.settings.device.strip_prefix("/dev/video") - .unwrap_or("0"); + // 创建更简单的GStreamer pipeline + let pipeline = "v4l2src ! videoconvert ! appsink".to_string(); - let pipeline = format!( - "v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink", - device_num, self.settings.resolution.dimensions().0, - self.settings.resolution.dimensions().1, self.settings.fps - ); - - info!("Trying GStreamer pipeline: {}", pipeline); + info!("Trying simple GStreamer pipeline: {}", pipeline); self.settings.device = pipeline; // 重新初始化相机 @@ -299,15 +285,10 @@ impl CameraController { let use_gstreamer = is_raspberry_pi && !device.contains("!"); if use_gstreamer { - // 创建GStreamer pipeline - let device_num = device.strip_prefix("/dev/video").unwrap_or("0"); + // 创建更简单的GStreamer pipeline + let pipeline = "v4l2src ! videoconvert ! appsink".to_string(); - let pipeline = format!( - "v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink", - device_num, 1280, 720, fps - ); - - error!("Raspberry Pi detected - trying GStreamer pipeline: {}", pipeline); + error!("Raspberry Pi detected - trying simple GStreamer pipeline: {}", pipeline); // 尝试使用GStreamer打开摄像头 match OpenCVCamera::open(&pipeline) { diff --git a/src/camera/opencv.rs b/src/camera/opencv.rs index 6360255..4dbfb98 100644 --- a/src/camera/opencv.rs +++ b/src/camera/opencv.rs @@ -54,29 +54,115 @@ impl OpenCVCamera { fn create_capture_from_path(path_str: &str) -> Result { info!("Attempting to open camera with path/pipeline: {}", path_str); + // 检测是否为树莓派,以提供更多调试信息 + let is_raspberry_pi = std::fs::read_to_string("/proc/cpuinfo") + .map(|content| content.contains("Raspberry Pi") || content.contains("BCM")) + .unwrap_or(false); + + if is_raspberry_pi { + info!("Raspberry Pi detected, will try multiple approaches to open camera"); + + // 尝试列出可用的视频设备 + match std::process::Command::new("v4l2-ctl").arg("--list-devices").output() { + Ok(output) => { + let devices = String::from_utf8_lossy(&output.stdout); + info!("Available video devices:\n{}", devices); + }, + Err(e) => warn!("Could not list video devices: {}", e) + } + } + // 检测是否为GStreamer pipeline (通常包含感叹号) if path_str.contains("!") { info!("Detected GStreamer pipeline, using from_file with GSTREAMER backend"); - // 如果是GStreamer pipeline,直接使用from_file方法并指定GSTREAMER后端 + // 如果是GStreamer pipeline,尝试几种不同的方法 + + // 先尝试使用一个非常基本的pipeline + let simple_pipeline = "videotestsrc ! videoconvert ! appsink"; + info!("First trying a very basic test pipeline to check GStreamer: {}", simple_pipeline); + + match videoio::VideoCapture::from_file(simple_pipeline, videoio::CAP_GSTREAMER) { + Ok(cap) => { + if cap.is_opened().unwrap_or(false) { + info!("Basic GStreamer test pipeline works! Now trying the real pipeline"); + } else { + warn!("Basic GStreamer test pipeline opened but not ready - GStreamer may not be working properly"); + } + }, + Err(e) => warn!("Basic GStreamer test failed: {} - GStreamer may not be available", e) + } + + // 尝试使用原始的pipeline match videoio::VideoCapture::from_file(path_str, videoio::CAP_GSTREAMER) { Ok(cap) => { - if cap.is_opened()? { + if cap.is_opened().unwrap_or(false) { info!("Successfully opened GStreamer pipeline"); return Ok(cap); } else { - warn!("GStreamer pipeline opened but not ready, will try alternative methods"); + warn!("GStreamer pipeline opened but not ready"); } }, Err(e) => { warn!("Failed to open with GStreamer: {}. Will try alternative methods.", e); } } + + // 如果在树莓派上,尝试不同格式的pipeline + if is_raspberry_pi { + let alternate_pipelines = [ + "v4l2src ! video/x-raw,format=YUY2 ! videoconvert ! appsink", + "v4l2src ! video/x-raw,format=BGR ! videoconvert ! appsink", + "v4l2src device=/dev/video0 ! videoconvert ! appsink", + "v4l2src device=/dev/video0 ! video/x-raw,format=YUY2 ! videoconvert ! appsink" + ]; + + for pipeline in alternate_pipelines.iter() { + info!("Trying alternate pipeline: {}", pipeline); + match videoio::VideoCapture::from_file(pipeline, videoio::CAP_GSTREAMER) { + Ok(cap) => { + if cap.is_opened().unwrap_or(false) { + info!("Success with alternate pipeline: {}", pipeline); + return Ok(cap); + } + }, + Err(e) => warn!("Failed with alternate pipeline: {}", e) + } + } + } } // Try to parse as integer index first if let Ok(device_index) = path_str.parse::() { info!("Opening camera by index: {}", device_index); - return Ok(videoio::VideoCapture::new(device_index, videoio::CAP_ANY)?); + + // 在树莓派上,先尝试v4l2直接打开以获取更多诊断信息 + if is_raspberry_pi { + // 检查设备是否实际存在 + if std::path::Path::new(&format!("/dev/video{}", device_index)).exists() { + info!("Device /dev/video{} exists", device_index); + } else { + warn!("Device /dev/video{} does not exist!", device_index); + } + + // 尝试v4l2-ctl获取设备信息 + match std::process::Command::new("v4l2-ctl") + .args(&["-d", &format!("/dev/video{}", device_index), "-D"]) + .output() + { + Ok(output) => { + let info = String::from_utf8_lossy(&output.stdout); + info!("Device info for /dev/video{}:\n{}", device_index, info); + }, + Err(e) => warn!("Could not get device info: {}", e) + } + } + + let cap = videoio::VideoCapture::new(device_index, videoio::CAP_ANY)?; + if cap.is_opened()? { + return Ok(cap); + } else { + warn!("Device index {} opened but not ready", device_index); + } } // Handle platform-specific device paths @@ -129,33 +215,7 @@ impl OpenCVCamera { } } - // 对于树莓派,可能需要指定GSTREAMER后端 - if std::fs::read_to_string("/proc/cpuinfo") - .map(|content| content.contains("Raspberry Pi") || content.contains("BCM")) - .unwrap_or(false) - { - // 尝试使用带有GSTREAMER后端的方式打开 - let gst_pipeline = if path_str.starts_with("/dev/video") { - // 如果是设备路径,转换为gstreamer pipeline - let num_str = path_str.strip_prefix("/dev/video").unwrap_or("0"); - format!("v4l2src device={} ! video/x-raw,width=1280,height=720 ! videoconvert ! appsink", path_str) - } else { - path_str.to_string() - }; - - info!("Trying to open with GStreamer on Raspberry Pi: {}", gst_pipeline); - match videoio::VideoCapture::from_file(&gst_pipeline, videoio::CAP_GSTREAMER) { - Ok(cap) => { - if cap.is_opened()? { - info!("Successfully opened camera with GStreamer on Raspberry Pi"); - return Ok(cap); - } - }, - Err(e) => warn!("Failed to open with GStreamer on Raspberry Pi: {}", e) - } - } - - // For URLs, video files, or any other path type + // 最后尝试使用通用文件打开方法 info!("Using generic file opening method for: {}", path_str); let cap = videoio::VideoCapture::from_file(path_str, videoio::CAP_ANY)?;