背景
上一篇文章介紹了ffmpeg在windows平臺的使用,這一篇以Android 平臺為例券盅,介紹下ffmpeg的編譯频敛,以及在Android應(yīng)用層如何使用项郊。
編譯ffmpeg
環(huán)境
為保證統(tǒng)一的環(huán)境,本文使用wmware虛擬機(jī)進(jìn)行編譯斟赚。
我本地有兩套linux 環(huán)境呆抑,kali linux和r0env,其實(shí)只要普通的ubuntu 就能支持編譯汁展,推薦使用kali,很多組件免安裝厌殉。
下載
解壓
tar -xvJf ffmpeg-5.1.2.tar.xz
查看編譯配置
./configure --help
編譯
對于Android 環(huán)境食绿,需要生成動態(tài)鏈接庫,需要依賴Android ndk公罕,
下載ndk器紧,粘貼到虛擬機(jī)目錄,
wget https://dl.google.com/android/repository/android-ndk-r25b-linux.zip
解壓ndk到該目錄
unzip android-ndk-r25b-linux.zip
修改configure文件的路徑配置,注意空格是必須的
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
在ffmpeg根目錄下新建android_build.sh楼眷,執(zhí)行
chmod 777 android_build.sh
為避免出現(xiàn)bad interpreter: /bin/bash^M: 沒有那個(gè)文件或目錄問題铲汪,執(zhí)行
sed -i 's/\r$//' android_build.sh
格式處理之后,再執(zhí)行
./android_build.sh
android_build.sh 的 具體配置如下:
#!/bin/bash
# 如果編譯的時(shí)候罐柳,編譯日志輸出掌腰,同時(shí)最后成功,但是沒有找到文件夾张吉,說明編譯是失敗的齿梁,
# 設(shè)置你自己的NDK位置
NDK_HOME=/root/ndk/android-ndk-r25b
# 設(shè)置你自己的平臺,Mac上的是 darwin-x86_64 ,linux上的是 linux-x86_64勺择,linux上的是 windows-x86_64
NDK_HOST_PLATFORM=linux-x86_64
# 動態(tài)庫 so 編譯后的輸出文件夾
PREFIX=$(pwd)/android-build-out
# 刪除 so 的輸出文件夾
# rm -rf $PREFIX
# gcc的編譯選項(xiàng)拓展參數(shù)
# ADDI_CFLAGS="-marm"
# 編譯的庫應(yīng)用于什么系統(tǒng)创南,android 的可以有 linux、android
TARGET_OS=android
# 下面的參數(shù)都是需要變化的
# 處理器類型(有 arm省核、aarch64稿辙、x86、x86_64)
# ARCH_TYPE=arm
ARCH_TYPE=""
# CPU架構(gòu)類型
# CPU_TYPE=armv7-a
CPU_TYPE=""
# ndk 的cpu架構(gòu)對應(yīng)的文件夾气忠,就是(${NDK_HOME}/toolchains/llvm)這部分
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
NDK_TOOLCHAINS_CPU_TYPE_FOLDER=""
# ndk 的bin目錄下以指定字符串開頭的文件邻储,后面的 - 表示通配
# NDK_TOOLCHAINS_BIN_FOLDER_FILES="arm-linux-androideabi-"
NDK_TOOLCHAINS_BIN_FOLDER_FILES=""
TOOLCHAIN=""
# Android交叉編譯的工具鏈
# /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang
# ${NDK_HOME}/toolchains/llvm/prebuilt/${NDK_HOST_PLATFORM}/bin/aarch64-linux-android21-
# CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}"
CROSS_PREFIX=""
# SYSROOT 指向的目錄,即 ${NDK_HOME}/platforms 目錄下的具體平臺文件夾的cpu架構(gòu)文件夾
# SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi
SYSROOT_PLATFORM_AND_ARCH=""
# 交叉編譯工具的根路徑(android平臺使用的庫和頭文件的路徑)
# /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android
# SYSROOT="${TOOLCHAIN}/sysroot/"
# usr/lib/${SYSROOT_PLATFORM_AND_ARCH}/
SYSROOT=""
# android 里的 abi 類型[例如:armeabi, arm64-v8a, armeabi-v7a, x86, x86_64]
ANDROID_ABI=""
# ffmpeg 的一些常見選項(xiàng)
COMMON_OPTIONS="
--target-os=${TARGET_OS} \
--enable-static \
--enable-shared \
--enable-small \
--disable-programs \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-doc \
--disable-symver \
--disable-asm \
--enable-cross-compile \
--enable-pic \
--disable-neon \
--disable-debug \
"
CC=""
CXX=""
NM=""
STRIP=""
# 更新需要拼接的參數(shù)(不重新調(diào)用一下笔刹,最新值就沒有替換上去)
update_param() {
TOOLCHAIN="${NDK_HOME}/toolchains/${NDK_TOOLCHAINS_CPU_TYPE_FOLDER}/prebuilt/${NDK_HOST_PLATFORM}"
CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-"
SYSROOT="${TOOLCHAIN}/sysroot"
CC="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
CXX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang++"
LD="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
AR="${TOOLCHAIN}/bin/llvm-ar"
NM="${TOOLCHAIN}/bin/llvm-nm"
STRIP="${TOOLCHAIN}/bin/llvm-strip"
RUNLIB="${TOOLCHAIN}/bin/llvm-ranlib"
CC_L="${TOOLCHAIN}/lib64/clang/14.0.6/lib/linux"
SYSROOT_L="${SYSROOT}/usr/lib/${SYSROOT_PLATFORM_AND_ARCH}"
LD_L="${TOOLCHAIN}/bin/ld.lld"
}
# 聲明一個(gè)編譯 Android 的方法
build_android()
{
update_param
./configure \
--prefix=${PREFIX} \
--libdir=${PREFIX}/libs/${ANDROID_ABI} \
--incdir=${PREFIX}/includes \
--pkgconfigdir=${PREFIX}/pkgconfig/${ANDROID_ABI} \
--arch=${ARCH_TYPE} \
--cpu=${CPU_TYPE} \
--cross-prefix=${CROSS_PREFIX} \
--sysroot=${SYSROOT} \
--extra-cflags="-Os -fpic -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOA -DVK_ENABLE_BETA_EXTENSIONS=0" \
--extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L${PREFIX}/libs" \
--extra-ldexeflags=-pie \
--cc=${CC} \
--cxx=${CXX} \
--ld=${LD} \
--ar=${AR} \
--ranlib=${RUNLIB} \
--nm=${NM} \
--strip=${STRIP} \
${COMMON_OPTIONS} \
${ADDITIONAL_CONFIGURE_FLAG}
make clean
make -j8
make install
echo "${ANDROID_ABI} install完成了"
# ${LD_L} -L${PREFIX}/libs/${ANDROID_ABI} -L${CC_L} \
# -rpath-link=${SYSROOT_L} -L${SYSROOT_L} -soname libffmpeg.so \
# -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o ${PREFIX}/libffmpeg.so \
# -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -lswresample \
# -lc -ldl -lm -lz -llog \
# --dynamic-linker=/system/bin/linker
# echo "${ANDROID_ABI} 編譯完成了"
}
#編譯 armeabi-v7a
ANDROID_ABI=armeabi-v7a
ARCH_TYPE=arm
CPU_TYPE=armv7-a
NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
API=21
NDK_TOOLCHAINS_BIN_FOLDER_FILES=armv7a-linux-androideabi${API}
SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi/${API}
build_android
# # 編譯 arm64-v8a
# ANDROID_ABI=arm64-v8a
# ARCH_TYPE=aarch64
# CPU_TYPE=armv8-a
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=aarch64-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=aarch64-linux-android/${API}
# build_android
# # 編譯 x86
# ANDROID_ABI=x86
# ARCH_TYPE=x86
# CPU_TYPE=i686
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=i686-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=x86_64-linux-android/${API}
# build_android
# # 編譯 x86_64
# ANDROID_ABI=x86_64
# ARCH_TYPE=x86_64
# CPU_TYPE=x86_64
# NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
# API=21
# NDK_TOOLCHAINS_BIN_FOLDER_FILES=x86_64-linux-android${API}
# SYSROOT_PLATFORM_AND_ARCH=i686-linux-android/${API}
# build_android
編譯之后的產(chǎn)物:每種架構(gòu)都包含了 libavcodec芥备、libavdevice、libavfilter舌菜、libavformat萌壳、libavutil、libswscale日月、libswresample袱瓮。
這樣多個(gè)so不便于項(xiàng)目管理,可以通過改進(jìn)腳本去只生成一個(gè)so爱咬。
遇到的問題:
1.clang is unable to create an executable file. C compiler test failed尺借,可以查看ffbuild 目錄下的config.log文件,該文件記錄了編譯日志
2.include/vulkan/vulkan.h:89:10: fatal error: 'vulkan_beta.h' file not found精拟,找到ndk發(fā)現(xiàn)該文件確實(shí)不存在燎斩,解決辦法:
配置extra-cflags時(shí)添加 -DVK_ENABLE_BETA_EXTENSIONS=0,
或者禁用vulkan: --disable-vulkan
3.注意配置nm和strip
4.編譯static的時(shí)候蜂绎,遇到install-libavdevice-static報(bào)錯栅表,發(fā)現(xiàn)ranlib的配置缺少空格
gcc版本的ndk編譯ffmpeg的編譯可以參考以下文章:http://www.ihubin.com/blog/android-ffmpeg-demo-3/
編譯腳本總結(jié)
以下是編譯腳本相關(guān)參數(shù)的含義,參考了該文章https://juejin.cn/post/7149468268674154510#heading-3
--disable-static : 不構(gòu)建靜態(tài)庫
--enable-shared : 編譯生成動態(tài)庫
--enable-small : 降低庫體積
--disable-programs : 不構(gòu)建命令行程序(指ffmpeg师枣、ffplay以及ffprobe)
--disable-ffmpeg : 不構(gòu)建ffmpeg程序
--disable-ffplay : 不構(gòu)建ffplay程序
--disable-ffprobe : 不構(gòu)建ffprobe程序
--disable-doc : 不產(chǎn)生文檔
--disable-htmlpages : 不產(chǎn)生 HTML類型的文檔
--disable-manpages : 不產(chǎn)生 manpage類型的文檔
--disable-podpages : 不產(chǎn)生 POD 類型的文檔
--disable-txtpages : 不產(chǎn)生 text 類型的文檔
--enable-cross-compile : 開啟交叉編譯
--enable-pic : 生成位置無關(guān)代碼
--disable-asm:禁止所有匯編優(yōu)化
--disable-neon:禁止NEON優(yōu)化
--disable-debug:禁止調(diào)試符號怪瓶,腳本中默認(rèn)開啟。
獲取生成的文件
打包out目錄
zip -r ffmpeg_out.zip .
在虛擬機(jī)的可視化界面践美,復(fù)制生成的 ffmpeg_out.zip洗贰,粘貼到電腦中。
ffmpeg編譯之后的是so陨倡,并沒有自帶的java層調(diào)用庫敛滋,所以我們使用的時(shí)候可以自己寫一個(gè)類庫,或者用網(wǎng)上的寫好第三方庫兴革。
Android調(diào)用
創(chuàng)建Android C++項(xiàng)目
引用so庫矛缨,編寫c調(diào)用類
在 app/src/main/ 目錄下,新建文件夾,并命名為 jniLibs 箕昭,接著灵妨,在 jniLibs 目錄下,新建 armeabi-v7a 目錄
在 cpp 目錄下落竹,新建 ffmpeg 目錄泌霍,然后把編譯時(shí)生成的 includes 文件粘貼進(jìn)來。
修改native-lib.cpp
#include <jni.h>
#include <string>
#include <unistd.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavcodec/jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_ffmpeglib_MainActivity_ffmpegInfo(JNIEnv *env, jobject /* this */) {
std::string ffmpeg = avcodec_configuration();
return env->NewStringUTF(ffmpeg.c_str());
}
}
遇到的問題:
No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi
配置ndk路徑即可
編寫java調(diào)用類
執(zhí)行程序述召,可以看到編譯信息
編譯jni生成aar
將項(xiàng)目改造成app和lib兩個(gè)module朱转,將剛才的代碼移植到module中,可以編譯生成aar积暖,包含lib中的so和jni鏈接生成的動態(tài)庫藤为。
主項(xiàng)目引用或者發(fā)布到私服,即可在項(xiàng)目正常使用夺刑。