fix: 树莓派gstreamer pipeline报错

This commit is contained in:
grabbit 2025-04-05 16:09:26 +08:00
parent 5fc78d7ef9
commit d1d3cbfdab
2 changed files with 42 additions and 115 deletions

View File

@ -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;
// 确保相机没有在流式传输
if camera_ref.is_streaming() {
camera_ref.stop_streaming()?;
// 重新创建相机实例
let mut new_camera = match OpenCVCamera::open(&self.settings.device) {
Ok(camera) => camera,
Err(e) => {
error!("Failed to reopen camera for streaming: {}", e);
// 等待资源释放
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
}
// 尝试使用不同的方法打开相机
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);
// 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,

View File

@ -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();
}
}
}