fix: 树莓派gstreamer pipeline报错

This commit is contained in:
grabbit 2025-04-05 16:45:51 +08:00
parent 94cf574161
commit 40f17315a9

View File

@ -1,6 +1,7 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex};
use opencv::{core, prelude::*, videoio}; use opencv::{core, prelude::*, videoio};
@ -9,7 +10,7 @@ use crate::camera::{ExposureMode, Resolution};
/// OpenCV camera driver /// OpenCV camera driver
pub struct OpenCVCamera { pub struct OpenCVCamera {
/// The VideoCapture instance /// The VideoCapture instance
capture: videoio::VideoCapture, capture: Arc<Mutex<videoio::VideoCapture>>,
/// Camera width /// Camera width
width: u32, width: u32,
/// Camera height /// Camera height
@ -42,7 +43,7 @@ impl OpenCVCamera {
); );
Ok(Self { Ok(Self {
capture, capture: Arc::new(Mutex::new(capture)),
width, width,
height, height,
is_streaming: false, is_streaming: false,
@ -101,13 +102,16 @@ impl OpenCVCamera {
pub fn set_format(&mut self, resolution: Resolution) -> Result<()> { pub fn set_format(&mut self, resolution: Resolution) -> Result<()> {
let (width, height) = resolution.dimensions(); let (width, height) = resolution.dimensions();
// 获取互斥锁守卫
let mut capture_guard = self.capture.lock().unwrap();
// Set resolution // Set resolution
self.capture.set(videoio::CAP_PROP_FRAME_WIDTH, width as f64)?; capture_guard.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_HEIGHT, height as f64)?;
// Read back actual resolution (might be different from requested) // Read back actual resolution (might be different from requested)
let actual_width = self.capture.get(videoio::CAP_PROP_FRAME_WIDTH)? as u32; let actual_width = capture_guard.get(videoio::CAP_PROP_FRAME_WIDTH)? as u32;
let actual_height = self.capture.get(videoio::CAP_PROP_FRAME_HEIGHT)? as u32; let actual_height = capture_guard.get(videoio::CAP_PROP_FRAME_HEIGHT)? as u32;
if actual_width != width || actual_height != height { if actual_width != width || actual_height != height {
warn!( warn!(
@ -126,10 +130,13 @@ impl OpenCVCamera {
/// Set the camera frame rate /// Set the camera frame rate
pub fn set_fps(&mut self, fps: u32) -> Result<()> { 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 // 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 { if (actual_fps - fps as f64).abs() > 0.1 {
warn!("Requested {} fps but got {} fps", fps, actual_fps); warn!("Requested {} fps but got {} fps", fps, actual_fps);
@ -142,19 +149,22 @@ impl OpenCVCamera {
/// Set camera exposure mode and value /// Set camera exposure mode and value
pub fn set_exposure(&mut self, mode: ExposureMode) -> Result<()> { pub fn set_exposure(&mut self, mode: ExposureMode) -> Result<()> {
// 获取互斥锁守卫
let mut capture_guard = self.capture.lock().unwrap();
match mode { match mode {
ExposureMode::Auto => { ExposureMode::Auto => {
// Set auto exposure mode // 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"); info!("Set camera exposure: Auto");
}, },
ExposureMode::Manual(exposure_time) => { ExposureMode::Manual(exposure_time) => {
// First disable auto exposure // 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 // Then set exposure value - might need conversion based on camera
let exposure_value = exposure_time as f64 / 10000.0; // Convert microseconds to OpenCV units 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); info!("Set camera exposure: Manual ({})", exposure_time);
} }
@ -165,9 +175,12 @@ impl OpenCVCamera {
/// Set camera gain (ISO) /// Set camera gain (ISO)
pub fn set_gain(&mut self, gain: u8) -> Result<()> { 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); info!("Set camera gain: {} (actual: {})", gain, actual_gain);
Ok(()) Ok(())
@ -175,10 +188,13 @@ impl OpenCVCamera {
/// Lock focus at infinity (if supported) /// Lock focus at infinity (if supported)
pub fn lock_focus_at_infinity(&mut self) -> Result<()> { pub fn lock_focus_at_infinity(&mut self) -> Result<()> {
// 获取互斥锁守卫
let mut capture_guard = self.capture.lock().unwrap();
// First, set focus mode to manual // 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) // 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"); info!("Locked focus at infinity");
return Ok(()); return Ok(());
} }
@ -190,59 +206,20 @@ impl OpenCVCamera {
/// Start streaming from the camera /// Start streaming from the camera
pub fn start_streaming(&mut self) -> Result<OpenCVCaptureStream> { pub fn start_streaming(&mut self) -> Result<OpenCVCaptureStream> {
// 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 let capture_guard = self.capture.lock().unwrap();
if self.device.starts_with("/dev/video") { if !capture_guard.is_opened()? {
if let Some(num_str) = self.device.strip_prefix("/dev/video") { return Err(anyhow!("Camera is not open"));
if let Ok(device_index) = num_str.parse::<i32>() {
// 使用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,
});
}
}
} }
} }
// 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; self.is_streaming = true;
info!("Started camera streaming"); info!("Started camera streaming");
// 使用同一个相机实例来避免重复打开设备
Ok(OpenCVCaptureStream { Ok(OpenCVCaptureStream {
capture: stream_capture, capture: self.capture.clone(),
}) })
} }
@ -271,14 +248,18 @@ impl OpenCVCamera {
/// Wrapper around OpenCV VideoCapture for streaming /// Wrapper around OpenCV VideoCapture for streaming
pub struct OpenCVCaptureStream { pub struct OpenCVCaptureStream {
capture: videoio::VideoCapture, capture: Arc<Mutex<videoio::VideoCapture>>,
} }
impl OpenCVCaptureStream { impl OpenCVCaptureStream {
/// Capture a single frame from the camera /// Capture a single frame from the camera
pub fn capture_frame(&mut self) -> Result<core::Mat> { pub fn capture_frame(&mut self) -> Result<core::Mat> {
let mut frame = core::Mat::default(); 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() { if frame.empty() {
return Err(anyhow!("Captured frame is empty")); return Err(anyhow!("Captured frame is empty"));
} }