293 lines
7.9 KiB
Rust
293 lines
7.9 KiB
Rust
#![allow(non_upper_case_globals, non_snake_case, clippy::missing_safety_doc)]
|
|
|
|
mod connection;
|
|
mod connection_utils;
|
|
mod logging_backend;
|
|
|
|
#[cfg(target_os = "android")]
|
|
mod audio;
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
use alvr_common::{prelude::*, ALVR_VERSION};
|
|
use alvr_sockets::{HeadsetInfoPacket, PrivateIdentity};
|
|
use jni::{
|
|
objects::{JClass, JObject, JString},
|
|
JNIEnv,
|
|
};
|
|
use lazy_static::lazy_static;
|
|
use parking_lot::Mutex;
|
|
use std::{
|
|
ptr, slice,
|
|
sync::{
|
|
atomic::{AtomicBool, Ordering},
|
|
Arc,
|
|
},
|
|
};
|
|
use tokio::{runtime::Runtime, sync::mpsc, sync::Notify};
|
|
|
|
lazy_static! {
|
|
static ref MAYBE_RUNTIME: Mutex<Option<Runtime>> = Mutex::new(None);
|
|
static ref IDR_REQUEST_NOTIFIER: Notify = Notify::new();
|
|
static ref IDR_PARSED: AtomicBool = AtomicBool::new(false);
|
|
static ref MAYBE_LEGACY_SENDER: Mutex<Option<mpsc::UnboundedSender<Vec<u8>>>> =
|
|
Mutex::new(None);
|
|
static ref ON_PAUSE_NOTIFIER: Notify = Notify::new();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_polygraphene_alvr_OvrActivity_initNativeLogging(
|
|
_: JNIEnv,
|
|
_: JClass,
|
|
) {
|
|
logging_backend::init_logging();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_polygraphene_alvr_OvrActivity_createIdentity(
|
|
env: JNIEnv,
|
|
_: JClass,
|
|
jidentity: JObject,
|
|
) {
|
|
alvr_common::show_err(|| -> StrResult {
|
|
let identity = alvr_sockets::create_identity(None)?;
|
|
|
|
let jhostname = trace_err!(env.new_string(identity.hostname))?.into();
|
|
trace_err!(env.set_field(jidentity, "hostname", "Ljava/lang/String;", jhostname))?;
|
|
|
|
let jcert_pem = trace_err!(env.new_string(identity.certificate_pem))?.into();
|
|
trace_err!(env.set_field(jidentity, "certificatePEM", "Ljava/lang/String;", jcert_pem))?;
|
|
|
|
let jkey_pem = trace_err!(env.new_string(identity.key_pem))?.into();
|
|
trace_err!(env.set_field(jidentity, "privateKey", "Ljava/lang/String;", jkey_pem))
|
|
}());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_DecoderThread_DecoderInput(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
frame_index: i64,
|
|
) {
|
|
decoderInput(frame_index);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_DecoderThread_DecoderOutput(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
frame_index: i64,
|
|
) {
|
|
decoderOutput(frame_index);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_DecoderThread_setWaitingNextIDR(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
waiting: bool,
|
|
) {
|
|
IDR_PARSED.store(waiting, Ordering::Relaxed);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onCreateNative(
|
|
env: JNIEnv,
|
|
activity: JObject,
|
|
asset_manager: JObject,
|
|
jout_result: JObject,
|
|
) {
|
|
extern "C" fn legacy_send(buffer_ptr: *const u8, len: u32) {
|
|
if let Some(sender) = &*MAYBE_LEGACY_SENDER.lock() {
|
|
let mut vec_buffer = vec![0; len as _];
|
|
|
|
// use copy_nonoverlapping (aka memcpy) to avoid freeing memory allocated by C++
|
|
unsafe {
|
|
ptr::copy_nonoverlapping(buffer_ptr, vec_buffer.as_mut_ptr(), len as _);
|
|
}
|
|
|
|
sender.send(vec_buffer).ok();
|
|
}
|
|
}
|
|
|
|
legacySend = Some(legacy_send);
|
|
|
|
alvr_common::show_err(|| -> StrResult {
|
|
let result = onCreate(
|
|
env.get_native_interface() as _,
|
|
*activity as _,
|
|
*asset_manager as _,
|
|
);
|
|
|
|
trace_err!(env.set_field(
|
|
jout_result,
|
|
"streamSurfaceHandle",
|
|
"I",
|
|
result.streamSurfaceHandle.into()
|
|
))?;
|
|
trace_err!(env.set_field(
|
|
jout_result,
|
|
"loadingSurfaceHandle",
|
|
"I",
|
|
result.loadingSurfaceHandle.into()
|
|
))?;
|
|
|
|
Ok(())
|
|
}());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_destroyNative(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
) {
|
|
destroyNative(env.get_native_interface() as _)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_renderNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
rendered_frame_index: i64,
|
|
) {
|
|
renderNative(rendered_frame_index)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_renderLoadingNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) {
|
|
renderLoadingNative()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onResumeNative(
|
|
env: JNIEnv,
|
|
jactivity: JObject,
|
|
nal_class: JClass,
|
|
jhostname: JString,
|
|
jcertificate_pem: JString,
|
|
jprivate_key: JString,
|
|
jscreen_surface: JObject,
|
|
dark_mode: u8,
|
|
) {
|
|
alvr_common::show_err(|| -> StrResult {
|
|
let java_vm = trace_err!(env.get_java_vm())?;
|
|
let activity_ref = trace_err!(env.new_global_ref(jactivity))?;
|
|
let nal_class_ref = trace_err!(env.new_global_ref(nal_class))?;
|
|
|
|
let result = onResumeNative(*jscreen_surface as _, dark_mode == 1);
|
|
|
|
let device_name = if result.deviceType == DeviceType_OCULUS_GO {
|
|
"Oculus Go"
|
|
} else if result.deviceType == DeviceType_OCULUS_QUEST {
|
|
"Oculus Quest"
|
|
} else if result.deviceType == DeviceType_OCULUS_QUEST_2 {
|
|
"Oculus Quest 2"
|
|
} else {
|
|
"Unknown device"
|
|
};
|
|
|
|
let available_refresh_rates =
|
|
slice::from_raw_parts(result.refreshRates, result.refreshRatesCount as _).to_vec();
|
|
let preferred_refresh_rate = available_refresh_rates.last().cloned().unwrap_or(60_f32);
|
|
|
|
let headset_info = HeadsetInfoPacket {
|
|
recommended_eye_width: result.recommendedEyeWidth as _,
|
|
recommended_eye_height: result.recommendedEyeHeight as _,
|
|
available_refresh_rates,
|
|
preferred_refresh_rate,
|
|
reserved: format!("{}", *ALVR_VERSION),
|
|
};
|
|
|
|
let private_identity = PrivateIdentity {
|
|
hostname: trace_err!(env.get_string(jhostname))?.into(),
|
|
certificate_pem: trace_err!(env.get_string(jcertificate_pem))?.into(),
|
|
key_pem: trace_err!(env.get_string(jprivate_key))?.into(),
|
|
};
|
|
|
|
let runtime = trace_err!(Runtime::new())?;
|
|
|
|
runtime.spawn(async move {
|
|
let connection_loop = connection::connection_lifecycle_loop(
|
|
headset_info,
|
|
device_name,
|
|
private_identity,
|
|
Arc::new(java_vm),
|
|
Arc::new(activity_ref),
|
|
Arc::new(nal_class_ref),
|
|
);
|
|
|
|
tokio::select! {
|
|
_ = connection_loop => (),
|
|
_ = ON_PAUSE_NOTIFIER.notified() => ()
|
|
};
|
|
});
|
|
|
|
*MAYBE_RUNTIME.lock() = Some(runtime);
|
|
|
|
Ok(())
|
|
}());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onStreamStartNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) {
|
|
onStreamStartNative()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onPauseNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) {
|
|
ON_PAUSE_NOTIFIER.notify_waiters();
|
|
|
|
// shutdown and wait for tasks to finish
|
|
drop(MAYBE_RUNTIME.lock().take());
|
|
|
|
onPauseNative();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onHapticsFeedbackNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
start_time: i64,
|
|
amplitude: f32,
|
|
duration: f32,
|
|
frequency: f32,
|
|
hand: u8,
|
|
) {
|
|
onHapticsFeedbackNative(start_time, amplitude, duration, frequency, hand)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_onBatteryChangedNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
battery: i32,
|
|
plugged: i32,
|
|
) {
|
|
onBatteryChangedNative(battery, plugged)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_isConnectedNative(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) -> u8 {
|
|
isConnectedNative()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "system" fn Java_com_polygraphene_alvr_OvrActivity_requestIDR(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) {
|
|
IDR_REQUEST_NOTIFIER.notify_waiters();
|
|
}
|