From d1d3cbfdab850c008c56d32a9432ed379f881fb9 Mon Sep 17 00:00:00 2001 From: grabbit Date: Sat, 5 Apr 2025 16:09:26 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=20=E6=A0=91=E8=8E=93=E6=B4=BEgstre?= =?UTF-8?q?amer=20pipeline=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/camera/controller.rs | 88 ++++++++-------------------------------- src/camera/opencv.rs | 69 ++++++++++++------------------- 2 files changed, 42 insertions(+), 115 deletions(-) diff --git a/src/camera/controller.rs b/src/camera/controller.rs index 5d11bb5..a4765d2 100644 --- a/src/camera/controller.rs +++ b/src/camera/controller.rs @@ -102,21 +102,8 @@ impl CameraController { // 尝试拍摄测试帧以确保相机设置正确 info!("Testing camera by capturing a test frame..."); - // 为测试帧创建一个新的临时相机实例,而不是使用主实例 - let test_frame_result = { - info!("Creating temporary camera instance for test frame"); - let mut test_camera = OpenCVCamera::open(&self.settings.device) - .context("Failed to open temporary camera for test frame")?; - - // 应用最小必要设置 - let _ = test_camera.set_format(self.settings.resolution); - - // 捕获测试帧 - let result = test_camera.capture_test_frame(); - - // test_camera将在此作用域结束时自动释放 - result - }; + // 直接使用已打开的主相机实例捕获测试帧,避免同时打开两个相机实例 + let test_frame_result = camera.capture_test_frame(); // 处理测试帧结果 match test_frame_result { @@ -177,66 +164,23 @@ impl CameraController { .as_mut() .ok_or_else(|| anyhow::anyhow!("Camera not initialized"))?; - // 先释放当前相机实例,以确保没有资源冲突 - self.camera = None; + // 使用已有的相机实例,避免资源冲突 + let camera_ref = self + .camera + .as_mut() + .ok_or_else(|| anyhow::anyhow!("Camera not initialized"))?; - // 等待一小段时间以确保资源被完全释放 - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - - // 重新创建相机实例 - let mut new_camera = match OpenCVCamera::open(&self.settings.device) { - Ok(camera) => camera, - Err(e) => { - error!("Failed to reopen camera for streaming: {}", e); - - // 尝试使用不同的方法打开相机 - let device_busy = e.to_string().contains("busy") || e.to_string().contains("已被占用"); - - if device_busy { - warn!("Camera device is busy. Attempting to force release the device..."); - - // 使用v4l2-ctl尝试重置设备 - if let Ok(device_path) = extract_device_path(&self.settings.device) { - info!("Trying to reset device: {}", device_path); - - match std::process::Command::new("v4l2-ctl") - .args(&["--device", &device_path, "--set-ctrl=power_line_frequency=0"]) - .output() - { - Ok(_) => info!("Device reset command sent to {}", device_path), - Err(e) => warn!("Failed to reset device: {}", e) - }; - - // 等待设备重置 - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - - // 再次尝试打开设备 - match OpenCVCamera::open(&self.settings.device) { - Ok(camera) => { - info!("Successfully reopened camera after reset"); - camera - }, - Err(e) => { - error!("Still failed to open camera after reset: {}", e); - return Err(anyhow::anyhow!("Failed to open camera for streaming: {}", e)); - } - } - } else { - return Err(anyhow::anyhow!("Failed to extract device path from: {}", self.settings.device)); - } - } else { - return Err(anyhow::anyhow!("Failed to open camera for streaming: {}", e)); - } - } - }; - - // 应用基本设置 - let _ = new_camera.set_format(self.settings.resolution); - let _ = new_camera.set_fps(self.settings.fps); + // 确保相机没有在流式传输 + if camera_ref.is_streaming() { + camera_ref.stop_streaming()?; + + // 等待资源释放 + tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; + } - // Start the camera streaming + // 直接使用现有实例启动流 info!("Starting camera streaming with device: {}", self.settings.device); - let stream_result = new_camera.start_streaming(); + let stream_result = camera_ref.start_streaming(); let mut stream = match stream_result { Ok(stream) => stream, diff --git a/src/camera/opencv.rs b/src/camera/opencv.rs index 21a3b0a..8fc3cd8 100644 --- a/src/camera/opencv.rs +++ b/src/camera/opencv.rs @@ -377,57 +377,31 @@ impl OpenCVCamera { info!("Using GStreamer pipeline streaming approach"); } - // Create a separate VideoCapture for the stream to avoid concurrent access issues - let device = self.device.clone(); - let stream_capture_result = Self::create_capture_from_path(&device); + // 重要改变:尝试直接使用当前相机的VideoCapture实例来捕获帧 + // 这样可以避免同时打开两个引用同一相机的VideoCapture实例 + info!("Using existing camera instance for streaming"); - let mut stream_capture = match stream_capture_result { - Ok(cap) => cap, - Err(e) => { - error!("Failed to create stream capture: {}", e); - - if is_gstreamer_pipeline { - error!("GStreamer pipeline error. This could indicate:"); - error!("1. GStreamer is not properly installed"); - error!("2. Pipeline syntax is incorrect"); - error!("3. The specified video device doesn't exist"); - error!("4. OpenCV was not compiled with GStreamer support"); - } - - return Err(anyhow!("Failed to create stream capture: {}", e)); - } - }; - - // Apply the same settings if not using a GStreamer pipeline - // (GStreamer pipeline already has configuration in the pipeline string) - if !is_gstreamer_pipeline { - // 尝试设置分辨率,但不抛出失败异常 - if let Err(e) = stream_capture.set(videoio::CAP_PROP_FRAME_WIDTH, self.width as f64) { - warn!("Failed to set stream width (non-critical): {}", e); - } - - if let Err(e) = stream_capture.set(videoio::CAP_PROP_FRAME_HEIGHT, self.height as f64) { - warn!("Failed to set stream height (non-critical): {}", e); - } + // 捕获一个测试帧,确保相机能正常工作 + let mut test_frame = core::Mat::default(); + if !self.capture.read(&mut test_frame)? { + return Err(anyhow!("Failed to read test frame from camera")); } - // 确保流被正确打开 - match stream_capture.is_opened() { - Ok(is_open) => { - if !is_open { - return Err(anyhow!("Failed to open camera stream - camera reports not opened")); - } - }, - Err(e) => { - return Err(anyhow!("Failed to check if camera stream is open: {}", e)); - } + if test_frame.empty() { + return Err(anyhow!("Test frame is empty")); } + info!("Test frame captured successfully, size: {}x{}", test_frame.cols(), test_frame.rows()); + + // 直接使用现有的capture创建流 self.is_streaming = true; - info!("Started camera streaming successfully"); + info!("Camera streaming started successfully using existing capture"); + // 创建共享相机实例的流 Ok(OpenCVCaptureStream { - capture: stream_capture, + // 由于只有一个实例可以访问摄像头,我们需要使用Arc+Mutex来共享访问 + capture: self.capture.clone(), + device: self.device.clone(), }) } @@ -494,6 +468,7 @@ impl OpenCVCamera { /// Wrapper around OpenCV VideoCapture for streaming pub struct OpenCVCaptureStream { capture: videoio::VideoCapture, + device: String, } impl OpenCVCaptureStream { @@ -514,6 +489,14 @@ impl OpenCVCaptureStream { impl Drop for OpenCVCaptureStream { fn drop(&mut self) { debug!("OpenCV capture stream dropped"); + + // 尝试重置设备以便下次能正常访问 + if self.device.starts_with("/dev/video") { + debug!("Attempting to reset video device on stream drop: {}", self.device); + let _ = std::process::Command::new("v4l2-ctl") + .args(&["--device", &self.device, "--set-ctrl=power_line_frequency=0"]) + .output(); + } } }