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...");
|
||||
|
||||
// 使用已打开的相机实例直接捕获一帧,而不是创建新的流
|
||||
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(_) => {
|
||||
info!("Successfully captured test frame - camera is working correctly");
|
||||
},
|
||||
@ -134,6 +150,9 @@ impl CameraController {
|
||||
}
|
||||
}
|
||||
|
||||
// 给设备一点时间完全释放资源
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||
|
||||
self.camera = Some(camera);
|
||||
info!("Camera initialized successfully");
|
||||
|
||||
@ -158,9 +177,66 @@ impl CameraController {
|
||||
.as_mut()
|
||||
.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
|
||||
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 {
|
||||
Ok(stream) => stream,
|
||||
@ -176,38 +252,24 @@ impl CameraController {
|
||||
// 在树莓派上但没有使用GStreamer pipeline,尝试使用pipeline重新初始化
|
||||
warn!("Running on Raspberry Pi without GStreamer pipeline. Attempting to reinitialize camera with GStreamer...");
|
||||
|
||||
// 关闭当前相机
|
||||
self.camera = None;
|
||||
|
||||
// 创建更简单的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;
|
||||
|
||||
// 重新初始化相机
|
||||
match OpenCVCamera::open(&self.settings.device) {
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
// 保存新的相机实例
|
||||
self.camera = Some(new_camera);
|
||||
|
||||
// 先保存一个测试帧副本
|
||||
let test_frame = match stream.capture_frame() {
|
||||
Ok(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 {
|
||||
fn drop(&mut self) {
|
||||
info!("Cleaning up camera controller resources");
|
||||
|
||||
@ -243,6 +243,9 @@ impl OpenCVCamera {
|
||||
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) {
|
||||
Ok(w) if w > 0.0 => w as u32,
|
||||
@ -430,6 +433,21 @@ impl OpenCVCamera {
|
||||
|
||||
/// Stop streaming from the camera
|
||||
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;
|
||||
info!("Stopped camera streaming");
|
||||
Ok(())
|
||||
@ -498,3 +516,25 @@ impl Drop for OpenCVCaptureStream {
|
||||
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