Apply video source fourcc before fps - #3105
ColePBryan wants to merge 11 commits into
Conversation
|
👋 Thanks for the pull request! Here is how automated Claude review works here, so you spend credits (and reviewer time) wisely. 🚦 This PR is marked Ready for review, so automated Claude review will run — and every pass spends real credits. Warning 💸 The Claude reviewer bills in credits, not vibesAutomated review spins up a real agent that reads real code and spends real credits on every pass. It is glad to help — but it is not a rubber duck, a linter you poke in a loop, or a substitute for reading the contributing guide. Treat it like an expensive senior reviewer whose time you booked, and show up prepared. Draft when unsure, Ready when you mean it:
However you get there, arrive prepared:
Reviews are not free. A draft costs nothing to review; a Ready PR is a promise that it is worth reviewing.
|
alexnorell
left a comment
There was a problem hiding this comment.
The camera property ordering and both producer integrations pass all 28 focused tests at this head. Please remove the duplicate pass-through ordering case below; no real UVC camera was available for FPS negotiation validation.
| assert "Ignoring invalid fourcc" in streamvision_caplog.text | ||
|
|
||
|
|
||
| def test_properties_without_fourcc_and_fps_are_applied_in_given_order() -> None: |
There was a problem hiding this comment.
[P2] Remove this pass-through ordering case. test_other_properties_keep_their_given_order_between_fourcc_and_fps already checks height, brightness and width retain their input order while also exercising the camera-sensitive FOURCC/FPS reordering. This additional recording-fake assertion covers no separate camera negotiation outcome or failure mode.
shntu
left a comment
There was a problem hiding this comment.
Reviewed with Claude and Codex. Codex found no regressions. Applying fourcc, then the other properties, then fps is the right V4L2 order, and both OpenCV-backed producers use the new helper.
Two small follow-ups are inline: one docstring and one input-validation gap. Neither is a blocker. A non-blocking note: the public InferencePipeline.init* and prepare_video_sources signatures, WebRTCVideoFrameProducer.initialize_source_properties, and inference_sdk's start_inference_pipeline_with_workflow are still typed Dict[str, float], so passing a string fourcc through the Python API is a type error even though it works at runtime.
| """Set capture properties on ``stream`` with ``fourcc`` first and ``fps`` last. | ||
|
|
||
| Properties other than ``fourcc`` and ``fps`` keep their given order. | ||
| ``fourcc`` may be a four-character code (``"MJPG"``, ``"mjpg"``) or its |
There was a problem hiding this comment.
[Claude] Since 85cc12e dropped .upper(), "mjpg" is encoded as the FOURCC mjpg, not MJPG. V4L2 pixel formats are case-sensitive, so the driver rejects it and stays on YUYV. That rejection is logged only at DEBUG, so it is invisible at the default level. Suggest removing "mjpg" from the docstring, or saying explicitly that codes are case-sensitive.
There was a problem hiding this comment.
[Claude] Rechecked at 468ce8b: the force-push rebased the same two commits onto main without changing them, so this is still open.
There was a problem hiding this comment.
Fixed in 7791a79. The docstring says codes are case-sensitive, and a fourcc the camera rejects is now logged at WARNING.
| f"Video source property {property_id!r} must be a " | ||
| f"number, got {property_value!r}." | ||
| ) | ||
| validated[property_id] = property_value |
There was a problem hiding this comment.
[Claude] This accepts any string as fourcc, so "MJPEG" or "MJ" return 200 from the API and are then dropped on the device with a warning. Validating with _parse_fourcc here (and reusing FOURCC_PROPERTY) would give callers a 422 and keep a single definition of a valid code. It would also catch strings like "²²²²": str.isdigit() is True for them, but int() raises, so at the moment they crash source startup instead of being skipped. Alternatively, guard that with code.isascii() and code.isdigit() in _parse_fourcc.
There was a problem hiding this comment.
[Claude] Rechecked at 468ce8b: the force-push rebased the same two commits onto main without changing them, so this is still open.
There was a problem hiding this comment.
V4L2 cameras such as the Logitech C920 open in YUYV, where 1080p is capped at 5 fps. Properties arrive from a JSON map with no guaranteed key order, so fps could be set (and clamped) before fourcc switched the device to MJPG, leaving the stream at 5 fps. Apply fourcc first, other properties in their given order, then fps. Accept fourcc as a four-character code (e.g. "MJPG") as well as its numeric value, skip invalid values with a warning, and log the effective format read back from the capture. The stream manager VideoConfiguration now accepts a string fourcc while still coercing all other properties to float.
strip() and upper() broke valid codes: "Y16 " ends in a space and avc1 or pRAA are case-sensitive. A four-character value is now used exactly as given; other strings are trimmed. A property the capture doesn't accept is logged at debug level, and the source-property type hints in stream_vision accept string values such as a fourcc code.
85cc12e to
468ce8b
Compare
Expose parse_fourcc and reuse it in VideoConfiguration so an invalid fourcc is a validation error instead of being silently dropped on the device. Only ASCII digits are parsed as a numeric code, so values like "²²²²" are rejected rather than crashing source startup. A fourcc the device rejects is now logged at WARNING; the docstring states codes are case-sensitive. Drop a duplicate ordering test.
InferencePipeline init methods, prepare_video_sources, the WebRTC producer and the SDK's start_inference_pipeline_with_workflow accept a string fourcc at runtime, so widen their annotations to match.
Validating fourcc in the stream manager's entities imported capture_properties, which imports cv2, and broke the isolation check that entities import without OpenCV. Move parse_fourcc and FOURCC_PROPERTY to streamvision.camera.fourcc, packing the code as cv2.VideoWriter_fourcc does (tested against it); capture_properties re-exports both.
alexnorell
left a comment
There was a problem hiding this comment.
The Unicode validation and lightweight-import fixes are verified: all 54 camera tests pass, and a fresh client/entities import loads none of the forbidden heavy modules. The previous pass-through duplicate is removed. Please consolidate the two remaining direct-helper duplicates below while retaining the entity/API boundary tests. Current package CI is still being rechecked after the import fix.
| None, | ||
| ], | ||
| ) | ||
| def test_parse_fourcc_returns_none_for_invalid_values(fourcc: Any) -> None: |
There was a problem hiding this comment.
[P2] Move the unique Arabic-digit case into test_invalid_fourcc_is_skipped_with_a_warning, then remove this direct-parser table. Its other values are already exercised through apply_capture_properties, which checks the same parser result plus the warning and continued property application. Keep the entity rejection tests: they verify a separate API validation outcome.
|
|
||
|
|
||
| @pytest.mark.parametrize("code", ["MJPG", "YUYV", "avc1", "Y16 ", "pRAA"]) | ||
| def test_fourcc_matches_opencv_packing(code: str) -> None: |
There was a problem hiding this comment.
[P2] Remove this duplicate packing test. The existing capture tests already compare MJPG, avc1, Y16-with-space, and pRAA against the same cv2.VideoWriter_fourcc oracle through the production apply path. YUYV is another ordinary four-byte ASCII sample, adding no distinct packing branch or failure mode. The existing fresh-import isolation probe covers the reason for moving the parser.
The public-contract tests freeze InferencePipeline.init* signatures and docstrings, so restore them; the widened hints stay on the helpers outside that contract. The request-entities import check lists the modules entities may load, which now include streamvision.camera.fourcc.
alexnorell
left a comment
There was a problem hiding this comment.
The latest commit restores the intentional legacy signature contract and accounts for the new lightweight FOURCC module. The camera implementation is unchanged from the 54 passing focused tests. The import-format CI failure remains in unchanged code, alongside the two existing test-cleanup findings.
| import cv2 | ||
|
|
||
| from streamvision.camera.fourcc import FOURCC_PROPERTY, parse_fourcc | ||
|
|
There was a problem hiding this comment.
[P2] Format this import group with the repository isort configuration. The code-quality job failed on this file alone (Imports are incorrectly sorted and/or formatted), and this file is unchanged by the latest commit, so the deterministic failure remains. Run the configured isort on the file and rerun the gate: https://github.com/roboflow/inference/actions/runs/36921392196/job/110568057950.
Apply the repository isort configuration to capture_properties. Move the non-ASCII-digit case into the skipped-with-warning table and drop the direct parser table and the packing test, which the capture-path tests already cover.
|
🤖 Claude review started at commit New commits are not auto-reviewed. Add the |
|
Review summary Skills: review-sdk, review-topic-backward-compat-and-versioning, review-topic-test-hygiene No dedicated surface skill covers What I verified against the code (zero-trust, static trace — I cannot run tests):
Prior automated-review threads (docstring case-sensitivity; validation gap / non-ASCII-digit crash) are resolved in the current code; the remaining prior notes were test-dedup NITs already trimmed. Maintainer note (non-blocking, does not gate): the Reviewed at HEAD: 59bd775 |
|
😎 PR passes the vibe-check and trust-me-bro verification. |
|
Maintainer review discussion: Slack thread. Final approval and merge remain in GitHub. |
alexnorell
left a comment
There was a problem hiding this comment.
The FOURCC-before-FPS ordering, invalid-format handling and lightweight parser import are verified, and the redundant tests are removed. All 41 focused camera tests, pinned isort, independent tensor integration runs for Python 3.10/3.12, and current required PR checks pass. Older optional OCR-proxy failures are external HTTP 500s; physical USB-camera negotiation was not exercised.
|
[P2] Preserve previously accepted numeric FOURCC representations during validation At I reproduced this against the actual base ( VideoConfiguration(
type="VideoConfiguration",
video_reference=0,
video_source_properties={"fourcc": "1196444237.0"},
)Before: accepted and converted to Please preserve numeric coercion for valid integral FOURCC values while retaining support for case-sensitive four-character codes and rejection of genuinely invalid values. Regression coverage should include the previously accepted numeric representations above. This is distinct from the earlier discussion about rejecting malformed codes such as |
Validation ran parse_fourcc before numeric coercion, and parse_fourcc only read plain digit strings, so values the previous Dict[str, float] field accepted now failed: "1196444237.0", "1.196444237e9", "+1196444237", and numpy integers from Python callers. Read strings with float() (ASCII only, so non-ASCII digits stay rejected) and any numbers.Real value, keeping finite, non-negative integral results; non-numeric four-character strings are still case-sensitive codes.
|
@dkosowski87 Thanks, good catch. Fixed in 4032c22: |
alexnorell
left a comment
There was a problem hiding this comment.
One reproduced input-validation regression remains: oversized camera-property integers escape as server errors instead of validation errors. See the inline reproduction.
| ) | ||
| try: | ||
| validated[property_id] = float(property_value) | ||
| except (TypeError, ValueError): |
There was a problem hiding this comment.
[P2] Keep oversized numeric inputs as validation errors. A valid JSON payload containing video_source_properties={"width": 10**400} raises an uncaught OverflowError here; the same value for fourcc raises earlier in parse_fourcc at its float(value) conversion. I reproduced the actual base and current DTOs through FastAPI: both properties return HTTP 422 at base 23a01eb3, but HTTP 500 at this head (the 45 focused tests pass). Handle OverflowError in both numeric conversion paths and add these inputs to the existing invalid-value cases so malformed requests retain a structured validation response.
There was a problem hiding this comment.
Fixed in a069ad9. OverflowError is now handled in both numeric paths, so 10**400 / -10**400 for width or fourcc return a 422 again. Non-fourcc properties are now validated with pydantic's float (TypeAdapter(float)), so they match the field's original float semantics exactly (this also rejects "١٢" and bytearray(b"12") again). A numeric fourcc goes through the same float validation before the FOURCC check, and parse_fourcc guards its float conversions and returns None, so the device-side apply path logs "Ignoring invalid fourcc" instead of raising. Integral b"12" / Decimal fourcc values are accepted.
New test cases: 10**400 and -(10**400) in the invalid VideoConfiguration cases for both fps and fourcc; "١٢" and bytearray(b"12") in the rejected non-fourcc cases; bytes/Decimal/Fraction forms in the accepted fourcc cases; 10**400 in the invalid fourcc skipped-with-warning table.
An integer too large for a float, such as 10**400, raised OverflowError from the video_source_properties validator (and from parse_fourcc for fourcc), so the request failed with a 500 instead of a 422. Validate non-fourcc properties with pydantic's float adapter so they keep the field's float semantics, including rejecting oversized integers, non-ASCII digits and bytearrays. Validate a numeric fourcc the same way before checking that it is a valid code, and guard parse_fourcc's float conversions so it returns None for any value it cannot convert. This also accepts integral bytes and Decimal fourcc values.
Description
V4L2 cameras such as the Logitech C920 open in YUYV, where 1080p is capped at 5 FPS.
video_source_propertiesarrive as a JSON map with no guaranteed key order, sofpscould be set (and clamped) beforefourccswitched the device to MJPG, leaving the stream at 5 FPS even withfourccset.apply_capture_propertiesnow appliesfourccfirst, other properties in their given order, thenfps. It acceptsfourccas a four-character code (e.g."MJPG", used exactly as given since codes likeavc1andY16are case- and space-sensitive) or its numeric value, skips invalid values with a warning, logs a property the capture rejects at debug level, and logs the effective format read back from the capture. The stream manager'sVideoConfigurationand the stream_vision source-property type hints accept a stringfourcc.Type of change
How has this change been tested, please provide a testcase or example of how you tested the change?
Any specific deployment considerations
None.
Docs