use std::env; use std::fs; use std::path::PathBuf; fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); if target_os == "windows" { bundle_windows_dlls(); } if target_os != "linux" { return; } // Skip bundling if using static FFmpeg linking if env::var("PKG_CONFIG_ALL_STATIC").is_ok() || env::var("FFMPEG_STATIC").is_ok() { println!("cargo:warning=Skipping FFmpeg library bundling (static linking enabled)"); return; } // Get the output directory let out_dir = env::var("OUT_DIR").unwrap(); let target_dir = PathBuf::from(&out_dir) .parent().unwrap() .parent().unwrap() .parent().unwrap() .to_path_buf(); // Create lib directory in target let lib_dir = target_dir.join("lib"); fs::create_dir_all(&lib_dir).ok(); println!("cargo:warning=Bundling FFmpeg libraries to {:?}", lib_dir); // List of FFmpeg 8.x libraries to bundle let ffmpeg_libs = [ "libavcodec.so.62", "libavdevice.so.62", "libavfilter.so.11", "libavformat.so.62", "libavutil.so.60", "libpostproc.so.57", // Actually version 57 in Ubuntu 24.04 "libswresample.so.6", // Actually version 6 "libswscale.so.9", ]; let lib_search_paths = [ "/usr/lib/x86_64-linux-gnu", "/usr/lib64", "/usr/lib", ]; // Copy FFmpeg libraries for lib_name in &ffmpeg_libs { copy_library(lib_name, &lib_search_paths, &lib_dir); } // Also bundle all FFmpeg codec dependencies to avoid version mismatches let codec_libs = [ // Codec libraries "libaom.so.3", "libdav1d.so.7", "librav1e.so.0", "libSvtAv1Enc.so.1", "libvpx.so.9", "libx264.so.164", "libx265.so.199", "libopus.so.0", "libvorbis.so.0", "libvorbisenc.so.2", "libmp3lame.so.0", "libtheora.so.0", "libtheoraenc.so.1", "libtheoradec.so.1", "libtwolame.so.0", "libspeex.so.1", "libshine.so.3", "libwebp.so.7", "libwebpmux.so.3", "libjxl.so.0.7", "libjxl_threads.so.0.7", // Container/protocol libraries "librabbitmq.so.4", "librist.so.4", "libsrt-gnutls.so.1.5", "libzmq.so.5", "libbluray.so.2", "libdvdnav.so.4", "libdvdread.so.8", // Other dependencies "libaribb24.so.0", "libcodec2.so.1.2", "libgsm.so.1", "libopencore-amrnb.so.0", "libopencore-amrwb.so.0", "libvo-amrwbenc.so.0", "libfdk-aac.so.2", "libilbc.so.3", "libopenjp2.so.7", "libsnappy.so.1", "libvvenc.so.1.12", ]; for lib_name in &codec_libs { copy_library(lib_name, &lib_search_paths, &lib_dir); } // Set rpath to look in ./lib and $ORIGIN/lib println!("cargo:rustc-link-arg=-Wl,-rpath,$ORIGIN/lib"); println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display()); } fn bundle_windows_dlls() { let ffmpeg_dir = match env::var("FFMPEG_DIR") { Ok(dir) => PathBuf::from(dir), Err(_) => return, }; let bin_dir = ffmpeg_dir.join("bin"); if !bin_dir.exists() { println!("cargo:warning=FFMPEG_DIR/bin not found, skipping DLL bundling"); return; } let out_dir = env::var("OUT_DIR").unwrap(); let target_dir = PathBuf::from(&out_dir) .parent().unwrap() .parent().unwrap() .parent().unwrap() .to_path_buf(); let dlls = [ "avcodec-62.dll", "avdevice-62.dll", "avfilter-11.dll", "avformat-62.dll", "avutil-60.dll", "swresample-6.dll", "swscale-9.dll", ]; for dll in &dlls { let src = bin_dir.join(dll); let dst = target_dir.join(dll); if src.exists() { if let Err(e) = fs::copy(&src, &dst) { println!("cargo:warning=Failed to copy {}: {}", dll, e); } } else { println!("cargo:warning=FFmpeg DLL not found: {}", src.display()); } } println!("cargo:warning=Bundled FFmpeg DLLs to {}", target_dir.display()); } fn copy_library(lib_name: &str, search_paths: &[&str], lib_dir: &PathBuf) { let mut copied = false; for search_path in search_paths { let src = PathBuf::from(search_path).join(lib_name); if src.exists() { let dst = lib_dir.join(lib_name); if let Err(e) = fs::copy(&src, &dst) { println!("cargo:warning=Failed to copy {}: {}", lib_name, e); } else { copied = true; break; } } } if !copied { // Don't warn for optional libraries if !lib_name.contains("shine") && !lib_name.contains("fdk-aac") { println!("cargo:warning=Could not find {} (optional)", lib_name); } } }