在linux下交叉編譯android平臺的ffmpeg4.2動態庫

在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"

錯誤處理

  • 如果很不幸的你在編譯過程中出現了各種錯誤,那麼就到ffmpeg目錄下的ffbuild/config.log里拉到末尾,查看具體的出錯信息,然後百度谷歌解決掉它吧

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章