Skip to content

feat: Add explicit yuv-420-8-bit-video/yuv-420-8-bit-full target pixel formats#4022

Open
wcandillon wants to merge 2 commits into
mrousavy:mainfrom
wcandillon:feat/explicit-yuv-target-format
Open

feat: Add explicit yuv-420-8-bit-video/yuv-420-8-bit-full target pixel formats#4022
wcandillon wants to merge 2 commits into
mrousavy:mainfrom
wcandillon:feat/explicit-yuv-target-format

Conversation

@wcandillon

Copy link
Copy Markdown

What

Adds two explicit values to TargetVideoPixelFormat (used by FrameOutputOptions.pixelFormat), reusing the naming of the existing VideoPixelFormat type:

  • 'yuv-420-8-bit-full': explicitly YUV 4:2:0 8-bit full-range (kCVPixelFormatType_420YpCbCr8BiPlanarFullRange on iOS, same as 'yuv' today)
  • 'yuv-420-8-bit-video': explicitly YUV 4:2:0 8-bit video-range (kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange on iOS)

The 'yuv' target is unchanged, so existing behavior is not affected.

Why

The 'yuv' target resolves to kCVPixelFormatType_420YpCbCr8BiPlanarFullRange (420f) on iOS. Some GPU video pipelines can only import video-range YUV buffers. Concretely, WebGPU (Dawn) only maps video-range formats to wgpu::TextureFormat for external texture import on Metal; full-range 420f fails validation with "Unsupported IOSurface format". See GetFormatEquivalentToIOSurfaceFormat in Dawn's SharedTextureMemoryMTL.mm, which supports kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange (mapped to R8BG8Biplanar420Unorm) but not the full-range variant.

With this change, react-native-webgpu users can request pixelFormat: 'yuv-420-8-bit-video' on a CameraFrameOutput and import the resulting Frames zero-copy as WebGPU external textures.

Notes

  • On Android both new values resolve to YUV_420_888 (same as 'yuv'), as CameraX/Camera2 do not allow configuring the color range of the output. This is documented on the type and in the docs page.
  • Nitrogen specs regenerated via bun camera specs.
  • Docs: extended the "Pixel Formats Map" page with the two new targets.

🤖 Generated with Claude Code

…pixel formats

The `'yuv'` target resolves to `kCVPixelFormatType_420YpCbCr8BiPlanarFullRange`
on iOS. Full-range YUV IOSurfaces cannot be imported by some GPU video
pipelines; for example WebGPU (Dawn) only supports video-range YUV formats
for external textures (`kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange`).

This adds two explicit `TargetVideoPixelFormat` values, reusing the naming
of the existing `VideoPixelFormat` type:

- `'yuv-420-8-bit-full'`: explicitly full-range (same as `'yuv'` today)
- `'yuv-420-8-bit-video'`: explicitly video-range, required for zero-copy
  WebGPU/Dawn external texture import

On Android both resolve to `YUV_420_888` (same as `'yuv'`), as the color
range of the Camera output cannot be configured there.

The `'yuv'` target is unchanged, so existing behavior is not affected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

@wcandillon is attempting to deploy a commit to the Margelo Team on Vercel.

A member of the Team first needs to authorize it.

@mrousavy

Copy link
Copy Markdown
Owner

Are you sure Dawn only supports kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange? Isn't kCVPixelFormatType_420YpCbCr8BiPlanarFullRange the default YUV format supported everywhere?

I think it can be good to have control over which format it should be, but the fact that changing from 'yuv-420-8-bit-full' to 'yuv-420-8-bit-video' does absolutely nothing and gives you the exact same buffer on Android is a bit weird to me.

Technically that would make a big difference and could break assumptions based on Pixel Data. But I guess since the Frame gives you a .pixelFormat it's fine, it's just weird that you request -video and receive -full on Android then.. 🤔

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