A native Android XR app that plays 180° side-by-side and MV-HEVC spatial 3D video on a curved hemisphere in full-space mode, with eye-tracked foveated sharpening. Built with Jetpack XR, SceneCore, and Media3/ExoPlayer.
I built a small native Android XR app that plays 180° side-by-side (SBS) stereoscopic video — and now MV-HEVC spatial video too — on a curved hemisphere in full-space mode. It’s the kind of immersive playback you want from a VR headset, without going through any third-party player. It targets devices with OpenXR support and is tested on a Samsung Galaxy XR.
The app has picked up a lot since the first release. The highlights:
MV-HEVC spatial video. Multiview HEVC files are detected automatically and played as true multiview stereo, so each eye gets its own full-resolution view. SBS files work exactly as before.
Eye-tracked foveated sharpening. A luma-only unsharp mask, strongest where you’re looking and fading out toward the periphery. It follows your eyes when the headset supports eye tracking and falls back to head gaze otherwise. Both eyes sharpen the same patch, so the stereo stays consistent, and blinks are bridged so the zone doesn’t flicker.
Sharpening tune panel. Pick eye or head gaze and adjust strength, zone size, fade, spacing, noise floor and halo limit live. Clean / Balanced / Noisy presets give you a quick starting point, and a debug mode tints the sharpened region green so you can see exactly where it lands.
Zoom that actually zooms. Scaling a dome that’s centered on your head turned out to be invisible (angular size doesn’t change), so zoom now slides the hemisphere along your view axis: the person in front of you appears closer or further (0.5×–10×).
Head-anchored dome. The video is anchored to a real-world pose captured from your head, so it stays put while you look around. The headset’s horizon-reset button recenters it, and pitch, onto your current gaze.
Smooth 8K seeking. While you scrub, the player shows nearby keyframe previews instead of stalling on every drag event. On release it lands frame-accurately, ignoring the last bit of hand-tracking jitter.
A better control panel. Bigger, headset-readable controls sit 30° right of your view, stay upright at steep pitch, and are always drawn in front of the zoomed video. After a horizon reset they briefly flash so you can see where they landed. A new Settings page (gear button) lets you set the menu size (50–150%) and distance (1–3 m), and both are remembered.
Lock panel. Locking the controls moves the unlock button to its own 3D panel 75° to your right, so stray pinches can’t move the screen mid-playback.
Startup feature checks. Head tracking, eye tracking (including calibration and permission states) and GPU support are probed up front instead of assumed. If the sharpening pipeline ever fails, the video simply replays from the same spot without it.
Under the hood. Upgraded to Jetpack XR 1.0.0 and Media3 1.11.1, plus fixes for readable seek-bar timestamps on bright frames and cleaner sensor/battery behavior when the app is paused.
Foveated sharpening: full strength within 20° of where you look, fading to nothing by 40°. Periphery stays untouched (illustration, effect exaggerated).
Screenshots
Bring your own 3D video: 180° SBS or MV-HEVC spatial, copied over USB or a cloud drive. Everything plays locally.The auto-hiding control panel — transport, seek, pitch and zoom steppers, file picker and lock.Pitch (±60°) tilts the dome; zoom (0.5×–10×) brings the video closer or pushes it away.Pinch & drag to seek without bringing up the full panel.Lock the controls so stray gestures won’t move the screen mid-playback.
Features
Plays 180° SBS video on a 15 m hemisphere surface (StereoMode.SIDE_BY_SIDE)
Plays MV-HEVC spatial video as multiview stereo (MULTIVIEW_LEFT_PRIMARY / MULTIVIEW_RIGHT_PRIMARY)
Eye- or head-gaze foveated sharpening with presets and a live tune panel
System file picker for any video/* source; URI permission is persisted
Dome anchored to head pose; horizon-reset/recenter snaps it and the controls to your gaze
Pitch control (±60°) and zoom by moving the dome along the view axis (0.5×–10×)
Pinch-and-drag gestures for seeking and pitch, with keyframe previews while scrubbing
Auto-hiding control panel with adjustable size and distance, plus a 3D lock panel
Looped playback via ExoPlayer, with playback state restored across pause/resume
How it’s built
Three runtime pieces are wired together in a single ComponentActivity:
An XR Session from androidx.xr.runtime — created once on launch, which also probes head and eye tracking.
A SurfaceEntity from androidx.xr.scenecore shaped as a Hemisphere(15m), in SIDE_BY_SIDE or multiview stereo mode depending on the file. This is the 3D screen.
An ExoPlayer instance from Media3 that decodes the picked video onto the hemisphere’s surface, looped. For SBS video the frames pass through a Media3 GlEffect that applies the foveated sharpening.
Two controls are ported from a Unity reference, adapted to what the Android XR SDK exposes. Pitch tilts the hemisphere and is recentered from the head pose (with an accelerometer gravity-vector fallback). Zoom slides the dome toward or away from you, since there’s no skybox-shader equivalent and scaling a head-centered sphere changes nothing you can see.
ExoPlayer decodes straight onto the SurfaceEntity’s hardware surface, so frames go from the video decoder to the XR compositor without the extra texture-resample step a Unity skybox needs. That keeps the full source resolution of 8K footage.
Sideload a debug build
Older debug APKs are on the GitHub releases page. Note that they predate the features above. For the latest, install from Google Play or build from source.
Heads up — these are debug builds. They’re signed with the Android debug keystore (not a release key), have android:debuggable="true", ship unminified, and are intended for sideloading and testing only.
adb install -r app-debug.apk
Source
Code, issues, and full release notes are on GitHub: