fix: 树莓派gstreamer pipeline报错
This commit is contained in:
parent
5ccaca3743
commit
5fc78d7ef9
@ -102,8 +102,24 @@ impl CameraController {
|
|||||||
// 尝试拍摄测试帧以确保相机设置正确
|
// 尝试拍摄测试帧以确保相机设置正确
|
||||||
info!("Testing camera by capturing a test frame...");
|
info!("Testing camera by capturing a test frame...");
|
||||||
|
|
||||||
// 使用已打开的相机实例直接捕获一帧,而不是创建新的流
|
// 为测试帧创建一个新的临时相机实例,而不是使用主实例
|
||||||
match camera.capture_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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理测试帧结果
|
||||||
|
match test_frame_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successfully captured test frame - camera is working correctly");
|
info!("Successfully captured test frame - camera is working correctly");
|
||||||
},
|
},
|
||||||
@ -133,6 +149,9 @@ impl CameraController {
|
|||||||
return Err(anyhow::anyhow!("Camera initialization failed: {}", e));
|
return Err(anyhow::anyhow!("Camera initialization failed: {}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 给设备一点时间完全释放资源
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||||
|
|
||||||
self.camera = Some(camera);
|
self.camera = Some(camera);
|
||||||
info!("Camera initialized successfully");
|
info!("Camera initialized successfully");
|
||||||
@ -158,9 +177,66 @@ impl CameraController {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| anyhow::anyhow!("Camera not initialized"))?;
|
.ok_or_else(|| anyhow::anyhow!("Camera not initialized"))?;
|
||||||
|
|
||||||
|
// 先释放当前相机实例,以确保没有资源冲突
|
||||||
|
self.camera = None;
|
||||||
|
|
||||||
|
// 等待一小段时间以确保资源被完全释放
|
||||||
|
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);
|
||||||
|
|
||||||
// Start the camera streaming
|
// Start the camera streaming
|
||||||
info!("Starting camera streaming with device: {}", self.settings.device);
|
info!("Starting camera streaming with device: {}", self.settings.device);
|
||||||
let stream_result = camera.start_streaming();
|
let stream_result = new_camera.start_streaming();
|
||||||
|
|
||||||
let mut stream = match stream_result {
|
let mut stream = match stream_result {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
@ -176,37 +252,23 @@ impl CameraController {
|
|||||||
// 在树莓派上但没有使用GStreamer pipeline,尝试使用pipeline重新初始化
|
// 在树莓派上但没有使用GStreamer pipeline,尝试使用pipeline重新初始化
|
||||||
warn!("Running on Raspberry Pi without GStreamer pipeline. Attempting to reinitialize camera with GStreamer...");
|
warn!("Running on Raspberry Pi without GStreamer pipeline. Attempting to reinitialize camera with GStreamer...");
|
||||||
|
|
||||||
// 关闭当前相机
|
|
||||||
self.camera = None;
|
|
||||||
|
|
||||||
// 创建更简单的GStreamer pipeline
|
// 创建更简单的GStreamer pipeline
|
||||||
let pipeline = "v4l2src ! videoconvert ! appsink".to_string();
|
let pipeline = format!("v4l2src device={} ! videoconvert ! appsink",
|
||||||
|
extract_device_path(&self.settings.device).unwrap_or("/dev/video0".to_string()));
|
||||||
|
|
||||||
info!("Trying simple GStreamer pipeline: {}", pipeline);
|
info!("Trying GStreamer pipeline: {}", pipeline);
|
||||||
self.settings.device = pipeline;
|
self.settings.device = pipeline;
|
||||||
|
|
||||||
// 重新初始化相机
|
// 使用 Box::pin 来解决递归 async 调用问题
|
||||||
match OpenCVCamera::open(&self.settings.device) {
|
return Box::pin(self.start_capture_impl()).await;
|
||||||
Ok(mut camera) => {
|
|
||||||
// 基本配置
|
|
||||||
let _ = camera.set_format(self.settings.resolution);
|
|
||||||
let _ = camera.set_fps(self.settings.fps);
|
|
||||||
|
|
||||||
self.camera = Some(camera);
|
|
||||||
|
|
||||||
// 使用 Box::pin 来解决递归 async 调用问题
|
|
||||||
return Box::pin(self.start_capture_impl()).await;
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to reinitialize camera with GStreamer: {}", e);
|
|
||||||
return Err(anyhow::anyhow!("Camera streaming failed on Raspberry Pi: {}", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(anyhow::anyhow!("Failed to start camera streaming: {}", e));
|
return Err(anyhow::anyhow!("Failed to start camera streaming: {}", e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 保存新的相机实例
|
||||||
|
self.camera = Some(new_camera);
|
||||||
|
|
||||||
// 先保存一个测试帧副本
|
// 先保存一个测试帧副本
|
||||||
let test_frame = match stream.capture_frame() {
|
let test_frame = match stream.capture_frame() {
|
||||||
@ -580,6 +642,40 @@ impl CameraController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 从设备字符串中提取/dev/video设备路径
|
||||||
|
fn extract_device_path(device_str: &str) -> Result<String, anyhow::Error> {
|
||||||
|
// 如果是简单的/dev/video路径,直接返回
|
||||||
|
if device_str.starts_with("/dev/video") {
|
||||||
|
return Ok(device_str.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是GStreamer pipeline,尝试提取device参数
|
||||||
|
if device_str.contains("v4l2src") && device_str.contains("device=") {
|
||||||
|
// 匹配device=/dev/videoX 格式
|
||||||
|
if let Some(start_idx) = device_str.find("device=") {
|
||||||
|
let device_part = &device_str[start_idx + 7..]; // "device=".len() == 7
|
||||||
|
|
||||||
|
// 如果device路径用引号括起来
|
||||||
|
if device_part.starts_with('"') || device_part.starts_with('\'') {
|
||||||
|
let quote = device_part.chars().next().unwrap();
|
||||||
|
if let Some(end_idx) = device_part[1..].find(quote) {
|
||||||
|
return Ok(device_part[1..=end_idx].to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 否则找到下一个空格或感叹号
|
||||||
|
else if let Some(end_idx) = device_part.find(|c| c == ' ' || c == '!') {
|
||||||
|
return Ok(device_part[..end_idx].to_string());
|
||||||
|
} else {
|
||||||
|
// 如果没有终止符,假设它是整个剩余部分
|
||||||
|
return Ok(device_part.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没找到设备路径,返回默认值
|
||||||
|
Err(anyhow::anyhow!("Could not extract device path from: {}", device_str))
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for CameraController {
|
impl Drop for CameraController {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!("Cleaning up camera controller resources");
|
info!("Cleaning up camera controller resources");
|
||||||
|
|||||||
@ -243,6 +243,9 @@ impl OpenCVCamera {
|
|||||||
info!("Using resolution from GStreamer pipeline (cannot be changed at runtime)");
|
info!("Using resolution from GStreamer pipeline (cannot be changed at runtime)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待一小段时间让摄像头设置生效
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
|
||||||
// 尝试读取实际分辨率
|
// 尝试读取实际分辨率
|
||||||
let actual_width = match self.capture.get(videoio::CAP_PROP_FRAME_WIDTH) {
|
let actual_width = match self.capture.get(videoio::CAP_PROP_FRAME_WIDTH) {
|
||||||
Ok(w) if w > 0.0 => w as u32,
|
Ok(w) if w > 0.0 => w as u32,
|
||||||
@ -430,6 +433,21 @@ impl OpenCVCamera {
|
|||||||
|
|
||||||
/// Stop streaming from the camera
|
/// Stop streaming from the camera
|
||||||
pub fn stop_streaming(&mut self) -> Result<()> {
|
pub fn stop_streaming(&mut self) -> Result<()> {
|
||||||
|
// 尝试重置或释放相机资源,以避免下次打开时出现"设备忙"的错误
|
||||||
|
|
||||||
|
// 对于V4L2设备,可以尝试通过v4l2-ctl重置设备
|
||||||
|
if self.device.starts_with("/dev/video") {
|
||||||
|
// 记录日志但继续执行,不要因为这个失败而中断
|
||||||
|
match std::process::Command::new("v4l2-ctl")
|
||||||
|
.args(&["--device", &self.device, "--set-ctrl=power_line_frequency=0"])
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
Ok(_) => info!("Successfully reset V4L2 device: {}", self.device),
|
||||||
|
Err(e) => warn!("Failed to reset V4L2 device (non-critical): {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在GStreamer pipeline中,这可能不那么重要
|
||||||
self.is_streaming = false;
|
self.is_streaming = false;
|
||||||
info!("Stopped camera streaming");
|
info!("Stopped camera streaming");
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -498,3 +516,25 @@ impl Drop for OpenCVCaptureStream {
|
|||||||
debug!("OpenCV capture stream dropped");
|
debug!("OpenCV capture stream dropped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for OpenCVCamera {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug!("OpenCV camera dropping, releasing resources");
|
||||||
|
|
||||||
|
// 在释放资源前尝试执行一些清理操作
|
||||||
|
if self.is_streaming {
|
||||||
|
if let Err(e) = self.stop_streaming() {
|
||||||
|
warn!("Error stopping camera during drop: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VideoCapture在超出作用域时会自动释放,但我们可以尝试显式释放
|
||||||
|
if self.device.starts_with("/dev/video") {
|
||||||
|
let _ = std::process::Command::new("v4l2-ctl")
|
||||||
|
.args(&["--device", &self.device, "--set-ctrl=power_line_frequency=0"])
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("OpenCV camera resources released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user