1.准备工作
文件下载:
NDK R25b下载地址:Android NDK历史版本下载网址 - 君*邪 - 博客园 (cnblogs.com)
FFmpeg4.4.4 下载地址:https://ffmpeg.org/releases/ffmpeg-4.4.4.tar.xz
环境配置:
本次编译环境是在PC虚拟机中使用Ubuntu18.04
下载好NDK和FFmpeg 之后,复制到Ubuntu下然后解压,为交叉编译做准备
2.交叉编译FFmpeg流程
解压完FFmpeg源码之后,进入源码根目录,新建编译脚本android.sh
新版 ndk 已放弃 gcc,转而使用更高效的 clang,下述脚本以 clang 为例编译 FFmpeg 源码。支持编译armv8-a 和armv7-a,注意修改成你的NDK目录地址
#!/bin/bash
# 修改成你的NDK目录
TOOLCHAIN=/home/marxist/ndk/android-ndk-r25b-linux/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64
# 最低支持的android sdk版本
API=21
function build_android
{
echo "Compiling FFmpeg for $CPU"
./configure \
--prefix=$PREFIX \
--enable-neon \
--enable-shared \
--enable-small \
--disable-vulkan \
--disable-gpl \
--disable-postproc \
--disable-jni \
--disable-mediacodec \
--disable-decoder=h264_mediacodec \
--disable-static \
--disable-doc \
--disable-programs \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-symver \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--sysroot=$SYSROOT \
--extra-cflags="-mno-stackrealign -Os -fpic -mfpu=neon $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"
#--disable-debug
#--disable-stripping
#--disable-linux-perf
#--disable-hwaccels
make clean
make -j4
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}
function print_supported_cpus
{
echo "Supports the following CPUs:"
echo "1. armv7-a"
echo "2. armv8-a"
}
function print_usage
{
echo "Usage: $0 [CPU]"
echo "Example: $0 armv7-a"
print_supported_cpus
}
# 传入CPU参数
CPU=$1
if [ -z "$CPU" ]; then
print_usage
exit 1
elif [ "$CPU" = "help" ]; then
print_usage
elif [ "$CPU" = "armv7-a" ]; then
ARCH=arm
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
ADDI_LDFLAGS=" "
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -march=$CPU"
build_android
elif [ "$CPU" = "armv8-a" ]; then
ARCH=arm64
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android
else
echo "Unsupported CPU: $CPU"
print_supported_cpus
fi
环境设置
TOOLCHAIN
: 指向 NDK 中包含的 LLVM 工具链的路径。这个路径用于定位编译工具(如 clang)和系统根目录。API
: 设置编译目标的最低 Android API 级别。
函数定义
-
build_android
- 打印正在为特定 CPU 架构编译 FFmpeg。
- 运行 FFmpeg 的
./configure
脚本来配置编译选项。 - 调用
make clean
清理之前的构建结果。 - 使用
make -j4
启动并行编译过程。 - 调用
make install
将编译结果安装到指定的前缀路径$PREFIX
。 - 打印完成编译的消息。
-
print_supported_cpus
- 打印支持的 CPU 类型。
-
print_usage
- 打印脚本的使用方法。
主体逻辑
- 脚本接收一个参数(
$1
),即 CPU 类型。 - 根据传入的 CPU 类型,设置相关的编译参数:
ARCH
: 指定目标架构。CC
和CXX
: 指定 C 和 C++ 编译器。SYSROOT
: 设置系统根目录。CROSS_PREFIX
: 设置交叉编译工具前缀。PREFIX
: 指定安装目录。ADDI_LDFLAGS
: 设置额外的链接器标志。OPTIMIZE_CFLAGS
: 设置针对特定 CPU 优化的编译标志。
- 调用
build_android
函数开始编译流程。
详细配置参数(./configure)
--prefix=$PREFIX
: 指定安装路径。--enable-neon
,--enable-shared
,--enable-small
: 启用 ARM NEON 指令集支持,生成共享库,优化库大小。--disable-...
: 禁用多个功能,如 Vulkan, GPL 功能,文档生成等。--enable-cross-compile
: 启用交叉编译模式。--cross-prefix=$CROSS_PREFIX
: 设置交叉编译前缀。--target-os=android
: 设置目标操作系统为 Android。--arch=$ARCH
: 设置目标架构。--cpu=$CPU
: 设置目标 CPU。--cc=$CC
,--cxx=$CXX
: 设置 C 和 C++ 编译器。--sysroot=$SYSROOT
: 设置系统根目录。--extra-cflags
: 设置额外的编译标志,主要用于性能优化和适应特定硬件。--extra-ldflags
: 设置额外的链接标志。
编译成功之后,在源码目录的android文件夹生成目标CPU的so库和相关的头文件,libavcodec.so libavformat.so libswresample.so libavfilter.so libavutil.so
如果需要其他的so 注释掉编译脚本的disable 就能生成对应的其他库
3.Android项目集成FFmpeg库
在项目main文件夹新建 ThirdLib文件夹,根据需求添加不同CPU架构下的so库,这里主要是添加了arm64v8-a的库(在ThirdLib文件夹新建arm64-v8a 文件夹,方便与CMakeLists.txt做对应)
在cpp文件夹新建include文件夹,将FFmpeg头文件放入进去,头文件编译的时候会生成。
项目目录层级如上图所示,ThirdLib文件夹与cpp文件夹同一目录,include文件夹与CMakeLists.txt同一目录。如果所有设置都与我一致,就可以直接copy CMakeLists.txt
接下来就是配置CMakeLists.txt
引入FFmpeg 头文件,添加FFmpeg相关的库
# Include directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libavcodec.so)
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libavfilter.so)
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libavformat.so)
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libavutil.so)
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libswresample.so)
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../ThirdLib/arm64-v8a/libswscale.so)
编译的时候链接库, firstjni是我的项目名称,不同项目名称自动生成不同的名字,主要是添加五个库
#编译链接库
target_link_libraries( # Specifies the target library.
firstjni
avcodec
avfilter
avformat
avutil
swresample
swscale
# Links the target library to the log library
# included in the NDK.
${log-lib})
4.示例代码,获取FFmpeg版本
在MainActivity生成JNI函数接口
public native String getFFMpegVersion();
使用Android Studio自动创建相应的c++实现
native-lib.cpp 引入FFmpeg头文件
extern "C"{
#include <libavutil/avutil.h>
}
实现接口,获取FFMpeg当前版本
extern "C"
JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_getFFMpegVersion(JNIEnv *env, jobject thiz) {
// TODO: implement getFFMpegVersion()
const char *ffmpeg_version = av_version_info();
return env->NewStringUTF(ffmpeg_version);
}
效果如图: 输出了4.4.4
5.资源下载
提供armv7 和 armv8两个版本
编译平台:NDK R25b
FFmpeg版本: 4.4.4
链接:https://pan.baidu.com/s/1PH6bVRv8_0hda-VjesoRyw
提取码:c0rc