VITURE XR Glasses SDK
VITURE Glasses SDK is the official C SDK for integrating Viture smart glasses into your application. It provides USB device management, head tracking (IMU / 6DoF VIO), display control, and pass-through camera streaming — all through a clean C API that works on Linux, macOS, Windows, and Android.
Supported Hardware
Product Name Tracking Camera
--------------------- -------- ---------------------------
Viture One 3DOF None
Viture Lite 3DOF None
Viture Pro 3DOF None
Viture Pro 2 3DOF None
Luma 3DOF None
Luma Pro 3DOF Pass-through (UVC)
Luma Cyber 3DOF Pass-through (UVC)
Luma Ultra 3/6DOF Pass-through (UVC) + Stereo (VIO)
Beast 3DOF* Pass-through (UVC)
All Viture glasses share USB Vendor ID 0x35CA.
*Beast supports native 3DOF head tracking when in native mode.
Supported Platforms
Platform Architecture
---------------- --------------------------------------
Linux x86_64, aarch64
macOS aarch64
Windows x86_64
Android armeabi-v7a, arm64-v8a
Browser WebAssembly (Chrome / Chromium only)
The browser SDK is distributed as a TypeScript API bundle over a WebAssembly build of the core, and reaches the glasses through WebHID. Luma Ultra is not supported in the browser.
Documentation Guide
If you are new to the SDK, read in this order:
1. Core Concepts Handles, lifecycle, device families
2. Quick Start — Desktop (C++) Minimal working example
3. Head Tracking Reading head orientation (IMU + 6DoF pose)
4. Device Control Brightness, volume, display mode, film tint
5. Camera Streaming Pass-through camera (UVC)
6. Android Integration Guide Android-specific integration
7. SDK Usage Reporting (Stat Reporter) Optional usage reporting (gain-sharing)
8. Native-DOF Devices Devices with on-device tracking (Beast)
9. API Reference Complete function reference
Five-Minute Overview
The entire SDK lifecycle in one diagram:
Application
|
|-- [1] Enumerate USB devices, find Viture PID
|
|-- [2] xr_device_provider_create(pid) -------> handle
|
|-- [3] xr_device_provider_initialize(handle, NULL, NULL)
|
|-- [4] xr_device_provider_start(handle)
|
|-- [5] Register callbacks or poll pose
| |
| |-- Gen1/Gen2: register_imu_pose_callback + open_imu
| |-- Carina: get_gl_pose_carina (poll in a loop)
|
|-- [6] Use device control functions (brightness, volume, etc.)
|
|-- [7] xr_device_provider_stop(handle)
| xr_device_provider_shutdown(handle)
| xr_device_provider_destroy(handle)
Headers at a Glance
Header Purpose
---------------------------- -----------------------------------------------
viture_glasses_provider.h Lifecycle: create/init/start/stop/shutdown/destroy
viture_device.h Gen1/Gen2 IMU callbacks and open/close IMU
viture_device_carina.h Luma Ultra pose polling, resets, DOF configuration
viture_protocol_public.h Device control: brightness, volume, display mode
viture_camera_provider.h Pass-through camera (UVC) streaming
viture_stat_reporter.h Optional SDK usage reporting (opt-in HTTPS)
viture_result.h Error code constants (VITURE_GLASSES_*)
SDK Version
The current version is defined in src/viture_version.h.
Use GetVersionString() at runtime to retrieve the version string.
This page explains the fundamental building blocks of the VITURE Glasses SDK before you write any code.
The Handle: XRDeviceProviderHandle
Every interaction with a device goes through an opaque handle:
XRDeviceProviderHandle handle = xr_device_provider_create(pid);
Think of the handle like a file descriptor: it represents one open connection to one physical device. You pass it to every API call. Never share a handle across threads without understanding the thread-safety rules below.
A NULL handle means creation failed. Always check:
if (!handle) {
// device not found, USB permission denied, or unsupported PID
}
Device Families
The SDK exposes three device types, determined after create():
int type = xr_device_provider_get_device_type(handle);
Constant Value Devices
----------------------------- ----- -------------------------------------------
XR_DEVICE_TYPE_VITURE_GEN1 0 One, Lite, Pro, Luma, Luma Pro, Luma Cyber
XR_DEVICE_TYPE_VITURE_GEN2 1 Beast, Pro 2
XR_DEVICE_TYPE_VITURE_CARINA 2 Luma Ultra
Why this matters:
- Gen1 and Gen2 deliver head orientation through callbacks (SDK pushes data to you).
- Carina (Luma Ultra) delivers pose through polling (you ask the SDK on your schedule).
- Some API functions are device-specific and will return
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif called on the wrong family.
Lifecycle States
A provider must be driven through a strict sequence of states. Skipping steps or calling functions out of order returns an error.
+----------+
| CREATED | xr_device_provider_create()
+----+-----+
|
v
+-------------+
| INITIALIZED | xr_device_provider_initialize()
+----+--------+
|
v
+----------+
| STARTED | xr_device_provider_start()
+----+-----+
| <-- normal operation: callbacks fire, poll pose, send commands
v
+----------+
| STOPPED | xr_device_provider_stop()
+----+-----+
|
v
+-----------+
| SHUTDOWN | xr_device_provider_shutdown()
+----+------+
|
v
+----------+
| DESTROYED| xr_device_provider_destroy()
+----------+
Important rules:
initialize()accepts an optionalcustom_configstring andcache_file_dirpath. PassNULLfor both unless you have a specific reason to configure them.start()is when the SDK starts background threads and the device becomes usable.- Register state callbacks before or after
start()— either works. - Register IMU callbacks before calling
open_imu(). - For Carina: call
set_dof_type_carina()aftercreate()but beforeinitialize(). - Always call stop → shutdown → destroy in order, even on error paths.
Callbacks vs. Polling
Gen1 / Gen2 — Callback Model
The SDK runs an internal IMU reader thread that pushes orientation data to your callback as fast as the device sends it:
// Register once before open_imu
xr_device_provider_register_imu_pose_callback(handle, my_pose_callback);
// Tell the device to start streaming
xr_device_provider_open_imu(handle, VITURE_IMU_MODE_POSE, VITURE_IMU_FREQUENCY_HIGH);
Your callback is invoked on an SDK-internal thread. Use a mutex or atomic variables if you need to share pose data with your render thread.
Carina — Polling Model
The Carina VIO engine computes a new pose continuously. You retrieve it on demand:
float pose[7];
int status = 0;
xr_device_provider_get_gl_pose_carina(handle, pose, 0.0, &status);
Poll at your desired rate from your own thread. The SDK returns the latest computed pose each time. There is no callback to register for the main pose.
Coordinate Systems
The two device families use different coordinate systems for pose output.
Gen1 / Gen2 — North-West-Up (NWU)
Euler layout: [roll, pitch, yaw, qw, qx, qy, qz]
X --> North (forward)
Y --> West (left)
Z --> Up
If your renderer uses OpenGL (Y-up, right-handed), you need a coordinate conversion.
Carina — OpenGL (Y-Up, Right-Handed)
Pose layout: [px, py, pz, qw, qx, qy, qz]
X --> Right
Y --> Up
Z --> Backward (out of screen)
No conversion needed to feed directly into a standard OpenGL view matrix.
Threading Model
Thread Responsibility
-------------------- ------------------------------------------------
Your app thread Call API, consume pose data
USB monitor thread Read USB responses, dispatch ACKs and events
IMU reader thread Poll IMU interface continuously (Gen1/Gen2 only)
Command worker Send commands, wait for ACK
Camera thread Deliver camera frames (if used)
The SDK is designed so that your application thread only needs to call API functions. All USB I/O happens on SDK-owned background threads.
Thread safety rules:
- Do not call lifecycle functions (
initialize,start,stop, etc.) from callbacks. - Do not share a handle between threads without external synchronization.
- Callbacks are safe to read and snapshot data from; do not block them.
Error Handling
All functions that can fail return int. Zero means success:
Code Value When returned
---------------------------------- ----- ----------------------------------------
VITURE_GLASSES_SUCCESS 0 Operation succeeded
VITURE_GLASSES_ERROR_INVALID_PARAM -1 NULL handle, NULL pointer, or bad value
VITURE_GLASSES_ERROR_USB_UNAVAILABLE -2 USB not connected or not accessible
VITURE_GLASSES_ERROR_USB_EXEC -3 USB read/write failed
VITURE_GLASSES_ERROR_NOT_SUPPORTED -4 Feature not available on this device
VITURE_GLASSES_ERROR_NO_DATA -5 No response from device (timeout)
VITURE_GLASSES_ERROR_DATA_PARSE -6 Response format mismatch
VITURE_GLASSES_ERROR_DEVICE_REJECTED -7 Device rejected the command
VITURE_GLASSES_ERROR_CALIB_INIT -8 Calibration init failed (initialize only)
VITURE_GLASSES_ERROR_SERIAL_FETCH -9 Serial number fetch failed (initialize only)
VITURE_GLASSES_ERROR_INVALID_STATE -10 Wrong state (e.g., already streaming)
VITURE_GLASSES_ERROR_UNKNOWN -99 Unclassified error
A minimal error-checking pattern:
int rc = xr_device_provider_initialize(handle, NULL, NULL);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "initialize failed: %d\n", rc);
xr_device_provider_destroy(handle);
return rc;
}
Logging
The SDK logs internally using Logcat on Android and stderr on other platforms. You can redirect logs to your own system:
void my_log_hook(int level, const char* tag, const char* message) {
// level: 0=none, 1=error, 2=info, 3=debug
my_logger(level, message);
}
xr_device_provider_set_log_hook(my_log_hook);
xr_device_provider_set_log_level(LOG_LEVEL_INFO); // 0..3
Pass NULL to set_log_hook to remove the hook.
This guide walks you from zero to a running program that connects to Viture glasses and prints head-orientation data. Estimated time: 5 minutes.
The example targets Linux / macOS / Windows. For Android, see Android Integration Guide.
Prerequisites
- VITURE Glasses SDK SDK distributed as:
libglasses.so(Linux),libglasses.dylib(macOS), orlibglasses.dll(Windows)- Header files:
viture_glasses_provider.h,viture_device.h,viture_device_carina.h,viture_protocol_public.h,viture_result.h,hidapi.h - A supported Viture device connected via USB
- On Linux: a udev rule so the SDK can open the HID interface without root (see below)
Linux udev Rule
Create /etc/udev/rules.d/70-viture.rules:
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="35ca", MODE="0660", TAG+="uaccess"
SUBSYSTEM=="hidraw", KERNEL=="hidraw[0-9]*", ATTRS{idVendor}=="35ca", MODE="0660", TAG+="uaccess"
Reload: sudo udevadm control --reload-rules && sudo udevadm trigger
CMake Setup
cmake_minimum_required(VERSION 3.16)
project(my_xr_app)
add_executable(my_xr_app main.cpp)
target_include_directories(my_xr_app PRIVATE /path/to/libglasses/include)
target_link_directories(my_xr_app PRIVATE /path/to/libglasses/lib)
target_link_libraries(my_xr_app PRIVATE glasses)
Step 1 — Find a Connected Device
All Viture devices share Vendor ID 0x35CA. Use hidapi to scan:
#include "hidapi.h"
#include "viture_glasses_provider.h"
#include <vector>
std::vector<int> find_viture_pids() {
std::vector<int> pids;
hid_init();
hid_device_info* devs = hid_enumerate(0, 0);
for (hid_device_info* d = devs; d; d = d->next) {
if (d->vendor_id != 0x35CA) continue;
int pid = (int)d->product_id;
if (!xr_device_provider_is_product_id_valid(pid)) continue;
// Deduplicate: some glasses expose multiple HID interfaces with the same VID
bool found = false;
for (int p : pids) if (p == pid) { found = true; break; }
if (!found) pids.push_back(pid);
}
hid_free_enumeration(devs);
hid_exit();
return pids;
}
Why deduplicate? A single pair of glasses may exposes several USB interfaces (IMU, MCU, audio, camera). hidapi lists each interface separately, so the same PID may appear multiple times.
Luma Ultra
The Luma Ultra control interface uses USB bulk transfer rather than HID, so
hidapi may not enumerate it on all platforms. If hidapi returns no results, fall back to the
native platform USB API to scan for all devices with VID 0x35CA:
Platform API to use
--------- -------------------------------------------
Linux sysfs: /sys/bus/usb/devices/*/idVendor|idProduct
macOS IOKit: IOServiceMatching(kIOUSBDeviceClassName)
Windows SetupAPI: SetupDiGetClassDevs + SPDRP_HARDWAREID
Filter any result with VID 0x35CA, then pass each PID through
xr_device_provider_is_product_id_valid before calling xr_device_provider_create.
Step 2 — Create the Provider
int pid = pids[0]; // Use the first found device
XRDeviceProviderHandle handle = xr_device_provider_create(pid);
if (!handle) {
fprintf(stderr, "Failed to open device PID=0x%04X\n", pid);
return 1;
}
If creation fails, the most common causes are:
- No device connected
- Missing udev rule on Linux (permission denied)
- Wrong PID (always validate with
is_product_id_validfirst)
Step 3 — Check Device Type
Device type determines which tracking API to use. Read it right after create():
int device_type = xr_device_provider_get_device_type(handle);
Step 4 — Register Callbacks (Gen1 / Gen2 only)
For Gen1 / Gen2 devices, register your pose callback before initialize():
static void on_pose(float* data, uint64_t ts) {
// data: [roll, pitch, yaw, qw, qx, qy, qz] in North-West-Up frame
printf("yaw=%.2f pitch=%.2f roll=%.2f\n", data[2], data[1], data[0]);
}
if (device_type != XR_DEVICE_TYPE_VITURE_CARINA) {
xr_device_provider_register_imu_pose_callback(handle, on_pose);
}
For Carina (Luma Ultra), you poll pose after start() — no callback registration needed.
Step 5 — Initialize and Start
int rc = xr_device_provider_initialize(handle, NULL, NULL);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "initialize failed: %d\n", rc);
xr_device_provider_destroy(handle);
return 1;
}
rc = xr_device_provider_start(handle);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "start failed: %d\n", rc);
xr_device_provider_shutdown(handle);
xr_device_provider_destroy(handle);
return 1;
}
Step 6 — Enable IMU or Start Pose Polling
Gen1 / Gen2: Tell the device to start streaming IMU data:
if (device_type != XR_DEVICE_TYPE_VITURE_CARINA) {
xr_device_provider_open_imu(handle, VITURE_IMU_MODE_POSE, VITURE_IMU_FREQUENCY_HIGH);
// Callback fires automatically at ~500 Hz
}
Carina (Luma Ultra): Poll the pose in your own loop:
if (device_type == XR_DEVICE_TYPE_VITURE_CARINA) {
float pose[7];
int pose_status = 0;
// Poll at your desired rate
xr_device_provider_get_gl_pose_carina(handle, pose, 0.0, &pose_status);
// pose: [px, py, pz, qw, qx, qy, qz] in OpenGL (Y-up) frame
// pose_status: 0=stable, 1=unstable (transient after startup)
}
Step 7 — Cleanup
Always call stop → shutdown → destroy, even if an earlier step failed:
// Close IMU before stopping (Gen1/Gen2 only)
if (device_type != XR_DEVICE_TYPE_VITURE_CARINA) {
xr_device_provider_close_imu(handle, VITURE_IMU_MODE_POSE);
}
xr_device_provider_stop(handle);
xr_device_provider_shutdown(handle);
xr_device_provider_destroy(handle);
Complete Minimal Example
// Copyright (C) 2026 Viture Inc. All rights reserved.
#include <chrono>
#include <cstdio>
#include <thread>
#include <vector>
#include "hidapi.h"
#include "viture_device.h"
#include "viture_device_carina.h"
#include "viture_glasses_provider.h"
#include "viture_protocol_public.h"
static void on_pose(float* data, uint64_t ts) {
printf("roll=%.2f pitch=%.2f yaw=%.2f\n", data[0], data[1], data[2]);
}
int main() {
// 1. Find devices
std::vector<int> pids;
hid_init();
hid_device_info* devs = hid_enumerate(0, 0);
for (hid_device_info* d = devs; d; d = d->next) {
if (d->vendor_id != 0x35CA) continue;
int pid = (int)d->product_id;
if (!xr_device_provider_is_product_id_valid(pid)) continue;
bool dup = false;
for (int p : pids) if (p == pid) { dup = true; break; }
if (!dup) pids.push_back(pid);
}
hid_free_enumeration(devs);
hid_exit();
if (pids.empty()) { printf("No Viture device found\n"); return 1; }
int pid = pids[0];
printf("Using PID=0x%04X\n", pid);
// 2. Create
XRDeviceProviderHandle h = xr_device_provider_create(pid);
if (!h) { printf("create failed\n"); return 1; }
// 3. Check type, register callback for Gen1/Gen2
int type = xr_device_provider_get_device_type(h);
if (type != XR_DEVICE_TYPE_VITURE_CARINA)
xr_device_provider_register_imu_pose_callback(h, on_pose);
// 4. Initialize + start
if (xr_device_provider_initialize(h, NULL, NULL) != 0 ||
xr_device_provider_start(h) != 0) {
printf("init/start failed\n");
xr_device_provider_destroy(h);
return 1;
}
// 5. Enable IMU / poll pose
if (type != XR_DEVICE_TYPE_VITURE_CARINA) {
xr_device_provider_open_imu(h, VITURE_IMU_MODE_POSE, VITURE_IMU_FREQUENCY_HIGH);
std::this_thread::sleep_for(std::chrono::seconds(5)); // callbacks fire here
xr_device_provider_close_imu(h, VITURE_IMU_MODE_POSE);
} else {
float pose[7];
int status = 0;
for (int i = 0; i < 100; ++i) {
xr_device_provider_get_gl_pose_carina(h, pose, 0.0, &status);
printf("pos=[%.3f, %.3f, %.3f] quat=[%.3f, %.3f, %.3f, %.3f] status=%d\n",
pose[0], pose[1], pose[2], pose[3], pose[4], pose[5], pose[6], status);
std::this_thread::sleep_for(std::chrono::milliseconds(8));
}
}
// 6. Cleanup
xr_device_provider_stop(h);
xr_device_provider_shutdown(h);
xr_device_provider_destroy(h);
return 0;
}
Next Steps
- Head Tracking — Full guide for both Gen1/Gen2 callbacks and Carina polling, including coordinate systems and recentering.
- Device Control — Change brightness, volume, display mode, and electrochromic film.
- Android Integration Guide — Android-specific integration with USB permissions and JNI.
VITURE Glasses SDK exposes two distinct head-tracking APIs depending on the device family. This page covers both in full detail.
Which API Should I Use?
Device Family API
------------------ -------- ---------------------------------------------------
Viture One Gen1 IMU callback (viture_device.h)
Viture Lite Gen1 IMU callback (viture_device.h)
Viture Pro Gen1 IMU callback (viture_device.h)
Luma Gen1 IMU callback (viture_device.h)
Luma Pro Gen1 IMU callback (viture_device.h)
Luma Cyber Gen1 IMU callback (viture_device.h)
Beast Gen2 IMU callback (viture_device.h)
Viture Pro 2 Gen2 IMU callback (viture_device.h)
Luma Ultra Carina Pose polling (viture_device_carina.h)
Check the device type at runtime:
int type = xr_device_provider_get_device_type(handle);
if (type == XR_DEVICE_TYPE_VITURE_CARINA) {
// use Carina polling API
} else {
// use Gen1/Gen2 callback API
}
Gen1 / Gen2 — IMU Callbacks
Two Callback Modes
The IMU can deliver data in two modes. Choose one; both cannot be active at the same time.
Mode constant Value Description
--------------------- ----- -------------------------------------------
VITURE_IMU_MODE_POSE 1 Pre-processed Euler angles + quaternion
VITURE_IMU_MODE_RAW 0 Raw gyroscope, accelerometer, magnetometer
For most applications, VITURE_IMU_MODE_POSE is the right choice.
Pose Callback
Register the callback before calling open_imu:
#include "viture_device.h"
static void on_imu_pose(float* data, uint64_t timestamp) {
// Coordinate system: North-West-Up (NWU)
// X -> North (forward)
// Y -> West (left)
// Z -> Up
//
// data layout: [roll, pitch, yaw, qw, qx, qy, qz]
float roll = data[0];
float pitch = data[1];
float yaw = data[2];
float qw = data[3];
float qx = data[4];
float qy = data[5];
float qz = data[6];
}
// Register before open_imu
xr_device_provider_register_imu_pose_callback(handle, on_imu_pose);
Raw Callback
Use this when you need unprocessed sensor values for custom fusion algorithms:
static void on_imu_raw(float* data, uint64_t timestamp, uint64_t vsync) {
// data layout (all devices):
// [0..2] gyroscope (x, y, z)
// [3..5] accelerometer (x, y, z)
// [6..8] magnetometer (x, y, z) -- zero on One/Lite/Pro
// [9] temperature
float gx = data[0], gy = data[1], gz = data[2];
float ax = data[3], ay = data[4], az = data[5];
float temp = data[9];
}
xr_device_provider_register_imu_raw_callback(handle, on_imu_raw);
Enabling the IMU
After registering a callback and calling start():
// Frequencies: LOW=60Hz MEDIUM_LOW=90Hz MEDIUM=120Hz MEDIUM_HIGH=240Hz
// HIGH=500Hz ULTRA_HIGH=1000Hz (select products only -- check with
// xr_device_provider_is_product_support_imu_frequency)
int rc = xr_device_provider_open_imu(handle,
VITURE_IMU_MODE_POSE,
VITURE_IMU_FREQUENCY_HIGH);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "open_imu failed: %d\n", rc);
}
The callback will fire on an SDK-internal thread at the requested frequency.
Disabling the IMU
xr_device_provider_close_imu(handle, VITURE_IMU_MODE_POSE);
// Pass the same mode you used in open_imu
Always close the IMU before calling stop().
Sharing Pose Data with the Render Thread
The callback runs on a background thread. Use atomics or a mutex to safely transfer data to your render thread:
#include <pthread.h>
static pthread_mutex_t g_pose_lock = PTHREAD_MUTEX_INITIALIZER;
static float g_pose[7];
static void on_imu_pose(float* data, uint64_t ts) {
pthread_mutex_lock(&g_pose_lock);
memcpy(g_pose, data, 7 * sizeof(float));
pthread_mutex_unlock(&g_pose_lock);
}
// In render thread:
float local_pose[7];
pthread_mutex_lock(&g_pose_lock);
memcpy(local_pose, g_pose, sizeof(local_pose));
pthread_mutex_unlock(&g_pose_lock);
Coordinate Conversion: NWU to OpenGL
Gen1/Gen2 output is in North-West-Up. To convert the quaternion to OpenGL (Y-up, right-handed), apply a static rotation that maps NWU to the OpenGL frame. The relationship is:
NWU X (North/forward) --> OpenGL -Z
NWU Y (West/left) --> OpenGL -X
NWU Z (Up) --> OpenGL Y
In practice, the reference demo applies a fixed initial offset quaternion to make the
glasses' forward direction align with the camera's -Z axis. See glasses-demo
for the full implementation.
Carina (Luma Ultra) — 6DoF Pose Polling
Luma Ultra uses the onboard Carina VIO engine, which provides full 6-degree-of-freedom tracking: both position and orientation. It uses a polling model — your application calls the API at its own frame rate.
Setting DOF Mode
This must be called after create() and before initialize():
#include "viture_device_carina.h"
// Default is 6DOF. Set to 3DOF if you only need rotation (no position tracking).
// is_6dof: 1 = 6DOF, 0 = 3DOF
xr_device_provider_set_dof_type_carina(handle, 1); // 6DOF
// Then initialize as normal
xr_device_provider_initialize(handle, NULL, NULL);
Polling the Pose
float pose[7];
int pose_status = 0;
int rc = xr_device_provider_get_gl_pose_carina(handle, pose, 0.0, &pose_status);
if (rc == VITURE_GLASSES_SUCCESS) {
// Coordinate system: OpenGL (Y-up, right-handed)
// X -> right
// Y -> up
// Z -> backward (out of screen)
//
// pose layout: [px, py, pz, qw, qx, qy, qz]
float px = pose[0], py = pose[1], pz = pose[2]; // position (meters)
float qw = pose[3], qx = pose[4], qy = pose[5], qz = pose[6]; // quaternion
// pose_status: 0 = stable, 1 = unstable
// Unstable occurs briefly after startup or after a sudden movement.
// Render normally but consider showing a visual indicator when unstable.
}
Pose Prediction
Rendering a frame takes time. By the moment a frame reaches the display, the user's head
may have already moved, making the scene feel laggy. predict_time compensates for this by
extrapolating the pose forward to the expected display time — typically the sum of render
time and display pipeline latency:
// 11 ms render + 5 ms display latency = 16 ms lookahead
xr_device_provider_get_gl_pose_carina(handle, pose, 16e6, &pose_status);
Pass 0.0 for the latest measured pose with no extrapolation.
Polling Rate
Choose a polling interval based on your application's needs. Higher rates reduce motion-to- photon latency at the cost of more CPU usage; lower rates save CPU but increase the chance of using a stale pose when a frame is rendered.
#include <time.h>
void carina_poll_loop(XRDeviceProviderHandle handle, volatile bool* running) {
struct timespec ts;
float pose[7];
int status = 0;
while (*running) {
xr_device_provider_get_gl_pose_carina(handle, pose, 0.0, &status);
// copy pose to shared state ...
// Sleep 8 ms for 120Hz polling
ts.tv_sec = 0;
ts.tv_nsec = 8 * 1000 * 1000;
nanosleep(&ts, NULL);
}
}
Recentering and Resetting
Carina supports two reset operations with different behaviors:
1. Reset Origin — lightweight, recommended
Resets position and yaw to the values encoded in the supplied pose. Pitch and roll remain gravity-anchored and are not affected.
Pass the current pose from get_gl_pose_carina to anchor the origin at the user's present
physical location and heading:
float pose[7];
int status = 0;
xr_device_provider_get_gl_pose_carina(handle, pose, 0.0, &status);
xr_device_provider_reset_origin_carina(handle, pose);
You can also pass a manually constructed pose to teleport the origin to any specific position and heading:
// Place origin 2 meters to the right, facing the same direction
float custom[7] = {2.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f};
xr_device_provider_reset_origin_carina(handle, custom);
2. Reset Pose — heavyweight, only when necessary
Triggers a full VIO re-initialization. This briefly interrupts tracking and takes longer to restabilize. Use only when the tracking has become irrecoverably lost.
xr_device_provider_reset_pose_carina(handle);
Operation When to use Effect
--------------------- ------------------------ -----------------------------------
reset_origin_carina Normal recentering Yaw + position reset; lightweight
reset_pose_carina Tracking lost / corrupted Full VIO re-init; interrupts briefly
Carina Optional Callbacks
Carina also exposes optional callbacks for raw IMU data, VSync events, and stereo camera frames from the side cameras. These are separate from the main pose and are rarely needed for standard XR applications.
xr_device_provider_register_callbacks_carina(
handle,
NULL, // XRPoseCallback: pose at camera rate (25 Hz) — usually unneeded
NULL, // XRVSyncCallback: display vsync timestamp
NULL, // XRImuCallback: raw IMU [ax, ay, az, gx, gy, gz]
on_carina_camera // XRCameraCallback: stereo 8-bit grayscale frames
);
The stereo camera callback delivers raw grayscale frames from the two tracking cameras on the sides of Luma Ultra. These are different from the front-facing pass-through camera. See Camera page for details.
Switching Between 3DOF and 6DOF (Carina)
To change the DOF mode at runtime, you must restart the session:
// 1. Stop the current session
xr_device_provider_stop(handle);
xr_device_provider_shutdown(handle);
xr_device_provider_destroy(handle);
// 2. Create a new session with the new DOF type
handle = xr_device_provider_create(pid);
xr_device_provider_set_dof_type_carina(handle, 0); // 0 = 3DOF
xr_device_provider_initialize(handle, NULL, NULL);
xr_device_provider_start(handle);
There is no in-session DOF switch; a full restart is required.
Summary: Gen1/Gen2 vs. Carina
Aspect Gen1 / Gen2 Carina (Luma Ultra)
-------------------- ------------------------- ----------------------------
Degrees of Freedom 3DOF (rotation only) 3DOF or 6DOF (configurable)
API model Callback (push) Polling (pull)
Coordinate system North-West-Up (NWU) OpenGL Y-up (right-handed)
Position tracking No Yes (6DOF mode)
Pose data format [roll, pitch, yaw, qw...] [px, py, pz, qw, qx, qy, qz]
first 3: Euler angles first 3: position in meters
last 4: quaternion last 4: quaternion
Callback thread SDK internal IMU thread None (you own the poll thread)
Recentering N/A reset_origin_carina (lightweight)
The functions in this section let your application read and change device settings: brightness, volume, display resolution/refresh rate, and electrochromic film tint. They are all synchronous — each call sends a USB command and waits for a response.
All functions require the provider to be in the STARTED state.
State Change Notifications
The device can notify your application when the user changes settings via the
physical buttons on the glasses. Register a state callback after start():
#include "viture_protocol_public.h"
static void on_state_change(int id, int value) {
switch (id) {
case VITURE_CALLBACK_ID_BRIGHTNESS:
printf("Brightness changed to %d\n", value);
break;
case VITURE_CALLBACK_ID_VOLUME:
printf("Volume changed to %d\n", value);
break;
case VITURE_CALLBACK_ID_DISPLAY_MODE:
// Currently only Luma Ultra
printf("Display mode changed to 0x%02X\n", value);
break;
case VITURE_CALLBACK_ID_ELECTROCHROMIC_FILM:
// Currently only Beast
printf("Film mode changed to %d\n", value);
break;
case VITURE_CALLBACK_ID_NATIVE_DOF:
// Currently only Beast
printf("Native DOF changed to %d\n", value);
break;
case VITURE_CALLBACK_ID_WEAR_STATUS:
// Gen2 only. 0 = not worn, 1 = worn
printf("Wear status changed to %d\n", value);
break;
}
}
xr_device_provider_register_state_callback(handle, on_state_change);
To unregister: xr_device_provider_register_state_callback(handle, NULL);
Brightness
Value Ranges
Device Range
----------------- -------
Viture One [0, 6]
Viture Pro [0, 8]
Viture Pro 2 [0, 8]
Luma series [0, 8]
Beast [0, 8]
Read
int brightness = xr_device_provider_get_brightness_level(handle);
if (brightness < 0) {
fprintf(stderr, "get_brightness failed: %d\n", brightness);
}
Write
int rc = xr_device_provider_set_brightness_level(handle, 5);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "set_brightness failed: %d\n", rc);
}
Volume
Value Ranges
Device Range
----------------- --------
Viture One [0, 7]
Viture Pro [0, 8]
Viture Pro 2 [0, 8]
Luma series [0, 8]
Beast [0, 15]
Read / Write
int volume = xr_device_provider_get_volume_level(handle);
int rc = xr_device_provider_set_volume_level(handle, 4);
Display Mode
The display mode controls resolution and refresh rate. There are two sets of constants: standard modes (used on all non-Beast devices and Beast in bypass mode) and native modes (Beast only, when in native mode).
Standard Display Modes
Constant Value Resolution Rate
-------------------------------- ----- ---------- ----
VITURE_DISPLAY_MODE_1920_1080_60HZ 0x31 1920x1080 60Hz
VITURE_DISPLAY_MODE_1920_1080_90HZ 0x33 1920x1080 90Hz
VITURE_DISPLAY_MODE_1920_1080_120HZ 0x34 1920x1080 120Hz
VITURE_DISPLAY_MODE_3840_1080_60HZ 0x32 3840x1080 60Hz (3D SBS)
VITURE_DISPLAY_MODE_3840_1080_90HZ 0x35 3840x1080 90Hz (3D SBS)
VITURE_DISPLAY_MODE_1920_1200_60HZ 0x41 1920x1200 60Hz
VITURE_DISPLAY_MODE_1920_1200_90HZ 0x43 1920x1200 90Hz
VITURE_DISPLAY_MODE_1920_1200_120HZ 0x44 1920x1200 120Hz
VITURE_DISPLAY_MODE_3840_1200_60HZ 0x42 3840x1200 60Hz (3D SBS)
VITURE_DISPLAY_MODE_3840_1200_90HZ 0x45 3840x1200 90Hz (3D SBS)
Read / Write
int mode = xr_device_provider_get_display_mode(handle);
int rc = xr_device_provider_set_display_mode(handle, VITURE_DISPLAY_MODE_1920_1080_120HZ);
Convenience: 2D / 3D Toggle
Switches between 1920x1080@60Hz (2D) and 3840x1080@60Hz (3D side-by-side):
xr_device_provider_switch_dimension(handle, 1); // 1 = 3D
xr_device_provider_switch_dimension(handle, 0); // 0 = 2D
If the device is already in the target mode, this returns success without sending a command.
Beast in native mode: Use the
native_*APIs instead (see below).switch_dimensionreturnsVITURE_GLASSES_ERROR_NOT_SUPPORTEDon Beast in native mode.
Default Power-On Mode
Sets the display mode that is applied automatically when the glasses power on. The value is stored persistently in the device. Supported on Viture Luma / Luma Pro only; other devices (including Luma Ultra) return VITURE_GLASSES_ERROR_NOT_SUPPORTED.
int rc = xr_device_provider_set_default_display_mode(handle, VITURE_DISPLAY_MODE_1920_1080_60HZ);
Hardware Button Lock
Enables or disables the physical 2D/3D switch button on the glasses. When disabled, pressing the button has no effect. The setting is not persistent and resets to enabled on the next power cycle. Supported on Viture Luma / Luma Pro only; other devices (including Luma Ultra) return VITURE_GLASSES_ERROR_NOT_SUPPORTED.
xr_device_provider_set_display_mode_button_enabled(handle, 0); // lock button
xr_device_provider_set_display_mode_button_enabled(handle, 1); // unlock button
Electrochromic Film
The electrochromic film tints the lenses to reduce light bleed-through. The API uses a voltage value in [0.0, 1.0].
Behavior by Device Generation
Generation Interpretation
---------- -----------------------------------------------------------
Gen1 Binary: 0.0 = off, any non-zero value = fully on
Gen2 (Beast) Multi-level: value maps to discrete tint steps [0, 8]
Viture Pro 2 has no electrochromic film. Both
get_film_modeandset_film_modereturnVITURE_GLASSES_ERROR_NOT_SUPPORTED.
Read / Write
float voltage = 0.0f;
xr_device_provider_get_film_mode(handle, &voltage);
// Turn film on fully
xr_device_provider_set_film_mode(handle, 1.0f);
// Turn film off
xr_device_provider_set_film_mode(handle, 0.0f);
Wear Status
Gen2 devices report whether the glasses are currently being worn, based on the wear detection sensor. Read the current value, or subscribe to changes through the state callback.
Gen2 only. Gen1 devices and Luma Ultra return
VITURE_GLASSES_ERROR_NOT_SUPPORTED. A glasses firmware update is also required.
Read
uint8_t wear_status = 0;
int ret = xr_device_provider_get_wear_status(handle, &wear_status);
if (ret == VITURE_GLASSES_SUCCESS) {
printf("Glasses are %s\n", wear_status ? "worn" : "not worn");
}
Change Notifications
Wear status changes are delivered to the state callback with
VITURE_CALLBACK_ID_WEAR_STATUS:
static void on_state_change(int id, int value) {
if (id == VITURE_CALLBACK_ID_WEAR_STATUS) {
// value: 0 = not worn, 1 = worn
printf("Wear status changed: %d\n", value);
}
}
Use it to pause rendering or IMU processing when the glasses are taken off.
Beast (Gen2) — Native Mode
Beast supports a native display mode where the device handles head tracking internally
with on-device sensors. In native mode, a separate set of API functions controls the
display. These functions return VITURE_GLASSES_ERROR_NOT_SUPPORTED on all other devices.
Mode: Bypass vs. Native
In bypass mode the glasses pass the video signal through without any on-device processing — what the host sends is what the display shows.
In native mode the glasses take over 3DOF head tracking internally. Depending on the selected native display mode, the device may also perform on-device frame interpolation.
After switching modes, you must also set the corresponding display mode for the change to take full effect:
// Switch to native and configure display mode
xr_device_provider_native_set_mode(handle, 1);
xr_device_provider_native_set_display_mode(handle, VITURE_NATIVE_DISPLAY_MODE_1920_1080_90HZ);
// Switch back to bypass and configure display mode
xr_device_provider_native_set_mode(handle, 0);
xr_device_provider_set_display_mode(handle, VITURE_DISPLAY_MODE_1920_1080_90HZ);
Native Display Mode
// Native display modes (Beast only, native mode)
// Constants: VITURE_NATIVE_DISPLAY_MODE_1920_1080_60HZ ... _120HZ
// VITURE_NATIVE_DISPLAY_MODE_1920_1200_* ...
// VITURE_NATIVE_DISPLAY_MODE_3D_SBS_* ...
// VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_* ...
int native_mode = xr_device_provider_native_get_display_mode(handle);
xr_device_provider_native_set_display_mode(handle, VITURE_NATIVE_DISPLAY_MODE_1920_1080_90HZ);
Native DOF
// 0 = no native DOF, 1 = native 3DOF, 2 = smooth-follow
int dof = xr_device_provider_native_get_dof(handle);
xr_device_provider_native_set_dof(handle, VITURE_NATIVE_DOF_3);
// Recenter the native DOF reference frame
xr_device_provider_native_recenter_dof(handle);
Native Display Size and Distance
// Size: VITURE_DISPLAY_SIZE_SMALL=0 ... VITURE_DISPLAY_SIZE_ULTRA=4
xr_device_provider_native_set_display_size(handle, VITURE_DISPLAY_SIZE_MEDIUM);
// Distance: device-defined range (query current value first)
int dist = xr_device_provider_native_get_display_distance(handle);
xr_device_provider_native_set_display_distance(handle, dist + 1);
Native 2D / 3D Toggle
xr_device_provider_native_switch_dimension(handle, 1); // 3D
xr_device_provider_native_switch_dimension(handle, 0); // 2D
Display Duty Cycle (Advanced)
The duty cycle controls raw display brightness at the pixel level (percentage of
on-time). Most applications should use set_brightness_level instead. This is
an advanced function for low-level display tuning.
int dc = xr_device_provider_get_duty_cycle(handle); // returns [0, 100]
xr_device_provider_set_duty_cycle(handle, 98); // VITURE_DUTY_CYCLE_H = 98
// Presets: VITURE_DUTY_CYCLE_H = 98, VITURE_DUTY_CYCLE_M = 42, VITURE_DUTY_CYCLE_L = 30
Practical Example
General Device Setup
// Set up state callback to track changes
xr_device_provider_register_state_callback(handle, on_state_change);
// Read current values (start() already populated them via callbacks)
int brightness = xr_device_provider_get_brightness_level(handle);
int volume = xr_device_provider_get_volume_level(handle);
printf("Brightness: %d Volume: %d\n", brightness, volume);
// Set a comfortable level
xr_device_provider_set_brightness_level(handle, 5);
xr_device_provider_set_volume_level(handle, 3);
// Switch to 120 Hz
xr_device_provider_set_display_mode(handle, VITURE_DISPLAY_MODE_1920_1080_120HZ);
// Tint the lenses
xr_device_provider_set_film_mode(handle, 1.0f);
Beast — Switching to Bypass Mode
// Exit native mode and set a standard display mode
xr_device_provider_native_set_mode(handle, 0);
xr_device_provider_set_display_mode(handle, VITURE_DISPLAY_MODE_1920_1080_90HZ);
Beast — Switching Back to Native Mode
// Re-enter native mode and set the display resolution/rate
xr_device_provider_native_set_mode(handle, 1);
xr_device_provider_native_set_display_mode(handle, VITURE_NATIVE_DISPLAY_MODE_1920_1080_90HZ);
// Enable 3DOF head tracking and set display size
xr_device_provider_native_set_dof(handle, VITURE_NATIVE_DOF_3);
xr_device_provider_native_set_display_size(handle, VITURE_DISPLAY_SIZE_MEDIUM);
Viture glasses expose two distinct camera systems depending on the model. This page covers both.
Camera Overview
Camera Type What it is Devices
--------------- -------------------------- ----------------------------------
Pass-through Front-facing UVC camera, Luma Pro, Luma Cyber, Luma Ultra,
shows the real world Beast
Stereo tracking Side-mounted grayscale Luma Ultra only
cameras for VIO (Carina)
These two camera systems use completely different APIs and have no relation to each other.
Pass-Through Camera (UVC)
The pass-through camera is a standard USB UVC device. It has its own USB VID/PID, separate from the glasses control interface, and uses a completely independent lifecycle with its own handle and create/start/stop/destroy calls.
Supported devices:
Device Camera VID Camera PID
----------- ---------- ----------
Luma Pro 0x0C45 0x636B
Luma Cyber 0x0C45 0x636B
Luma Ultra 0x0C45 0x636B
Beast 0x0C45 0x6368
Devices without a pass-through camera: Luma, Viture One, Lite, Pro, Pro 2 — get_camera_pid
returns 0 for these.
Step 1 — Discover the Camera VID/PID
Use the glasses product ID to look up the paired camera VID/PID:
#include "viture_camera_provider.h"
int camera_vid = xr_camera_provider_get_camera_vid(glasses_pid);
int camera_pid = xr_camera_provider_get_camera_pid(glasses_pid);
if (camera_vid == 0 || camera_pid == 0) {
printf("This device has no pass-through camera.\n");
return;
}
You can also validate any enumerated USB device against the known Viture camera set:
if (xr_camera_provider_is_valid_camera(some_vid, some_pid)) {
// It's a Viture camera
}
Step 2 — Create the Camera Provider
The camera provider has its own handle and lifecycle, independent of the glasses provider.
Desktop (Linux / macOS / Windows):
XRCameraProviderHandle cam = xr_camera_provider_create(camera_vid, camera_pid);
if (!cam) {
fprintf(stderr, "Camera not found or failed to open\n");
}
Android: Pass the UsbDeviceConnection file descriptor (see Android Integration Guide):
XRCameraProviderHandle cam = xr_camera_provider_create(camera_vid, camera_pid, fd);
Step 3 — Start Streaming
The camera always streams at 1920x1080 @ 30fps in MJPEG format. Provide a callback and
an optional user_data pointer. Because the callback runs on an SDK-internal thread, there
is no other way to access application state from inside it — user_data is passed through
unchanged each time the callback fires, letting you avoid global variables:
static void on_camera_frame(const XRCameraFrame* frame, void* user_data) {
// frame->data pointer to MJPEG-compressed bytes (valid only during this call)
// frame->size byte count of the MJPEG payload
// frame->width always 1920
// frame->height always 1080
// frame->format XR_CAMERA_FORMAT_MJPEG
// frame->timestamp nanoseconds
// frame->sequence frame counter (starts at 0)
// user_data is whatever you passed to xr_camera_provider_start
MyRenderer* renderer = (MyRenderer*)user_data;
renderer->submitFrame(frame->data, frame->size);
}
int rc = xr_camera_provider_start(cam, on_camera_frame, my_renderer /* user_data */);
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "camera start failed: %d\n", rc);
}
The callback is invoked on a dedicated camera thread. If you need to pass data to the render thread, copy it and use a lock.
Step 4 — Decoding MJPEG
The frame data is standard MJPEG (JFIF). Any JPEG decoder can handle it.
The desktop demo (glasses-demo) uses Raylib's built-in image loader, which requires no
additional library:
Image img = LoadImageFromMemory(".jpg", frame->data, (int)frame->size);
ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
// first frame: LoadTextureFromImage(img)
// subsequent: UpdateTexture(texture, img.data)
UnloadImage(img);
On Android, BitmapFactory.decodeByteArray decodes MJPEG directly without any third-party
dependency.
Step 5 — Stop and Destroy
xr_camera_provider_stop(cam);
xr_camera_provider_destroy(cam);
destroy() stops streaming automatically if it was still active, but it is better
practice to stop explicitly before destroy.
Checking Streaming State
if (xr_camera_provider_is_streaming(cam)) {
// camera is currently delivering frames
}
Complete Pass-Through Camera Example
#include "viture_camera_provider.h"
#include "viture_glasses_provider.h"
#include <stdio.h>
#include <unistd.h>
static int g_frame_count = 0;
static void on_frame(const XRCameraFrame* frame, void* ctx) {
g_frame_count++;
if (g_frame_count % 30 == 0) { // log once per second
printf("Frame #%u size=%u bytes ts=%llu\n",
frame->sequence, frame->size, (unsigned long long)frame->timestamp);
}
}
int demo_camera(int glasses_pid) {
int cvid = xr_camera_provider_get_camera_vid(glasses_pid);
int cpid = xr_camera_provider_get_camera_pid(glasses_pid);
if (!cvid || !cpid) { printf("No camera on this device\n"); return 1; }
XRCameraProviderHandle cam = xr_camera_provider_create(cvid, cpid);
if (!cam) { printf("Failed to open camera\n"); return 1; }
int rc = xr_camera_provider_start(cam, on_frame, NULL);
if (rc != VITURE_GLASSES_SUCCESS) {
printf("Camera start failed: %d\n", rc);
xr_camera_provider_destroy(cam);
return 1;
}
sleep(5); // receive frames for 5 seconds
xr_camera_provider_stop(cam);
xr_camera_provider_destroy(cam);
return 0;
}
Stereo Tracking Cameras (Carina / Luma Ultra only)
Luma Ultra has two grayscale cameras on its sides that feed the VIO tracking engine.
These cameras can also deliver raw frames to your application via the Carina callback
registered with xr_device_provider_register_callbacks_carina.
Note: The stereo tracking cameras and the pass-through UVC camera are completely separate hardware. You can use both simultaneously.
Property Value
------------- -------------------
Format 8-bit grayscale
Stream count 4 buffers per frame (left0, right0, left1, right1)
Rate 25 Hz (tied to camera VSync)
Registering the Stereo Callback
#include "viture_device_carina.h"
static void on_stereo(char* left0, char* right0,
char* left1, char* right1,
double timestamp, int width, int height) {
// left0 / right0: primary stereo frame (8-bit grayscale)
// left1 / right1: secondary stereo frame
// Data is valid only during this callback. Copy if needed.
// width x height is the resolution per camera.
printf("Stereo frame at %.3f %dx%d\n", timestamp, width, height);
}
xr_device_provider_register_callbacks_carina(
handle,
NULL, // XRPoseCallback (25 Hz pose — use get_gl_pose_carina instead)
NULL, // XRVSyncCallback
NULL, // XRImuCallback (raw Carina IMU)
on_stereo // XRCameraCallback
);
You may pass NULL for any callback you do not need.
Exposure Control
The stereo cameras run in automatic exposure by default. Switch to a fixed exposure when the scene lighting is controlled and you need stable frame brightness — for example when feeding the frames to your own vision pipeline.
// Fixed exposure: 4 ms, gain 8. Out-of-range values are clamped.
xr_device_provider_set_manual_exposure_carina(handle, 4.0f, 8);
// Back to automatic
xr_device_provider_set_auto_exposure_carina(handle);
Parameter Range
----------------- --------------
exposure_time_ms [0.01, 8.0]
exposure_gain [0, 15]
Note: Exposure affects the frames the VIO engine consumes as well. A manual setting that is too dark or too bright degrades 6DoF tracking quality. Switch back to auto exposure will re-apply stock strategy.
Choosing the Right Camera
Use case Camera to use
------------------------------------ --------------------------------------------------
Show real-world video overlay Pass-through UVC camera (XRCameraProvider)
Custom VIO / SLAM / CV algorithm Stereo tracking cameras (Carina callback)
Record what the user sees Pass-through UVC camera
Access raw IMU-synced camera data Stereo tracking cameras (Carina callback)
Device: One / Lite / Pro / Pro 2 / Luma Neither (no camera hardware on these models)
This guide covers everything specific to Android: USB permission flow, file descriptor handling, JNI bridge patterns, and the camera permission requirement on Android 14+.
How Android Differs from Desktop
On desktop platforms, the SDK opens the USB HID device directly. Android does not allow this. Your app must:
- Enumerate devices via
UsbManager - Request runtime permission from the user
- Open a
UsbDeviceConnection - Pass the connection's file descriptor to the SDK
The SDK function signatures on Android include an extra file_descriptor parameter:
// Android
XRDeviceProviderHandle xr_device_provider_create(int product_id, int file_descriptor);
XRCameraProviderHandle xr_camera_provider_create(int vid, int pid, int file_descriptor);
// Other platforms
XRDeviceProviderHandle xr_device_provider_create(int product_id);
XRCameraProviderHandle xr_camera_provider_create(int vid, int pid);
The file_descriptor is the value returned by UsbDeviceConnection.getFileDescriptor().
AndroidManifest.xml
Declare the USB host feature and add the permission:
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<!-- Android 14+: required for UVC camera access -->
<uses-permission android:name="android.permission.CAMERA" />
To receive attach/detach events automatically, add an intent filter to your activity:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
Create res/xml/device_filter.xml to pre-filter by Viture VID:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="13770" /> <!-- 0x35CA in decimal -->
</resources>
USB Permission Flow
The complete flow from device attachment to a valid file descriptor:
USB plugged in
|
v
ACTION_USB_DEVICE_ATTACHED broadcast
|
v
Filter by VID 0x35CA + validate PID
|
v
usbManager.hasPermission(device)?
|-- YES: open connection, get fd, call SDK
|
+-- NO: usbManager.requestPermission(device, pendingIntent)
|
v
User approves dialog
|
v
ACTION_USB_PERMISSION broadcast (granted=true)
|
v
open connection, get fd, call SDK
Kotlin Implementation
class MainActivity : AppCompatActivity() {
private val ACTION_USB_PERMISSION = "com.example.USB_PERMISSION"
private lateinit var usbManager: UsbManager
private var usbConnection: UsbDeviceConnection? = null
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_USB_PERMISSION -> {
val granted = intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false)
val device = intent.getParcelableExtra<UsbDevice>(
UsbManager.EXTRA_DEVICE) ?: return
if (granted) openDevice(device)
}
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
val device = intent.getParcelableExtra<UsbDevice>(
UsbManager.EXTRA_DEVICE) ?: return
handleDeviceAttached(device)
}
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
closeDevice()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usbManager = getSystemService(USB_SERVICE) as UsbManager
val filter = IntentFilter().apply {
addAction(ACTION_USB_PERMISSION)
addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
}
registerReceiver(usbReceiver, filter)
// Handle device already connected when app starts
usbManager.deviceList.values
.filter { it.vendorId == 0x35CA }
.forEach { handleDeviceAttached(it) }
}
private fun handleDeviceAttached(device: UsbDevice) {
if (device.vendorId != 0x35CA) return
val pid = device.productId
if (!GlassesBridge.isProductIdValid(pid)) return
if (usbManager.hasPermission(device)) {
openDevice(device)
} else {
val intent = Intent(ACTION_USB_PERMISSION)
val pendingIntent = PendingIntent.getBroadcast(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
usbManager.requestPermission(device, pendingIntent)
}
}
private fun openDevice(device: UsbDevice) {
val conn = usbManager.openDevice(device) ?: return
usbConnection = conn
val fd = conn.fileDescriptor
val pid = device.productId
val ok = GlassesBridge.create(pid, fd)
if (!ok) { conn.close(); usbConnection = null; return }
// ... initialize, start, etc.
}
private fun closeDevice() {
GlassesBridge.stop()
GlassesBridge.shutdown()
GlassesBridge.destroy()
usbConnection?.close()
usbConnection = null
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(usbReceiver)
closeDevice()
}
}
JNI Bridge Pattern
The SDK is a C library. On Android it is typically called through JNI from Kotlin or Java.
The recommended pattern is a singleton Kotlin object that declares all external functions:
// GlassesBridge.kt
object GlassesBridge {
init {
System.loadLibrary("glasses_bridge")
}
external fun isProductIdValid(pid: Int): Boolean
// Lifecycle
external fun create(pid: Int, fd: Int): Boolean
external fun getDeviceType(): Int
external fun initialize(): Int
external fun start(): Int
external fun stop(): Int
external fun shutdown(): Int
external fun destroy()
// IMU (Gen1/Gen2)
external fun openImu(): Int
external fun closeImu(): Int
// Carina
external fun setDofTypeCarina(is6dof: Boolean): Int
external fun startCarinaPollThread()
external fun stopCarinaPollThread()
external fun getPose(): FloatArray
external fun isPoseFresh(): Boolean
external fun getPoseStatus(): Int
// Device state
external fun getBrightness(): Int
external fun getVolume(): Int
external fun getFilm(): Int
const val DEVICE_TYPE_GEN1 = 0
const val DEVICE_TYPE_GEN2 = 1
const val DEVICE_TYPE_CARINA = 2
}
The corresponding C++ file (glasses_bridge.cpp) holds a global XRDeviceProviderHandle
and implements each Java_..._GlassesBridge_* function.
Key pattern for storing the handle:
static XRDeviceProviderHandle g_handle = nullptr;
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_GlassesBridge_create(JNIEnv*, jobject, jint pid, jint fd) {
if (g_handle) {
xr_device_provider_destroy(g_handle);
g_handle = nullptr;
}
g_handle = xr_device_provider_create((int)pid, (int)fd);
return g_handle != nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_GlassesBridge_destroy(JNIEnv*, jobject) {
if (g_handle) {
xr_device_provider_destroy(g_handle);
g_handle = nullptr;
}
}
CMake Configuration for Android
Place the SDK headers under app/src/main/include/ and the prebuilt .so files under
app/src/main/jniLibs/<abi>/:
app/src/main/
├── include/
│ ├── viture_glasses_provider.h
│ ├── viture_device.h
│ └── ...
└── jniLibs/
└── arm64-v8a/
└── libglasses.so
In app/src/main/cpp/CMakeLists.txt:
cmake_minimum_required(VERSION 3.22.1)
project(glasses_bridge)
set(JNILIBS_DIR "/../jniLibs/")
set(INCLUDE_DIR "/../include")
add_library(glasses SHARED IMPORTED)
set_target_properties(glasses PROPERTIES
IMPORTED_LOCATION "/libglasses.so")
add_library(glasses_bridge SHARED glasses_bridge.cpp)
target_include_directories(glasses_bridge PRIVATE )
target_link_libraries(glasses_bridge glasses android log)
In app/build.gradle.kts:
android {
ndkVersion = "27.3.13750724"
defaultConfig {
ndk { abiFilters += "arm64-v8a" }
externalNativeBuild { cmake { cppFlags += "-std=c++17" } }
}
externalNativeBuild {
cmake { path = file("src/main/cpp/CMakeLists.txt"); version = "3.22.1" }
}
}
Carina Pose Polling on Android
Pose polling is a blocking loop and must not run on the main thread. Start a dedicated background thread:
private var pollThread: Thread? = null
private var pollRunning = false
private val poseData = FloatArray(7)
private val poseLock = Any()
fun startPollThread() {
pollRunning = true
pollThread = Thread {
while (pollRunning) {
val pose = GlassesBridge.getPose() // calls get_gl_pose_carina
if (pose.isNotEmpty()) {
synchronized(poseLock) {
pose.copyInto(poseData)
}
}
Thread.sleep(8) // ~120 Hz
}
}.also { it.start() }
}
fun stopPollThread() {
pollRunning = false
pollThread?.join()
pollThread = null
}
In your JNI bridge:
extern "C"
JNIEXPORT jfloatArray JNICALL
Java_com_example_GlassesBridge_getPose(JNIEnv* env, jobject) {
jfloatArray result = env->NewFloatArray(7);
if (!g_handle) return result;
float pose[7] = {};
int status = 0;
xr_device_provider_get_gl_pose_carina(g_handle, pose, 0.0, &status);
env->SetFloatArrayRegion(result, 0, 7, pose);
return result;
}
Camera on Android (Android 14+)
Android 14 requires the android.permission.CAMERA permission at runtime to access any UVC device.
Request it before opening the camera USB device:
// In Activity
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
The camera USB device has a different VID/PID from the glasses control interface. Handle its permission separately:
// When enumerating USB devices, also check for the camera
val cameraVid = GlassesBridge.cameraGetCameraVid(glassesPid)
val cameraPid = GlassesBridge.cameraGetCameraPid(glassesPid)
usbManager.deviceList.values
.filter { it.vendorId == cameraVid && it.productId == cameraPid }
.firstOrNull()
?.let { cameraDevice ->
if (!usbManager.hasPermission(cameraDevice)) {
usbManager.requestPermission(cameraDevice, pendingIntent)
} else {
openCamera(cameraDevice)
}
}
Then open the camera with its own file descriptor:
fun openCamera(device: UsbDevice) {
val conn = usbManager.openDevice(device) ?: return
cameraConnection = conn
GlassesBridge.cameraCreate(
device.vendorId, device.productId, conn.fileDescriptor)
GlassesBridge.cameraStart()
}
Full Sequence Diagram
Activity UsbManager / System GlassesBridge (JNI)
-------------------------------- ------------------------- --------------------------
onCreate()
register BroadcastReceivers
scan usbManager.deviceList
for existing devices
|
| VID == 0x35CA found
v
hasPermission(device)?
NO: requestPermission() -----> shows system dialog
|
user approves
|
ACTION_USB_PERMISSION broadcast
|<---------------------------------'
|
YES (or after approval):
openDevice(device)
conn = usbManager.openDevice()
fd = conn.fileDescriptor
|
+------------------------------------------------> create(pid, fd)
| initialize()
| start()
|
Carina: startPollThread()
| loop: getPose() -----------------------------> get_gl_pose_carina
|
Gen1/2: openImu() --------------------------------------> open_imu
| (pose callback fires on SDK internal thread)
|
~ [app running]
|
onDestroy()
Carina: stopPollThread()
Gen1/2: closeImu() --------------------------------------> close_imu
|
+------------------------------------------------> stop()
| shutdown()
| destroy()
conn.close()
The stat reporter sends optional usage events from your application to Viture's gain-sharing endpoint. It is a small, opt-in subsystem layered on top of the regular SDK and is only active when you explicitly initialize it with credentials issued by Viture.
The only event currently supported is glasses bind: a one-shot signal that your application has successfully connected to a Viture device on this user's machine. Reporting it allows Viture to count installations of integrations that participate in the gain-sharing program.
If you do not call xr_stat_reporter_init, the reporter is dormant and no
network traffic is generated. Existing apps that integrate libglasses are not
affected by upgrading to a version that includes this module.
When to Use
You should call the reporter if... You can ignore the reporter if...
------------------------------------------ -------------------------------------
You participate in Viture's gain-sharing You are evaluating libglasses or
program and have AK/SK credentials. building an internal-only app.
Your end user has consented to anonymous You do not have AK/SK credentials
product-usage telemetry. issued by Viture.
If in doubt, don't call it.
Lifecycle
The reporter is keyed on the XRDeviceProviderHandle you already use for the
rest of the SDK:
xr_device_provider_create
|
v
xr_device_provider_initialize (must complete first; bind needs serial numbers)
|
v
xr_device_provider_start
|
v
xr_stat_reporter_init (opt-in: store AK/SK against this handle)
|
v
xr_stat_reporter_glasses_bind (synchronous HTTPS POST; call once per session)
|
v
... continue normal use ...
|
v
xr_device_provider_stop / shutdown / destroy
Credentials live in process memory only — they are not persisted, never sent over the network in cleartext, and are discarded when the process exits.
Network Behaviour
xr_stat_reporter_glasses_bind is synchronous: it builds the HTTPS request,
opens an SSL connection to cloud.viture.dev, sends the payload, waits for the
response, and returns. Connection timeout is 5 s and read timeout is 10 s, so
the worst case is a ~15 s block. Always call it from a worker thread (or
coroutine on platforms with built-in async) — never from the UI thread.
Each request carries three required headers:
Date RFC 7231 GMT timestamp
Authorization "VITURE <ak> <hmac>" — HMAC-SHA1 of canonical request, signed with SK
User-Agent "libglasses/<sdk_version> os/<os> arch/<arch>"
The body is a small JSON document with the device's product ID, serial numbers, firmware version, and your app name. No personal data is collected.
Return Codes
xr_stat_reporter_glasses_bind reports the HTTP outcome through its return
value, with the exact status code optionally written to a caller-supplied
int*:
Return Meaning
-------------------------------------------- ----------------------------------------
VITURE_GLASSES_SUCCESS Server returned 2xx — report accepted.
VITURE_GLASSES_ERROR_UNKNOWN Network error, TLS failure, or non-2xx
HTTP status. Inspect the optional
http_status out-param to disambiguate
(0 = network failure).
VITURE_GLASSES_ERROR_INVALID_PARAM Null handle.
VITURE_GLASSES_ERROR_INVALID_STATE init was not called for this handle.
VITURE_GLASSES_ERROR_NO_DATA Could not collect bind params (the
device has not finished initializing or
the firmware refused to provide a
serial number).
Treat the bind report as best-effort. A failed report should not block the user from using the glasses.
Minimal Example
#include "viture_glasses_provider.h"
#include "viture_stat_reporter.h"
#include <pthread.h>
static void* report_bind(void* arg) {
XRDeviceProviderHandle h = (XRDeviceProviderHandle)arg;
int http_status = 0;
int rc = xr_stat_reporter_glasses_bind(h, &http_status);
printf("Bind report: rc=%d http=%d\n", rc, http_status);
return NULL;
}
void integrate(XRDeviceProviderHandle h) {
// ... initialize() / start() the device first ...
// Opt in to usage reporting (call once per process, after start())
xr_stat_reporter_init(h, "<your-ak>", "<your-sk>", "MyApp");
// Fire the bind event on a worker thread so the UI never blocks
pthread_t t;
pthread_create(&t, NULL, report_bind, h);
pthread_detach(t);
}
For a complete reference of the two functions, see viture_stat_reporter.h.
Some Viture devices can take over head tracking and frame interpolation entirely
on the device. This is called native DOF. Devices with this capability
expose an additional set of native_* APIs and operate in one of two modes:
Mode Who handles the video and tracking
-------- -------------------------------------------------------------
Bypass Host renders frames; the glasses pass them through unchanged.
Behaves the same as a non-native-DOF device.
Native Glasses run on-device 3DOF tracking and may interpolate
frames; the host picks resolution/refresh from a different
display-mode set.
Currently only Beast supports native DOF, but the API is intentionally device-agnostic. Detect the capability with the API below rather than checking the device type or product ID — code written this way will keep working when future products gain native DOF.
Capability Detection
#include "viture_glasses_provider.h"
int product_id = /* discovered via USB enumeration */;
if (xr_device_provider_is_product_support_native_dof(product_id)) {
// Application should expose native/bypass mode controls,
// native display modes, and a DOF selector.
} else {
// Application should expose only the standard display-mode controls.
}
xr_device_provider_is_product_support_native_dof takes a product ID (not a
handle) and returns 1 if the product supports native DOF, 0 otherwise. It
can be called before create() so the application can branch its UI before
opening the device.
Display Mode: Two Disjoint Sets
A native-DOF device has two display-mode tables. Which set is valid depends on the current native mode:
Native mode API to call Constants
----------- ---------------------------------------------- --------------------------------
Bypass (0) xr_device_provider_get/set_display_mode VITURE_DISPLAY_MODE_*
Native (1) xr_device_provider_native_get/set_display_mode VITURE_NATIVE_DISPLAY_MODE_*
- The bypass set is the same standard set every other device uses (1920x1080 through 3840x1200, 60 / 90 / 120 Hz).
- The native set is larger: in addition to the same resolutions/rates it includes 3D SBS and ultrawide variants that only make sense with on-device scaling.
- The two sets share numeric values for some entries but are not
interchangeable — calling the bypass API in native mode (or vice versa)
returns
VITURE_GLASSES_ERROR_NOT_SUPPORTED.
Because of this, every read or write of the display mode on a native-DOF device
must first know the current native mode. The recommended pattern is to cache
the native mode in the application after calling native_get_mode / setting
it via native_set_mode, and update the cached value whenever the
VITURE_CALLBACK_ID_NATIVE_DOF state callback fires.
DOF Selector
Native mode exposes a DOF selector that has no analogue on other devices:
Constant Meaning
-------------------------------- ----------------------------------------
VITURE_NATIVE_DOF_0 No on-device tracking
VITURE_NATIVE_DOF_3 Native 3DOF
VITURE_NATIVE_DOF_SMOOTH_FOLLOW Smooth-follow (head leads, image lags)
Read with xr_device_provider_native_get_dof, write with
xr_device_provider_native_set_dof. These calls are only meaningful when the
device is in native mode — applications should hide or disable the DOF
selector while in bypass mode.
Recommended Logic Flow
The flow below is the recommended way for a host application to handle the display-mode and DOF logic for both native-DOF and non-native-DOF devices in a single code path.
on connect (have product_id and handle):
supports_native_dof = is_product_support_native_dof(product_id)
if not supports_native_dof:
expose only the standard display-mode control
populate it from VITURE_DISPLAY_MODE_*
done
// device supports native DOF
expose the native-mode toggle, the display-mode control, and the DOF selector
native_mode = native_get_mode(handle) // 0 = bypass, 1 = native
rebuild the display-mode list to match native_mode
enable the DOF selector only if native_mode == 1
rebuild the display-mode list for native_mode:
if native_mode == 0:
populate from VITURE_DISPLAY_MODE_*
disable the DOF selector
else:
populate from VITURE_NATIVE_DISPLAY_MODE_*
enable the DOF selector
user changes the native mode:
native_set_mode(handle, new_mode)
native_mode = new_mode
rebuild the display-mode list
user reads the display mode:
if native_mode == 0:
value = get_display_mode(handle)
decode against VITURE_DISPLAY_MODE_*
else:
value = native_get_display_mode(handle)
decode against VITURE_NATIVE_DISPLAY_MODE_*
user writes the display mode:
value = current selection from the active table
if native_mode == 0:
set_display_mode(handle, value)
else:
native_set_display_mode(handle, value)
readback and verify
state callback fires (device-initiated change):
VITURE_CALLBACK_ID_DISPLAY_MODE:
if supports_native_dof:
decode against the table for the current native_mode
else:
decode against VITURE_DISPLAY_MODE_*
VITURE_CALLBACK_ID_NATIVE_DOF:
only meaningful on native-DOF devices
decode against VITURE_NATIVE_DOF_*
The key invariants are:
- Capability is determined by
is_product_support_native_dof(product_id), not by device type or product ID checks. This keeps host code working when future devices gain the capability. - The display-mode table and the API entry point are jointly chosen by the
cached
native_mode. Mixing them producesERROR_NOT_SUPPORTED. - The DOF selector is gated by
native_mode == 1, not by whether the device supports native DOF. A native-DOF device in bypass mode should not expose DOF controls.
Things That Are Not Tied to Native DOF
These device-specific traits are properties of the Beast hardware, not of the native-DOF capability, so they should be detected separately:
Trait How to detect
------------------------------- ----------------------------------------------
Volume range 0..15 Beast only (other devices, incl. Pro 2: 0..8)
Multi-step electrochromic film Beast only (other devices: binary; Pro 2: none)
XR_DEVICE_TYPE_VITURE_GEN2 alone no longer identifies these traits: Pro 2 is
also Gen2 but uses volume range 0..8 and has no electrochromic film. Confirm the
specific model with xr_device_provider_get_market_name before relying on
Beast-only ranges, and note that set_film_mode returns
VITURE_GLASSES_ERROR_NOT_SUPPORTED on Pro 2.
get_market_name returns the bare market name — "Beast", "Pro 2" — without
the "Viture" prefix used elsewhere in this guide. Compare against the
VITURE_MARKET_NAME_* constants rather than hardcoding the string:
char name[32];
int len = sizeof(name);
if (xr_device_provider_get_market_name(product_id, name, &len) == VITURE_GLASSES_SUCCESS) {
if (strcmp(name, VITURE_MARKET_NAME_BEAST) == 0) {
// Beast-only volume range 0..15 and multi-step film
}
}
Even on a hypothetical future device that adds native-DOF support without being
Gen2, these would not automatically apply. Use the model check for
those, and is_product_support_native_dof for the display/DOF logic described
above.
Quick Reference
Operation API
------------------------------- -----------------------------------------------------
Detect capability xr_device_provider_is_product_support_native_dof
Read/write native mode (0/1) xr_device_provider_native_get_mode / native_set_mode
Display mode in bypass xr_device_provider_get_display_mode / set_display_mode
Display mode in native xr_device_provider_native_get_display_mode / native_set_display_mode
DOF mode (native only) xr_device_provider_native_get_dof / native_set_dof
Recenter native DOF xr_device_provider_native_recenter_dof
2D / 3D in native xr_device_provider_native_switch_dimension
Display size in native xr_device_provider_native_get/set_display_size
Display distance in native xr_device_provider_native_get/set_display_distance
State callback IDs VITURE_CALLBACK_ID_DISPLAY_MODE, VITURE_CALLBACK_ID_NATIVE_DOF
For full per-function documentation see viture_glasses_provider.h and viture_protocol_public.h.
Types
XRDeviceProviderHandle
typedef void* XRDeviceProviderHandle;
Opaque pointer type for XRDeviceProvider instances.
GlassStateCallback
typedef void (*GlassStateCallback)(int glass_state_id, int glass_value);
Callback for reporting glass state changes (brightness, volume, display mode, etc.).
glass_state_id: Identifier for the state (see VITURECALLBACKID_* constants)glass_value: Integer value associated with the state
LogHook
typedef void (*LogHook)(int level, const char* tag, const char* message);
Log hook function type.
level: Log level (0: None, 1: Error, 2: Info, 3: Debug)tag: Log tag (typically "libglasses")message: Log message content
XRDeviceType
typedef enum {
XR_DEVICE_TYPE_VITURE_GEN1 = 0,
XR_DEVICE_TYPE_VITURE_GEN2 = 1,
XR_DEVICE_TYPE_VITURE_CARINA = 2
} XRDeviceType;
Enumeration of supported device types.
Required Call Order
create() -> [set_dof_type_carina()] -> initialize() -> start()
-> [register callbacks, open_imu / poll pose, device control]
-> [close_imu / stop poll thread] -> stop() -> shutdown() -> destroy()
Functions
xr_device_provider_create
// Android variant
VITURE_API XRDeviceProviderHandle xr_device_provider_create(int product_id,
int file_descriptor);
// Non-Android variant
VITURE_API XRDeviceProviderHandle xr_device_provider_create(int product_id);
Create an XRDeviceProvider instance.
- Android variant requires detailed USB information due to platform restrictions
- Returns: Handle to created instance, or NULL on failure
xr_device_provider_initialize
VITURE_API int xr_device_provider_initialize(XRDeviceProviderHandle handle,
const char* custom_config,
const char* cache_file_dir);
Initialize the XRDeviceProvider.
custom_config: Optional custom configuration string. PassNULLfor default behavior.cache_file_dir: Optional directory path for caching calibration data. PassNULLto disable.- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
For Carina devices, call xr_device_provider_set_dof_type_carina between create() and initialize().
xr_device_provider_start
VITURE_API int xr_device_provider_start(XRDeviceProviderHandle handle);
Start the XRDeviceProvider. Begins device-callback streams: Gen1 IMU callbacks start firing,
Carina pose/vsync/imu/camera callbacks start firing. (Gen2 has no callback stream to start.)
Device control commands are already usable after initialize() returns.
- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_stop
VITURE_API int xr_device_provider_stop(XRDeviceProviderHandle handle);
Stop the XRDeviceProvider. Close IMU / stop poll thread before calling this.
- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_shutdown
VITURE_API int xr_device_provider_shutdown(XRDeviceProviderHandle handle);
Shutdown the XRDeviceProvider.
- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_destroy
VITURE_API void xr_device_provider_destroy(XRDeviceProviderHandle handle);
Releases all resources. Always call as the final step, even on error paths.
xr_device_provider_get_thread_id
VITURE_API int xr_device_provider_get_thread_id(XRDeviceProviderHandle handle, int thread_ids[VITURE_THREAD_ID_COUNT]);
Get the core thread IDs for data transfer and calculation (must be called after start).
thread_ids: Array of at leastVITURE_THREAD_ID_COUNT(4) ints. Unused slots are set to-1.- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_register_state_callback
VITURE_API int xr_device_provider_register_state_callback(XRDeviceProviderHandle handle, GlassStateCallback callback);
Registers a callback for device state change notifications (brightness, volume, etc.).
Pass NULL to unregister. Can be called before or after start().
- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_device_type
VITURE_API int xr_device_provider_get_device_type(XRDeviceProviderHandle handle);
Get the device type. Can be called immediately after create().
- Returns:
XRDeviceTypeenum value on success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_is_product_id_valid
VITURE_API int xr_device_provider_is_product_id_valid(int product_id);
Check if product ID is valid.
- Returns: 1 if valid, 0 if not
xr_device_provider_is_product_support_native_dof
VITURE_API int xr_device_provider_is_product_support_native_dof(int product_id);
Check whether the product supports native DoF mode. Use this capability check
instead of hardcoding device types when deciding whether to expose the
xr_device_provider_native_* APIs (display mode, DoF, recenter, etc.).
- Returns: 1 if supported, 0 if not
xr_device_provider_is_product_support_imu_frequency
VITURE_API int xr_device_provider_is_product_support_imu_frequency(int product_id, int imu_mode, int imu_report_frequency);
Check whether the product supports the given IMU report frequency
(VITURE_IMU_FREQUENCY_* constants) in the given IMU mode
(VITURE_IMU_MODE_*). Use this capability check instead of hardcoding device
types. Frequency support can differ between raw and pose modes on some products:
for example, Pro 2 supports every frequency in raw mode but limits pose mode to
VITURE_IMU_FREQUENCY_LOW / VITURE_IMU_FREQUENCY_MEDIUM_LOW /
VITURE_IMU_FREQUENCY_MEDIUM (≤120Hz). VITURE_IMU_FREQUENCY_ULTRA_HIGH
(1000Hz) is only available on select products; for Luma Ultra it applies to
polling xr_device_provider_get_gl_pose_carina rather than IMU callback
streaming.
imu_mode: IMU mode to check (VITURE_IMU_MODE_RAW/VITURE_IMU_MODE_POSE)imu_report_frequency: Frequency to check (see VITUREIMUFREQUENCY_* constants)- Returns: 1 if supported, 0 if not
xr_device_provider_get_market_name
VITURE_API int xr_device_provider_get_market_name(int product_id, char* market_name, int* length);
Writes the human-readable product name (e.g., "Luma Ultra") into market_name.
market_name: Buffer to store market namelength: Must be set to the buffer size on input; receives the actual length on output- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_set_log_level
VITURE_API void xr_device_provider_set_log_level(int level);
Set log level (0: None, 1: Error, 2: Info, 3: Debug).
xr_device_provider_get_log_level
VITURE_API int xr_device_provider_get_log_level(void);
Get current log level.
- Returns: Current log level (0-3)
xr_device_provider_set_log_hook
VITURE_API void xr_device_provider_set_log_hook(LogHook hook);
Set a log hook to capture library log messages. Pass NULL to disable.
VITURE api
// Windows/Cygwin
#define VITURE_API __declspec(dllexport)
// GCC/Clang (Unix-like)
#define VITURE_API __attribute__((visibility("default")))
// Other
#define VITURE_API
Unified platform-specific export macro. Always exports (dllexport), never imports.
Log Level Macros
#define LOG_LEVEL_NONE 0
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_INFO 2
#define LOG_LEVEL_DEBUG 3
Standard log level definitions.
Constants
Product Market Names
#define VITURE_MARKET_NAME_ONE "One"
#define VITURE_MARKET_NAME_PRO "Pro"
#define VITURE_MARKET_NAME_LITE "Lite"
#define VITURE_MARKET_NAME_LUMA "Luma"
#define VITURE_MARKET_NAME_LUMA_PRO "Luma Pro"
#define VITURE_MARKET_NAME_LUMA_ULTRA "Luma Ultra"
#define VITURE_MARKET_NAME_LUMA_CYBER "Luma Cyber"
#define VITURE_MARKET_NAME_BEAST "Beast"
#define VITURE_MARKET_NAME_PRO2 "Pro 2"
All VITURE XR glasses products.
Result Codes
All API functions return VITURE_GLASSES_SUCCESS (0) on success, or a negative error code on failure.
#define VITURE_GLASSES_SUCCESS 0 // Operation completed successfully
#define VITURE_GLASSES_ERROR_INVALID_PARAM -1 // Null handle, null pointer, or argument out of range
#define VITURE_GLASSES_ERROR_USB_UNAVAILABLE -2 // USB connection not available or not established
#define VITURE_GLASSES_ERROR_USB_EXEC -3 // USB read/write operation failed
#define VITURE_GLASSES_ERROR_NOT_SUPPORTED -4 // Feature not supported by this device model
#define VITURE_GLASSES_ERROR_NO_DATA -5 // No valid response data (timeout or empty)
#define VITURE_GLASSES_ERROR_DATA_PARSE -6 // Response data format or length mismatch
#define VITURE_GLASSES_ERROR_DEVICE_REJECTED -7 // Device rejected the command at firmware level
#define VITURE_GLASSES_ERROR_CALIB_INIT -8 // Calibration initialization failed
#define VITURE_GLASSES_ERROR_SERIAL_FETCH -9 // Serial number retrieval failed
#define VITURE_GLASSES_ERROR_INVALID_STATE -10 // Operation not valid in current state
#define VITURE_GLASSES_ERROR_UNKNOWN -99 // Unclassified error
Display Mode Identifiers
#define VITURE_DISPLAY_MODE_1920_1080_60HZ 0x31
#define VITURE_DISPLAY_MODE_3840_1080_60HZ 0x32
#define VITURE_DISPLAY_MODE_1920_1080_90HZ 0x33
#define VITURE_DISPLAY_MODE_1920_1080_120HZ 0x34
#define VITURE_DISPLAY_MODE_3840_1080_90HZ 0x35
#define VITURE_DISPLAY_MODE_1920_1200_60HZ 0x41
#define VITURE_DISPLAY_MODE_3840_1200_60HZ 0x42
#define VITURE_DISPLAY_MODE_1920_1200_90HZ 0x43
#define VITURE_DISPLAY_MODE_1920_1200_120HZ 0x44
#define VITURE_DISPLAY_MODE_3840_1200_90HZ 0x45
Display modes specify both resolution and refresh rate.
Used by all Gen1 / Carina devices, and by VITURE Beast in bypass mode (native_mode_=0).
For Beast in native mode, see VITURE_NATIVE_DISPLAY_MODE_* below.
Per-device support matrix (Y = supported, - = not supported):
Mode One/Lite/Pro Luma/Luma Pro Luma Ultra Beast (bypass)
VITURE_DISPLAY_MODE_1920_1080_60HZ Y Y Y Y
VITURE_DISPLAY_MODE_3840_1080_60HZ Y Y Y -
VITURE_DISPLAY_MODE_1920_1080_90HZ Y Y Y -
VITURE_DISPLAY_MODE_1920_1080_120HZ Y Y Y -
VITURE_DISPLAY_MODE_3840_1080_90HZ - - Y Y
VITURE_DISPLAY_MODE_1920_1200_60HZ - Y Y Y
VITURE_DISPLAY_MODE_3840_1200_60HZ - Y Y -
VITURE_DISPLAY_MODE_1920_1200_90HZ - Y Y -
VITURE_DISPLAY_MODE_1920_1200_120HZ - Y Y -
VITURE_DISPLAY_MODE_3840_1200_90HZ - - Y Y
Duty Cycle Presets
#define VITURE_DUTY_CYCLE_H 98
#define VITURE_DUTY_CYCLE_M 42
#define VITURE_DUTY_CYCLE_L 30
Duty cycle controls the display brightness by adjusting the on-time percentage of the display pixels.
Display Size Identifiers (for native 3DoF devices)
#define VITURE_DISPLAY_SIZE_SMALL 0x00
#define VITURE_DISPLAY_SIZE_MEDIUM 0x01
#define VITURE_DISPLAY_SIZE_LARGE 0x02
#define VITURE_DISPLAY_SIZE_EXTRA 0x03
#define VITURE_DISPLAY_SIZE_ULTRA 0x04
Display size controls the apparent size of the virtual display.
Native DOF Modes (for native 3DoF devices)
#define VITURE_NATIVE_DOF_0 0x00
#define VITURE_NATIVE_DOF_3 0x01
#define VITURE_NATIVE_DOF_SMOOTH_FOLLOW 0x02
Native DOF mode identifiers.
Display Modes for Native Mode (currently Beast only)
#define VITURE_NATIVE_DISPLAY_MODE_1920_1080_60HZ 0x31
#define VITURE_NATIVE_DISPLAY_MODE_1920_1080_90HZ 0x32
#define VITURE_NATIVE_DISPLAY_MODE_1920_1080_120HZ 0x33
#define VITURE_NATIVE_DISPLAY_MODE_1920_1200_60HZ 0x34
#define VITURE_NATIVE_DISPLAY_MODE_1920_1200_90HZ 0x35
#define VITURE_NATIVE_DISPLAY_MODE_1920_1200_120HZ 0x36
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_60HZ 0x37
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_90HZ 0x38
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_120HZ 0x39
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_60HZ 0x3A
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_90HZ 0x3B
#define VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_120HZ 0x3C
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_60HZ 0x3D
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_90HZ 0x3E
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_120HZ 0x3F
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_60HZ 0x40
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_90HZ 0x41
#define VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_120HZ 0x42
Display modes for Beast in native mode (native_mode_=1). Used with
xr_device_provider_native_get/set_display_mode.
Beast native-mode support (Y = supported, - = not supported):
Mode Beast (native)
VITURE_NATIVE_DISPLAY_MODE_1920_1080_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_1920_1080_90HZ -
VITURE_NATIVE_DISPLAY_MODE_1920_1080_120HZ Y
VITURE_NATIVE_DISPLAY_MODE_1920_1200_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_1920_1200_90HZ -
VITURE_NATIVE_DISPLAY_MODE_1920_1200_120HZ Y
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_90HZ -
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_120HZ -
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_90HZ -
VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1200_120HZ -
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_90HZ -
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1080_120HZ -
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_60HZ Y
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_90HZ -
VITURE_NATIVE_DISPLAY_MODE_ULTRAWIDE_3840_1200_120HZ -
IMU Configuration
#define VITURE_IMU_MODE_RAW 0
#define VITURE_IMU_MODE_POSE 1
#define VITURE_IMU_FREQUENCY_LOW 0 // 60Hz
#define VITURE_IMU_FREQUENCY_MEDIUM_LOW 1 // 90Hz
#define VITURE_IMU_FREQUENCY_MEDIUM 2 // 120Hz
#define VITURE_IMU_FREQUENCY_MEDIUM_HIGH 3 // 240Hz
#define VITURE_IMU_FREQUENCY_HIGH 4 // 500Hz
#define VITURE_IMU_FREQUENCY_ULTRA_HIGH 5 // 1000Hz (select products only)
IMU data reporting modes and frequencies. VITURE_IMU_FREQUENCY_ULTRA_HIGH is only available on select products; check with xr_device_provider_is_product_support_imu_frequency.
Callback Identifiers
#define VITURE_CALLBACK_ID_BRIGHTNESS 0
#define VITURE_CALLBACK_ID_VOLUME 1
#define VITURE_CALLBACK_ID_DISPLAY_MODE 2
#define VITURE_CALLBACK_ID_ELECTROCHROMIC_FILM 3
#define VITURE_CALLBACK_ID_NATIVE_DOF 4
#define VITURE_CALLBACK_ID_WEAR_STATUS 5
Callback identifiers for glass state change notifications.
VITURE_CALLBACK_ID_WEAR_STATUS reports wear status changes (value: 0 = not worn, 1 = worn) and is only sent by Gen2 devices.
Callback Value Ranges
#define VITURE_CALLBACK_BRIGHTNESS_VALUE_RANGE
Device Model Value Range
------------------ -----------
Viture One [0, 6]
Viture Pro [0, 8]
Viture Pro 2 [0, 8]
Viture Luma Series [0, 8]
Viture Beast [0, 8]
#define VITURE_CALLBACK_VOLUME_VALUE_RANGE
Device Model Value Range
------------------ -----------
Viture One [0, 7]
Viture Pro [0, 8]
Viture Pro 2 [0, 8]
Viture Luma Series [0, 8]
Viture Beast [0, 15]
#define VITURE_CALLBACK_ELECTROCHROMIC_FILM_VALUE_RANGE
Device Model Value Range
----------------------- -------------
Viture One & Viture Pro [0, 1]
Viture Pro 2 Not supported
Viture Luma Series [0, 1]
Viture Beast [0, 8]
Functions
xr_device_provider_get_film_mode
VITURE_API int xr_device_provider_get_film_mode(XRDeviceProviderHandle handle, float* voltage);
Get electrochromic film mode.
voltage: Pointer to store voltage data (interpretation varies by generation)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_set_film_mode
VITURE_API int xr_device_provider_set_film_mode(XRDeviceProviderHandle handle, float voltage);
Set electrochromic film mode.
voltage: Voltage parameter (0.0-1.0, interpretation varies by generation)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_duty_cycle
VITURE_API int xr_device_provider_get_duty_cycle(XRDeviceProviderHandle handle);
Get screen duty cycle.
- Returns: Duty cycle value [0, 100] on success, negative
VITURE_GLASSES_ERROR_*code on failure
xr_device_provider_set_duty_cycle
VITURE_API int xr_device_provider_set_duty_cycle(XRDeviceProviderHandle handle, int duty_cycle);
Set screen duty cycle.
duty_cycle: Duty cycle value (0-100)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_wear_status
VITURE_API int xr_device_provider_get_wear_status(XRDeviceProviderHandle handle, uint8_t* wear_status);
Get the current wear status (Gen2 devices only). Wear status changes are also
reported through the state callback with VITURE_CALLBACK_ID_WEAR_STATUS.
wear_status: Output parameter: 0 = not worn, 1 = worn- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_NOT_SUPPORTEDfor non-Gen2 devices, other negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_display_mode
VITURE_API int xr_device_provider_get_display_mode(XRDeviceProviderHandle handle);
Get display mode (Gen1 / Carina devices and Gen 2 in bypass mode).
- Returns: Display mode value (see
VITURE_DISPLAY_MODE_*) on success, negativeVITURE_GLASSES_ERROR_*code on failure - For Gen 2 devices: returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in native mode. Usexr_device_provider_native_get_display_modeinstead.
xr_device_provider_set_display_mode
VITURE_API int xr_device_provider_set_display_mode(XRDeviceProviderHandle handle, int display_mode);
Set display mode (Gen1 / Carina devices and Gen 2 in bypass mode).
display_mode: Display mode value (seeVITURE_DISPLAY_MODE_*constants)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - For Gen 2 devices: returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in native mode. Usexr_device_provider_native_set_display_modeinstead.
xr_device_provider_set_default_display_mode
VITURE_API int xr_device_provider_set_default_display_mode(XRDeviceProviderHandle handle, int display_mode);
Set the default display mode that is applied when the glasses power on. The configured mode is stored in the device and persists across power cycles. Supported on Viture Luma / Luma Pro only.
display_mode: Display mode value (seeVITURE_DISPLAY_MODE_*constants)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDfor all other devices (including Luma Ultra).
xr_device_provider_set_display_mode_button_enabled
VITURE_API int xr_device_provider_set_display_mode_button_enabled(XRDeviceProviderHandle handle, int enabled);
Enable or disable the physical 2D/3D display mode switch button on the glasses. When disabled, pressing the hardware button has no effect. The setting is not persistent and resets to enabled on the next power cycle. Supported on Viture Luma / Luma Pro only.
enabled:1to enable the button,0to disable it- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDfor all other devices (including Luma Ultra).
xr_device_provider_native_get_mode
VITURE_API int xr_device_provider_native_get_mode(XRDeviceProviderHandle handle);
Get the current operating mode for Beast.
- Returns:
0(bypass) or1(native) on success, negativeVITURE_GLASSES_ERROR_*code on failure - In bypass mode, use
xr_device_provider_get/set_display_modewithVITURE_DISPLAY_MODE_*. - In native mode, use
xr_device_provider_native_get/set_display_modewithVITURE_NATIVE_DISPLAY_MODE_*.
xr_device_provider_native_set_mode
VITURE_API int xr_device_provider_native_set_mode(XRDeviceProviderHandle handle, int mode);
Set the operating mode for Gen 2 devices (Beast only). Default mode is native.
mode:0for bypass,1for native- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - After calling this function, set a display mode to complete the switch: call
xr_device_provider_native_set_display_modewhen switching to native, orxr_device_provider_set_display_modewhen switching to bypass.
xr_device_provider_native_get_display_mode
VITURE_API int xr_device_provider_native_get_display_mode(XRDeviceProviderHandle handle);
Get the display mode while the device is in native mode (Gen 2 / Beast only).
- Returns: Display mode value (see
VITURE_NATIVE_DISPLAY_MODE_*) on success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_set_display_mode
VITURE_API int xr_device_provider_native_set_display_mode(XRDeviceProviderHandle handle, int display_mode);
Set the display mode while the device is in native mode (Gen 2 / Beast only).
display_mode: SeeVITURE_NATIVE_DISPLAY_MODE_*constants- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_get_side_mode
VITURE_API int xr_device_provider_native_get_side_mode(XRDeviceProviderHandle handle);
Retrieve the current side mode (Gen 2 / Beast only). Side mode shifts the
displayed image to one side of the glasses. Requires the device to be in
native mode (mode == 1).
- Returns:
0(disabled) or1(enabled) on success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device does not support native DOF or is not in native mode.
xr_device_provider_native_set_side_mode
VITURE_API int xr_device_provider_native_set_side_mode(XRDeviceProviderHandle handle, int side_mode);
Enable or disable side mode (Gen 2 / Beast only). Requires the device to be
in native mode (mode == 1).
side_mode:1to enable,0to disable- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device does not support native DOF or is not in native mode.
xr_device_provider_native_switch_dimension
VITURE_API int xr_device_provider_native_switch_dimension(XRDeviceProviderHandle handle, int is_3d);
Switch between 2D and 3D display in native mode (Gen 2 / Beast only).
is_3d:1for 3D (VITURE_NATIVE_DISPLAY_MODE_3D_SBS_3840_1080_60HZ),0for 2D (VITURE_NATIVE_DISPLAY_MODE_1920_1080_60HZ)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_get_dof
VITURE_API int xr_device_provider_native_get_dof(XRDeviceProviderHandle handle);
Get the active DOF tracking type (Gen 2 / Beast only, native mode required).
- Returns: DOF type (see
VITURE_NATIVE_DOF_*) on success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_set_dof
VITURE_API int xr_device_provider_native_set_dof(XRDeviceProviderHandle handle, int dof);
Set the DOF tracking type (Gen 2 / Beast only, native mode required).
dof: SeeVITURE_NATIVE_DOF_*constants (VITURE_NATIVE_DOF_0,VITURE_NATIVE_DOF_3,VITURE_NATIVE_DOF_SMOOTH_FOLLOW)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_recenter_dof
VITURE_API int xr_device_provider_native_recenter_dof(XRDeviceProviderHandle handle);
Recenter the display for native DOF tracking (Gen 2 / Beast only, native mode required).
- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_get_display_distance
VITURE_API int xr_device_provider_native_get_display_distance(XRDeviceProviderHandle handle);
Get display distance (Gen 2 / Beast only, native mode required).
- Returns: Distance value [1, 10] on success, negative
VITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_set_display_distance
VITURE_API int xr_device_provider_native_set_display_distance(XRDeviceProviderHandle handle, int distance);
Set display distance (Gen 2 / Beast only, native mode required).
distance: Range [1, 10]- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_get_display_size
VITURE_API int xr_device_provider_native_get_display_size(XRDeviceProviderHandle handle);
Get display size (Gen 2 / Beast only, native mode required).
- Returns: Display size value [0, 4] (see
VITURE_DISPLAY_SIZE_*) on success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_native_set_display_size
VITURE_API int xr_device_provider_native_set_display_size(XRDeviceProviderHandle handle, int size);
Set display size (Gen 2 / Beast only, native mode required).
size: SeeVITURE_DISPLAY_SIZE_*constants- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure - Returns
VITURE_GLASSES_ERROR_NOT_SUPPORTEDif the device is in bypass mode.
xr_device_provider_switch_dimension
VITURE_API int xr_device_provider_switch_dimension(XRDeviceProviderHandle handle, int is_3d);
Convenience toggle between 2D (1920x1080@60Hz) and 3D (3840x1080@60Hz). Gen1 and Gen2 bypass mode only.
is_3d: 1 for 3D, 0 for 2D- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_brightness_level
VITURE_API int xr_device_provider_get_brightness_level(XRDeviceProviderHandle handle);
Get screen brightness level.
- Returns: Brightness level on success, negative
VITURE_GLASSES_ERROR_*code on failure
xr_device_provider_set_brightness_level
VITURE_API int xr_device_provider_set_brightness_level(XRDeviceProviderHandle handle, int level);
Set screen brightness level.
level: Brightness level to set- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_volume_level
VITURE_API int xr_device_provider_get_volume_level(XRDeviceProviderHandle handle);
Get speaker volume level.
- Returns: Volume level on success, negative
VITURE_GLASSES_ERROR_*code on failure
xr_device_provider_set_volume_level
VITURE_API int xr_device_provider_set_volume_level(XRDeviceProviderHandle handle, int level);
Set speaker volume level.
level: Volume level to set- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_glasses_version
VITURE_API int xr_device_provider_get_glasses_version(XRDeviceProviderHandle handle, char* response, int* length);
Get glasses firmware version.
response: Buffer to store responselength: Pointer to store response length- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_get_sn_hash
VITURE_API int xr_device_provider_get_sn_hash(XRDeviceProviderHandle handle, uint8_t* hash_out);
Get the SHA-256 hash of the board serial number. The raw serial number is never exposed; the caller receives a 32-byte digest that uniquely identifies the device and can be used for device binding or license validation.
hash_out: Caller-allocated buffer of at least 32 bytes to receive the digest- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
Callback Types
VitureImuRawCallback
typedef void (*VitureImuRawCallback)(float* data, uint64_t timestamp, uint64_t vsync);
Callback for IMU raw data.
data: Pointer to raw data buffer (device-defined layout)timestamp: Timestamp in device timebase for IMU samplevsync: VSync timestamp associated with the sample
Data format varies by device:
- Viture One/Pro/Lite: [gyroscoperawx, y, z, accelerometerrawx, y, z, 0, 0, 0, temperature]
- Viture Luma/Luma Pro/Beast/Pro 2: [gyroscoperawx, y, z, accelerometerrawx, y, z, magnetometerrawx, y, z, temperature]
VitureImuPoseCallback
typedef void (*VitureImuPoseCallback)(float* data, uint64_t timestamp);
Callback for IMU pose data.
data: Pointer to pose data buffer: [roll, pitch, yaw, quaternionw, quaternionx, quaterniony, quaternionz]timestamp: Timestamp in device timebase for IMU sample Notes:- Coordinate system: North-West-Up (NWU), X->North, Y->West, Z->Up.
Functions
xr_device_provider_register_imu_raw_callback
VITURE_API int xr_device_provider_register_imu_raw_callback(XRDeviceProviderHandle handle, VitureImuRawCallback imu_raw_callback);
Register IMU raw data callback.
- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_register_imu_pose_callback
VITURE_API int xr_device_provider_register_imu_pose_callback(XRDeviceProviderHandle handle, VitureImuPoseCallback imu_pose_callback);
Register IMU pose data callback.
- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_open_imu
VITURE_API int xr_device_provider_open_imu(XRDeviceProviderHandle handle, uint8_t imu_mode, uint8_t imu_report_frequency);
Enables IMU data delivery. Register the appropriate callback before calling this. Only one mode can be active at a time.
imu_mode: IMU mode (see VITUREIMUMODE_* constants)imu_report_frequency: Report frequency (see VITUREIMUFREQUENCY_* constants). Not all products support all frequencies; check withxr_device_provider_is_product_support_imu_frequency.- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
xr_device_provider_close_imu
VITURE_API int xr_device_provider_close_imu(XRDeviceProviderHandle handle, uint8_t imu_mode);
Disables IMU data delivery. Pass the same mode used in open_imu. Call before stop().
imu_mode: IMU mode (see VITUREIMUMODE_* constants)- Returns:
VITURE_GLASSES_SUCCESSon success, negativeVITURE_GLASSES_ERROR_*code on failure
Callback Types
XRPoseCallback
typedef void (*XRPoseCallback)(float* pose, double timestamp);
Callback invoked when a new pose sample is available, This function returns pose data at the Camera's frequency (25 Hz). In most cases, this interface can be ignored.
pose: Pose data array:[px, py, pz, qw, qx, qy, qz]in OpenGL coordinate system (Y-up)timestamp: Monotonic timestamp in seconds
XRVSyncCallback
typedef void (*XRVSyncCallback)(double timestamp);
VSync notification callback.
timestamp: Monotonic timestamp in seconds when VSync occurred
XRImuCallback
typedef void (*XRImuCallback)(float* imu, double timestamp);
IMU data callback for Carina device.
imu: IMU data: [ax, ay, az, gx, gy, gz]timestamp: Timestamp in seconds for the IMU sample
XRCameraCallback
typedef void (*XRCameraCallback)(char* image_left0, char* image_right0,
char* image_left1, char* image_right1,
double timestamp, int width, int height);
Camera frame callback for stereo frames.
image_left0,image_right0: Pointers to left and right image buffers (frame 0)image_left1,image_right1: Pointers to left and right image buffers (frame 1)timestamp: Frame timestampwidth,height: Frame dimensions in pixels
Functions
xr_device_provider_register_callbacks_carina
VITURE_API int xr_device_provider_register_callbacks_carina(XRDeviceProviderHandle handle,
XRPoseCallback pose_callback,
XRVSyncCallback vsync_callback,
XRImuCallback imu_callback,
XRCameraCallback camera_callback);
Register callbacks for a Carina device instance. Callbacks are ignored for non-Carina devices.
Pass NULL for any callback you do not need.
- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_set_dof_type_carina
VITURE_API int xr_device_provider_set_dof_type_carina(XRDeviceProviderHandle handle, int is_6dof);
Set DOF type for Carina device. Must be called after xr_device_provider_create and before xr_device_provider_initialize. Default is 6DOF.
is_6dof: 1 for 6DOF, 0 for 3DOF- Returns:
VITURE_GLASSES_SUCCESSon success, error code on failure
xr_device_provider_reset_pose_carina
VITURE_API int xr_device_provider_reset_pose_carina(XRDeviceProviderHandle handle);
Trigger a full VIO re-initialization. Briefly interrupts tracking.
Use only when tracking is lost. Prefer xr_device_provider_reset_origin_carina for normal recentering.
- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_reset_origin_carina
VITURE_API int xr_device_provider_reset_origin_carina(XRDeviceProviderHandle handle, float *pose);
Lightweight recenter. Sets a new tracking origin in OpenGL coordinate system (x -> right, y -> up, z -> backward).
Position and yaw are reset to the values in pose. Pitch and roll remain gravity-anchored and are unaffected.
Typical uses:
Pass the result of
xr_device_provider_get_gl_pose_carinato anchor the origin at the current physical pose.Pass a manually constructed pose to relocate the tracking origin to any desired position and heading.
pose: Target origin pose:[px, py, pz, qw, qx, qy, qz]Returns:
VITURE_GLASSES_SUCCESSon success, error code on failure
xr_device_provider_get_gl_pose_carina
VITURE_API int xr_device_provider_get_gl_pose_carina(XRDeviceProviderHandle handle, float *pose, double predict_time, int *pose_status);
Retrieves the current 6DoF pose in OpenGL coordinate system (Y-up, right-handed). Must be polled on a dedicated background thread; do not call on the main thread.
pose: Output array of 7 floats:[px, py, pz, qw, qx, qy, qz]predict_time: Forward prediction in seconds. Pass0.0for current pose. Use a positive value to compensate for render and display pipeline latency.pose_status: Output:0= stable,1= unstable (may occur briefly after device start). May beNULL.- Returns:
VITURE_GLASSES_SUCCESSon success,VITURE_GLASSES_ERROR_INVALID_PARAMon failure
xr_device_provider_set_auto_exposure_carina
VITURE_API int xr_device_provider_set_auto_exposure_carina(XRDeviceProviderHandle handle);
Enables automatic exposure for the stereo tracking cameras. This is the default mode.
- Returns:
VITURE_GLASSES_SUCCESSon success, error code on failure
xr_device_provider_set_manual_exposure_carina
VITURE_API int xr_device_provider_set_manual_exposure_carina(XRDeviceProviderHandle handle, float exposure_time_ms, int exposure_gain);
Disables automatic exposure and applies a fixed exposure time and gain. Out-of-range values are clamped to the accepted ranges.
exposure_time_ms: Exposure time in milliseconds, range[0.01, 8.0]exposure_gain: Sensor gain, range[0, 15]- Returns:
VITURE_GLASSES_SUCCESSon success, error code on failure
Overview
viture_camera_provider.h exposes the xr_camera_provider_* API for devices that include
a front-facing pass-through camera (Luma Cyber, Luma Pro, Luma Ultra, Beast).
The camera is a separate USB UVC device from the glasses HID interface and has its own
VID/PID. It must be opened and managed independently from XRDeviceProvider.
Stream configuration is fixed: 1920×1080 @ 30 fps, MJPEG. MJPEG decoding is the caller's responsibility (e.g. libjpeg-turbo, MediaCodec on Android).
Types
XRCameraProviderHandle
typedef void* XRCameraProviderHandle;
Opaque pointer type for XRCameraProvider instances. Each handle represents one physical camera device. Instances are independent and must not be shared across threads without external synchronization.
XRCameraFormat
typedef enum {
XR_CAMERA_FORMAT_MJPEG = 0
} XRCameraFormat;
Camera frame pixel format. The current implementation always delivers
XR_CAMERA_FORMAT_MJPEG.
XRCameraFrame
typedef struct {
uint8_t* data; // Frame data pointer (valid only during callback)
uint32_t size; // Frame data size in bytes
uint32_t width; // Frame width in pixels
uint32_t height; // Frame height in pixels
XRCameraFormat format; // Frame format
uint64_t timestamp; // Frame capture timestamp in nanoseconds
uint32_t sequence; // Monotonically increasing frame counter
} XRCameraFrame;
Delivered to the application on every captured frame. The data pointer is only valid
for the duration of the callback; copy the data if it must outlive the callback.
XRCameraFrameCallback
typedef void (*XRCameraFrameCallback)(const XRCameraFrame* frame, void* user_data);
Frame delivery callback. Invoked on a dedicated camera thread at ~30 Hz.
frame: Pointer to frame data (valid only during the callback)user_data: Application context passed toxr_camera_provider_start
Device Support
Glasses model Camera VID:PID
------------------- ----------------
Luma Cyber 0x0C45 : 0x636B
Luma Pro 0x0C45 : 0x636B
Luma Ultra 0x0C45 : 0x636B
Beast 0x0C45 : 0x6368
Luma (no camera)
One / One Lite / Pro (no camera)
Functions
xr_camera_provider_get_camera_vid
VITURE_API int xr_camera_provider_get_camera_vid(int glasses_product_id);
Get the USB Vendor ID of the camera device paired with the given glasses product ID.
glasses_product_id: Product ID of the glasses (obtained fromxr_device_provider_*)- Returns: Camera VID if the device has a camera,
0otherwise
xr_camera_provider_get_camera_pid
VITURE_API int xr_camera_provider_get_camera_pid(int glasses_product_id);
Get the USB Product ID of the camera device paired with the given glasses product ID.
glasses_product_id: Product ID of the glasses- Returns: Camera PID if the device has a camera,
0otherwise
xr_camera_provider_is_valid_camera
VITURE_API int xr_camera_provider_is_valid_camera(int vendor_id, int product_id);
Check whether the given USB VID/PID identifies a supported Viture camera.
Use this to filter enumerated USB devices before opening them — it avoids treating
unrelated UVC devices (e.g. webcams) as a Viture camera. On Android this is typically
called when handling ACTION_USB_DEVICE_ATTACHED before requesting permission; on other
platforms it can be used after enumerating UVC devices via libuvc or the OS USB stack.
vendor_id: USB Vendor ID of the device to checkproduct_id: USB Product ID of the device to check- Returns:
1if the VID/PID matches a known Viture camera,0otherwise
xr_camera_provider_create
// Android variant
VITURE_API XRCameraProviderHandle xr_camera_provider_create(int camera_vid,
int camera_pid,
int file_descriptor);
// Non-Android variant
VITURE_API XRCameraProviderHandle xr_camera_provider_create(int camera_vid,
int camera_pid);
Create an XRCameraProvider instance.
- On Android, pass the file descriptor of the already-opened USB camera device
(acquired after requesting
android.permission.USB_PERMISSION). - On other platforms, the library locates the device by VID/PID automatically.
- Returns: Handle to the created instance, or
NULLon failure (unsupported VID/PID or allocation error)
xr_camera_provider_start
VITURE_API int xr_camera_provider_start(XRCameraProviderHandle handle,
XRCameraFrameCallback callback,
void* user_data);
Open the UVC device and begin streaming.
callback: Must not beNULLuser_data: Passed verbatim to each callback invocation; may beNULL- Returns:
VITURE_GLASSES_SUCCESSon success, or:VITURE_GLASSES_ERROR_INVALID_PARAM— null handle or null callbackVITURE_GLASSES_ERROR_USB_UNAVAILABLE— camera device not found or failed to openVITURE_GLASSES_ERROR_NOT_SUPPORTED— failed to negotiate 1920×1080@30fps MJPEGVITURE_GLASSES_ERROR_USB_EXEC— failed to start the UVC streamVITURE_GLASSES_ERROR_INVALID_STATE— already streaming
xr_camera_provider_stop
VITURE_API int xr_camera_provider_stop(XRCameraProviderHandle handle);
Stop the camera stream. No more frames are delivered after this returns.
- Returns:
VITURE_GLASSES_SUCCESSon success, or:VITURE_GLASSES_ERROR_INVALID_PARAM— null handleVITURE_GLASSES_ERROR_INVALID_STATE— not currently streaming
xr_camera_provider_destroy
VITURE_API void xr_camera_provider_destroy(XRCameraProviderHandle handle);
Stop streaming (if active) and release all resources. The handle is invalid after this call.
xr_camera_provider_is_streaming
VITURE_API int xr_camera_provider_is_streaming(XRCameraProviderHandle handle);
- Returns:
1if streaming,0if not streaming or handle isNULL
Usage Example
// 1. Determine the camera VID/PID from the connected glasses product ID
int glasses_pid = /* product ID from xr_device_provider */;
int cam_vid = xr_camera_provider_get_camera_vid(glasses_pid);
int cam_pid = xr_camera_provider_get_camera_pid(glasses_pid);
if (cam_vid == 0 || cam_pid == 0) {
// Device has no pass-through camera
return;
}
// 2. Create the provider (non-Android shown; Android requires file_descriptor)
XRCameraProviderHandle cam = xr_camera_provider_create(cam_vid, cam_pid);
if (!cam) {
// creation failed
return;
}
// 3. Start streaming
int result = xr_camera_provider_start(cam,
[](const XRCameraFrame* frame, void* /*ctx*/) {
// frame->data points to MJPEG-compressed data, frame->size bytes
// Copy or decode here; data is invalid after this function returns
},
nullptr);
if (result != VITURE_GLASSES_SUCCESS) {
xr_camera_provider_destroy(cam);
return;
}
// ... use camera ...
// 4. Cleanup
xr_camera_provider_stop(cam);
xr_camera_provider_destroy(cam);
Overview
viture_stat_reporter.h exposes the xr_stat_reporter_* API for sending optional
SDK usage events to Viture's gain-sharing endpoint. Reporting is opt-in: nothing
happens unless your application calls xr_stat_reporter_init with valid AK/SK
credentials issued by Viture.
The reporter is keyed on the XRDeviceProviderHandle you already use elsewhere
in the SDK. Each handle has its own credential record. Calls are synchronous
HTTPS requests against cloud.viture.dev and must be invoked from a worker
thread because they may block for up to 15 s on network I/O.
For an integration walkthrough, see "SDK Usage Reporting".
Network Endpoint
Host cloud.viture.dev
Path POST /api/v1/glassesbind
Port 443 (TLS)
Auth HMAC-SHA1 of canonical request, signed locally with the SK
The SK is never transmitted over the network. Each request carries:
Date RFC 7231 GMT timestamp
Authorization "VITURE <ak> <hex_hmac_sha1>"
User-Agent "libglasses/<sdk_version> os/<os> arch/<arch>"
The body is a small JSON document containing the device product ID, vendor ID, package and board serial numbers, market product name, application name, and firmware version. No personally identifying information is collected.
Functions
xr_stat_reporter_init
VITURE_API int xr_stat_reporter_init(XRDeviceProviderHandle handle,
const char* ak,
const char* sk,
const char* app_name);
Store the developer's credentials in memory against handle.
Must be called after xr_device_provider_initialize and before any
xr_stat_reporter_* event-report call. Credentials are used locally to sign
each request and are never persisted.
handle: Handle returned byxr_device_provider_createak: Access Key assigned by Viture (null-terminated, non-empty)sk: Secret Key assigned by Viture (null-terminated, non-empty)app_name: Developer's application identifier (null-terminated, non-empty); appears in the JSON body- Returns:
VITURE_GLASSES_SUCCESSon success, or:VITURE_GLASSES_ERROR_INVALID_PARAM— null handle or empty credentialVITURE_GLASSES_ERROR_UNKNOWN— unexpected internal failure
Re-calling init for the same handle replaces the previous credentials.
xr_stat_reporter_glasses_bind
VITURE_API int xr_stat_reporter_glasses_bind(XRDeviceProviderHandle handle,
int* http_status);
Synchronously send a glasses-bind event for handle.
Builds the request body from the device's product ID, serial numbers, and
firmware version, signs it with the credentials previously registered via
xr_stat_reporter_init, opens a TLS connection to Viture's server, and waits
for the response.
The connection timeout is 5 s and the read timeout is 10 s, so the worst-case block is ~15 s. Always call this from a background thread.
handle: Handle returned byxr_device_provider_createhttp_status: Optional out-pointer receiving the HTTP status code (e.g. 200) when the request reached the server, or0when the request never completed (network failure, TLS error, exception). May beNULL.- Returns:
VITURE_GLASSES_SUCCESSif the server returned 2xx, or:VITURE_GLASSES_ERROR_INVALID_PARAM— null handleVITURE_GLASSES_ERROR_INVALID_STATE—initwas not called for this handleVITURE_GLASSES_ERROR_NO_DATA— could not collect bind params (device not yet initialized, or firmware refused to provide a serial number)VITURE_GLASSES_ERROR_UNKNOWN— network/TLS failure or non-2xx HTTP status (use*http_statusto disambiguate)
Treat the result as best-effort. A failed report should never block normal use of the glasses.
Threading
Function Thread
-------------------------------- -------------------------------------------
xr_stat_reporter_init Any (cheap; takes a single mutex)
xr_stat_reporter_glasses_bind Worker thread only — blocks on HTTPS
Multiple handles may be initialized concurrently from different threads — the
internal credential map is mutex-protected. A bind call holds no SDK locks
across the network round-trip.
Usage Example
#include "viture_glasses_provider.h"
#include "viture_stat_reporter.h"
#include <pthread.h>
#include <stdio.h>
// 1. Run the device through its normal lifecycle first.
XRDeviceProviderHandle handle = xr_device_provider_create(product_id);
xr_device_provider_initialize(handle, NULL, NULL);
xr_device_provider_start(handle);
// 2. Opt in to usage reporting (once per process, after start()).
int rc = xr_stat_reporter_init(handle,
"<your-access-key>",
"<your-secret-key>",
"MyApp");
if (rc != VITURE_GLASSES_SUCCESS) {
fprintf(stderr, "stat reporter init failed: %d\n", rc);
}
// 3. Fire the bind event on a worker thread.
static void* report_bind(void* arg) {
XRDeviceProviderHandle h = (XRDeviceProviderHandle)arg;
int http_status = 0;
int r = xr_stat_reporter_glasses_bind(h, &http_status);
printf("Bind report: rc=%d http=%d\n", r, http_status);
return NULL;
}
pthread_t t;
pthread_create(&t, NULL, report_bind, handle);
pthread_detach(t);

