新增摄像头能力探针 tools/camera_probe.py,用来把 16.7 fps 归因到格式协商 - What: 新脚本对每个摄像头索引 × 两种后端(DSHOW/MSMF)× 五组请求(MJPG 1920x1080@30、MJPG 1920x1080@auto、MJPG 1280x720@30、YUYV 1920x1080@30、YUYV 1280x720@30)实际打开并连续抓 60 帧,逐行打印"申请 → 驱动协商到的 fourcc/尺寸/帧率 + 实测帧率",这样\"协商到 30\"与\"真的给到 30\"是两个分开的事实;没有摄像头时给出明确说明(含"要关掉 app,两个进程不能同时占用同一个摄像头")并返回 1;--help/--index/--max-index 可用。tests/test_ui_smoke.py 增加 test_the_camera_probe_is_usable_without_a_camera:无摄像头时返回 1 且输出解释、fourcc 编解码往返正确('MJPG' → 'MJPG'、0 → '-')。 - Why: 用户按上一轮读数确认"还是摄像头 16.7",即摄像头自身只发布 16.7。配置申请的是 resolution_raw=1920x1080、fps=30、fourcc 走 CaptureConfig 默认 MJPG、backend=auto;SDK 已在 open 里做了 width/height → fourcc → fps → fourcc 再设一次 + warmup 后校验并在 fourcc 不符时重试一次,但仍存在两种可能:这颗摄像头在 1080p 下就只有 16.67(UVC 常见),或者它在 MJPG 之外落到了别的格式/后端。这两种的处置完全不同(前者要改分辨率/换摄像头,而 1920x1080 是 5x3 瓦片网格的结构参数,改它要重做拼接标定;后者是申请顺序/格式的问题,可以在 SDK 侧修),而在应用里看不出来。因此需要一个不经过应用的独立探针,把驱动真正提供的东西列出来。 - Verification: 本机(无摄像头)运行 --index 9 → 输出 "No camera answered on indices 9..9." 与关闭 app 的提示、退出码 1;--help 正常;_decode(_fourcc_code('MJPG')) == 'MJPG'。studio 全量 91 passed(新增 1 项);ui_check 554 项检查 0 失败。 - Not in this change: 未改 SDK 的采集申请顺序或后端选择(等探针结果:若显示 MJPG+1080p 下驱动只给 16.67,则是硬件边界;若显示申请 MJPG 却协商成 YUYV,再改 SDK);未做分辨率自动降级。

This commit is contained in:
ykcheungab
2026-09-14 13:30:21 +08:00
parent 50122d19a2
commit 3db55b2077
2 changed files with 150 additions and 0 deletions
+13
View File
@@ -2128,6 +2128,19 @@ def test_the_rail_reads_the_rate_and_what_it_cost(qt_app, make_window):
window.close()
def test_the_camera_probe_is_usable_without_a_camera(capsys):
"""The probe has to be safe to run on a machine with no camera attached."""
import camera_probe
assert camera_probe.main(["--index", "9"]) == 1
out = capsys.readouterr().out
assert "No camera answered" in out
assert "with the app closed" in out, "the probe must say why it found nothing"
# The fourcc round trip is what makes the table readable.
assert camera_probe._decode(camera_probe._fourcc_code("MJPG")) == "MJPG"
assert camera_probe._decode(0) == "-"
# ------------------------------------------------------- plot settings dialog
def test_the_chart_gear_opens_the_settings_dialog(qt_app, make_window):
"""The gear is the one place the filters can be changed from the UI."""