在 Android 音视频开发领域,FFmpeg 就像是一把瑞士军刀。它包含了几乎所有音视频处理所需的功能:编解码、转码、流媒体处理、滤镜效果等等。但官方提供的 FFmpeg 并不能直接在 Android 平台上使用,主要原因有:
提示:FFmpeg 6.1 版本特别加强了对 Android 15 的兼容性,包括对 16K 内存页大小的支持,这也是我们选择这个版本的重要原因。
在终端执行以下命令安装基础工具链:
bash复制sudo apt update && sudo apt install -y \
curl tar xz-utils \
make automake autoconf libtool \
gcc g++ clang \
pkg-config \
nasm \
git
这些工具各自的作用:
curl:用于下载文件tar 和 xz-utils:解压工具make/automake/autoconf:构建工具gcc/g++/clang:编译器pkg-config:库文件路径管理nasm:汇编器(FFmpeg 某些优化需要)NDK(Native Development Kit)是 Android 原生开发的工具链。我们选择 r27 版本是因为:
bash复制# 创建 Android SDK 目录(如果不存在)
mkdir -p ~/Android/Sdk/ndk
# 下载 NDK r27b
curl -O https://dl.google.com/android/repository/android-ndk-r27b-linux.zip
# 解压到指定目录
unzip android-ndk-r27b-linux.zip -d ~/Android/Sdk/ndk/
# 验证安装
ls ~/Android/Sdk/ndk/android-ndk-r27b
注意:NDK 路径非常重要,后续编译脚本会引用这个路径。建议保持默认路径不要修改。
bash复制# 下载源码包
curl -O https://ffmpeg.org/releases/ffmpeg-6.1.1.tar.xz
# 解压
tar -xf ffmpeg-6.1.1.tar.xz
# 进入源码目录
cd ffmpeg-6.1.1
libavcodec/:编解码器实现libavformat/:格式处理libavutil/:通用工具函数libswscale/:图像缩放和色彩空间转换configure:配置脚本Makefile:构建规则bash复制nano build_ffmpeg_android.sh
将以下脚本内容粘贴进去(注意修改 NDK 路径):
bash复制#!/bin/bash
# FFmpeg 6.1 Android 编译脚本
# ======== 配置部分 ========
NDK_PATH="$HOME/Android/Sdk/ndk/android-ndk-r27b"
API_LEVEL=24 # 最低支持 Android 7.0
# =========================
# 工具链路径
TOOLCHAIN="$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64"
# 支持的ABI列表
ABIS=("arm64-v8a" "armeabi-v7a" "x86_64")
# 清理旧编译
make clean > /dev/null 2>&1
for ABI in "${ABIS[@]}"; do
echo "正在编译 $ABI 版本..."
case $ABI in
"arm64-v8a")
ARCH="aarch64"
CPU="armv8-a"
TARGET="aarch64-linux-android"
EXTRA_CFLAGS="-march=armv8-a"
EXTRA_LDFLAGS=""
;;
"armeabi-v7a")
ARCH="arm"
CPU="armv7-a"
TARGET="armv7a-linux-androideabi"
EXTRA_CFLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=softfp"
EXTRA_LDFLAGS="-Wl,--fix-cortex-a8"
;;
"x86_64")
ARCH="x86_64"
CPU="x86-64"
TARGET="x86_64-linux-android"
EXTRA_CFLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"
EXTRA_LDFLAGS=""
;;
esac
# 设置交叉编译工具
CC="$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang"
CXX="$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang++"
AR="$TOOLCHAIN/bin/llvm-ar"
STRIP="$TOOLCHAIN/bin/llvm-strip"
# 输出目录
PREFIX="$(pwd)/android/$ABI"
# 配置参数
./configure \
--prefix="$PREFIX" \
--enable-cross-compile \
--target-os=android \
--arch="$ARCH" \
--cpu="$CPU" \
--cc="$CC" \
--cxx="$CXX" \
--ar="$AR" \
--strip="$STRIP" \
--extra-cflags="$EXTRA_CFLAGS -Os -fPIC" \
--extra-ldflags="$EXTRA_LDFLAGS" \
--sysroot="$TOOLCHAIN/sysroot" \
--enable-neon \
--enable-hwaccels \
--enable-mediacodec \
--enable-jni \
--enable-static \
--disable-shared \
--disable-programs \
--disable-doc \
--disable-avdevice \
--disable-postproc \
--enable-zlib \
--enable-network \
--enable-protocols \
--enable-demuxers \
--enable-muxers \
--enable-decoders \
--enable-encoders \
--enable-bsfs \
--enable-swscale \
--enable-swresample || exit 1
# 开始编译
make -j$(nproc) || exit 1
make install || exit 1
echo "$ABI 版本编译完成!"
done
echo "所有架构编译完成!"
echo "输出目录: $(pwd)/android"
硬件加速支持:
--enable-neon:启用 ARM NEON 指令集优化--enable-mediacodec:启用 Android MediaCodec 硬件编解码体积优化:
--disable-shared --enable-static:生成静态库减小体积--disable-programs:不编译 ffmpeg/ffprobe 等可执行文件Android 特定配置:
--target-os=android:指定目标系统--enable-jni:启用 JNI 支持bash复制# 给脚本执行权限
chmod +x build_ffmpeg_android.sh
# 开始编译
./build_ffmpeg_android.sh
编译过程大约需要 10-30 分钟,取决于机器性能。成功后会输出类似信息:
code复制arm64-v8a 版本编译完成!
armeabi-v7a 版本编译完成!
x86_64 版本编译完成!
所有架构编译完成!
输出目录: /path/to/ffmpeg-6.1.1/android
编译完成后,android 目录结构如下:
code复制android/
├── arm64-v8a/
│ ├── include/ # 头文件
│ └── lib/ # 静态库文件
├── armeabi-v7a/
│ ├── include/
│ └── lib/
└── x86_64/
├── include/
└── lib/
创建 jniLibs 目录:
code复制app/
└── src/
└── main/
├── jniLibs/
│ ├── arm64-v8a/
│ ├── armeabi-v7a/
│ └── x86_64/
└── cpp/
└── include/ # FFmpeg 头文件
复制库文件:
lib/*.a 文件复制到对应 jniLibs/ABI/ 目录include/ 内容复制到 cpp/include/(只需复制一份)配置 CMakeLists.txt:
cmake复制cmake_minimum_required(VERSION 3.10.2)
# 设置 FFmpeg 路径
set(FFMPEG_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
set(FFMPEG_LIB_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
# 添加头文件路径
include_directories(${FFMPEG_DIR}/include)
# 添加 FFmpeg 库
add_library(avcodec STATIC IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_LIB_DIR}/libavcodec.a)
# 其他库类似添加...
# avformat, avutil, swresample, swscale
# 链接到你的原生库
target_link_libraries(your-lib
avcodec
avformat
avutil
swresample
swscale
# 其他依赖...
)
"C++20 不支持" 错误:
--extra-cxxflags 包含 -std=c++20"找不到头文件" 错误:
sysroot 路径设置正确链接错误:
Android 15 崩溃:
-Wl,-z,max-page-size=16384 链接参数视频播放黑屏:
音频不同步:
如果需要支持更多编解码器,可以修改脚本中的以下参数:
bash复制--enable-decoder=mp3,aac,h264,hevc,... # 添加需要的解码器
--enable-encoder=aac,pcm_s16le,... # 添加需要的编码器
如果需要使用滤镜功能,可以添加:
bash复制--enable-avfilter
--enable-filters
但要注意这会显著增加库体积。
针对特定设备优化:
bash复制# 针对 ARMv8 的额外优化
EXTRA_CFLAGS="-march=armv8.2-a+fp16+dotprod"
启用硬件加速:
--enable-mediacodec 已启用AV_HWDEVICE_TYPE_MEDIACODEC内存优化:
av_frame_get_buffer 分配对齐的内存多线程处理:
c复制AVCodecContext *codec_ctx = ...;
codec_ctx->thread_count = 4;
实时流优化:
c复制// 初始化 FFmpeg
avformat_network_init();
// 打开媒体文件
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, url, NULL, NULL);
// 查找流信息
avformat_find_stream_info(fmt_ctx, NULL);
// 查找视频流
int video_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
// 获取解码器
AVCodecParameters *codecpar = fmt_ctx->streams[video_stream_idx]->codecpar;
AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
// 创建解码器上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codecpar);
// 启用硬件加速
if (is_hw_accel_supported) {
codec_ctx->get_format = get_hw_format;
av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_MEDIACODEC, NULL, NULL, 0);
}
// 打开解码器
avcodec_open2(codec_ctx, codec, NULL);
// 解码循环
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index == video_stream_idx) {
avcodec_send_packet(codec_ctx, pkt);
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
// 处理解码后的帧
render_frame(frame);
}
}
av_packet_unref(pkt);
}
c复制// 创建输出上下文
AVFormatContext *out_fmt_ctx = NULL;
avformat_alloc_output_context2(&out_fmt_ctx, NULL, "flv", rtmp_url);
// 添加视频流
AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
// 打开输出
avio_open(&out_fmt_ctx->pb, rtmp_url, AVIO_FLAG_WRITE);
// 写头信息
avformat_write_header(out_fmt_ctx, NULL);
// 推流循环
while (1) {
av_read_frame(in_fmt_ctx, pkt);
if (pkt->stream_index == video_idx) {
// 重新计算时间戳
av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base);
pkt->stream_index = out_stream->index;
av_interleaved_write_frame(out_fmt_ctx, pkt);
}
av_packet_unref(pkt);
}
禁用不需要的组件:
bash复制--disable-everything # 先禁用所有
--enable-decoder=h264,hevc,aac # 只启用需要的
优化编译标志:
bash复制--extra-cflags="-Os -ffunction-sections -fdata-sections"
--extra-ldflags="-Wl,--gc-sections"
去除调试信息:
bash复制--disable-debug
--extra-cflags="-g0"
bash复制# 针对 ARM Cortex-A75 优化
--extra-cflags="-mcpu=cortex-a75 -mtune=cortex-a75"
# 启用所有 NEON 优化
--extra-cflags="-mfpu=neon-vfpv4 -mfloat-abi=hard"
如果需要为多个平台编译,注意:
macOS 编译:
Windows 交叉编译:
多版本兼容:
c复制// 设置日志级别
av_log_set_level(AV_LOG_DEBUG);
// 自定义日志回调
av_log_set_callback(my_log_callback);
bash复制# 编译时启用性能分析
--extra-cflags="-pg"
# 使用 perf 工具分析
perf record ./ffmpeg ...
perf report
输入验证:
内存安全:
网络安全:
Android 15 适配:
64-bit 强制要求:
FFmpeg 版本升级:
预编译库:
FFmpegKit:
自行编译:
官方文档:
开源项目参考:
论坛支持:
可以将编译过程集成到 CI/CD 流程中:
yaml复制build_android:
image: ubuntu:20.04
script:
- apt update && apt install -y curl tar make gcc
- ./build_ffmpeg_android.sh
artifacts:
paths:
- android/
yaml复制jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt update && sudo apt install -y curl tar make gcc
- run: ./build_ffmpeg_android.sh
- uses: actions/upload-artifact@v2
with:
name: ffmpeg-android
path: android/
在实际项目中使用 FFmpeg 时,我有几点经验分享:
版本控制:将编译好的 FFmpeg 库和头文件纳入版本管理,确保团队一致性。
文档记录:详细记录编译参数和配置选项,便于后续维护。
性能测试:在不同设备上测试性能,特别是低端设备。
逐步升级:FFmpeg 版本升级时,先在小范围测试兼容性。
社区参与:遇到问题时,积极查阅社区讨论和 issue。
FFmpeg 是一个功能强大但复杂的库,需要耐心学习和实践。希望本教程能帮助你顺利在 Android 项目中使用 FFmpeg。如果在实践中遇到问题,建议先查阅 FFmpeg 文档和社区讨论,大多数常见问题都能找到解决方案。