先唠唠:为啥要编译 FFmpeg?
FFmpeg 就像音视频开发的 “万能工具箱”—— 做视频播放、直播推流、剪视频都得用它。但它默认不能直接在安卓手机上用,得用 Linux 系统把它 “改造” 成安卓能识别的文件,这个 “改造” 过程就是 “编译”。
咱们这次要做的:用 Linux 系统(我用的 Ubuntu 20.04),把 FFmpeg 6.1 改成适配安卓的版本,还能兼容最新的 Android 15 手机,开启视频硬件加速。
准备工作:先搞懂 3 个 “基础工具”(小白别慌,超简单)
- 终端:Linux 系统里敲命令的地方;
- NDK:谷歌出的 “安卓编译工具包”,没有它改不了 FFmpeg;
- FFmpeg 源码:就是 FFmpeg 的 “原始代码”,咱们要改的就是它。
第一步:安装编译需要的 “基础配件”(复制粘贴就行)
打开终端(Ctrl+Alt+T),先装几个必须的工具,少一个都可能报错。
操作步骤:
- 终端里输入下面这行命令,按回车(输入时可以直接复制,终端粘贴是
Ctrl + Shift + V,不是普通的 Ctrl+V!):
bash
运行
sudo apt update && sudo apt install -y curl tar make gcc binutils
- 可能会让你输电脑密码(输的时候终端不会显示,输完直接回车就行),等它跑完,没红色 “error” 就说明装好了。
小白解惑:
sudo:就是 “获取管理员权限”,装软件必须用;- 这些工具的作用:
curl是下载文件、tar是解压文件、make/gcc是干活的 “编译器”,不用记,复制就行。
第二步:下载 NDK 27(关键!版本错了必翻车)
NDK 必须用 27 版本(低版本不支持新手机),我教你最简单的下载方式:
操作步骤:
- 终端输入下面的命令,回车(自动下载 NDK 到当前文件夹):
bash
运行
curl -O https://dl.google.com/android/repository/android-ndk-r27b-linux.zip
- 下载完后,输入下面的命令解压(解压需要 1-2 分钟):
bash
运行
# 先建个专门放NDK的文件夹,避免找不着
mkdir -p ~/Android/Sdk/ndk
# 解压NDK到这个文件夹
unzip android-ndk-r27b-linux.zip -d ~/Android/Sdk/ndk/
- 验证是否解压成功:输入下面的命令,能看到 “android-ndk-r27b” 就对了:
bash
运行
ls ~/Android/Sdk/ndk/
避坑:
- 如果下载太慢(curl 命令卡着不动):可以手动下载 —— 打开浏览器,输入链接
https://dl.google.com/android/repository/android-ndk-r27b-linux.zip,下载完后,把文件拖到桌面,再在终端里输入mv ~/桌面/android-ndk-r27b-linux.zip ~/,再执行上面的解压命令就行; - 记住 NDK 的路径:
~/Android/Sdk/ndk/android-ndk-r27b(后面要用到,别改这个路径!)。
第三步:下载 FFmpeg 源码(就 2 行命令)
选 6.1.1 版本(稳定,不容易出问题),还是在终端里操作:
- 输入命令下载源码包:
bash
运行
curl -O https://ffmpeg.org/releases/ffmpeg-6.1.1.tar.xz
- 输入命令解压:
bash
运行
tar -xf ffmpeg-6.1.1.tar.xz
- 进入解压后的文件夹(后续所有操作都在这个文件夹里):
bash
运行
cd ffmpeg-6.1.1
小白解惑:
- 解压后会在 “主文件夹” 里看到一个叫 “ffmpeg-6.1.1” 的文件夹,后续编译都在这里面做,别乱跑文件夹!
第四步:写编译脚本(小白只改 1 个地方,其余复制)
编译 FFmpeg 需要一堆复杂参数,我已经把所有坑都踩过,写好了现成的脚本,你只需要复制粘贴,改 1 个路径就行。
操作步骤:
- 终端里输入下面的命令,新建一个脚本文件(会打开一个简单的编辑器):
bash
运行
nano build_ffmpeg_android.sh
- 把下面的脚本完整复制(复制后,在编辑器里按
Ctrl + Shift + V粘贴):
#!/bin/bash
# 纯小白专用 FFmpeg 编译脚本
# 不用改任何参数,只确认第10行的NDK路径对不对!
# ======================= 只看这一行!确认路径对不对 =======================
NDK_27_PATH="/opt/android-ndk-r27d"
# ========================================================================
# 下面的都不用改!
FFMPEG_VERSION="6.1.1"
API=24 # 最低支持安卓7.0,改高了老手机用不了
set -e # 出错就停,避免越错越远
TOOLCHAIN="$NDK_27_PATH/toolchains/llvm/prebuilt/linux-x86_64"
SYSROOT="$TOOLCHAIN/sysroot"
echo "===== 开始准备编译啦 ====="
echo "NDK路径:$NDK_27_PATH"
echo "最低支持安卓7.0"
# 不同手机版本的优化(不用懂)
configure_arch_specific_options() {
local arch_specific_options=""
case "$ANDROID_ABI" in
"arm64-v8a")
arch_specific_options="--enable-neon --enable-asm"
;;
"armeabi-v7a")
arch_specific_options="--enable-neon --enable-thumb --enable-vfp --extra-cflags=-march=armv7-a"
;;
"x86_64")
arch_specific_options="--disable-neon --disable-asm --extra-cflags=-march=x86-64"
;;
esac
echo "$arch_specific_options"
}
# 适配Android15的16K页大小(必须有)
configure_link_options() {
local link_options=""
case "$ANDROID_ABI" in
"arm64-v8a"|"armeabi-v7a"|"x86_64")
link_options="-Wl,-z,max-page-size=16384 -Wl,-z,separate-loadable-segments"
;;
esac
echo "$link_options"
}
# 核心编译功能(不用懂)
build_android() {
echo -e "\n===== 开始编译 $ANDROID_ABI 版本 ====="
local CC="$TOOLCHAIN/bin/${HOST}${API}-clang"
local CXX="$TOOLCHAIN/bin/${HOST}${API}-clang++"
local NM="$TOOLCHAIN/bin/llvm-nm"
local AR="$TOOLCHAIN/bin/llvm-ar"
local RANLIB="$TOOLCHAIN/bin/llvm-ranlib"
local STRIP="$TOOLCHAIN/bin/llvm-strip"
local PREFIX="$(pwd)/android/$ANDROID_ABI"
mkdir -p "$PREFIX"
# 检查C++20是否能用(我当初栽在这!)
echo "检查工具是否能用..."
cat > /tmp/cpp20_check.cpp << 'EOF'
#include <version>
#if __cplusplus >= 202002L
int main() { return 0; }
#else
#error "工具版本不对,检查NDK!"
#endif
EOF
if ! "$CXX" -std=c++20 -c /tmp/cpp20_check.cpp -o /tmp/cpp20_check.o 2>/dev/null; then
echo "❌ 出错了!大概率是NDK路径写错了,回头看第二步!"
rm -f /tmp/cpp20_check.cpp /tmp/cpp20_check.o
exit 1
fi
rm -f /tmp/cpp20_check.cpp /tmp/cpp20_check.o
echo "✅ 工具没问题!"
local ARCH_OPTS=$(configure_arch_specific_options)
local LINK_OPTS=$(configure_link_options)
# FFmpeg核心配置(修复了参数行格式问题!)
./configure \
--prefix="$PREFIX" \
--target-os=android \
--arch="$ARCH" \
--cpu="$CPU" \
--cc="$CC" \
--cxx="$CXX" \
--nm="$NM" \
--ar="$AR" \
--ranlib="$RANLIB" \
--strip="$STRIP" \
--cross-prefix="$TOOLCHAIN/bin/${HOST}-" \
--sysroot="$SYSROOT" \
$ARCH_OPTS \
--enable-cross-compile \
--enable-pic \
--enable-static \
--disable-shared \
--disable-debug \
--disable-programs \
--disable-doc \
--disable-avdevice \
--disable-postproc \
--disable-avfilter \
--enable-swscale \
--disable-iconv \
--disable-vulkan \
--enable-network \
--enable-zlib \
--enable-jni \
--enable-mediacodec \
--enable-hwaccels \
--enable-swresample \
--enable-protocol=file,http,https,tcp,udp,rtp,rtsp,rtmp \
--enable-demuxer=rtsp,rtp,sdp,mov,mp4,matroska,flv,h264,hevc,aac,mp3 \
--enable-muxer=mp4,mov,matroska,flv \
--enable-decoder=h264,hevc,mpeg4,aac,mp3,pcm_s16le,h264_mediacodec,hevc_mediacodec \
--enable-encoder=aac,pcm_s16le \
--enable-bsf=h264_mp4toannexb,hevc_mp4toannexb,aac_adtstoasc \
--extra-cflags="-Os -fPIC -DANDROID -fno-sanitize=all -DGWP_ASAN_HOOKS=0" \
--extra-cxxflags="-Os -fPIC -DANDROID -std=c++20 -fno-sanitize=all" \
--extra-ldexeflags="-pie" \
--extra-ldflags="-L$SYSROOT/usr/lib/$HOST/$API -landroid -lmediandk -lm -llog -lz $LINK_OPTS" || {
echo "❌ 配置失败!看上面的红色报错,大概率是路径错了"
exit 1
}
# 开始编译(等几分钟就行)
echo "开始编译啦,耐心等~"
make clean
make -j$(nproc) || { echo "❌ 编译失败!"; exit 1; }
make install || { echo "❌ 安装失败!"; exit 1; }
# 检查有没有编译出文件
local LIB_AVCODEC="$PREFIX/lib/libavcodec.a"
if [ -f "$LIB_AVCODEC" ]; then
echo "✅ $ANDROID_ABI 编译成功!文件大小:$(du -h $LIB_AVCODEC | awk '{print $1}')"
else
echo "❌ 没找到编译后的文件,失败了!"
exit 1
fi
echo -e "===== $ANDROID_ABI 搞定!文件在:$PREFIX =====\n"
}
# 编译64位手机版本(主流)
ARCH="aarch64"
CPU="armv8-a"
HOST="aarch64-linux-android"
ANDROID_ABI="arm64-v8a"
build_android
# 编译32位手机版本(老手机)
ARCH="arm"
CPU="armv7-a"
HOST="armv7a-linux-androideabi"
ANDROID_ABI="armeabi-v7a"
build_android
# 编译模拟器版本(测试用)
ARCH="x86_64"
CPU="x86-64"
HOST="x86_64-linux-android"
ANDROID_ABI="x86_64"
build_android
echo "🎉 所有版本都编译完啦!"
echo "📁 文件在:$(pwd)/android/ 文件夹里"
echo "💡 能在安卓7.0+手机用,支持视频硬件加速、直播推流!"
- 关键检查:确认脚本里第 10 行的
NDK_27_PATH和你第二步的 NDK 路径一致(默认就是~/Android/Sdk/ndk/android-ndk-r27b,不用改); - 保存并退出编辑器:按
Ctrl + O→ 按回车 → 按Ctrl + X(这三步别错!)。
避坑:
- 粘贴脚本时如果乱码 / 少行:先把终端窗口放大,再重新复制粘贴;
- 编辑器里别乱改其他内容,哪怕多删一个空格,都可能编译失败!
第五步:运行脚本,坐等编译完成(最轻松的一步)
- 终端里输入命令,给脚本 “运行权限”:
bash
运行
chmod +x build_ffmpeg_android.sh
- 输入命令开始编译(编译需要 5-10 分钟,看电脑快慢,别关终端!):
bash
运行
./build_ffmpeg_android.sh
- 等终端最后显示
🎉 所有版本都编译完啦!,就说明成功了!
小白解惑:
- 编译时终端会刷很多字,不用看,只要没有红色的 “error”“❌” 就没事;
- 如果中途卡住:别关终端,等 10 分钟,大概率是电脑在干活,不是卡了。
第六步:找编译好的文件
-
打开 Linux 文件管理器,进入 “主文件夹”;
-
找到 “ffmpeg-6.1.1” 文件夹,内部会新增 “android” 目录;
-
“android” 文件夹包含 3 个架构目录(
arm64-v8a/armeabi-v7a/x86_64),每个架构目录下有两个核心文件夹:lib:存放.a后缀的 FFmpeg 静态库文件(程序运行的核心);include:存放 FFmpeg 的头文件(代码开发时必须引用,否则无法调用 FFmpeg 的 API);- 补充说明:
arm64-v8a适配 64 位 Android 手机(主流设备),armeabi-v7a适配 32 位 Android 手机(旧设备),x86_64适配 Android 模拟器(开发测试用);
-
集成到 Android 项目的完整步骤:
- 第一步:在 Android 项目的
src/main/目录下,新建jniLibs文件夹,再按架构新建子文件夹(arm64-v8a/armeabi-v7a/x86_64); - 第二步:将各架构目录下
lib文件夹中的.a文件,分别复制到jniLibs对应架构的子文件夹中; - 第三步:在 Android 项目的
src/main/目录下,新建cpp文件夹(若无),将各架构目录下include文件夹中的所有文件,复制到cpp目录下(所有架构的include内容一致,复制一份即可); - 第四步:在项目的
CMakeLists.txt(或Android.mk)中,配置头文件路径和链接静态库,即可调用 FFmpeg 的 API。
- 第一步:在 Android 项目的
小白最常踩的 5 个坑(我全踩过!)
坑 1:终端粘贴用 Ctrl+V 没反应
- 解决:Linux 终端粘贴是
Ctrl + Shift + V,复制是Ctrl + Shift + C(和普通软件不一样!);
坑 2:编译时提示 “C++20 工具不对”
- 原因:NDK 路径错了 / NDK 版本不是 27;
- 解决:回到第二步,重新检查 NDK 路径,确保是 27 版本;
坑 3:Android 15 手机运行崩溃
- 原因:没适配 16K 页大小(脚本里已经加了,不用改);
- 解决:确认脚本里有
max-page-size=16384这行;
坑 4:终端提示 “权限不够”
- 解决:命令前加
sudo(比如sudo chmod +x build_ffmpeg_android.sh),输密码就行;
坑 5:编译完找不到文件
- 原因:没进入
ffmpeg-6.1.1文件夹就运行脚本; - 解决:先输
cd ffmpeg-6.1.1,再运行脚本。
总结
- 全程只需要复制粘贴命令,不用懂任何专业知识;
- 唯一要确认的:NDK 路径是
~/Android/Sdk/ndk/android-ndk-r27b; - 编译成功后,
android文件夹里的.a文件就是能用的库; - 遇到报错先看终端里的红色字,大概率是路径错了 / 没装依赖。
真的不用怕!我第一次弄的时候,光是终端粘贴就试了 10 次,现在回头看,其实就是 “复制→粘贴→等” 三步。如果还是卡壳,评论区问我,我看到就回~
最后打个广告,低延迟播放器就是基于此打造的项目地址:https://github.com/anwz0611/android-ffmpeg-stream-player 有需求的小伙伴star一下 谢谢关注~