一个脚本搞定x264、x265编译并集成到FFmpeg

56 阅读2分钟

ubuntu环境 ffmpeg 6.1.4

坑采完了,拿走不谢!

#!/usr/bin/env bash
set -e

# ==========================================
# 配置区域 (请根据实际情况修改)
# ==========================================
# NDK 安装路径 (请修改为你自己的路径)
export NDK_ROOT=/home/xbin/Android/Sdk/ndk/28.2.13676358

# 目标架构 (这里演示 arm64-v8a,也是目前最通用的)
ARCH=aarch64
CPU=armv8-a
API=21
ANDROID_ARCH=arm64-v8a
HOST_TAG=linux-x86_64 # Ubuntu 系统

# 编译生成目录
ROOT=$(pwd)/build
SRC=$ROOT/src
INSTALL=$ROOT/install
TOOLCHAIN=$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG

# 检查 NDK 是否存在
if [ ! -d "$NDK_ROOT" ]; then
    echo "Error: NDK_ROOT not found at $NDK_ROOT"
    exit 1
fi

# 设置编译器别名 (Clang)
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/aarch64-linux-android${API}-clang
export CXX=$TOOLCHAIN/bin/aarch64-linux-android${API}-clang++
export AS=$CC
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip

# 放入 Path 方便调用
export PATH=$TOOLCHAIN/bin:$PATH

JOBS=$(nproc)
mkdir -p $SRC $INSTALL

echo "使用 NDK: $NDK_ROOT"
echo "编译架构: $ANDROID_ARCH (API $API)"

################################
# 1. build x264 (Static)
################################
echo "==== build x264 ===="
cd $SRC
if [ ! -d x264 ]; then
  git clone https://code.videolan.org/videolan/x264.git -b stable
fi
cd x264

make clean || true
# x264 交叉编译配置
./configure \
  --prefix=$INSTALL \
  --enable-static \
  --disable-shared \
  --enable-pic \
  --disable-cli \
  --disable-asm \
  --host=aarch64-linux-android \
  --sysroot=$TOOLCHAIN/sysroot \
  --cross-prefix="$TOOLCHAIN/bin/llvm-" \
  --extra-cflags="-fPIC"

make -j$JOBS
make install

################################
# 2. build x265 (Static)
################################
echo "==== build x265 ===="
cd $SRC
if [ ! -d x265 ]; then
  git clone https://bitbucket.org/multicoreware/x265_git.git x265
fi
cd x265/build/linux

make clean || true

# x265 使用 CMake,利用 NDK 自带的 toolchain 文件最方便
cmake ../../source \
  -DCMAKE_SYSTEM_NAME=Android \
  -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake \
  -DANDROID_PLATFORM=android-$API \
  -DANDROID_ABI=$ANDROID_ARCH \
  -DENABLE_SHARED=OFF \
  -DENABLE_ASSEMBLY=OFF \
  -DENABLE_CLI=OFF \
  -DENABLE_PIC=ON \
  -DCMAKE_INSTALL_PREFIX=$INSTALL 

make -j$JOBS
make install


sed -i 's/-l-l:libunwind.a//g' $INSTALL/lib/pkgconfig/x265.pc

################################
# 3. build ffmpeg with x264+x265
################################
echo "==== build ffmpeg ===="

cd $SRC
if [ ! -d ffmpeg ]; then
  git clone https://git.ffmpeg.org/ffmpeg.git
  # 建议 checkout 一个稳定版本,例如 6.0
  cd ffmpeg && git checkout release/6.1.4 && cd ..
fi
cd ffmpeg

make distclean || true
# 关键:x265是C++库,FFmpeg是C,链接时需要 explicitly 链接 C++ 运行时
# 并且要告诉 pkg-config 只看我们编译的目录
export PKG_CONFIG_PATH=$INSTALL/lib/pkgconfig
export PKG_CONFIG_LIBDIR=$PKG_CONFIG_PATH

./configure \
  --prefix=$INSTALL/out \
  --target-os=android \
  --arch=$ARCH \
  --cpu=$CPU \
  --cc=$CC \
  --cxx=$CXX \
  --ar=$AR \
  --ranlib=$RANLIB \
  --strip=$STRIP \
  --sysroot=$TOOLCHAIN/sysroot \
  --enable-pic \
  --enable-gpl \
  --enable-small \
  --enable-neon \
  --enable-jni \
  --enable-cross-compile \
  --enable-pthreads \
  --enable-libx264 \
  --enable-libx265 \
  --enable-mediacodec \
  --enable-encoder=h264_mediacodec \
  --enable-encoder=hevc_mediacodec \
  --enable-decoder=h264_mediacodec \
  --enable-decoder=hevc_mediacodec \
  --enable-decoder=aac_mediacodec \
  --enable-static \
  --disable-shared \
  --disable-encoders \
  --disable-doc \
  --disable-htmlpages \
  --disable-manpages \
  --disable-podpages \
  --disable-txtpages \
  --disable-ffmpeg \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-symver \
  --disable-asm \
  --disable-x86asm \
  --disable-avdevice \
  --disable-postproc \
  --disable-cuvid \
  --disable-nvenc \
  --disable-vaapi \
  --disable-vdpau \
  --disable-videotoolbox \
  --disable-audiotoolbox \
  --disable-appkit \
  --disable-avfoundation \
  --extra-cflags="-I$INSTALL/include -fPIC -DPIC" \
  --extra-cxxflags="-I$INSTALL/include -fPIC -DPIC" \
  --extra-ldflags="-L$INSTALL/lib" \
  --extra-libs="-lc++_shared" \
  --pkg-config="pkg-config --static"

make -j$JOBS
make install

echo "================================="
echo " Android Build Success!"
echo " Output: $INSTALL/out"
echo "================================="


################################
# 4. 合并所有 .a 文件为单个 .so
################################
echo "==== 正在合并生成 libffmpeg.so ===="

# 定义输出文件路径
OUTPUT=$INSTALL/out/lib

# 这里的顺序有一定影响,通常 ffmpeg 基础库在后,插件库在前
# 使用 -Wl,--whole-archive 确保所有符号都被强制包含
$CC -shared -fPIC -Wl,-soname,libffmpeg.so \
  -Wl,--allow-multiple-definition \
  -Wl,--whole-archive \
  $INSTALL/lib/libx264.a \
  $INSTALL/lib/libx265.a \
  $OUTPUT/libavformat.a \
  $OUTPUT/libavcodec.a \
  $OUTPUT/libavfilter.a \
  $OUTPUT/libswresample.a \
  $OUTPUT/libswscale.a \
  $OUTPUT/libavutil.a \
  -Wl,--no-whole-archive \
  -lc++_static \
  -lm -lz -ldl -llog -landroid \
  -lmediandk \
  -o $OUTPUT/libffmpeg.so

# 去除符号表减小体积
$STRIP --strip-unneeded $OUTPUT/libffmpeg.so

echo "================================="
echo " Android Build Success!"
echo " 合并后的库文件: $OUTPUT/libffmpeg.so"
echo "================================="

集成到Android项目中,会报错:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found:

externalNativeBuild {
    cmake {
        cppFlags '-std=c++11'
        arguments "-DANDROID_STL=c++_shared"  // 增加这行就能解决
    }
}

检测是否集成成功,将编译之后的so集成到项目中,JNI中执行如下代码即可检测:

    const char *config = avcodec_configuration();
    __android_log_print(ANDROID_LOG_INFO, "FFmpegConfig", "%s", config);

    // 或者检查特定的编码器是否存在
    const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_HEVC);
    if (codec) {
        __android_log_print(ANDROID_LOG_INFO, "FFmpegConfig", "libx265 encoder found!");
    } else {
        __android_log_print(ANDROID_LOG_ERROR, "FFmpegConfig", "libx265 encoder NOT found!");
    }

//    codec = avcodec_find_decoder(AV_CODEC_ID_HEVC);
    codec = avcodec_find_decoder_by_name("libx265");
    if (codec) {
        __android_log_print(ANDROID_LOG_ERROR, "FFmpegConfig", "✓ H265解码器可用\n");
    }