mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-14 00:11:01 +03:00
Encode AV1 with SVT-AV1 v4.2.0 (rtc + low delay + CBR) instead of libaom on all 64-bit targets; 32-bit targets keep aom. Decoding is unchanged and the wire format is still AV1, so old peers are unaffected. - vcpkg overlay port pinned to v4.2.0: static build, LTO off (mixed toolchain safety), MSVC >= 1950 inline workaround, no forced llvm binutils - low delay contract: one packet out per picture in, blocking get_packet - bitrate and frame rate change on the fly via RATE_CHANGE_EVENT / FRAME_RATE_CHANGE_EVENT; VideoQoS pushes its pacing rate through the new EncoderApi::set_fps, applied only after a successful send - fall back to aom for i444 (svt-av1 is 4:2:0 only), for unsupported resolutions and on encoder init failure - test_av1 now gates auto AV1 on the encoder actually used, the cached verdict is re-keyed (SVT-Y/SVT-N) so upgrades re-measure - unit tests: per-frame packet contract, aom cross-decode of the bitstream, on-the-fly event acceptance Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Wf7v1KJM6mbhiYxWSz4PH
309 lines
11 KiB
Rust
309 lines
11 KiB
Rust
use std::{
|
|
env, fs,
|
|
path::{Path, PathBuf},
|
|
println,
|
|
};
|
|
|
|
/// The static library name may differ from the package name.
|
|
fn link_lib_name(package: &str) -> &str {
|
|
match package {
|
|
"svt-av1" => "SvtAv1Enc",
|
|
_ => package.trim_start_matches("lib"),
|
|
}
|
|
}
|
|
|
|
#[cfg(all(target_os = "linux", feature = "linux-pkg-config"))]
|
|
fn link_pkg_config(name: &str) -> Vec<PathBuf> {
|
|
// sometimes an override is needed
|
|
let pc_name = match name {
|
|
"libvpx" => "vpx",
|
|
"svt-av1" => "SvtAv1Enc",
|
|
_ => name,
|
|
};
|
|
let mut pc = pkg_config::Config::new();
|
|
if name == "svt-av1" {
|
|
// svt_av1.rs requires the 4.x API (rtc, PredStructure, on-the-fly rate events)
|
|
pc.atleast_version("4.2");
|
|
}
|
|
let pkg_hint = match name {
|
|
"svt-av1" => "libsvt-av1-dev (needs svt-av1 >= 4.2)".to_owned(),
|
|
_ => format!("{pc_name}-dev"),
|
|
};
|
|
let lib = pc.probe(pc_name)
|
|
.expect(format!(
|
|
"unable to find '{pc_name}' development headers with pkg-config (feature linux-pkg-config is enabled).
|
|
try installing '{pkg_hint}' from your system package manager.").as_str());
|
|
|
|
let mut include_paths = lib.include_paths;
|
|
// SvtAv1Enc.pc's Cflags is -I${includedir}/svt-av1, but svt_av1_ffi.h uses
|
|
// #include <svt-av1/...>, so the parent include root is needed too.
|
|
if name == "svt-av1" {
|
|
let parents: Vec<PathBuf> = include_paths
|
|
.iter()
|
|
.filter(|p| p.ends_with("svt-av1"))
|
|
.filter_map(|p| p.parent().map(|p| p.to_path_buf()))
|
|
.collect();
|
|
include_paths.extend(parents);
|
|
}
|
|
include_paths
|
|
}
|
|
#[cfg(not(all(target_os = "linux", feature = "linux-pkg-config")))]
|
|
fn link_pkg_config(_name: &str) -> Vec<PathBuf> {
|
|
unimplemented!()
|
|
}
|
|
|
|
/// Link vcpkg package.
|
|
fn link_vcpkg(mut path: PathBuf, name: &str) -> PathBuf {
|
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
let mut target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
if target_arch == "x86_64" {
|
|
target_arch = "x64".to_owned();
|
|
} else if target_arch == "x86" {
|
|
target_arch = "x86".to_owned();
|
|
} else if target_arch == "loongarch64" {
|
|
target_arch = "loongarch64".to_owned();
|
|
} else if target_arch == "aarch64" {
|
|
target_arch = "arm64".to_owned();
|
|
} else {
|
|
target_arch = "arm".to_owned();
|
|
}
|
|
let mut target = if target_os == "macos" {
|
|
if target_arch == "x64" {
|
|
"x64-osx".to_owned()
|
|
} else if target_arch == "arm64" {
|
|
"arm64-osx".to_owned()
|
|
} else {
|
|
format!("{}-{}", target_arch, target_os)
|
|
}
|
|
} else if target_os == "windows" {
|
|
format!("{}-windows-static", target_arch)
|
|
} else {
|
|
format!("{}-{}", target_arch, target_os)
|
|
};
|
|
if target_arch == "x86" {
|
|
target = target.replace("x64", "x86");
|
|
}
|
|
println!("cargo:info={}", target);
|
|
if let Ok(vcpkg_root) = std::env::var("VCPKG_INSTALLED_ROOT") {
|
|
path = vcpkg_root.into();
|
|
} else {
|
|
path.push("installed");
|
|
}
|
|
path.push(target);
|
|
println!("cargo:rustc-link-lib=static={}", link_lib_name(name));
|
|
println!(
|
|
"cargo:rustc-link-search={}",
|
|
path.join("lib").to_str().unwrap()
|
|
);
|
|
let include = path.join("include");
|
|
println!("cargo:include={}", include.to_str().unwrap());
|
|
include
|
|
}
|
|
|
|
/// Link homebrew package(for Mac M1).
|
|
fn link_homebrew_m1(name: &str) -> PathBuf {
|
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
if target_os != "macos" || target_arch != "aarch64" {
|
|
panic!("Couldn't find VCPKG_ROOT, also can't fallback to homebrew because it's only for macos aarch64.");
|
|
}
|
|
let mut path = PathBuf::from("/opt/homebrew/Cellar");
|
|
path.push(name);
|
|
let entries = if let Ok(dir) = std::fs::read_dir(&path) {
|
|
dir
|
|
} else {
|
|
panic!("Could not find package in {}. Make sure your homebrew and package {} are all installed.", path.to_str().unwrap(),&name);
|
|
};
|
|
let mut directories = entries
|
|
.into_iter()
|
|
.filter(|x| x.is_ok())
|
|
.map(|x| x.unwrap().path())
|
|
.filter(|x| x.is_dir())
|
|
.collect::<Vec<_>>();
|
|
// Find the newest version.
|
|
directories.sort_unstable();
|
|
if directories.is_empty() {
|
|
panic!(
|
|
"There's no installed version of {} in /opt/homebrew/Cellar",
|
|
name
|
|
);
|
|
}
|
|
path.push(directories.pop().unwrap());
|
|
// Link the library. Homebrew's svt-av1 bottle only ships a dylib.
|
|
if name == "svt-av1" {
|
|
println!("cargo:rustc-link-lib={}", link_lib_name(name));
|
|
println!("cargo:warning=svt-av1 is linked dynamically from homebrew, the binary needs homebrew's svt-av1 at runtime and is not relocatable; set VCPKG_ROOT for a static build");
|
|
} else {
|
|
println!("cargo:rustc-link-lib=static={}", link_lib_name(name));
|
|
}
|
|
// Add the library path.
|
|
println!(
|
|
"cargo:rustc-link-search={}",
|
|
path.join("lib").to_str().unwrap()
|
|
);
|
|
// Add the include path.
|
|
let include = path.join("include");
|
|
println!("cargo:include={}", include.to_str().unwrap());
|
|
include
|
|
}
|
|
|
|
/// Find package. By default, it will try to find vcpkg first, then homebrew(currently only for Mac M1).
|
|
/// If building on a linux host and feature "linux-pkg-config" is enabled, will try to use pkg-config
|
|
/// unless check fails (e.g. NO_PKG_CONFIG_libyuv=1). Note the cfg! below sees the build host, not
|
|
/// the target, so cross builds (e.g. linux -> android) must not enable linux-pkg-config.
|
|
fn find_package(name: &str) -> Vec<PathBuf> {
|
|
let no_pkg_config_var_name = format!("NO_PKG_CONFIG_{name}");
|
|
println!("cargo:rerun-if-env-changed={no_pkg_config_var_name}");
|
|
if cfg!(all(target_os = "linux", feature = "linux-pkg-config"))
|
|
&& std::env::var(no_pkg_config_var_name).as_deref() != Ok("1")
|
|
{
|
|
link_pkg_config(name)
|
|
} else if let Ok(vcpkg_root) = std::env::var("VCPKG_ROOT") {
|
|
vec![link_vcpkg(vcpkg_root.into(), name)]
|
|
} else {
|
|
// Try using homebrew
|
|
vec![link_homebrew_m1(name)]
|
|
}
|
|
}
|
|
|
|
fn generate_bindings(
|
|
ffi_header: &Path,
|
|
include_paths: &[PathBuf],
|
|
ffi_rs: &Path,
|
|
exact_file: &Path,
|
|
regex: &str,
|
|
) {
|
|
let mut b = bindgen::builder()
|
|
.header(ffi_header.to_str().unwrap())
|
|
.allowlist_type(regex)
|
|
.allowlist_var(regex)
|
|
.allowlist_function(regex)
|
|
.rustified_enum(regex)
|
|
.trust_clang_mangling(false)
|
|
.layout_tests(false) // breaks 32/64-bit compat
|
|
.generate_comments(false); // comments have prefix /*!\
|
|
|
|
for dir in include_paths {
|
|
b = b.clang_arg(format!("-I{}", dir.display()));
|
|
}
|
|
|
|
b.generate().unwrap().write_to_file(ffi_rs).unwrap();
|
|
fs::copy(ffi_rs, exact_file).ok(); // ignore failure
|
|
}
|
|
|
|
fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &str) {
|
|
let includes = find_package(package);
|
|
let src_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
|
let src_dir = Path::new(&src_dir);
|
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
let out_dir = Path::new(&out_dir);
|
|
|
|
let ffi_header = src_dir.join("src").join("bindings").join(ffi_header);
|
|
println!("cargo:rerun-if-changed={}", ffi_header.display());
|
|
for dir in &includes {
|
|
println!("cargo:rerun-if-changed={}", dir.display());
|
|
}
|
|
|
|
let ffi_rs = out_dir.join(generated);
|
|
let exact_file = src_dir.join("generated").join(generated);
|
|
generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
|
|
}
|
|
|
|
// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci
|
|
// Linux require link in hwcodec
|
|
/*
|
|
fn ffmpeg() {
|
|
// ffmpeg
|
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
let static_libs = vec!["avcodec", "avutil", "avformat"];
|
|
static_libs.iter().for_each(|lib| {
|
|
find_package(lib);
|
|
});
|
|
if target_os == "windows" {
|
|
println!("cargo:rustc-link-lib=static=libmfx");
|
|
}
|
|
|
|
// os
|
|
let dyn_libs: Vec<&str> = if target_os == "windows" {
|
|
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
|
|
} else if target_os == "linux" {
|
|
let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec();
|
|
if target_arch == "x86_64" {
|
|
v.push("z");
|
|
}
|
|
v
|
|
} else if target_os == "macos" || target_os == "ios" {
|
|
["c++", "m"].to_vec()
|
|
} else if target_os == "android" {
|
|
["z", "m", "android", "atomic"].to_vec()
|
|
} else {
|
|
panic!("unsupported os");
|
|
};
|
|
dyn_libs
|
|
.iter()
|
|
.map(|lib| println!("cargo:rustc-link-lib={}", lib))
|
|
.count();
|
|
|
|
if target_os == "macos" || target_os == "ios" {
|
|
println!("cargo:rustc-link-lib=framework=CoreFoundation");
|
|
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
|
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
|
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
|
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
|
}
|
|
}
|
|
*/
|
|
|
|
fn main() {
|
|
// in this crate, these are also valid configurations
|
|
println!("cargo:rustc-check-cfg=cfg(dxgi,quartz,x11)");
|
|
|
|
// there is problem with cfg(target_os) in build.rs, so use our workaround
|
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
|
|
// note: all link symbol names in x86 (32-bit) are prefixed wth "_".
|
|
// run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
|
|
// please install x64 toolchain by "rustup toolchain install stable-x86_64-pc-windows-msvc",
|
|
// then set x64 to default by "rustup default stable-x86_64-pc-windows-msvc"
|
|
let target = target_build_utils::TargetInfo::new();
|
|
if target.unwrap().target_pointer_width() != "64" {
|
|
// panic!("Only support 64bit system");
|
|
}
|
|
env::remove_var("CARGO_CFG_TARGET_FEATURE");
|
|
env::set_var("CARGO_CFG_TARGET_FEATURE", "crt-static");
|
|
|
|
find_package("libyuv");
|
|
gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
|
|
gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
|
|
gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
|
|
// Skipped on 32-bit targets: svt-av1 does not support them, the vcpkg
|
|
// manifest does not install it there and disable_av1() never uses AV1 on
|
|
// them anyway. Must stay in sync with the cfg gates in
|
|
// mod.rs/codec.rs/video_service.rs.
|
|
if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").as_deref() == Ok("64") {
|
|
gen_vcpkg_package(
|
|
"svt-av1",
|
|
"svt_av1_ffi.h",
|
|
"svt_av1_ffi.rs",
|
|
"^(svt_av1_|Svt|SVT_|Eb|EB_|PredStructure|PrivDataType).*",
|
|
);
|
|
}
|
|
// ffmpeg();
|
|
|
|
if target_os == "ios" {
|
|
// nothing
|
|
} else if target_os == "android" {
|
|
println!("cargo:rustc-cfg=android");
|
|
} else if cfg!(windows) {
|
|
// The first choice is Windows because DXGI is amazing.
|
|
println!("cargo:rustc-cfg=dxgi");
|
|
} else if cfg!(target_os = "macos") {
|
|
// Quartz is second because macOS is the (annoying) exception.
|
|
println!("cargo:rustc-cfg=quartz");
|
|
} else if cfg!(unix) {
|
|
// On UNIX we pray that X11 (with XCB) is available.
|
|
println!("cargo:rustc-cfg=x11");
|
|
}
|
|
}
|