Skip to content

fix(android): bypass vendor limited-range YUV conversion - #67

Closed
manaikha wants to merge 4 commits into
tranvuongquocdat:mainfrom
manaikha:fix/android-bt709-surface-dataspace
Closed

manaikha wants to merge 4 commits into
tranvuongquocdat:mainfrom
manaikha:fix/android-bt709-surface-dataspace

Conversation

@manaikha

@manaikha manaikha commented Sep 6, 2026

Copy link
Copy Markdown

Problem

Some light gray colors in the SideScreen video stream were rendered as white on some Android tablets.

For example:

  • #F1F3F4 was rendered as white on the Android display
  • The same color appeared correctly as light gray on the Mac display

The issue was reproduced on a Samsung Galaxy Tab S7 FE (SM-T736B) running Android 14.

Root cause

The Android client previously decoded HEVC directly into a SurfaceView using the vendor display/compositor path.

The decoder correctly reports the stream as BT.709 / SDR / full-range, but on affected devices the vendor YUV→RGB presentation path can apply a limited-range matrix regardless of the full-range flag.

This causes light grayscale values to be clipped toward white. For example, #F1F3F4 was displayed as white even though the source frame contained the correct light-gray value.

Changing the SurfaceView dataspace alone did not resolve the issue.

Fix

Replace the zero-copy MediaCodecSurfaceView rendering path with an application-controlled rendering path:

  • Configure MediaCodec for byte-buffer/Image output instead of decoding directly to a Surface.
  • Extract decoded YUV frames using MediaCodec.getOutputImage() / output buffers.
  • Handle flexible YUV420 layouts and interleaved NV12/QTI output.
  • Reuse decoded frame buffers with a small frame pool.
  • Use a bounded render queue to prevent excessive frame buffering.
  • Render frames through a custom GLSurfaceView.
  • Perform explicit BT.709 full-range YUV→RGB conversion in the GLSL fragment shader.
  • Bypass the affected vendor YUV→RGB conversion path.
  • Preserve frame-drop, stale-output handling, and keyframe recovery.
  • Keep the existing Mac capture, encoder, and stream format unchanged.

Validation

Tested on:

  • Samsung Galaxy Tab S7 FE (SM-T736B)
  • Android 14
  • USB-C connection
  • Default installed SideScreen Mac application
  • Custom terminal-built MacHost

Verified correct rendering at:

  • 1280×800
  • 1920×1200
  • 2560×1600

Also tested with:

  • Ultra Low
  • Medium
  • High

The light-gray test color #F1F3F4 now remains visibly gray instead of being clipped to white.

Scope

This PR replaces the previous SurfaceView dataspace-only approach with an application-controlled YUV→RGB rendering path.

No Mac-side encoder or capture changes are required.

The fix is implemented entirely on the Android client.

@vercel

vercel Bot commented Sep 6, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the tranvuongquocdat2-7001's projects Team on Vercel.

A member of the Team first needs to authorize it.

@manaikha
manaikha marked this pull request as draft September 7, 2026 02:27
@manaikha manaikha changed the title fix(android): set SurfaceView dataspace to BT.709 fix(android): bypass vendor limited-range YUV conversion Sep 7, 2026
@manaikha
manaikha marked this pull request as ready for review September 7, 2026 05:14
@tranvuongquocdat

Copy link
Copy Markdown
Owner

Thanks for digging into this — the S7 FE repro is useful. One thing to check before we go further: the Mac side on main (unreleased, after 0.11.2) already switched capture to video-range and tags the stream BT.709 explicitly, which is the fix for #55. Your tests ran against the released 0.11.2 Mac app, which still sends full-range. Could you build MacHost from main and re-test with the stock Android client? If the greys come out right, I'd rather not give up the zero-copy Surface path — the byte-buffer + GL route costs a CPU copy per frame, which hurts on the budget tablets we're already fighting black screens on.

@tranvuongquocdat

Copy link
Copy Markdown
Owner

0.11.3 is released now, so you can test with the stock Mac app instead of building from main.

@manaikha

manaikha commented Sep 7, 2026

Copy link
Copy Markdown
Author

0.11.3 is released now, so you can test with the stock Mac app instead of building from main.

Thanks for pointing me to 0.11.3. I tested the official 0.11.3 Mac app together with the stock 0.11.3 Android app on the Galaxy Tab S7 FE using the same reproduction case.

The issue is now fixed — #F1F3F4 renders correctly with the stock 0.11.3 builds. I also confirmed that 0.11.3 Mac works correctly with my Path B Android build, but since the stock Android client is now working, I agree that keeping the zero-copy SurfaceView path is preferable.

I'll close this PR. Thanks for the guidance!

@manaikha manaikha closed this Sep 7, 2026
@tranvuongquocdat

Copy link
Copy Markdown
Owner

Thanks for closing the loop on real hardware — that's the first tablet confirmation for #55.

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