You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

300 lines
11 KiB

#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BUILD_DIR="$SCRIPT_DIR/ffmpeg_build"
SRC_DIR="$BUILD_DIR/src"
INSTALL_DIR="$BUILD_DIR/install"
JOBS=$(nproc 2>/dev/null || echo 4)
AMF_HEADERS_VERSION="${AMF_HEADERS_VERSION:-1.5.3}"
FFMPEG_PROFILE_MARKER="$INSTALL_DIR/share/utun-chatgui-ffmpeg-minimal-v2"
case "$(uname -s)" in
Linux) PLATFORM=linux ;;
MINGW*|MSYS*|CYGWIN*) PLATFORM=windows ;;
*) echo "Unsupported platform: $(uname -s)"; exit 1 ;;
esac
log() { echo "[FFmpeg] $1"; }
die() { echo "[FFmpeg] ERROR: $1" >&2; exit 1; }
check_cmd() { command -v "$1" >/dev/null 2>&1 || die "$1 not found. Install it first."; }
do_clone() {
local name="$1" url="$2" tag="$3"
if [ ! -d "$SRC_DIR/$name" ]; then
log "Clone $name..."
for i in 1 2 3; do
git clone --depth 1 --branch "$tag" "$url" "$SRC_DIR/$name" 2>&1 && break
log "Clone attempt $i failed, retrying..."
rm -rf "$SRC_DIR/$name"
sleep 2
done
[ -d "$SRC_DIR/$name/.git" ] || die "Failed to clone $name after 3 attempts"
else
log "Skip clone $name: already exists"
fi
}
# ============================================================================
# Step 0: System dependencies
# ============================================================================
if [ "$PLATFORM" = "linux" ]; then
MISSING=""
for pkg in build-essential cmake nasm pkg-config git; do
dpkg -s "$pkg" >/dev/null 2>&1 || MISSING="$MISSING $pkg"
done
if [ -n "$MISSING" ]; then
log "Installing build tools:$MISSING"
sudo apt update -qq && sudo apt install -y $MISSING
fi
HW_DEPS="libva-dev libvpl-dev"
for pkg in $HW_DEPS; do
dpkg -s "$pkg" >/dev/null 2>&1 || { log "Installing $pkg (HW accel)"; sudo apt install -y $pkg; }
done
else
check_cmd pacman
for pkg in mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-nasm \
mingw-w64-ucrt-x86_64-pkg-config mingw-w64-ucrt-x86_64-libvpl; do
pacman -Qi "$pkg" >/dev/null 2>&1 || { log "Installing $pkg"; pacman -S --noconfirm "$pkg"; }
done
fi
check_cmd cmake
check_cmd nasm
check_cmd pkg-config
check_cmd git
check_cmd curl
# ============================================================================
# Step 1: Codec libraries (static .a, built with -fPIC)
# ============================================================================
mkdir -p "$SRC_DIR" "$INSTALL_DIR/lib/pkgconfig"
export PKG_CONFIG_PATH="$INSTALL_DIR/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
# --- x264 (H.264 encoder) ---
if [ ! -f "$INSTALL_DIR/lib/pkgconfig/x264.pc" ]; then
do_clone x264 https://code.videolan.org/videolan/x264.git stable
cd "$SRC_DIR/x264"
log "Configure x264..."
./configure --prefix="$INSTALL_DIR" --enable-static --disable-shared \
--disable-opencl --disable-avs --disable-swscale --disable-lavf \
--disable-ffms --disable-gpac --disable-lsmash \
--extra-cflags="-fPIC" --disable-cli
log "Build x264..."
make -j"$JOBS"
make install
log "x264 OK"
else
log "Skip x264: already built"
fi
# --- x265 (H.265/HEVC encoder) ---
if [ ! -f "$INSTALL_DIR/lib/pkgconfig/x265.pc" ]; then
do_clone x265 https://bitbucket.org/multicoreware/x265_git.git master
if [ -f "$SRC_DIR/x265/.git/shallow" ]; then
log "Fetch x265 history and release tags..."
git -C "$SRC_DIR/x265" fetch --unshallow --tags origin
fi
X265_TAG=$(git -C "$SRC_DIR/x265" describe --abbrev=0 --tags 2>/dev/null || true)
[ -n "$X265_TAG" ] || die "Cannot determine x265 release tag; check the x265 Git remote and tags"
cd "$SRC_DIR/x265"
mkdir -p build && cd build
log "Configure x265..."
cmake ../source -G "Unix Makefiles" \
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DENABLE_CLI=OFF -DENABLE_SHARED=OFF \
-DHIGH_BIT_DEPTH=OFF -DENABLE_HDR10_PLUS=OFF
log "Build x265..."
cmake --build . -j"$JOBS"
cmake --install .
cat > "$INSTALL_DIR/lib/pkgconfig/x265.pc" <<EOF
prefix=$INSTALL_DIR
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib
includedir=\${prefix}/include
Name: x265
Description: H.265/HEVC video encoder
Version: 4.1
Libs: -L\${libdir} -lx265
Libs.private: -lstdc++ -lm -lpthread
Cflags: -I\${includedir}
EOF
log "x265 OK"
else
log "Skip x265: already built"
fi
# --- libvpx (VP8/VP9 encoder) ---
if [ ! -f "$INSTALL_DIR/lib/pkgconfig/vpx.pc" ]; then
do_clone libvpx https://chromium.googlesource.com/webm/libvpx main
cd "$SRC_DIR/libvpx"
log "Configure libvpx..."
./configure --prefix="$INSTALL_DIR" --enable-static --disable-shared \
--enable-pic --enable-vp8 --enable-vp9 --disable-vp9-highbitdepth \
--disable-examples --disable-tools --disable-docs --disable-unit-tests
log "Build libvpx..."
make -j"$JOBS"
make install
log "libvpx OK"
else
log "Skip libvpx: already built"
fi
# --- libaom (reference AV1 encoder) ---
if [ ! -f "$INSTALL_DIR/lib/pkgconfig/aom.pc" ]; then
LIBAOM_TAG=v3.13.2
do_clone libaom https://aomedia.googlesource.com/aom "$LIBAOM_TAG"
if ! git -C "$SRC_DIR/libaom" describe --tags --exact-match 2>/dev/null | grep -Fxq "$LIBAOM_TAG"; then
log "Update existing libaom source to $LIBAOM_TAG..."
git -C "$SRC_DIR/libaom" fetch --depth 1 origin "refs/tags/$LIBAOM_TAG:refs/tags/$LIBAOM_TAG"
git -C "$SRC_DIR/libaom" checkout --detach "$LIBAOM_TAG"
fi
cd "$SRC_DIR/libaom"
mkdir -p build && cd build
log "Configure libaom..."
cmake .. -G "Unix Makefiles" \
-DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
-DBUILD_SHARED_LIBS=OFF -DENABLE_TESTS=OFF \
-DENABLE_TOOLS=OFF -DENABLE_DOCS=OFF \
-DENABLE_NASM=ON
log "Build libaom..."
cmake --build . -j"$JOBS"
cmake --install .
log "libaom OK"
else
log "Skip libaom: already built"
fi
# --- nv-codec-headers (NVIDIA NVENC/NVDEC headers, no build needed) ---
if [ ! -f "$INSTALL_DIR/include/ffnvcodec/nvEncodeAPI.h" ]; then
do_clone nv-codec-headers https://github.com/FFmpeg/nv-codec-headers.git master
cd "$SRC_DIR/nv-codec-headers"
log "Install nv-codec-headers..."
make install PREFIX="$INSTALL_DIR"
log "nv-codec-headers OK"
else
log "Skip nv-codec-headers: already installed"
fi
# ============================================================================
# Step 2: FFmpeg
# ============================================================================
if [ "$PLATFORM" = "windows" ] && [ ! -f "$INSTALL_DIR/include/AMF/core/Factory.h" ]; then
AMF_ARCHIVE="$SRC_DIR/AMF-headers-v$AMF_HEADERS_VERSION.tar.gz"
AMF_EXTRACT_DIR="$SRC_DIR/amf-headers-$AMF_HEADERS_VERSION"
mkdir -p "$AMF_EXTRACT_DIR" "$INSTALL_DIR/include/AMF"
if [ ! -f "$AMF_ARCHIVE" ]; then
log "Download AMD AMF headers v$AMF_HEADERS_VERSION..."
curl -fL --retry 3 -o "$AMF_ARCHIVE" \
"https://github.com/GPUOpen-LibrariesAndSDKs/AMF/releases/download/v$AMF_HEADERS_VERSION/AMF-headers-v$AMF_HEADERS_VERSION.tar.gz"
fi
log "Extract AMD AMF headers..."
tar -xzf "$AMF_ARCHIVE" -C "$AMF_EXTRACT_DIR"
AMF_FACTORY_HEADER=$(find "$AMF_EXTRACT_DIR" -type f -path '*/AMF/core/Factory.h' -print -quit)
[ -n "$AMF_FACTORY_HEADER" ] || die "AMF headers archive does not contain AMF/core/Factory.h"
AMF_HEADER_ROOT="${AMF_FACTORY_HEADER%/AMF/core/Factory.h}"
cp -a "$AMF_HEADER_ROOT/AMF/." "$INSTALL_DIR/include/AMF/"
log "AMD AMF headers installed in $INSTALL_DIR/include/AMF"
fi
if [ -f "$INSTALL_DIR/lib/pkgconfig/libavcodec.pc" ] && [ -f "$FFMPEG_PROFILE_MARKER" ]; then
log "FFmpeg already built, skipping."
log "Done: $INSTALL_DIR"
exit 0
fi
FFMPEG_SRC="$SRC_DIR/ffmpeg"
if [ ! -d "$FFMPEG_SRC" ]; then
log "Download FFmpeg 7.1.2..."
FFMPEG_TARBALL="$SRC_DIR/ffmpeg-7.1.2.tar.xz"
if [ ! -f "$FFMPEG_TARBALL" ]; then
for url in \
"https://ffmpeg.org/releases/ffmpeg-7.1.2.tar.xz" \
"https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.1.2.tar.gz"; do
curl -L --retry 3 -o "$FFMPEG_TARBALL" "$url" 2>&1 && break
log "Download from $url failed, trying next..."
rm -f "$FFMPEG_TARBALL"
done
[ -f "$FFMPEG_TARBALL" ] || die "Failed to download FFmpeg tarball"
log "Download OK ($(du -h "$FFMPEG_TARBALL" | cut -f1))"
fi
log "Extract FFmpeg..."
mkdir -p "$FFMPEG_SRC"
tar -xf "$FFMPEG_TARBALL" -C "$FFMPEG_SRC" --strip-components=1
rm -f "$FFMPEG_TARBALL"
fi
cd "$SRC_DIR/ffmpeg"
log "Configure FFmpeg..."
CONFIGURE_ARGS=(
--prefix="$INSTALL_DIR"
--disable-everything --disable-network
--enable-avcodec --enable-avformat --enable-avutil --enable-swscale --enable-swresample
--enable-shared --disable-static
--enable-gpl --enable-version3
--pkg-config-flags=--static
--extra-cflags="-I$INSTALL_DIR/include"
--extra-ldflags="-L$INSTALL_DIR/lib"
--enable-libx264 --enable-libx265 --enable-libvpx --enable-libaom --enable-libvpl --enable-nvenc
--enable-demuxer=avi --enable-demuxer=matroska --enable-demuxer=mov
--enable-protocol=file --enable-parsers
--enable-bsf=h264_mp4toannexb --enable-bsf=hevc_mp4toannexb
--disable-programs --disable-doc --disable-avdevice
--disable-avfilter --disable-postproc --disable-muxers
--disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-debug --enable-optimizations
)
if [ "$PLATFORM" = "windows" ]; then
CONFIGURE_ARGS+=(--extra-libs="-lpthread -lm")
else
CONFIGURE_ARGS+=(--extra-libs="-lpthread -lm -ldl")
fi
DECODERS=(h264 hevc av1 vp8 vp9 mpeg4 mpeg2video mpeg1video mjpeg h263 msmpeg4v3 wmv1 wmv2 vc1
aac mp3 opus vorbis flac ac3 eac3 alac wmav1 wmav2 pcm_s16le pcm_s24le pcm_s32le pcm_u8 pcm_f32le)
ENCODERS=(libx264 libx265 libvpx_vp9 libaom_av1 h264_nvenc hevc_nvenc av1_nvenc
h264_qsv hevc_qsv vp9_qsv av1_qsv)
for decoder in "${DECODERS[@]}"; do CONFIGURE_ARGS+=("--enable-decoder=$decoder"); done
for encoder in "${ENCODERS[@]}"; do CONFIGURE_ARGS+=("--enable-encoder=$encoder"); done
if [ "$PLATFORM" = "windows" ]; then
CONFIGURE_ARGS+=(
--enable-amf --enable-d3d11va
--enable-encoder=h264_amf --enable-encoder=hevc_amf --enable-encoder=av1_amf
)
else
CONFIGURE_ARGS+=(
--enable-vaapi
--enable-encoder=h264_vaapi --enable-encoder=hevc_vaapi
--enable-encoder=vp9_vaapi --enable-encoder=av1_vaapi
)
fi
"./configure" "${CONFIGURE_ARGS[@]}"
if [ "$PLATFORM" = "windows" ]; then
for encoder in CONFIG_H264_AMF_ENCODER CONFIG_HEVC_AMF_ENCODER; do
grep -Eq "^#define $encoder 1$" config_components.h \
|| die "FFmpeg configure did not enable $encoder; check AMF headers and config.log"
done
fi
log "Build FFmpeg..."
make clean
make -j"$JOBS"
make install
mkdir -p "$(dirname "$FFMPEG_PROFILE_MARKER")"
touch "$FFMPEG_PROFILE_MARKER"
log "==============================================================="
log "FFmpeg build SUCCESS"
log "Install prefix: $INSTALL_DIR"
log "Libraries: $(ls "$INSTALL_DIR/lib/"*.so* "$INSTALL_DIR/lib/"*.dll.a "$INSTALL_DIR/bin/"*.dll 2>/dev/null | tr '\n' ' ')"
log "==============================================================="