From 037b0c4ec111ac28d71849259ac8271aa80a3d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Monnom?= Date: Thu, 11 Aug 2022 12:20:23 +0200 Subject: [PATCH] Basic Android version --- .gitignore | 8 ++- CMakeLists.txt | 16 ----- Cargo.lock | 33 +++++++++ Cargo.toml | 8 ++- build.rs | 114 ++++++++++++++++++++++++++---- include/peer_connection_factory.h | 2 + src/jni_onload.cc | 37 ++++++++++ src/{main.rs => lib.rs} | 12 ++-- 8 files changed, 189 insertions(+), 41 deletions(-) delete mode 100644 CMakeLists.txt create mode 100644 src/jni_onload.cc rename src/{main.rs => lib.rs} (63%) diff --git a/.gitignore b/.gitignore index 5633367..6515d6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ -/target +target cmake-build-debug -.idea \ No newline at end of file +.idea/ +.cargo/ +test/ +libwebrtc/ +CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a55b3a7..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# IMPORTANT NOTE -# This file is just used because some IDEs need to understand how to do autocompletion. -# This file is completely ignored by the library ( See build.rs for the build system ) - -cmake_minimum_required(VERSION 3.22) -project(client_sdk_native) - -set(CMAKE_CXX_STANDARD 14) - -add_definitions(-DWEBRTC_POSIX -DWEBRTC_MAC) - -include_directories(include/) -include_directories(libwebrtc/include) - -# Fake library -add_library(client_sdk_native src/peer_connection_factory.cpp) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f7bd026..12e9ece 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "cc" version = "1.0.73" @@ -84,8 +93,15 @@ dependencies = [ "cxx", "cxx-build", "glob", + "regex", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "once_cell" version = "1.13.0" @@ -110,6 +126,23 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + [[package]] name = "scratch" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 844b63f..c51d159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "livekit-native" version = "0.1.0" edition = "2021" homepage = "https://livekit.io" +crate-type = ["lib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,4 +12,9 @@ cxx = "1.0" [build-dependencies] cxx-build = "1.0" -glob = "0.3.0" \ No newline at end of file +glob = "0.3.0" +regex = "1.0" + +[lib] +crate-type = ["cdylib"] +bench = false diff --git a/build.rs b/build.rs index cbba419..88fd7ad 100644 --- a/build.rs +++ b/build.rs @@ -1,27 +1,38 @@ +use regex::Regex; +use std::env; +use std::fs; +use std::io::Write; +use std::path; fn main() { - let target_os = "macos"; + let target_os = "android"; //let target_arch = "arm64"; - let libwebrtc_dir = std::path::PathBuf::from("./libwebrtc"); - + let libwebrtc_dir = path::PathBuf::from("libwebrtc"); // Just required for the bridge build to succeed. let includes = &[ - std::path::PathBuf::from("./include"), + path::PathBuf::from("./include"), libwebrtc_dir.join("include/"), libwebrtc_dir.join("include/third_party/abseil-cpp/"), libwebrtc_dir.join("include/third_party/libc++/"), ]; - let mut builder = cxx_build::bridges(&["src/main.rs"]); - builder.flag("-std=c++17"); // Not sure about this .. Shouldn't this be c++14 ? + let mut builder = cxx_build::bridges(&["src/lib.rs"]); + builder.flag("-std=c++17"); builder.file("src/peer_connection_factory.cpp"); + builder.file("src/jni_onload.cc"); for include in includes { builder.include(include); } + println!( + "cargo:rustc-link-search=native={}", + libwebrtc_dir.canonicalize().unwrap().to_str().unwrap() + ); + println!("cargo:rustc-link-lib=static=webrtc"); + match target_os { "macos" => { println!("cargo:rustc-link-lib=dylib=c++"); @@ -33,28 +44,103 @@ fn main() { println!("cargo:rustc-link-lib=framework=CoreMedia"); println!("cargo:rustc-link-lib=framework=CoreGraphics"); - builder.flag("-stdlib=libc++") + builder + .flag("-stdlib=libc++") .define("WEBRTC_ENABLE_OBJC_SYMBOL_EXPORT", None) .define("WEBRTC_POSIX", None) .define("WEBRTC_MAC", None); } + "ios" => { + // TODO(theomonnom) + } + "android" => { + let ndk_env = env::var("ANDROID_NDK_HOME").expect( + "ANDROID_NDK_HOME is not set, please set it to the path of your Android NDK", + ); + let android_ndk = path::PathBuf::from(ndk_env); + + let host_os = if cfg!(target_os = "linux") { + "linux-x86_64" + } else if cfg!(target_os = "macos") { + "darwin-x86_64" + } else if cfg!(target_os = "windows") { + "windows-x86_64" + } else { + panic!("Unsupported host OS"); + }; + + let toolchain = android_ndk.join(std::format!("toolchains/llvm/prebuilt/{}", host_os)); + + // libgcc ( redirects to libunwind ) + println!( + "cargo:rustc-link-search={}", + toolchain + .join("lib") + .to_str() + .unwrap() + ); + + // Needed when loading the library in the JVM ( System.loadLibrary ) + println!("cargo:rustc-link-lib=egl"); + println!("cargo:rustc-link-lib=OpenSLES"); + + // Find JNI symbols + let readelf_output = std::process::Command::new(toolchain.join("bin/llvm-readelf")) + .arg("-Ws") + .arg(libwebrtc_dir.join("libwebrtc.a")) + .output() + .expect("failed to run llvm-readelf"); + + // Get all JNI symbols + let jni_regex = Regex::new(r"(Java_org_webrtc.*)").unwrap(); + let content = &String::from_utf8_lossy(&readelf_output.stdout); + let mut jni_symbols = Vec::new(); + jni_regex.captures_iter(&content).for_each(|cap| { + jni_symbols.push(cap.get(1).unwrap().as_str()); + }); + + // JNI Version Script & Keep JNI symbols + let vs_path = path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("webrtc_jni.map"); + let mut vs_file = fs::File::create(&vs_path).unwrap(); + println!("cargo:rustc-link-arg=-Wl,--undefined=JNI_OnLoad"); + + write!(vs_file, "JNI_WEBRTC {{\n\tglobal: ").unwrap(); + write!(vs_file, "JNI_OnLoad; ").unwrap(); + for x in &jni_symbols { + println!("cargo:rustc-link-arg=-Wl,--undefined={}", x); + write!(vs_file, "{}; ", x).unwrap(); + } + write!(vs_file, "\n}};").unwrap(); + + println!( + "cargo:rustc-link-arg=-Wl,--version-script={}", + vs_path.to_str().unwrap() + ); + + builder + .define("WEBRTC_LINUX", None) + .define("WEBRTC_POSIX", None) + .define("WEBRTC_ANDROID", None); + } _ => { - eprintln!("Unsupported platform, {}", target_os); - std::process::exit(1); + panic!("Unsupported platform, {}", target_os); } } builder.warnings(false).compile("lkwebrtc"); - println!("cargo:rustc-link-search=native={}", libwebrtc_dir.canonicalize().unwrap().to_str().unwrap()); - println!("cargo:rustc-link-lib=static=webrtc"); - for entry in glob::glob("./src/**/*.cpp").unwrap() { - println!("cargo:rerun-if-changed={}", entry.unwrap().display().to_string()); + println!( + "cargo:rerun-if-changed={}", + entry.unwrap().display().to_string() + ); } for entry in glob::glob("./include/**/*.h").unwrap() { - println!("cargo:rerun-if-changed={}", entry.unwrap().display().to_string()); + println!( + "cargo:rerun-if-changed={}", + entry.unwrap().display().to_string() + ); } println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/include/peer_connection_factory.h b/include/peer_connection_factory.h index d55ae39..f904d12 100644 --- a/include/peer_connection_factory.h +++ b/include/peer_connection_factory.h @@ -2,10 +2,12 @@ // Created by Théo Monnom on 03/08/2022. // + #ifndef PEER_CONNECTION_FACTORY_H #define PEER_CONNECTION_FACTORY_H #include "api/peer_connection_interface.h" +#include "sdk/android/src/jni/jni_helpers.h" namespace lk { diff --git a/src/jni_onload.cc b/src/jni_onload.cc new file mode 100644 index 0000000..e562cd8 --- /dev/null +++ b/src/jni_onload.cc @@ -0,0 +1,37 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "rtc_base/logging.h" +#include "rtc_base/ssl_adapter.h" +#include "sdk/android/native_api/jni/class_loader.h" +#include "sdk/android/src/jni/jni_helpers.h" + +namespace webrtc { + namespace jni { + + extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) { + jint ret = InitGlobalJniVariables(jvm); + if (ret < 0) + return -1; + + RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()"; + webrtc::InitClassLoader(GetEnv()); + + return ret; + } + + extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM* jvm, void* reserved) { + RTC_CHECK(rtc::CleanupSSL()) << "Failed to CleanupSSL()"; + } + +} // namespace jni +} // namespace webrtc \ No newline at end of file diff --git a/src/main.rs b/src/lib.rs similarity index 63% rename from src/main.rs rename to src/lib.rs index fa63461..6f963f5 100644 --- a/src/main.rs +++ b/src/lib.rs @@ -1,6 +1,3 @@ -use std::thread::sleep; -use std::time; - #[cxx::bridge(namespace = "lk")] mod ffi { @@ -12,9 +9,8 @@ mod ffi { } } - -fn main() { - println!("Hello, world!"); - - let factory = ffi::CreatePeerConnectionFactory(); +#[no_mangle] +extern "C" fn test_rust() { + println!("Called test_rust"); + let _factory = ffi::CreatePeerConnectionFactory(); }