From 446ff3f3e1137c978551910f2c02b574b947c16e Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 24 Jun 2020 17:26:59 +0000 Subject: [PATCH 1/2] Support binding generation with system FFmpeg --- .github/workflows/ci.yml | 21 ++++ build.rs | 228 +++++++++++++++++++++------------------ 2 files changed, 142 insertions(+), 107 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76264c6..3e5eecf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,6 +214,27 @@ jobs: - name: BindingBuild run: PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" cargo build --verbose + build_with_system_ffmpeg: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + + - name: Install System FFmpeg + run: | + sudo apt-get install -y libavcodec-dev libavdevice-dev \ + libavfilter-dev libavformat-dev libavutil-dev \ + libpostproc-dev libswresample-dev libswscale-dev + + - name: Binding Build + run: cargo build --verbose + # Check if correct documentation can be generated by docs.rs docs_rs_check: runs-on: ubuntu-latest diff --git a/build.rs b/build.rs index 0e2b91a..e83d7c9 100644 --- a/build.rs +++ b/build.rs @@ -115,13 +115,30 @@ impl callbacks::ParseCallbacks for FilterCargoCallbacks { } } +fn probe_system_ffmpeg() -> Result<(), String> { + match (&*LIBS) + .iter() + .map(|name| "lib".to_owned() + name) + .find(|libname| { + pkgconfig::Config::new() + .statik(true) + // Remove side effect + .cargo_metadata(false) + .probe(&libname) + .is_err() + }) { + Some(libname) => Err(format!("{} not found", libname)), + None => Ok(()), + } +} + fn main() { // If it's a documentation generation from docs.rs, just copy the bindings // generated locally to `OUT_DIR`. We do this because the building // environment of docs.rs doesn't have an network connection, so we cannot // git clone the FFmpeg. And they also have a limitation on crate's size: - // 10MB, which is not enough to fit in full FFmpeg source files. So the only - // thing we can do is copy the locally generated binding files to the + // 10MB, which is not enough to fit in FFmpeg source files. So the only + // thing we can do is copying the locally generated binding files to the // `OUT_DIR`. if env::var("DOCS_RS").is_ok() { fs::copy("src/binding.rs", &*BINDING_FILE_PATH) @@ -130,127 +147,124 @@ fn main() { } if env::var("PKG_CONFIG_PATH").is_err() { - // All outputs are stored in ./ffmpeg/build/{bin, lib, share, include} - // If no prebuilt FFmpeg libraries provided, we custom build a FFmpeg. - env::set_var( - "PKG_CONFIG_PATH", - format!("{}/build/lib/pkgconfig", *FFMPEG_DIR), - ); - env::set_var("PATH", format!("{}/build/bin:{}", *FFMPEG_DIR, *PATH)); + // If no system FFmpeg found, download and build one + if let Err(msg) = probe_system_ffmpeg() { + eprintln!("{}! Try to git clone an FFmpeg and build.", msg); + // All outputs are stored in ./ffmpeg/build/{bin, lib, share, include} + // If no prebuilt FFmpeg libraries provided, we custom build a FFmpeg. + env::set_var( + "PKG_CONFIG_PATH", + format!("{}/build/lib/pkgconfig", *FFMPEG_DIR), + ); + env::set_var("PATH", format!("{}/build/bin:{}", *FFMPEG_DIR, *PATH)); - // Check if submodule is not get cloned. - if !path::PathBuf::from(format!("{}/fftools", &*FFMPEG_DIR)).is_dir() { - Command::new("git") - .current_dir(&*OUT_DIR) - .args(["clone", "https://github.com/ffmpeg/ffmpeg", "--depth", "1"].iter()) + // Check if FFmpeg is not get cloned. + if !path::PathBuf::from(format!("{}/fftools", &*FFMPEG_DIR)).is_dir() { + Command::new("git") + .current_dir(&*OUT_DIR) + .args(["clone", "https://github.com/ffmpeg/ffmpeg", "--depth", "1"].iter()) + .spawn() + .expect("FFmpeg submodule failed to clone.") + .wait() + .expect("FFmpeg submodule failed to clone."); + } + + // Corresponding to the shell script below: + // ./configure \ + // --prefix="$PWD/build" \ + // --extra-cflags="-I$PWD/build/include" \ + // --extra-ldflags="-L$PWD/build/lib" \ + // --bindir="$PWD/build/bin" \ + // --pkg-config-flags="--static" \ + // --extra-libs="-lpthread -lm" \ + // --enable-gpl \ + // --enable-libass \ + // --enable-libfdk-aac \ + // --enable-libfreetype \ + // --enable-libmp3lame \ + // --enable-libopus \ + // --enable-libvorbis \ + // --enable-libvpx \ + // --enable-libx264 \ + // --enable-libx265 \ + // --enable-nonfree + Command::new(format!("{}/configure", *FFMPEG_DIR)) + .current_dir(&*FFMPEG_DIR) + .env( + "PKG_CONFIG_PATH", + format!("{}/build/lib/pkgconfig", *FFMPEG_DIR), + ) + .args( + [ + format!(r#"--prefix={}/build"#, *FFMPEG_DIR), + format!(r#"--extra-cflags=-I{}/build/include"#, *FFMPEG_DIR), + format!(r#"--extra-ldflags=-L{}/build/lib"#, *FFMPEG_DIR), + format!(r#"--bindir={}/build/bin"#, *FFMPEG_DIR), + ] + .iter(), + ) + .args( + [ + "--pkg-config-flags=--static", + "--extra-libs=-lpthread -lm", + "--enable-gpl", + "--enable-libass", + "--enable-libfdk-aac", + "--enable-libfreetype", + "--enable-libmp3lame", + "--enable-libopus", + "--enable-libvorbis", + "--enable-libvpx", + "--enable-libx264", + "--enable-libx265", + "--enable-nonfree", + ] + .iter(), + ) .spawn() - .expect("FFmpeg submodule failed to clone.") + .expect("FFmpeg build process: configure failed!") .wait() - .expect("FFmpeg submodule failed to clone."); - } + .expect("FFmpeg build process: configure failed!"); - // Corresponding to the shell script below: - // ./configure \ - // --prefix="$PWD/build" \ - // --extra-cflags="-I$PWD/build/include" \ - // --extra-ldflags="-L$PWD/build/lib" \ - // --bindir="$PWD/build/bin" \ - // --pkg-config-flags="--static" \ - // --extra-libs="-lpthread -lm" \ - // --enable-gpl \ - // --enable-libass \ - // --enable-libfdk-aac \ - // --enable-libfreetype \ - // --enable-libmp3lame \ - // --enable-libopus \ - // --enable-libvorbis \ - // --enable-libvpx \ - // --enable-libx264 \ - // --enable-libx265 \ - // --enable-nonfree - Command::new(format!("{}/configure", *FFMPEG_DIR)) - .current_dir(&*FFMPEG_DIR) - .env( - "PKG_CONFIG_PATH", - format!("{}/build/lib/pkgconfig", *FFMPEG_DIR), - ) - .args( - [ - format!(r#"--prefix={}/build"#, *FFMPEG_DIR), - format!(r#"--extra-cflags=-I{}/build/include"#, *FFMPEG_DIR), - format!(r#"--extra-ldflags=-L{}/build/lib"#, *FFMPEG_DIR), - format!(r#"--bindir={}/build/bin"#, *FFMPEG_DIR), - ] - .iter(), - ) - .args( - [ - "--pkg-config-flags=--static", - "--extra-libs=-lpthread -lm", - "--enable-gpl", - "--enable-libass", - "--enable-libfdk-aac", - "--enable-libfreetype", - "--enable-libmp3lame", - "--enable-libopus", - "--enable-libvorbis", - "--enable-libvpx", - "--enable-libx264", - "--enable-libx265", - "--enable-nonfree", - ] - .iter(), - ) - .spawn() - .expect("FFmpeg build process: configure failed!") - .wait() - .expect("FFmpeg build process: configure failed!"); - - Command::new("make") - .current_dir(&*FFMPEG_DIR) - .arg(format!("-j{}", *NUM_CPUS)) - .spawn() - .expect("FFmpeg build process: make compile failed!") - .wait() - .expect("FFmpeg build process: make compile failed!"); + Command::new("make") + .current_dir(&*FFMPEG_DIR) + .arg(format!("-j{}", *NUM_CPUS)) + .spawn() + .expect("FFmpeg build process: make compile failed!") + .wait() + .expect("FFmpeg build process: make compile failed!"); - Command::new("make") - .current_dir(&*FFMPEG_DIR) - .arg(format!("-j{}", *NUM_CPUS)) - .arg("install") - .spawn() - .expect("FFmpeg build process: make install failed!") - .wait() - .expect("FFmpeg build process: make install failed!"); + Command::new("make") + .current_dir(&*FFMPEG_DIR) + .arg(format!("-j{}", *NUM_CPUS)) + .arg("install") + .spawn() + .expect("FFmpeg build process: make install failed!") + .wait() + .expect("FFmpeg build process: make install failed!"); - /* Commented because it's not needed, we are not using any specific shell. - Command::new("hash") - .current_dir(&*FFMPEG_DIR) - .arg("-r") - .spawn() - .expect("FFmpeg build process: clear hash cache failed!") - .wait() - .expect("FFmpeg build process: clear hash cache failed!"); - */ + /* Commented because it's not needed, we are not using any specific shell. + Command::new("hash") + .current_dir(&*FFMPEG_DIR) + .arg("-r") + .spawn() + .expect("FFmpeg build process: clear hash cache failed!") + .wait() + .expect("FFmpeg build process: clear hash cache failed!"); + */ + } } // We currently only support building with static libraries. - - /* Thanks to pkg-config, we don't need this. - // Output link libraries - (&*LIBS) - .iter() - .for_each(|name| println!("cargo:rustc-link-lib={}={}", "static", name)); - */ - // Probe libraries - // TODO if not enabled, we should not probe it + // TODO if not enabled, we should not probe it. Should modify probe_system_ffmpeg() too. let include_paths = (&*LIBS) .iter() .map(|name| "lib".to_owned() + name) .map(|libname| { pkgconfig::Config::new() .statik(true) + .cargo_metadata(true) .probe(&libname) .unwrap_or_else(|_| panic!(format!("{} not found!", libname))) .include_paths From df41ee4f51e4987f1657a3d8f4cbaaf8584363aa Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 24 Jun 2020 17:27:10 +0000 Subject: [PATCH 2/2] Update README for binding generation with system FFmpeg --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 854e876..06e6f1c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ FFI binding for FFmpeg inner library. $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly ``` 2. Generate and build the bindings: - Run `cargo build` to build the bindings, we will compile the FFmpeg in the git submodule for you. If you have a pre-built ffmpeg, set `PKG_CONFIG_PATH` to the path which points to `*.pc` files in the build result(e.g. `PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" cargo build`) then we will use the pre-built FFmpeg libraries. After the FFmpeg is built, the build script will take advantage of the package-config(`*.pc`) files to: + Run `cargo build` to build the bindings. If you have a pre-built ffmpeg, set `PKG_CONFIG_PATH` to the path which points to `*.pc` files in the build result(e.g. `PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" cargo build`) then it will use the pre-built FFmpeg libraries. If no `PKG_CONFIG_PATH` is set, it will first check if there are `libav*-dev` installed. If not, it will git clone the FFmpeg from and then configure and compile it for you. After the FFmpeg libraries is ready, the build script will take advantage of the package-config(`*.pc`) files to: 1. Probe paths of the header files for binding generation and generate the binding. 2. Probe library dependencies as project dependencies to ensure this project can be built successfully.