文章目錄
在linux下交叉編譯android平臺的ffmpeg4.2動態庫
環境及版本
-
linux(本人在cestos和ubuntu上測試通過)
- 這裏貼一下cestos的內核
[root@bogon lib]# uname -a Linux bogon 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
-
ffmpeg4.2.x
-
ndk r21
編譯過程
下載ffmpeg
- 用git下載,這樣下載到的是最新版的,我當時最新版是4.2.X
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
- 下載完後打開ffmpeg中的configure文件,搜索android關鍵字,可以發現如下代碼
set_default target_os
if test "$target_os" = android; then
cc_default="clang"
fi
ar_default="${cross_prefix}${ar_default}"
cc_default="${cross_prefix}${cc_default}"
cxx_default="${cross_prefix}${cxx_default}"
nm_default="${cross_prefix}${nm_default}"
pkg_config_default="${cross_prefix}${pkg_config_default}"
if ${cross_prefix}${ranlib_default} 2>&1 | grep -q "\-D "; then
ranlib_default="${cross_prefix}${ranlib_default} -D"
else
ranlib_default="${cross_prefix}${ranlib_default}"
fi
strip_default="${cross_prefix}${strip_default}"
windres_default="${cross_prefix}${windres_default}"
- 我們可以看到當指定target_os爲android,cc默認用的是clang編譯,而且最後的cc_default都會加上前綴${cross_prefix}的值,其他的還有cxx,nm,ar,ranlib,strip等
- 這裏順便說下我的目錄結構,我在ffmpeg目錄中創建了build-android/build-android.sh腳本,用於編譯
-ffmpeg
- ffbuild
- config.log
- ...
- configure
- ...
- build-android
- build-android.sh
下載ndk
- 用wget下載ndk r21
- 如果沒有wget就安裝一下,ubuntu下用 apt install wget 命令
wget -c https://dl.google.com/android/repository/android-ndk-r21-linux-x86_64.zip?hl=zh_cn
unzip android-ndk-r21-linux-x86_64.zip
- 解壓後就可以看到android-ndk-r21這個目錄了
- 打開這個目錄:android-ndk-r21/toolchains/llvm/prebuilt/linux-x86_64/bin
- 從上圖我們可以看到clang的不僅與架構有關,還與android的版本有關,這裏我選擇armv7a(最新ndk支持最低的就是armv7a了)和androideabi21(也就是android的5.0)
- 但是我們除了指定cc外,還得指定nm和ar等,可是你發現沒有,armv7a只有clang和clang++,而nm和ar等則放在arm開頭,而且不需要指定android版本
如果你對ar,nm等命令感興趣,可以閱讀下這篇文章:ar、ranlib、nm命令詳解 - 簡書
arm-linux-androideabi-nm arm-linux-androideabi-ar armv7a-linux-androideabi21-clang armv7a-linux-androideabi21-clang++
- 這將導致什麼問題呢,前面我們說過configure中的cc_default以及ar_default等是與${cross_prefix}這個變量拼接起來的,但目前看來他們前綴不一致啊,一個是arm-linux-androideabi-,一個是armv7a-linux-androideabi21-,那麼沒辦法了,爲了順利編譯通過,只能修改configure文件了(但其實我不是很情願修改configure文件,畢竟這個文件不是我寫的,最好不要去動它,後來真的找到了一種不用修改configure文件的辦法)
配置clang頭文件和庫的位置
- 這裏由於使用到clang,因此最後的配置文件中要加入clang的頭文件和庫,這個可以配置在 –extra-cflags 和 –extra-ldflags 裏,如下所示
# ndk的路徑
export NDK=/home/hwj/ndk/android-ndk-r21
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
./configure
...
--extra-cflags="-I$SYSROOT/usr/include" \
--extra-ldflags="-L$SYSROOT/usr/lib" \
...
庫工具路徑配置
- ffmpeg在編譯成動靜態庫時會使用到諸如nm,ar,ranlib,strip等工具,但我們前面已經分析過,它們的前綴和clang不一致,而configure統一用cross_prefix這個參數進行設置前綴,這樣就導致clang能用了,nm等卻用不了,nm能用了clang就用不了
- 爲了解決這個尷尬的局面,要麼修改configure文件,增加一個參數配置單獨來給clang,或者單獨爲cc,cxx指定完整路徑,顯然後者似乎更好一些
修改configure文件
- 首先增加一個自定義參數配置,搜索Toolchain options關鍵字,在cross-prefix下邊增加一個cross-prefix-clang(名字可以隨便取,後面使用的時候保持一致即可),如下
Toolchain options:
--arch=ARCH select architecture [$arch]
--cpu=CPU select the minimum required CPU (affects
instruction selection, may crash on older CPUs)
--cross-prefix=PREFIX use PREFIX for compilation tools [$cross_prefix]
# 增加這一行
--cross-prefix-clang=PREFIX use PREFIX for compilation tools [$cross_prefix_clang]
--progs-suffix=SUFFIX program name suffix []
--enable-cross-compile assume a cross-compiler is used
--sysroot=PATH root of cross-build tree
- 然後搜索CMDLINE_SET關鍵字,找到cross_prefix的位置,在下面增加一行:cross_prefix_clang ,如下示例:
CMDLINE_SET="
$PATHS_LIST
ar
arch
as
assert_level
build_suffix
cc
objcc
cpu
cross_prefix
#增加這一行
cross_prefix_clang
custom_allocator
cxx
...
"
- 最後搜索set_default關鍵字,找到如下行,進行如下修改
set_default target_os
if test "$target_os" = android; then
cc_default="clang"
# 增加這一行
cxx_default="clang++"
fi
ar_default="${cross_prefix}${ar_default}"
# 修改這一行
cc_default="${cross_prefix_clang}${cc_default}"
# 修改這一行
cxx_default="${cross_prefix_clang}${cxx_default}"
nm_default="${cross_prefix}${nm_default}"
pkg_config_default="${cross_prefix}${pkg_config_default}"
if ${cross_prefix}${ranlib_default} 2>&1 | grep -q "\-D "; then
ranlib_default="${cross_prefix}${ranlib_default} -D"
else
ranlib_default="${cross_prefix}${ranlib_default}"
fi
strip_default="${cross_prefix}${strip_default}"
windres_default="${cross_prefix}${windres_default}"
- 這樣修改以後我們在編譯時就可以使用我們新增的cross_prefix_clang參數了,然後其他的如nm,ar,ranlib,strip則繼續使用cross_prefix參數
- 編譯腳本示例如下
#!/bin/bash
export NDK=/home/hwj/Android/Sdk/ndk/20.1.5948944
export CPU=armv7-a
export ARCH=arm
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export PREFIX=./build-android/dist
cd .. #回到ffmpeg根目錄
echo "start configure"
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--cross-prefix-clang=$TOOLCHAIN/bin/armv7a-linux-androideabi21- \
--target-os=android --arch=$ARCH --cpu=$CPU \
--enable-gpl --enable-version3 \
--disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe \
--disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages \
--disable-avdevice --disable-postproc --disable-avfilter \
--disable-debug \
--sysroot=$SYSROOT \
--extra-cflags="-I$SYSROOT/usr/include" \
--extra-ldflags="-L$SYSROOT/usr/lib" \
--enable-small \
--enable-jni \
--enable-mediacodec
echo "make clean start"
make clean
echo "make start"
make -j4
echo "make finished"
make install
echo "make install finished"
不修改configure文件(推薦)
- 前面說到修改configure文件做法確實不太好,畢竟configure不是我維護的,在測試過程中發現只要–cc指定了完整路徑,那麼就不會加上cross_prefix這個前綴,利用這個特性,我們可以分別爲nm,ar,ranlib,strip單獨指定路徑(測試發現ffmpeg編譯時需要這幾個),或者爲cc,cxx單獨指定路徑,這裏採用後者
- 編譯腳本示例如下:
#!/bin/bash
export NDK=/home/hwj/ndk/android-ndk-r21
export CPU=armv7-a
export ARCH=arm
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export PREFIX=./build-android/dist
cd ..
echo "start configure"
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-cross-compile \
--cc=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang \
--cxx=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang++ \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=android --arch=$ARCH --cpu=$CPU \
--enable-gpl --enable-version3 \
--disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe \
--disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages \
--disable-avdevice --disable-postproc --disable-avfilter \
--disable-debug \
--sysroot=$SYSROOT \
--extra-cflags="-I$SYSROOT/usr/include" \
--extra-ldflags="-L$SYSROOT/usr/lib" \
--enable-small \
--enable-jni \
--enable-mediacodec
echo "make clean start"
make clean
echo "make start"
make -j4
echo "make finished"
make install
echo "make install finished"
- 這種方式只要指定–cc和–cxx即可,其他nm,ar,ranlib,strip由於沒指定,會默認使用–cross-prefix的值最爲前綴
裁剪
- 如果按照上面的腳本,那麼編譯出來的庫還是多了好多可能用不到的東西,因此最好自己進行裁剪,可以使用–disable-everything之後再開啓自身需要的,以下僅供參考
#!/bin/bash
# ndk的路徑
export NDK=/home/hwj/ndk/android-ndk-r21
# 最新版ndk最低要從armv7a開始了
export CPU=armv7-a
# 手機當然是arm架構了
export ARCH=arm
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
# 交叉編譯工具鏈
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
# 編譯成功後庫的存放目錄,自己自由指定
export PREFIX=./build-android/dist-min
cd .. #回到ffmpeg根目錄,因爲下面用到的configure就在ffmpeg根目錄,要根據實際情況修改
echo "start configure"
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-cross-compile \
--cc=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang \
--cxx=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang++ \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=android --arch=$ARCH --cpu=$CPU \
--enable-gpl --enable-version3 \
--disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe \
--disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages \
--disable-avdevice --disable-postproc --disable-avfilter \
--disable-debug \
--sysroot=$SYSROOT \
--extra-cflags="-I$SYSROOT/usr/include" \
--extra-ldflags="-L$SYSROOT/usr/lib" \
--enable-small \
--enable-jni \
--enable-mediacodec \
--disable-everything \
--enable-decoder=hevc --enable-decoder=h264 --enable-decoder=aac \
--enable-parser=h264 --enable-parser=hevc --enable-parser=aac \
--enable-demuxer=flv --enable-demuxer=mov --enable-demuxer=avi --enable-demuxer=mpegts \
--enable-protocol=file --enable-protocol=hls
echo "make clean start"
make clean
echo "make start"
make -j4
echo "make finished"
make install
echo "make install finished"
- 編譯成功後你就能看到目標碼了,開心不
- 關於更多configure配置信息,可以查看這篇文章哦:ffmpeg----configure參數配置說明
錯誤處理
- 如果很不幸的你在編譯過程中出現了各種錯誤,那麼就到ffmpeg目錄下的ffbuild/config.log里拉到末尾,查看具體的出錯信息,然後百度谷歌解決掉它吧