Skip to content

fix(moq-video): pick the V4L2 mode nearest the requested resolution - #3355

Merged
kixelated merged 2 commits into
moq-dev:mainfrom
Frando:pr/v4l2-mode-nearest
Sep 5, 2026
Merged

fix(moq-video): pick the V4L2 mode nearest the requested resolution#3355
kixelated merged 2 commits into
moq-dev:mainfrom
Frando:pr/v4l2-mode-nearest

Conversation

@Frando

@Frando Frando commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

negotiate asked for YUYV, then MJPEG, and kept the first reply that came back as a format this crate can convert. Taking the first one pins most laptop webcams to VGA.

This PR is part of a series to update iroh-live to latest moq, see n0-computer/iroh-live#45. The initial code and description were written by Claude Code; the final review repair was written by Codex.

The problem

V4L2's non-mutating VIDIOC_TRY_FMT is optional. The required VIDIOC_S_FMT asks and applies in one step, substituting the driver's nearest supported mode for anything it does not have, and it reports success either way. A successful reply therefore says nothing about the geometry or pixel format that came back.

USB bandwidth does not fit uncompressed 4:2:2 much above VGA, so a typical UVC camera offers YUYV only at small sizes and reaches HD through MJPEG alone. Asking such a camera for YUYV at 720p gets 640x480: a valid YUYV mode, a successful call, and nowhere near what the caller asked for. Since YUYV was tried first and its reply accepted, MJPEG never got asked, and a camera capable of 1080p published VGA.

The fix

  • Probe every format this crate handles, treat an individual driver rejection as an unavailable candidate, discard zero or odd geometries that the I420 pipeline cannot encode, and score the remaining replies against the requested size. If every probe fails, return the last driver error.
  • Break equal-distance ties using the returned pixel format, preferring YUYV's resample over an MJPEG decode, then re-apply the winner.
  • Report the modes the driver actually returned when none are usable, and reject MJPEG frames whose decoded dimensions disagree with the negotiated stream.

Test plan

  • Unit regressions cover the real VGA-versus-HD case, a rejected probe after a valid YUYV reply, all probes failing, reversed-order format ties, zero and odd replies, distance scoring, and FourCC conversion.
  • Confirmed against an Integrated Camera (UVC) that offers MJPEG up to 2592x1944 but YUYV only at 640x480 and 640x360: requests for 1280x720 and 1920x1080 each returned 640x480 before this change and negotiate exactly after it.
  • The same machine's infrared camera, which offers GREY alone, now names the returned mode in its error.
  • just fix origin/main and GitHub Check, Test, and Swift workflows.

Public API changes

None. negotiate is internal to the V4L2 capture backend.

(Written by GPT-5.6 Sol)

Frando added a commit to Frando/moq that referenced this pull request Sep 3, 2026
@kixelated
kixelated marked this pull request as ready for review September 5, 2026 00:29
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 73db0646-6a32-408c-971b-cea6331cfb5d

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5e845 and 0b9ca41.

📒 Files selected for processing (1)
  • rs/moq-video/src/capture/v4l2.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-09-05T02:08:40.124585Z 0b9ca41 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bc5098dbe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-video/src/capture/v4l2.rs Outdated
`negotiate` asked for YUYV, then MJPEG, and kept the first reply that came
back as a format we convert. V4L2 has no "what would you offer me" call:
`VIDIOC_S_FMT` asks and applies in one step, substituting the driver's
nearest supported mode for anything it does not have. A successful reply
therefore says nothing about geometry, and taking the first one pins most
laptop webcams to VGA. USB bandwidth does not fit uncompressed 4:2:2 above
that, so they offer YUYV only at small sizes and reach HD through MJPEG
alone; asking such a camera for YUYV at 720p gets 640x480 back, which is a
valid YUYV mode and nowhere near what the caller asked for.

Probe every format we handle, score each reply against the requested size,
and re-apply the winner, so the geometry decides and the format only breaks
ties. Ties go to YUYV, which resamples, over MJPEG, which costs a JPEG
decode per frame.

Also report what the driver actually offered when nothing is convertible:
the error printed a `FourCC` with its derived `Debug`, so an IR camera that
speaks only GREY came out as `repr: [71, 82, 69, 89]`.

Confirmed against an Integrated Camera (UVC) that offers MJPEG up to
2592x1944 but YUYV only at 640x480 and 640x360: requests for 1280x720 and
1920x1080 each returned 640x480 before and now negotiate exactly.
@kixelated
kixelated force-pushed the pr/v4l2-mode-nearest branch from 9bc5098 to 1c5ccb6 Compare September 5, 2026 01:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c5ccb68b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-video/src/capture/v4l2.rs Outdated
Co-Authored-By: Codex <codex@openai.com>
@kixelated
kixelated force-pushed the pr/v4l2-mode-nearest branch from 1c5ccb6 to 0b9ca41 Compare September 5, 2026 02:02
@kixelated
kixelated merged commit 27c199b into moq-dev:main Sep 5, 2026
3 checks passed
@moq-bot moq-bot Bot mentioned this pull request Sep 5, 2026
steelhead99x added a commit to steelhead99x/moq that referenced this pull request Sep 5, 2026
* docs(quest): settle scope narrowing in place, and mark pre-media sidecar placement (moq-dev#3427)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* fix(moq-video): pick the V4L2 mode nearest the requested resolution (moq-dev#3355)

Co-authored-by: Luke Curley <kixelated@gmail.com>
Co-authored-by: Codex <codex@openai.com>

* feat(moq-video): add the Android MediaCodec encoder and decoder (moq-dev#3354)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Luke Curley <kixelated@gmail.com>

* docs(quest): import the post-grooming issues as quests (moq-dev#3431)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* docs(quest): apply the Codex findings on the issue import (moq-dev#3432)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* fix(claude): adopt a quest branch at the remote tip that was inspected (moq-dev#3421)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(quest): record four findings from the m1 quest wave (moq-dev#3424)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: reorganize the site around what a reader can do (moq-dev#3426)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* chore: ignore Claude Code's scratch directories (moq-dev#3428)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(moq-audio,moq-cli): assert publish_capture stays Send off macOS (moq-dev#3433)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs: correct claims found during merge review (moq-dev#3435)

Co-authored-by: GPT-5 <noreply@openai.com>

* docs(quest): import the open issues that had no quest, and gate the dev merge (moq-dev#3434)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* docs(moq-audio): scope the local-task guidance to macOS (moq-dev#3436)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs: track deferred review findings (moq-dev#3438)

Co-authored-by: GPT-5 <noreply@openai.com>

* chore: remove redundant packaging work and plan relay ownership fixes (moq-dev#3440)

Co-authored-by: GPT-6 <noreply@openai.com>

* perf(net): avoid redundant chunk copies and plan performance investigations (moq-dev#3443)

Co-authored-by: GPT-6 <noreply@openai.com>

* fix(transcode): follow a source resolution change with the ladder (moq-dev#3381)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: GPT-5 <noreply@openai.com>

* feat(watch): share one AudioContext across audio decoders

Spatial playback needs every remote in the same Web Audio graph. Injected
contexts are never closed.

Co-Authored-By: Cursor Grok 4.6 <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Luke Curley <kixelated@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Franz Heinzmann <frando@unbiskant.org>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: GPT-5 <noreply@openai.com>
Co-authored-by: Cursor Grok 4.6 <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants