fix: 树莓派gstreamer pipeline报错

This commit is contained in:
grabbit 2025-04-05 15:43:28 +08:00
parent ddac14fb3f
commit 8733afc987
3 changed files with 103 additions and 62 deletions

View File

@ -128,9 +128,9 @@ fn create_demo_config() -> Config {
// 根据操作系统和设备类型自动选择合适的设备路径
let device = if is_raspberry_pi {
// 使用特定的GStreamer pipeline字符串在树莓派上适配摄像头
// 这个pipeline使用v4l2src作为源并确保正确初始化
"v4l2src device=/dev/video0 ! video/x-raw,width=1280,height=720,framerate=30/1 ! videoconvert ! appsink".to_string()
// 使用更简单的GStreamer pipeline尝试不同配置方式和格式
// 此pipeline尝试放宽限制避免严格的格式约束
"v4l2src ! videoconvert ! appsink".to_string()
} else if std::env::consts::OS == "linux" {
"/dev/video0".to_string()
} else if std::env::consts::OS == "macos" {

View File

@ -111,17 +111,10 @@ impl CameraController {
// 在树莓派上但没有使用GStreamer pipeline尝试使用pipeline重新初始化
warn!("Running on Raspberry Pi without GStreamer pipeline. Attempting to reinitialize with GStreamer...");
// 创建GStreamer pipeline
let device_num = self.settings.device.strip_prefix("/dev/video")
.unwrap_or("0");
// 创建更简单的GStreamer pipeline
let pipeline = "v4l2src ! videoconvert ! appsink".to_string();
let pipeline = format!(
"v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink",
device_num, self.settings.resolution.dimensions().0,
self.settings.resolution.dimensions().1, self.settings.fps
);
info!("Trying GStreamer pipeline: {}", pipeline);
info!("Trying simple GStreamer pipeline: {}", pipeline);
self.settings.device = pipeline;
// 使用 Box::pin 来解决递归 async 调用问题
@ -189,17 +182,10 @@ impl CameraController {
// 关闭当前相机
self.camera = None;
// 创建GStreamer pipeline
let device_num = self.settings.device.strip_prefix("/dev/video")
.unwrap_or("0");
// 创建更简单的GStreamer pipeline
let pipeline = "v4l2src ! videoconvert ! appsink".to_string();
let pipeline = format!(
"v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink",
device_num, self.settings.resolution.dimensions().0,
self.settings.resolution.dimensions().1, self.settings.fps
);
info!("Trying GStreamer pipeline: {}", pipeline);
info!("Trying simple GStreamer pipeline: {}", pipeline);
self.settings.device = pipeline;
// 重新初始化相机
@ -299,15 +285,10 @@ impl CameraController {
let use_gstreamer = is_raspberry_pi && !device.contains("!");
if use_gstreamer {
// 创建GStreamer pipeline
let device_num = device.strip_prefix("/dev/video").unwrap_or("0");
// 创建更简单的GStreamer pipeline
let pipeline = "v4l2src ! videoconvert ! appsink".to_string();
let pipeline = format!(
"v4l2src device=/dev/video{} ! video/x-raw,width={},height={},framerate={}/1 ! videoconvert ! appsink",
device_num, 1280, 720, fps
);
error!("Raspberry Pi detected - trying GStreamer pipeline: {}", pipeline);
error!("Raspberry Pi detected - trying simple GStreamer pipeline: {}", pipeline);
// 尝试使用GStreamer打开摄像头
match OpenCVCamera::open(&pipeline) {

View File

@ -54,29 +54,115 @@ impl OpenCVCamera {
fn create_capture_from_path(path_str: &str) -> Result<videoio::VideoCapture> {
info!("Attempting to open camera with path/pipeline: {}", path_str);
// 检测是否为树莓派,以提供更多调试信息
let is_raspberry_pi = std::fs::read_to_string("/proc/cpuinfo")
.map(|content| content.contains("Raspberry Pi") || content.contains("BCM"))
.unwrap_or(false);
if is_raspberry_pi {
info!("Raspberry Pi detected, will try multiple approaches to open camera");
// 尝试列出可用的视频设备
match std::process::Command::new("v4l2-ctl").arg("--list-devices").output() {
Ok(output) => {
let devices = String::from_utf8_lossy(&output.stdout);
info!("Available video devices:\n{}", devices);
},
Err(e) => warn!("Could not list video devices: {}", e)
}
}
// 检测是否为GStreamer pipeline (通常包含感叹号)
if path_str.contains("!") {
info!("Detected GStreamer pipeline, using from_file with GSTREAMER backend");
// 如果是GStreamer pipeline直接使用from_file方法并指定GSTREAMER后端
// 如果是GStreamer pipeline尝试几种不同的方法
// 先尝试使用一个非常基本的pipeline
let simple_pipeline = "videotestsrc ! videoconvert ! appsink";
info!("First trying a very basic test pipeline to check GStreamer: {}", simple_pipeline);
match videoio::VideoCapture::from_file(simple_pipeline, videoio::CAP_GSTREAMER) {
Ok(cap) => {
if cap.is_opened().unwrap_or(false) {
info!("Basic GStreamer test pipeline works! Now trying the real pipeline");
} else {
warn!("Basic GStreamer test pipeline opened but not ready - GStreamer may not be working properly");
}
},
Err(e) => warn!("Basic GStreamer test failed: {} - GStreamer may not be available", e)
}
// 尝试使用原始的pipeline
match videoio::VideoCapture::from_file(path_str, videoio::CAP_GSTREAMER) {
Ok(cap) => {
if cap.is_opened()? {
if cap.is_opened().unwrap_or(false) {
info!("Successfully opened GStreamer pipeline");
return Ok(cap);
} else {
warn!("GStreamer pipeline opened but not ready, will try alternative methods");
warn!("GStreamer pipeline opened but not ready");
}
},
Err(e) => {
warn!("Failed to open with GStreamer: {}. Will try alternative methods.", e);
}
}
// 如果在树莓派上尝试不同格式的pipeline
if is_raspberry_pi {
let alternate_pipelines = [
"v4l2src ! video/x-raw,format=YUY2 ! videoconvert ! appsink",
"v4l2src ! video/x-raw,format=BGR ! videoconvert ! appsink",
"v4l2src device=/dev/video0 ! videoconvert ! appsink",
"v4l2src device=/dev/video0 ! video/x-raw,format=YUY2 ! videoconvert ! appsink"
];
for pipeline in alternate_pipelines.iter() {
info!("Trying alternate pipeline: {}", pipeline);
match videoio::VideoCapture::from_file(pipeline, videoio::CAP_GSTREAMER) {
Ok(cap) => {
if cap.is_opened().unwrap_or(false) {
info!("Success with alternate pipeline: {}", pipeline);
return Ok(cap);
}
},
Err(e) => warn!("Failed with alternate pipeline: {}", e)
}
}
}
}
// Try to parse as integer index first
if let Ok(device_index) = path_str.parse::<i32>() {
info!("Opening camera by index: {}", device_index);
return Ok(videoio::VideoCapture::new(device_index, videoio::CAP_ANY)?);
// 在树莓派上先尝试v4l2直接打开以获取更多诊断信息
if is_raspberry_pi {
// 检查设备是否实际存在
if std::path::Path::new(&format!("/dev/video{}", device_index)).exists() {
info!("Device /dev/video{} exists", device_index);
} else {
warn!("Device /dev/video{} does not exist!", device_index);
}
// 尝试v4l2-ctl获取设备信息
match std::process::Command::new("v4l2-ctl")
.args(&["-d", &format!("/dev/video{}", device_index), "-D"])
.output()
{
Ok(output) => {
let info = String::from_utf8_lossy(&output.stdout);
info!("Device info for /dev/video{}:\n{}", device_index, info);
},
Err(e) => warn!("Could not get device info: {}", e)
}
}
let cap = videoio::VideoCapture::new(device_index, videoio::CAP_ANY)?;
if cap.is_opened()? {
return Ok(cap);
} else {
warn!("Device index {} opened but not ready", device_index);
}
}
// Handle platform-specific device paths
@ -129,33 +215,7 @@ impl OpenCVCamera {
}
}
// 对于树莓派可能需要指定GSTREAMER后端
if std::fs::read_to_string("/proc/cpuinfo")
.map(|content| content.contains("Raspberry Pi") || content.contains("BCM"))
.unwrap_or(false)
{
// 尝试使用带有GSTREAMER后端的方式打开
let gst_pipeline = if path_str.starts_with("/dev/video") {
// 如果是设备路径转换为gstreamer pipeline
let num_str = path_str.strip_prefix("/dev/video").unwrap_or("0");
format!("v4l2src device={} ! video/x-raw,width=1280,height=720 ! videoconvert ! appsink", path_str)
} else {
path_str.to_string()
};
info!("Trying to open with GStreamer on Raspberry Pi: {}", gst_pipeline);
match videoio::VideoCapture::from_file(&gst_pipeline, videoio::CAP_GSTREAMER) {
Ok(cap) => {
if cap.is_opened()? {
info!("Successfully opened camera with GStreamer on Raspberry Pi");
return Ok(cap);
}
},
Err(e) => warn!("Failed to open with GStreamer on Raspberry Pi: {}", e)
}
}
// For URLs, video files, or any other path type
// 最后尝试使用通用文件打开方法
info!("Using generic file opening method for: {}", path_str);
let cap = videoio::VideoCapture::from_file(path_str, videoio::CAP_ANY)?;