Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
504 lines
20 KiB
Rust
504 lines
20 KiB
Rust
// Copyright 2025 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::{env, path, process::Command};
|
|
|
|
fn main() {
|
|
if env::var("DOCS_RS").is_ok() {
|
|
return;
|
|
}
|
|
|
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
let is_desktop = target_os == "linux" || target_os == "windows" || target_os == "macos";
|
|
|
|
println!("cargo:rerun-if-env-changed=LK_DEBUG_WEBRTC");
|
|
println!("cargo:rerun-if-env-changed=LK_CUSTOM_WEBRTC");
|
|
|
|
let mut rust_files = vec![
|
|
"src/peer_connection.rs",
|
|
"src/peer_connection_factory.rs",
|
|
"src/audio_device_controller.rs",
|
|
"src/media_stream.rs",
|
|
"src/media_stream_track.rs",
|
|
"src/audio_track.rs",
|
|
"src/video_track.rs",
|
|
"src/data_channel.rs",
|
|
"src/frame_cryptor.rs",
|
|
"src/jsep.rs",
|
|
"src/candidate.rs",
|
|
"src/rtp_parameters.rs",
|
|
"src/rtp_sender.rs",
|
|
"src/rtp_receiver.rs",
|
|
"src/rtp_transceiver.rs",
|
|
"src/rtc_error.rs",
|
|
"src/webrtc.rs",
|
|
"src/video_frame.rs",
|
|
"src/video_frame_buffer.rs",
|
|
"src/helper.rs",
|
|
"src/yuv_helper.rs",
|
|
"src/audio_resampler.rs",
|
|
"src/android.rs",
|
|
"src/prohibit_libsrtp_initialization.rs",
|
|
"src/recorded_audio_tap.rs",
|
|
"src/apm.rs",
|
|
"src/audio_mixer.rs",
|
|
"src/packet_trailer.rs",
|
|
];
|
|
|
|
if is_desktop {
|
|
rust_files.push("src/desktop_capturer.rs");
|
|
}
|
|
|
|
let mut builder = cxx_build::bridges(rust_files);
|
|
|
|
builder.files(&[
|
|
"src/peer_connection.cpp",
|
|
"src/peer_connection_factory.cpp",
|
|
"src/audio_device_controller.cpp",
|
|
"src/media_stream.cpp",
|
|
"src/media_stream_track.cpp",
|
|
"src/audio_track.cpp",
|
|
"src/video_track.cpp",
|
|
"src/data_channel.cpp",
|
|
"src/jsep.cpp",
|
|
"src/candidate.cpp",
|
|
"src/rtp_receiver.cpp",
|
|
"src/rtp_sender.cpp",
|
|
"src/rtp_transceiver.cpp",
|
|
"src/rtp_parameters.cpp",
|
|
"src/rtc_error.cpp",
|
|
"src/webrtc.cpp",
|
|
"src/video_frame.cpp",
|
|
"src/video_frame_buffer.cpp",
|
|
"src/video_encoder_factory.cpp",
|
|
"src/video_decoder_factory.cpp",
|
|
"src/synthetic_audio_device.cpp",
|
|
"src/adm_proxy.cpp",
|
|
"src/audio_resampler.cpp",
|
|
"src/frame_cryptor.cpp",
|
|
"src/global_task_queue.cpp",
|
|
"src/prohibit_libsrtp_initialization.cpp",
|
|
"src/recorded_audio_tap.cpp",
|
|
"src/apm.cpp",
|
|
"src/audio_mixer.cpp",
|
|
"src/packet_trailer.cpp",
|
|
]);
|
|
|
|
if is_desktop {
|
|
builder.file("src/desktop_capturer.cpp");
|
|
}
|
|
|
|
let webrtc_dir = webrtc_sys_build::webrtc_dir();
|
|
let webrtc_include = webrtc_dir.join("include");
|
|
let webrtc_lib = webrtc_dir.join("lib");
|
|
|
|
if !webrtc_dir.exists() {
|
|
webrtc_sys_build::download_webrtc().unwrap();
|
|
}
|
|
|
|
builder.includes(&[
|
|
path::PathBuf::from("./include"),
|
|
path::PathBuf::from("src/nvidia/NvCodec/include"),
|
|
webrtc_include.clone(),
|
|
webrtc_include.join("third_party/abseil-cpp/"),
|
|
webrtc_include.join("third_party/libyuv/include/"),
|
|
webrtc_include.join("third_party/libc++/"),
|
|
// For mac & ios
|
|
webrtc_include.join("sdk/objc"),
|
|
webrtc_include.join("sdk/objc/base"),
|
|
]);
|
|
builder.define("WEBRTC_APM_DEBUG_DUMP", "0");
|
|
|
|
println!("cargo:rustc-link-search=native={}", webrtc_lib.to_str().unwrap());
|
|
|
|
for (key, value) in webrtc_sys_build::webrtc_defines() {
|
|
let value = value.as_deref();
|
|
builder.define(key.as_str(), value);
|
|
}
|
|
|
|
// Link webrtc library
|
|
println!("cargo:rustc-link-lib=static=webrtc");
|
|
match target_os.as_str() {
|
|
"windows" => {
|
|
println!("cargo:rustc-link-lib=dylib=msdmo");
|
|
println!("cargo:rustc-link-lib=dylib=wmcodecdspuuid");
|
|
println!("cargo:rustc-link-lib=dylib=dmoguids");
|
|
println!("cargo:rustc-link-lib=dylib=crypt32");
|
|
println!("cargo:rustc-link-lib=dylib=iphlpapi");
|
|
println!("cargo:rustc-link-lib=dylib=ole32");
|
|
println!("cargo:rustc-link-lib=dylib=secur32");
|
|
println!("cargo:rustc-link-lib=dylib=winmm");
|
|
println!("cargo:rustc-link-lib=dylib=ws2_32");
|
|
println!("cargo:rustc-link-lib=dylib=strmiids");
|
|
println!("cargo:rustc-link-lib=dylib=d3d11");
|
|
println!("cargo:rustc-link-lib=dylib=gdi32");
|
|
println!("cargo:rustc-link-lib=dylib=dxgi");
|
|
println!("cargo:rustc-link-lib=dylib=dwmapi");
|
|
println!("cargo:rustc-link-lib=dylib=shcore");
|
|
|
|
//let path = env::current_dir().unwrap();
|
|
//println!("cargo:rustc-link-search=native={}/vaapi-windows/x64/lib", path.display());
|
|
//println!("cargo:rustc-link-lib=dylib=va");
|
|
//println!("cargo:rustc-link-lib=dylib=va_win32");
|
|
|
|
builder
|
|
//.include("./vaapi-windows/DirectX-Headers-1.0/include")
|
|
//.include(path::PathBuf::from("./vaapi-windows/x64/include"))
|
|
//.file("vaapi-windows/DirectX-Headers-1.0/src/dxguids.cpp")
|
|
//.file("src/vaapi/vaapi_display_win32.cpp")
|
|
//.file("src/vaapi/vaapi_h264_encoder_wrapper.cpp")
|
|
//.file("src/vaapi/vaapi_encoder_factory.cpp")
|
|
//.file("src/vaapi/h264_encoder_impl.cpp")
|
|
.include("src/nvidia/NvCodec/include")
|
|
.flag("-Isrc/nvidia/NvCodec/include")
|
|
.flag("-Isrc/nvidia/NvCodec/NvCodec")
|
|
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
|
|
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
|
|
.file("src/nvidia/h264_encoder_impl.cpp")
|
|
.file("src/nvidia/h265_encoder_impl.cpp")
|
|
.file("src/nvidia/nvidia_encoder_factory.cpp")
|
|
.file("src/nvidia/native_gpu_encode_bridge.cpp")
|
|
.file("src/nvidia/cuda_context.cpp")
|
|
.file("src/nvidia/cuda_driver_dynamic.cpp")
|
|
.flag("-DUSE_NVIDIA_VIDEO_ENCODER=1")
|
|
.flag("/std:c++20")
|
|
//.flag("/wd4819")
|
|
//.flag("/wd4068")
|
|
.flag("/EHsc");
|
|
}
|
|
"linux" => {
|
|
println!("cargo:rustc-link-lib=dylib=rt");
|
|
println!("cargo:rustc-link-lib=dylib=dl");
|
|
println!("cargo:rustc-link-lib=dylib=pthread");
|
|
println!("cargo:rustc-link-lib=dylib=m");
|
|
|
|
// In order to avoid any ABI mismatches we use the sysroot's headers.
|
|
add_gio_headers(&mut builder);
|
|
|
|
for lib_name in ["glib-2.0", "gobject-2.0", "gio-2.0"] {
|
|
pkg_config::probe_library(lib_name).unwrap();
|
|
}
|
|
|
|
add_lazy_load_so(
|
|
&mut builder,
|
|
"desktop_capturer",
|
|
["drm", "gbm", "X11", "Xfixes", "Xdamage", "Xrandr", "Xcomposite", "Xext"]
|
|
.map(String::from)
|
|
.to_vec(),
|
|
);
|
|
|
|
let x86 = target_arch == "x86_64" || target_arch == "i686";
|
|
let arm = target_arch == "aarch64" || target_arch.contains("arm");
|
|
|
|
if x86 {
|
|
if let Some(libva_include) = pkg_config::get_variable("libva", "includedir").ok() {
|
|
// Do not use pkg_config::probe_library because libva is dlopened
|
|
// and pkg_config::probe_library would link it.
|
|
builder
|
|
.include(libva_include)
|
|
.file("src/vaapi/vaapi_display_drm.cpp")
|
|
.file("src/vaapi/vaapi_h264_encoder_wrapper.cpp")
|
|
.file("src/vaapi/vaapi_encoder_factory.cpp")
|
|
.file("src/vaapi/h264_encoder_impl.cpp")
|
|
.flag("-DUSE_VAAPI_VIDEO_CODEC=1");
|
|
|
|
add_lazy_load_so(
|
|
&mut builder,
|
|
"vaapi",
|
|
["va", "va-drm"].map(String::from).to_vec(),
|
|
);
|
|
} else {
|
|
println!("cargo:warning=libva not found; building without hardware accelerated video codecs");
|
|
}
|
|
}
|
|
|
|
if x86 || arm {
|
|
let cuda_include_dir = PathBuf::from("src/nvidia/NvCodec/include");
|
|
|
|
// libcuda and libnvcuvid are dlopened, so do not link them.
|
|
if cuda_include_dir.join("cuda.h").exists() {
|
|
match probe_egl() {
|
|
Some(egl) => {
|
|
for include_path in egl.include_paths {
|
|
builder.include(include_path);
|
|
}
|
|
builder
|
|
.include(cuda_include_dir)
|
|
.flag("-Isrc/nvidia/NvCodec/include")
|
|
.flag("-Isrc/nvidia/NvCodec/NvCodec")
|
|
.file("src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp")
|
|
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
|
|
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
|
|
.file("src/nvidia/h264_encoder_impl.cpp")
|
|
.file("src/nvidia/h265_encoder_impl.cpp")
|
|
.file("src/nvidia/h264_decoder_impl.cpp")
|
|
.file("src/nvidia/h265_decoder_impl.cpp")
|
|
.file("src/nvidia/nvidia_decoder_factory.cpp")
|
|
.file("src/nvidia/nvidia_encoder_factory.cpp")
|
|
.file("src/nvidia/native_gpu_encode_bridge.cpp")
|
|
.file("src/nvidia/cuda_context.cpp")
|
|
.flag("-Wno-deprecated-declarations")
|
|
.flag("-DUSE_NVIDIA_VIDEO_CODEC=1");
|
|
println!("cargo:rustc-link-lib=dylib=EGL");
|
|
|
|
add_lazy_load_so(
|
|
&mut builder,
|
|
"nvidia",
|
|
["cuda", "nvcuvid"].map(String::from).to_vec(),
|
|
);
|
|
}
|
|
None => {
|
|
println!("cargo:warning=EGL headers not found; building without NVIDIA hardware accelerated video codec support");
|
|
}
|
|
}
|
|
} else {
|
|
println!("cargo:warning=vendored cuda.h not found; building without hardware accelerated video codec support for NVidia GPUs");
|
|
}
|
|
}
|
|
|
|
builder
|
|
.flag("-Wno-changes-meaning")
|
|
.flag("-Wno-deprecated-declarations")
|
|
.flag("-std=c++20");
|
|
}
|
|
"macos" => {
|
|
println!("cargo:rustc-link-lib=framework=Foundation");
|
|
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
|
println!("cargo:rustc-link-lib=framework=CoreAudio");
|
|
println!("cargo:rustc-link-lib=framework=AudioToolbox");
|
|
println!("cargo:rustc-link-lib=framework=Appkit");
|
|
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
|
println!("cargo:rustc-link-lib=framework=CoreGraphics");
|
|
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
|
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
|
println!("cargo:rustc-link-lib=framework=OpenGL");
|
|
println!("cargo:rustc-link-lib=framework=Metal");
|
|
println!("cargo:rustc-link-lib=framework=MetalKit");
|
|
println!("cargo:rustc-link-lib=framework=QuartzCore");
|
|
println!("cargo:rustc-link-lib=framework=IOKit");
|
|
println!("cargo:rustc-link-lib=framework=IOSurface");
|
|
println!("cargo:rustc-link-lib=framework=ScreenCaptureKit");
|
|
|
|
configure_darwin_sysroot(&mut builder);
|
|
|
|
builder
|
|
.file("src/objc_video_factory.mm")
|
|
.file("src/objc_video_frame_buffer.mm")
|
|
.flag("-stdlib=libc++")
|
|
.flag("-std=c++20")
|
|
.flag("-Wno-nullability-completeness");
|
|
}
|
|
"ios" => {
|
|
println!("cargo:rustc-link-lib=framework=Foundation");
|
|
println!("cargo:rustc-link-lib=framework=CoreFoundation");
|
|
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
|
println!("cargo:rustc-link-lib=framework=CoreAudio");
|
|
println!("cargo:rustc-link-lib=framework=UIKit");
|
|
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
|
println!("cargo:rustc-link-lib=framework=CoreGraphics");
|
|
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
|
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
|
println!("cargo:rustc-link-lib=framework=AudioToolbox");
|
|
println!("cargo:rustc-link-lib=framework=OpenGLES");
|
|
println!("cargo:rustc-link-lib=framework=GLKit");
|
|
println!("cargo:rustc-link-lib=framework=Metal");
|
|
println!("cargo:rustc-link-lib=framework=MetalKit");
|
|
println!("cargo:rustc-link-lib=framework=Network");
|
|
println!("cargo:rustc-link-lib=framework=QuartzCore");
|
|
|
|
configure_darwin_sysroot(&mut builder);
|
|
|
|
builder
|
|
.file("src/objc_video_factory.mm")
|
|
.file("src/objc_video_frame_buffer.mm")
|
|
.flag("-std=c++20");
|
|
}
|
|
"android" => {
|
|
webrtc_sys_build::configure_jni_symbols().unwrap();
|
|
|
|
println!("cargo:rustc-link-lib=EGL");
|
|
println!("cargo:rustc-link-lib=OpenSLES");
|
|
println!("cargo:rustc-link-lib=c++_static");
|
|
println!("cargo:rustc-link-lib=c++abi");
|
|
|
|
configure_android_sysroot(&mut builder);
|
|
builder.file("src/android.cpp").flag("-std=c++20");
|
|
}
|
|
_ => {
|
|
panic!("Unsupported target, {}", target_os);
|
|
}
|
|
}
|
|
|
|
// TODO(theomonnom) Only add this define when building tests
|
|
builder.define("LIVEKIT_TEST", None);
|
|
builder.warnings(false).compile("webrtcsys-cxx");
|
|
|
|
for entry in glob::glob("./src/**/*.cpp").unwrap() {
|
|
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
|
}
|
|
|
|
for entry in glob::glob("./src/**/*.mm").unwrap() {
|
|
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
|
}
|
|
|
|
for entry in glob::glob("./include/**/*.h").unwrap() {
|
|
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
|
}
|
|
|
|
for entry in glob::glob("./src/nvidia/**/*.h").unwrap() {
|
|
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
|
}
|
|
|
|
if target_os.as_str() == "android" {
|
|
copy_libwebrtc_jar(&PathBuf::from(Path::new(&webrtc_dir)));
|
|
}
|
|
}
|
|
|
|
fn copy_libwebrtc_jar(webrtc_dir: &PathBuf) {
|
|
let jar_path = webrtc_dir.join("libwebrtc.jar");
|
|
let output_path = get_output_path();
|
|
let output_jar_path = output_path.join("libwebrtc.jar");
|
|
let res = std::fs::copy(jar_path, output_jar_path);
|
|
if let Err(e) = res {
|
|
println!("Failed to copy libwebrtc.jar: {}", e);
|
|
}
|
|
}
|
|
|
|
fn get_output_path() -> PathBuf {
|
|
let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
let build_type = env::var("PROFILE").unwrap();
|
|
let build_target = env::var("TARGET").unwrap();
|
|
let path =
|
|
Path::new(&manifest_dir_string).join("../target").join(build_target).join(build_type);
|
|
return PathBuf::from(path);
|
|
}
|
|
|
|
fn configure_darwin_sysroot(builder: &mut cc::Build) {
|
|
let target_os = webrtc_sys_build::target_os();
|
|
|
|
let sdk = match target_os.as_str() {
|
|
"mac" => "macosx",
|
|
"ios-device" => "iphoneos",
|
|
"ios-simulator" => "iphonesimulator",
|
|
_ => panic!("Unsupported target_os: {}", target_os),
|
|
};
|
|
|
|
let clang_rt = match target_os.as_str() {
|
|
"mac" => "clang_rt.osx",
|
|
"ios-device" => "clang_rt.ios",
|
|
"ios-simulator" => "clang_rt.iossim",
|
|
_ => panic!("Unsupported target_os: {}", target_os),
|
|
};
|
|
|
|
println!("cargo:rustc-link-lib={}", clang_rt);
|
|
println!("cargo:rustc-link-arg=-ObjC");
|
|
|
|
let sysroot = Command::new("xcrun").args(["--sdk", sdk, "--show-sdk-path"]).output().unwrap();
|
|
|
|
let sysroot = String::from_utf8_lossy(&sysroot.stdout);
|
|
let sysroot = sysroot.trim();
|
|
|
|
let search_dirs = Command::new("cc").arg("--print-search-dirs").output().unwrap();
|
|
|
|
let search_dirs = String::from_utf8_lossy(&search_dirs.stdout);
|
|
for line in search_dirs.lines() {
|
|
if line.contains("libraries: =") {
|
|
let path = line.split('=').nth(1).unwrap();
|
|
let path = format!("{}/lib/darwin", path);
|
|
println!("cargo:rustc-link-search={}", path);
|
|
}
|
|
}
|
|
|
|
builder.flag(format!("-isysroot{}", sysroot).as_str());
|
|
}
|
|
|
|
fn configure_android_sysroot(builder: &mut cc::Build) {
|
|
let toolchain = webrtc_sys_build::android_ndk_toolchain().unwrap();
|
|
let sysroot = toolchain.join("sysroot").canonicalize().unwrap();
|
|
builder.flag(format!("-isysroot{}", sysroot.display()).as_str());
|
|
}
|
|
|
|
fn add_lazy_load_so(builder: &mut cc::Build, name: &str, libraries: Vec<String>) {
|
|
let target_arch = webrtc_sys_build::target_arch();
|
|
for lib_name in libraries {
|
|
let mut arch_dir = "x86_64-linux-gnu";
|
|
if target_arch.contains("arm64") {
|
|
arch_dir = "aarch64-linux-gnu";
|
|
}
|
|
let implib_file_c_name = "src/lazy_load_deps_for/".to_owned()
|
|
+ name
|
|
+ "/"
|
|
+ arch_dir
|
|
+ "/lib"
|
|
+ &lib_name
|
|
+ ".so.init.c";
|
|
let implib_file_asm_name = "src/lazy_load_deps_for/".to_owned()
|
|
+ name
|
|
+ "/"
|
|
+ arch_dir
|
|
+ "/lib"
|
|
+ &lib_name
|
|
+ ".so.tramp.S";
|
|
builder.file(implib_file_c_name).file(implib_file_asm_name);
|
|
}
|
|
}
|
|
|
|
fn add_gio_headers(builder: &mut cc::Build) {
|
|
let webrtc_dir = webrtc_sys_build::webrtc_dir();
|
|
let target_arch = webrtc_sys_build::target_arch();
|
|
let target_arch_sysroot = match target_arch.as_str() {
|
|
"arm64" => "arm64",
|
|
"x64" => "amd64",
|
|
_ => panic!("unsupported arch"),
|
|
};
|
|
let sysroot_path = format!("include/build/linux/debian_bullseye_{target_arch_sysroot}-sysroot");
|
|
let sysroot = webrtc_dir.join(sysroot_path);
|
|
let glib_path = sysroot.join("usr/include/glib-2.0");
|
|
println!("cargo:info=add_gio_headers {}", glib_path.display());
|
|
|
|
builder.include(&glib_path);
|
|
let arch_specific_path = match target_arch.as_str() {
|
|
"x64" => "x86_64-linux-gnu",
|
|
"arm64" => "aarch64-linux-gnu",
|
|
_ => panic!("unsupported target"),
|
|
};
|
|
|
|
let glib_path_config = sysroot.join("usr/lib");
|
|
let glib_path_config = glib_path_config.join(arch_specific_path);
|
|
let glib_path_config = glib_path_config.join("glib-2.0/include");
|
|
builder.include(&glib_path_config);
|
|
}
|
|
|
|
fn probe_egl() -> Option<pkg_config::Library> {
|
|
println!("cargo:rerun-if-env-changed=EGL_NO_PKG_CONFIG");
|
|
let egl = pkg_config::Config::new()
|
|
.cargo_metadata(false)
|
|
.probe("egl")
|
|
.ok()?;
|
|
if !egl.include_paths.iter().any(|path| path.join("EGL/egl.h").exists()) {
|
|
return None;
|
|
}
|
|
if !egl
|
|
.include_paths
|
|
.iter()
|
|
.any(|path| path.join("EGL/eglext.h").exists())
|
|
{
|
|
return None;
|
|
}
|
|
Some(egl)
|
|
}
|