From 40f17315a9a19b07dacaf6c773c11effe071cc28 Mon Sep 17 00:00:00 2001 From: grabbit Date: Sat, 5 Apr 2025 16:45:51 +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/opencv.rs | 105 ++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 62 deletions(-) diff --git a/src/camera/opencv.rs b/src/camera/opencv.rs index a443cee..e1e6d23 100644 --- a/src/camera/opencv.rs +++ b/src/camera/opencv.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Context, Result}; use log::{debug, error, info, warn}; use std::path::Path; +use std::sync::{Arc, Mutex}; use opencv::{core, prelude::*, videoio}; @@ -9,7 +10,7 @@ use crate::camera::{ExposureMode, Resolution}; /// OpenCV camera driver pub struct OpenCVCamera { /// The VideoCapture instance - capture: videoio::VideoCapture, + capture: Arc>, /// Camera width width: u32, /// Camera height @@ -42,7 +43,7 @@ impl OpenCVCamera { ); Ok(Self { - capture, + capture: Arc::new(Mutex::new(capture)), width, height, is_streaming: false, @@ -101,13 +102,16 @@ impl OpenCVCamera { pub fn set_format(&mut self, resolution: Resolution) -> Result<()> { let (width, height) = resolution.dimensions(); + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); + // Set resolution - self.capture.set(videoio::CAP_PROP_FRAME_WIDTH, width as f64)?; - self.capture.set(videoio::CAP_PROP_FRAME_HEIGHT, height as f64)?; + capture_guard.set(videoio::CAP_PROP_FRAME_WIDTH, width as f64)?; + capture_guard.set(videoio::CAP_PROP_FRAME_HEIGHT, height as f64)?; // Read back actual resolution (might be different from requested) - let actual_width = self.capture.get(videoio::CAP_PROP_FRAME_WIDTH)? as u32; - let actual_height = self.capture.get(videoio::CAP_PROP_FRAME_HEIGHT)? as u32; + let actual_width = capture_guard.get(videoio::CAP_PROP_FRAME_WIDTH)? as u32; + let actual_height = capture_guard.get(videoio::CAP_PROP_FRAME_HEIGHT)? as u32; if actual_width != width || actual_height != height { warn!( @@ -126,10 +130,13 @@ impl OpenCVCamera { /// Set the camera frame rate pub fn set_fps(&mut self, fps: u32) -> Result<()> { - self.capture.set(videoio::CAP_PROP_FPS, fps as f64)?; + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); + + capture_guard.set(videoio::CAP_PROP_FPS, fps as f64)?; // Read back actual FPS - let actual_fps = self.capture.get(videoio::CAP_PROP_FPS)?; + let actual_fps = capture_guard.get(videoio::CAP_PROP_FPS)?; if (actual_fps - fps as f64).abs() > 0.1 { warn!("Requested {} fps but got {} fps", fps, actual_fps); @@ -142,19 +149,22 @@ impl OpenCVCamera { /// Set camera exposure mode and value pub fn set_exposure(&mut self, mode: ExposureMode) -> Result<()> { + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); + match mode { ExposureMode::Auto => { // Set auto exposure mode - self.capture.set(videoio::CAP_PROP_AUTO_EXPOSURE, 0.75)?; // 0.75 is auto mode in OpenCV + capture_guard.set(videoio::CAP_PROP_AUTO_EXPOSURE, 0.75)?; // 0.75 is auto mode in OpenCV info!("Set camera exposure: Auto"); }, ExposureMode::Manual(exposure_time) => { // First disable auto exposure - self.capture.set(videoio::CAP_PROP_AUTO_EXPOSURE, 0.25)?; // 0.25 is manual mode in OpenCV + capture_guard.set(videoio::CAP_PROP_AUTO_EXPOSURE, 0.25)?; // 0.25 is manual mode in OpenCV // Then set exposure value - might need conversion based on camera let exposure_value = exposure_time as f64 / 10000.0; // Convert microseconds to OpenCV units - self.capture.set(videoio::CAP_PROP_EXPOSURE, exposure_value)?; + capture_guard.set(videoio::CAP_PROP_EXPOSURE, exposure_value)?; info!("Set camera exposure: Manual ({})", exposure_time); } @@ -165,9 +175,12 @@ impl OpenCVCamera { /// Set camera gain (ISO) pub fn set_gain(&mut self, gain: u8) -> Result<()> { - self.capture.set(videoio::CAP_PROP_GAIN, gain as f64)?; + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); - let actual_gain = self.capture.get(videoio::CAP_PROP_GAIN)?; + capture_guard.set(videoio::CAP_PROP_GAIN, gain as f64)?; + + let actual_gain = capture_guard.get(videoio::CAP_PROP_GAIN)?; info!("Set camera gain: {} (actual: {})", gain, actual_gain); Ok(()) @@ -175,10 +188,13 @@ impl OpenCVCamera { /// Lock focus at infinity (if supported) pub fn lock_focus_at_infinity(&mut self) -> Result<()> { + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); + // First, set focus mode to manual - if self.capture.set(videoio::CAP_PROP_AUTOFOCUS, 0.0).is_ok() { + if capture_guard.set(videoio::CAP_PROP_AUTOFOCUS, 0.0).is_ok() { // Then set focus to infinity (typically maximum value) - if self.capture.set(videoio::CAP_PROP_FOCUS, 1.0).is_ok() { + if capture_guard.set(videoio::CAP_PROP_FOCUS, 1.0).is_ok() { info!("Locked focus at infinity"); return Ok(()); } @@ -190,59 +206,20 @@ impl OpenCVCamera { /// Start streaming from the camera pub fn start_streaming(&mut self) -> Result { - // Ensure capture is opened - if !self.capture.is_opened()? { - return Err(anyhow!("Camera is not open")); - } - - #[cfg(target_os = "linux")] + // 获取互斥锁守卫检查相机是否打开 { - // For Linux device files like /dev/video0 - if self.device.starts_with("/dev/video") { - if let Some(num_str) = self.device.strip_prefix("/dev/video") { - if let Ok(device_index) = num_str.parse::() { - // 使用V4L2创建摄像头流 - info!("Starting stream using V4L2: device index {}", device_index); - let mut stream_capture = videoio::VideoCapture::new(device_index, videoio::CAP_V4L2)?; - - // 应用相同的设置 - stream_capture.set(videoio::CAP_PROP_FRAME_WIDTH, self.width as f64)?; - stream_capture.set(videoio::CAP_PROP_FRAME_HEIGHT, self.height as f64)?; - stream_capture.set(videoio::CAP_PROP_FPS, 30.0)?; // 使用固定30fps或从设置获取 - - if !stream_capture.is_opened()? { - return Err(anyhow!("Failed to open V4L2 camera stream")); - } - - self.is_streaming = true; - info!("Started camera streaming with V4L2"); - - return Ok(OpenCVCaptureStream { - capture: stream_capture, - }); - } - } + let capture_guard = self.capture.lock().unwrap(); + if !capture_guard.is_opened()? { + return Err(anyhow!("Camera is not open")); } } - // For non-Linux platforms, use regular approach - // Create a separate VideoCapture for the stream to avoid concurrent access issues - let device = self.device.clone(); - let mut stream_capture = Self::create_capture_from_path(&device)?; - - // Apply the same settings - stream_capture.set(videoio::CAP_PROP_FRAME_WIDTH, self.width as f64)?; - stream_capture.set(videoio::CAP_PROP_FRAME_HEIGHT, self.height as f64)?; - - if !stream_capture.is_opened()? { - return Err(anyhow!("Failed to open camera stream")); - } - self.is_streaming = true; info!("Started camera streaming"); + // 使用同一个相机实例来避免重复打开设备 Ok(OpenCVCaptureStream { - capture: stream_capture, + capture: self.capture.clone(), }) } @@ -271,14 +248,18 @@ impl OpenCVCamera { /// Wrapper around OpenCV VideoCapture for streaming pub struct OpenCVCaptureStream { - capture: videoio::VideoCapture, + capture: Arc>, } impl OpenCVCaptureStream { /// Capture a single frame from the camera pub fn capture_frame(&mut self) -> Result { let mut frame = core::Mat::default(); - if self.capture.read(&mut frame)? { + + // 获取互斥锁守卫 + let mut capture_guard = self.capture.lock().unwrap(); + + if capture_guard.read(&mut frame)? { if frame.empty() { return Err(anyhow!("Captured frame is empty")); }