Negotiate the best format based on user config and device capabilities. 根据用户配置和设备能力协商最佳格式。 Strategy: 1. If user specified pixel_format → use it (error if unsupported). 2. If user specified resolution → find formats supporting it. 3. Auto-select: prefer MJPEG for fps<60, YUYV for fps>=60. 4. If nothing specified → use device default. 策略: 1. 用户指定了 pixel_format → 直接使用(不支持则报错)。 2. 用户指定了分辨率 → 查找支持该分辨
(&mut self, config: &CameraConfig)
| 283 | /// 3. 自动选择:fps<60 优先 MJPEG,fps>=60 优先 YUYV。 |
| 284 | /// 4. 都未指定 → 使用设备默认值。 |
| 285 | fn negotiate_format(&mut self, config: &CameraConfig) -> Result<ResolvedConfig> { |
| 286 | let fd = self.fd; |
| 287 | |
| 288 | // Collect all supported formats and their resolutions. |
| 289 | // 收集所有支持的格式及其分辨率。 |
| 290 | let mut supported = Vec::new(); |
| 291 | let mut fmt_idx = 0; |
| 292 | while let Ok(desc) = v4l2_sys::enum_formats(fd, fmt_idx) { |
| 293 | let pf = PixelFormat::from_fourcc(desc.pixelformat); |
| 294 | // Enumerate frame sizes for this format. |
| 295 | // 枚举此格式的帧尺寸。 |
| 296 | let mut size_idx = 0; |
| 297 | while let Ok(size) = v4l2_sys::enum_frame_sizes(fd, desc.pixelformat, size_idx) { |
| 298 | // V4L2_FRMSIZE_TYPE_DISCRETE = 1 |
| 299 | if size.type_ == 1 { |
| 300 | let discrete = unsafe { &size.__bindgen_anon_1.discrete }; |
| 301 | supported.push((pf, discrete.width, discrete.height)); |
| 302 | } |
| 303 | size_idx += 1; |
| 304 | } |
| 305 | fmt_idx += 1; |
| 306 | } |
| 307 | |
| 308 | if supported.is_empty() { |
| 309 | return Err(CameraError::FormatNotSupported); |
| 310 | } |
| 311 | |
| 312 | // Select the best match. |
| 313 | // 选择最佳匹配。 |
| 314 | let target_w = config.width.unwrap_or(640); |
| 315 | let target_h = config.height.unwrap_or(480); |
| 316 | let target_fps = config.fps.unwrap_or(30); |
| 317 | |
| 318 | let selected = if let Some(pf) = config.pixel_format { |
| 319 | // User specified format — find matching resolution. |
| 320 | // 用户指定了格式 —— 查找匹配的分辨率。 |
| 321 | supported |
| 322 | .iter() |
| 323 | .filter(|(f, _, _)| *f == pf) |
| 324 | .min_by_key(|(_, w, h)| { |
| 325 | let dw = (*w as i64 - target_w as i64).abs(); |
| 326 | let dh = (*h as i64 - target_h as i64).abs(); |
| 327 | dw + dh |
| 328 | }) |
| 329 | .copied() |
| 330 | .ok_or(CameraError::FormatNotSupported)? |
| 331 | } else { |
| 332 | // Auto-select: score each candidate. |
| 333 | // 自动选择:对每个候选项评分。 |
| 334 | // |
| 335 | // Scoring: |
| 336 | // - Resolution match: lower distance = higher score |
| 337 | // - Format preference: MJPEG for low fps, YUYV for high fps |
| 338 | // 评分规则: |
| 339 | // - 分辨率匹配:距离越小得分越高 |
| 340 | // - 格式偏好:低帧率优先 MJPEG,高帧率优先 YUYV |
| 341 | *supported |
| 342 | .iter() |
no test coverage detected