ac_ffmpeg_build/
lib.rs

1use std::{env, ffi::OsStr, path::PathBuf};
2
3/// Get FFmpeg include directories.
4///
5/// # Arguments
6/// * `env_metadata` - if `true`, the function will emit Cargo metadata to
7///   re-run the build if the corresponding env. variables change
8pub fn ffmpeg_include_dirs(env_metadata: bool) -> Vec<PathBuf> {
9    if let Some(dir) = path_from_env("FFMPEG_INCLUDE_DIR", env_metadata) {
10        if dir.is_dir() {
11            return vec![dir];
12        }
13    }
14
15    let lib = find_ffmpeg_lib()
16        .expect("Unable to find FFmpeg include dir. You can specify it explicitly by setting the FFMPEG_INCLUDE_DIR environment variable.");
17
18    lib.include_paths
19}
20
21/// Get FFmpeg library directories.
22///
23/// # Arguments
24/// * `env_metadata` - if `true`, the function will emit Cargo metadata to
25///   re-run the build if the corresponding env. variables change
26pub fn ffmpeg_lib_dirs(env_metadata: bool) -> Vec<PathBuf> {
27    if let Some(dir) = path_from_env("FFMPEG_LIB_DIR", env_metadata) {
28        if dir.is_dir() {
29            return vec![dir];
30        }
31    }
32
33    let lib = find_ffmpeg_lib()
34        .expect("Unable to find FFmpeg lib dir. You can specify it explicitly by setting the FFMPEG_LIB_DIR environment variable.");
35
36    lib.link_paths
37}
38
39cfg_if::cfg_if! {
40    if #[cfg(any(target_os = "linux", target_os = "macos"))] {
41        use pkg_config::{Config, Library};
42
43        /// Find a given library using pkg-config.
44        fn find_ffmpeg_lib() -> Option<Library> {
45            Config::new()
46                .cargo_metadata(false)
47                .probe("libavcodec")
48                .ok()
49        }
50    } else if #[cfg(target_os = "windows")] {
51        use vcpkg::{Config, Library};
52
53        /// Find a given library/package in the vcpkg tree.
54        fn find_ffmpeg_lib() -> Option<Library> {
55            Config::new()
56                .cargo_metadata(false)
57                .find_package("ffmpeg")
58                .ok()
59        }
60    } else {
61        /// Helper struct.
62        struct Library {
63            include_paths: Vec<PathBuf>,
64            link_paths: Vec<PathBuf>,
65        }
66
67        /// Dummy function.
68        fn find_ffmpeg_lib() -> Option<Library> {
69            None
70        }
71    }
72}
73
74/// Get a given path from the environment.
75fn path_from_env(name: &str, env_metadata: bool) -> Option<PathBuf> {
76    let target = normalized_target();
77
78    if env_metadata {
79        emit_env_metadata(name, target.as_deref());
80    }
81
82    if let Some(target) = target {
83        if let Some(path) = path_from_var(format!("{name}_{target}")) {
84            return Some(path);
85        }
86    }
87
88    if let Some(path) = path_from_var(name) {
89        return Some(path);
90    }
91
92    None
93}
94
95/// Get path from a given env. variable.
96fn path_from_var<K>(key: K) -> Option<PathBuf>
97where
98    K: AsRef<OsStr>,
99{
100    Some(PathBuf::from(env::var_os(key)?))
101}
102
103/// Emit Cargo metadata that will rerun the build if a given variable or its
104/// target-specific variant changes.
105fn emit_env_metadata(name: &str, target: Option<&str>) {
106    if let Some(target) = target {
107        println!("cargo:rerun-if-env-changed={name}_{target}");
108    }
109
110    println!("cargo:rerun-if-env-changed={name}");
111}
112
113/// Get uppercase target with dashes replaced by underscores.
114fn normalized_target() -> Option<String> {
115    let target = env::var("TARGET").ok()?.to_uppercase().replace('-', "_");
116
117    Some(target)
118}