Develop

Explore Meta Passthrough Camera API samples

Updated: May 7, 2026

Overview

This sample demonstrates how to access and process passthrough camera feeds on Meta Quest 3 and Quest 3S using the standard Android Camera2 API with Quest-specific extensions. You learn how to discover Quest passthrough cameras, manage dual-permission requirements, and configure Camera2 sessions for live preview and frame processing. The sample also shows how to build a Jetpack Compose UI with real-time camera feed visualization.

What you will learn

  • How to discover Quest passthrough cameras using custom CameraCharacteristics keys
  • How to request and verify both Android and HorizonOS camera permissions
  • How to configure a Camera2 capture session with dual outputs for preview and image processing
  • How to process YUV camera frames on a background thread and update UI state using LiveData and Compose recomposition
  • How to integrate TextureView camera preview within a Jetpack Compose UI

Requirements

  • Meta Quest 3 or Quest 3S device with Quest build v74.0 or newer (Camera2 API requires HzOS v76+)
  • Android Studio Koala or newer
For complete build requirements and dependencies, see the sample README.

Get started

Clone the Meta Passthrough Camera API Samples repository and open the project in Android Studio. Build and deploy to your Quest 3 or Quest 3S device using the standard Android Studio run configuration. The sample launches a single-screen app with camera preview, brightness monitoring, and start/stop controls. For detailed build instructions, see the sample README.

Explore the sample

The sample uses MVVM architecture with Jetpack Compose for the UI layer and AndroidViewModel for camera lifecycle management. All source files are in app/src/main/java/com/oculus/camerademo/.
FileWhat it demonstratesKey concepts
MainActivity.kt
Jetpack Compose UI with camera preview integration
TextureView wrapped in AndroidView composable, LiveData observation, Material3 components
CameraDemoViewModel.kt
Complete Camera2 lifecycle management
Camera discovery, session configuration, repeating capture requests, background thread handling
QuestCameraHelpers.kt
Quest-specific camera characteristic keys
Custom CameraCharacteristics.Key definitions for position and source metadata
PermissionManager.kt
Dual permission handling for Quest cameras
Android and HorizonOS permission checking, ActivityResultContracts integration
ImageProcessHelpers.kt
Frame-by-frame image processing
YUV_420_888 format handling, Y-plane brightness calculation
DataModels.kt
Data architecture for camera state
Sealed classes for events, enums for camera position, UI state models
Utils.kt
Logging utilities
Consistent logging patterns

Runtime behavior

When you launch the sample, the UI displays a camera preview area, three control buttons (Start camera, Stop camera, Exit), and a brightness value display. After granting camera permissions, you select Start camera to begin the passthrough feed. The preview shows the live camera feed from the right passthrough camera by default, and the brightness value updates continuously as the sample processes each frame. Selecting Stop camera halts the feed and releases camera resources.

Key concepts

This section highlights the core patterns used in the sample.

Quest-specific camera discovery

The sample discovers passthrough cameras by reading custom CameraCharacteristics keys that extend the standard Camera2 API. QuestCameraHelpers.kt defines two vendor-specific keys:
val KEY_SOURCE = Key("com.meta.extra_metadata.camera_source", Int::class.java)
val KEY_POSITION = Key("com.meta.extra_metadata.position", Int::class.java)
The view model iterates the device camera list and reads both keys for each camera, recording the source type and position in a CameraConfig data class. The sample then selects a camera by position (defaulting to the right camera), and the isPassthrough flag is available for your own filtering logic.

Dual permission model

Quest passthrough cameras require both standard Android and HorizonOS-specific permissions. The sample uses PermissionManager.kt to track both android.permission.CAMERA and horizonos.permission.HEADSET_CAMERA. The activity requests both permissions together using ActivityResultContracts.RequestMultiplePermissions(), and camera access only proceeds when both grants succeed.

Camera2 session lifecycle

The sample configures a Camera2 session with dual outputs for simultaneous preview and frame processing. In CameraDemoViewModel.kt, the session uses SESSION_REGULAR mode with two OutputConfiguration targets, one for the preview surface (displayed in TextureView) and one for the ImageReader surface (processed for brightness). The sample uses a repeating capture request, which continuously captures frames, targeting both surfaces so every frame reaches both the preview and the processing pipeline.

Image processing pipeline

The sample processes frames on a dedicated background thread to avoid blocking the UI or camera callbacks. When ImageReader delivers a new frame, the callback runs on imageReaderThread and calls acquireLatestImage() to skip intermediate frames and get the most recent. The getBrightness() function reads the Y-plane buffer from the YUV_420_888 image, averages all pixel values, and posts the result to the main thread via Handler, where it updates LiveData and triggers Compose recomposition.

Extend the sample

  • Modify CameraDemoViewModel.kt to support dynamic camera switching between left and right passthrough cameras at runtime.
  • Add a custom image processor to ImageProcessHelpers.kt that detects edges or highlights in the camera feed.
  • Implement frame capture functionality in a new utility class that saves individual passthrough frames to device storage when the user taps a capture button.