Skip to content

fix(meta_quest): auto-discovery responder + viewer preview - #15

Merged
styu12 merged 1 commit into
mainfrom
fix/quest-discovery-and-preview
Apr 15, 2026
Merged

fix(meta_quest): auto-discovery responder + viewer preview#15
styu12 merged 1 commit into
mainfrom
fix/quest-discovery-and-preview

Conversation

@styu12

@styu12 styu12 commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Diagnosed on live Quest session

Viewer showed quest_tracking as 'Waiting for data…' and quest_cam as black even though both were 'connected'. Two distinct bugs:

Tracking: Quest app's hardcoded target IP is 172.30.1.51 (template default from a dev LAN); Mac was on 192.168.4.35. Quest's auto-discovery broadcasts SYNCFIELD_DISCOVER_RECORDER_V1 on UDP :14044 every 2s but we never responded → Quest stuck sending into the void. Python adapter now listens on :14044 and replies with the correct local IP per outbound interface.

Camera preview: MJPEG was actually arriving (verified with curl /preview/left — valid JPEG bytes), but viewer polls stream.latest_frame (a single attribute), and MetaQuestCameraStream only exposed latest_frame_left / latest_frame_right. Added a latest_frame proxy returning the left eye.

52 unit tests pass.

🤖 Generated with Claude Code

…empty

On-device diagnosis against a live Quest session showed two reasons
the viewer sat empty even though the Unity sender app was running
and the adapters were "connected":

1. Quest sender wasn't reaching the Mac's UDP :14043 at all. The
   Quest app ships with a hardcoded default target IP of
   ``172.30.1.51`` (a dev LAN from the original project template)
   and relies on auto-discovery to find the recorder at runtime.
   MetaQuestHandStream never responded to those discovery probes,
   so the Quest kept firing UDP into the void while the viewer
   cheerfully said "connected" (one stale packet from an old
   session had set the heartbeat).

   Add a background UDP :14044 responder that replies to the
   ``SYNCFIELD_DISCOVER_RECORDER_V1`` broadcasts Unity's
   UDPTrackingSender already emits every 2 s. Reply shape matches
   the ``RecorderDiscoveryResponse`` parser in the Unity client.
   We pick the "right" local IP per responder by opening a scratch
   UDP socket toward the Quest's address and reading getsockname —
   no packets sent, but the kernel chooses the correct outbound
   interface, which is the interface the Quest should target.

   Single-process multi-Quest setups would fight over :14044, so a
   bind failure silently falls back to "manual IP only" with a log
   hint. Normal single-Quest use now auto-resolves.

2. MetaQuestCameraStream declared ``kind="video"`` but exposed only
   ``latest_frame_left`` / ``latest_frame_right``, not the
   ``latest_frame`` attribute the viewer's StreamSnapshot polls for
   every video stream. Result: the camera card rendered black even
   while MjpegPreviewConsumer was happily decoding JPEGs into the
   per-eye slots. Add a ``latest_frame`` property that returns the
   left eye (falling back to right) so the existing video panel
   lights up without any viewer-side changes.

   Phase-1 caveat still applies: both eyes get the primary-camera
   view until Meta XR 2.4 exposes per-eye acquisition cleanly (spec
   §9 Q1). Once that lands ``latest_frame`` can switch to a
   side-by-side composite.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@styu12
styu12 merged commit e4bd7f0 into main Apr 15, 2026
0 of 4 checks passed
@styu12
styu12 deleted the fix/quest-discovery-and-preview branch April 15, 2026 01:16
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.

1 participant