Skip to content

Conversation

Mairramer
Copy link
Contributor

@Mairramer Mairramer commented Oct 3, 2025

This change replaces the previous planesToNV21() implementation with a direct and stride-aware conversion.

The prior version assumed that the U and V planes followed the same layout used by Camera2, which is not always true for CameraX. This assumption could cause color distortion or corrupted frames because CameraX applies internal transformations (rotation, cropping, or proxy wrapping) that modify rowStride, pixelStride, and overall buffer layout.

After investigating inconsistencies between the original areUVPlanesNV21()-based logic and the actual ImageProxy buffers returned by CameraX, this version explicitly reconstructs NV21 by reading each plane row by row while respecting their individual strides, rather than simply validating whether the format already matches NV21.

Fixes flutter/flutter#176410

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2 3

Copy link

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

@Mairramer Mairramer marked this pull request as ready for review October 3, 2025 18:24
@Mairramer Mairramer requested a review from camsim99 as a code owner October 3, 2025 18:24
Copy link

Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.Invoking GeminiYou can request assistance from Gemini at any point by creating a comment using either `/gemini ` or `@gemini-code-assist `. Below is a summary of the supported commands on the current page.Feature | Command | Description--- | --- | ---CustomizationTo customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).Limitations & FeedbackGemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up [here](https://google.qualtrics.com/jfe/form/SV_2cyuGuTWsEw84yG).


List<PlaneProxy> planes = Arrays.asList(yPlane, uPlane, vPlane);

ByteBuffer nv21Buffer = ImageProxyUtils.planesToNV21(planes, width, height);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

1280x720 → ~4.6 ms per frame. Further evaluation is needed to assess the impact on the processing pipeline.

@stuartmorgan-g stuartmorgan-g added the triage-android Should be looked at in Android triage label Oct 7, 2025
@camsim99
Copy link
Contributor

camsim99 commented Oct 7, 2025

@Mairramer two things -- First, can you possibly update to the PR description to describe how you came to these changes? I'm not an expert in the conversion so it would help me review this PR.

Second, I assumed the fix would be using this logic I left out initially:
https://github.com/googlesamples/mlkit/blob/da17257a78b9beedb57b7a9795b911296ae970a0/android/automl/app/src/main/java/com/google/mlkit/vision/automl/demo/BitmapUtils.java#L322-L330

Thoughts on that versus your approach?

@Mairramer
Copy link
Contributor Author

Mairramer commented Oct 7, 2025

Second, I assumed the fix would be using this logic I left out initially: https://github.com/googlesamples/mlkit/blob/da17257a78b9beedb57b7a9795b911296ae970a0/android/automl/app/src/main/java/com/google/mlkit/vision/automl/demo/BitmapUtils.java#L322-L330

Thoughts on that versus your approach?

While this approach is valid, it still encounters the same issue when checking areUVPlanesNV21().

My implementation essentially replicates part of the unpackPlane() logic, avoiding assumptions about the U/V buffer layout and ensuring a deterministic NV21 output. Additionally, it leverages CameraX’s internal ImageUtil
for a more robust solution.

Both approaches perform a complete manual reconstruction of the image, guaranteeing correctness regardless of how the U/V planes are arranged.

I am still investigating why position == limit occurs in some CameraX buffers. Understanding this behavior could help refine the reconstruction logic.

@Mairramer
Copy link
Contributor Author

My only concern is the per-frame processing time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p: camera platform-android triage-android Should be looked at in Android triage
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[camera_android_camerax] Setting NV21 format for image streaming sometimes throws java.lang.IllegalArgumentException: newPosition > limit
3 participants