达不到帧率的判据改用实测速率(DSHOW 回读值会说谎),探针加帧率扫描 - What: short_capture_fallacy() 改为以**实测**速率为准(新增 measured_fps 关键字参数),消息改成"it delivers about 15 fps while the config asks for 30 (1920x1080 reported 30, MJPG/DSHOW)"——把驱动自报值降级为"矛盾的一部分"而不是判据;app_window 的 _check_capture_negotiation() 相应改为用 packet 的 capture_fps(实测)判据、实测为 0 时不下结论(等窗口填满)。tools/camera_probe.py 重写:probe() 返回结构体、format_row() 把 reported 与 measured 分列并在达不到 90% 时标 "<-- misses the request";新增 --samples 与**帧率扫描**(同一格式依次申请 60/30/20/15/10 fps,逐行给实测值与"跟随申请 / 只有申请的 x 倍"的判断,最后给"本次看到的ceiling")。tests/test_worker_frame_rate.py 的归因测试同步更新,并显式覆盖"驱动自报 30 而实测 15 必须判为不达标"与"没有实测值时不说话"。 - Why: 用户跑探针的结果推翻了我上一轮的判据:MJPG 1920x1080@30 -> MJPG 1920x1080 @ 30.0 measured 15.0。DSHOW 后端下 CAP_PROP_FPS 只是回读你设的值,所以 SDK 的 status.fps 同样不可信 —— 我原来比较"申请 vs 协商"等于拿申请跟自己比,那个提示永远不会出现(用户实际看到的只有轨道上的实测 16.7)。这也解释了为什么这件事必须靠实测:真实速率只能测出来。另外探针显示 1280x720 MJPG 实测也是 15、YUYV 只有 1 fps,说明换分辨率不解决问题,需要区分"摄像头本身的上限"与"我们申请多少就被砍半"。 - Verification: shared 全量 161 passed(含新增/更新的 3 项断言);studio 91 passed;ui_check 554 项检查 0 失败;探针在本机(无摄像头)仍返回 1 并给出解释,format_row/_decode 单测通过("reported 30.0 measured 15.0 ... <-- misses the request")。 - Not in this change: 未改 SDK 采集申请顺序/后端;未做自动降级;frames.csv 仍未记录 process_ms/capture_fps。

This commit is contained in:
ykcheungab
2026-09-14 13:38:57 +08:00
parent 3db55b2077
commit a44eb2d65d
4 changed files with 154 additions and 54 deletions
+10 -8
View File
@@ -630,12 +630,13 @@ class StudioMainWindow(QMainWindow):
return {}
def _check_capture_negotiation(self) -> None:
"""Say it out loud when the camera cannot do what the config asks.
"""Say it out loud when the camera delivers less than the config asks.
A camera that only offers a lower rate than the config requests is not
something the pipeline can fix, and it is not something the operator
should have to infer from a small number on the rail. Said once per
session, and only once the driver has confirmed a rate.
The evidence is the *measured* capture rate, not the driver's reported
one: OpenCV's DSHOW backend echoes ``CAP_PROP_FPS`` back as whatever was
set, so a camera advertising 30 and delivering 15 reports "30.0" (measured
on this project's own hardware). Said once per session, and only once a
measured rate exists.
"""
if self._capture_checked:
return
@@ -649,10 +650,11 @@ class StudioMainWindow(QMainWindow):
logger.exception("Could not read the capture status")
self._capture_checked = True
return
if not ((status.get("negotiated") or {}).get("fps")):
return # the driver has not reported a rate yet: ask again next frame
measured = float(self._latest_capture_fps or 0.0)
if measured <= 0.0:
return # no measured rate yet: ask again on the next frame
self._capture_checked = True
detail = short_capture_fallacy(status)
detail = short_capture_fallacy(status, measured_fps=measured)
if detail:
self._notify(t("status.capture_slow", detail=detail))