为Android编译ffmpeg
近来学习音视频, ffmpeg又是音视频中绕不开的,于是编译ffmpeg. 试了好多天, 总算摸出点规律, 记录下来给需要的朋友提供一点线索.
编译
- 从ffmpeg仓库克隆源码
- 安装ndk
- 执行编译脚本
sysroot
sysroot
被称为逻辑根目录,只在链接过程中起作用,作为交叉编译工具链搜索库文件的根路径,如配置--sysroot=dir
,则dir
作为逻辑根目录,链接器将在dir/usr/lib
中搜索库文件。
脚本
ndk路径的tips:
打开Android Studio中SDK Manager,进到Android SDK Location文件夹下
再进到ndk文件夹,里面是你已经下载好的ndk,进到一个版本中,就是ndk的路径。
建议将脚本保存到克隆下来的ffmpeg仓库路径中.
脚本代码:
#!/bin/bash
# NDK的路径 (在bash.rc、zshrc中设置的路径,其中的NDK_HOME需要配置环境变量,如果你不配,在这改成你自己的也可以。)
NDK_HOME=~/Library/Android/sdk/ndk/25.1.8937393
# NDK_ROOT=你的NDK路径
TOOLCHAIN_PREFIX=$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64
echo "<<<<<< FFMPEG 交叉编译 <<<<<<"
echo "<<<<<< 基于当前系统NDK地址: $NDK_HOME <<<<<<"
# 编译的相关参数
# 目标平台的CPU指令类型 ARMv8
# armv7-a
CPU=armv8-a
# 架构类型 : ARM
ARCH=arm64
# 操作系统
OS=android
# 平台
PLATFORM=aarch64-linux-android
OPTIMIZE_CFLAGS="-march=$CPU"
# 指定输出路径
PREFIX=~/tools/outputs/ffmpeg/aarch64/$CPU
# SYSROOT
SYSROOT=$TOOLCHAIN_PREFIX/sysroot
# 交叉编译工具链
CROSS_PREFIX=$TOOLCHAIN_PREFIX/bin/llvm-
# Android交叉编译工具链的位置
ANDROID_CROSS_PREFIX=$TOOLCHAIN_PREFIX/bin/${PLATFORM}29
echo ">>>>>> FFMPEG 开始编译 >>>>>>"
# ffmpeg所有configure
# https://github.com/FFmpeg/FFmpeg/blob/master/configure
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-gpl \
--enable-neon \
--enable-hwaccels \
--enable-postproc \
--enable-jni \
--enable-small \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffplay \
--disable-avdevice \
--disable-debug \
--disable-static \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=$OS \
--arch=$ARCH \
--cpu=$CPU \
--cc=${ANDROID_CROSS_PREFIX}-clang \
--cxx=${ANDROID_CROSS_PREFIX}-clang++ \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fPIC $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"
make clean
# 创建目标路径,如果不存在的话,最终产物存储在Prefix对应路径之下。
mkdir -p $PREFIX
sudo make -j8
sudo make install
echo "<<<<<< 编译完成,产物存储在:$PREFIX <<<<<<"
找不到从哪抄来的了, 找到了再补上
编译成功后产物的路径中生成四个文件夹, 分别是bin, include, lib, share.
文件夹 | 包含内容 |
---|---|
bin | 可执行文件, Android中不需要 |
lib | so文件 |
include | 头文件 |
share | 示例, 文档. 编译configure禁用了文档, 因此只有示例 |
集成
Android Studio中创建native module.
将产物中的include文件夹复制到新建module的src/main/cpp文件夹下
产物lib文件夹下的so文件复制到module的src/main/jniLibs/ABI
ABI取决于你编译的. 比如你编译的是arm64-v8a的, 上面的ABI就是arm64-v8a. 那么你需要的目标路径就是src/main/jniLibs/arm64-v8a
CMakeLists.txt
# cmake version, 创建native module升成
cmake_minimum_required(VERSION 3.22.1)
# 项目名, 创建native module升成
project("native_ffmpeg")
#引入头文件, 引入include文件夹
include_directories(include)
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
native_ffmpeg.cpp)
# Set a normal, cache, or environment variable to a given value
# cmake官方这么写解释set, 设置变量
set(ANDROID_SO_DIR ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
# add_library: Add a library to the project using the specified source files.
# set_target_properties: Sets properties on targets. The syntax for the command is to list all the targets you want to change, and then provide the values you want to set next.
# avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libavcodec.so)
# avfilter
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libavfilter.so)
# avformat
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libavformat.so)
# avutil
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libavutil.so)
# postproc
add_library(postproc SHARED IMPORTED)
set_target_properties(postproc PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libpostproc.so)
# swresample
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libswresample.so)
# swscale
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${ANDROID_SO_DIR}/libswscale.so)
# Specify libraries or flags to use when linking a given target and/or its dependents.
# 还是cmake官方的解释
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log
# ffmpeg libs
avcodec
avfilter
avformat
avutil
postproc
swresample
swscale)
命令详情参考:
Android NDK开发基础NDK即Native Development Kit,是Android上用来开发c/c++ - 掘金
CMake Tutorial — CMake 3.30.3 Documentation
最后, 点击Android Studio上的小锤子开始build, 完成在module的build/outputs/aar中看到aar.
调用
编辑JNI文件
#include <jni.h>
#include <string>
// CPP中添加C的头文件
extern "C" {
#include <libavcodec/avcodec.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_native_1ffmpeg_NativeLib_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
return env->NewStringUTF(avcodec_configuration());
}
懒人提示: 先写Java native方法或者Kotlin external方法由IDE提示生成CPP文件更方便
依赖ffmpeg模块
dependencies {
implementation(project(":native-ffmpeg"))
}
Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// Return the libavcodec build-time configuration
findViewById<TextView>(R.id.tv_greeting).setText(NativeLib().stringFromJNI())
}
}