ac_ffmpeg_build/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::{env, ffi::OsStr, path::PathBuf};

/// Get FFmpeg include directories.
///
/// # Arguments
/// * `env_metadata` - if `true`, the function will emit Cargo metadata to
///   re-run the build if the corresponding env. variables change
pub fn ffmpeg_include_dirs(env_metadata: bool) -> Vec<PathBuf> {
    if let Some(dir) = path_from_env("FFMPEG_INCLUDE_DIR", env_metadata) {
        if dir.is_dir() {
            return vec![dir];
        }
    }

    let lib = find_ffmpeg_lib()
        .expect("Unable to find FFmpeg include dir. You can specify it explicitly by setting the FFMPEG_INCLUDE_DIR environment variable.");

    lib.include_paths
}

/// Get FFmpeg library directories.
///
/// # Arguments
/// * `env_metadata` - if `true`, the function will emit Cargo metadata to
///   re-run the build if the corresponding env. variables change
pub fn ffmpeg_lib_dirs(env_metadata: bool) -> Vec<PathBuf> {
    if let Some(dir) = path_from_env("FFMPEG_LIB_DIR", env_metadata) {
        if dir.is_dir() {
            return vec![dir];
        }
    }

    let lib = find_ffmpeg_lib()
        .expect("Unable to find FFmpeg lib dir. You can specify it explicitly by setting the FFMPEG_LIB_DIR environment variable.");

    lib.link_paths
}

cfg_if::cfg_if! {
    if #[cfg(any(target_os = "linux", target_os = "macos"))] {
        use pkg_config::{Config, Library};

        /// Find a given library using pkg-config.
        fn find_ffmpeg_lib() -> Option<Library> {
            Config::new()
                .cargo_metadata(false)
                .probe("libavcodec")
                .ok()
        }
    } else if #[cfg(target_os = "windows")] {
        use vcpkg::{Config, Library};

        /// Find a given library/package in the vcpkg tree.
        fn find_ffmpeg_lib() -> Option<Library> {
            Config::new()
                .cargo_metadata(false)
                .find_package("ffmpeg")
                .ok()
        }
    } else {
        /// Helper struct.
        struct Library {
            include_paths: Vec<PathBuf>,
            link_paths: Vec<PathBuf>,
        }

        /// Dummy function.
        fn find_ffmpeg_lib() -> Option<Library> {
            None
        }
    }
}

/// Get a given path from the environment.
fn path_from_env(name: &str, env_metadata: bool) -> Option<PathBuf> {
    let target = normalized_target();

    if env_metadata {
        emit_env_metadata(name, target.as_deref());
    }

    if let Some(target) = target {
        if let Some(path) = path_from_var(format!("{name}_{target}")) {
            return Some(path);
        }
    }

    if let Some(path) = path_from_var(name) {
        return Some(path);
    }

    None
}

/// Get path from a given env. variable.
fn path_from_var<K>(key: K) -> Option<PathBuf>
where
    K: AsRef<OsStr>,
{
    Some(PathBuf::from(env::var_os(key)?))
}

/// Emit Cargo metadata that will rerun the build if a given variable or its
/// target-specific variant changes.
fn emit_env_metadata(name: &str, target: Option<&str>) {
    if let Some(target) = target {
        println!("cargo:rerun-if-env-changed={name}_{target}");
    }

    println!("cargo:rerun-if-env-changed={name}");
}

/// Get uppercase target with dashes replaced by underscores.
fn normalized_target() -> Option<String> {
    let target = env::var("TARGET").ok()?.to_uppercase().replace('-', "_");

    Some(target)
}