Android-NDK-clang compiles FFmpeg

Android-NDK-clang compiles FFmpeg

Android-NDK-clang compile FFmpeg

Android-NDK-clang compiles FFmpeg

Android-NDK-clang compiles FFmpeg – Zhihu (zhihu.com)

Preliminary preparation

  1. Download Android-NDK
  2. Download FFmpeg source code Note: The author uses NDK-21 and ffmpeg-4.4 for compilation. It may be different if the versions are different.
    Test: NDK20 - NDK22 and ffmpeg 4.0 - ffmpeg 4.4 under mac and ubuntu can be used.

In this article you can learn

  • NDK20 – Main directory of cross-compilation toolchain provided by NDK22
  • Use clang to cross-compile the libffmpeg.so library that can be used on the Android platform
  • Some compilation details

1. Main directories and files of the cross-compilation tool chain provided by NDK

The directory structure of the compilation tool chain from NDK20 to NDK22 is basically unchanged. Here we use NDK21 as a demonstration (the directories of NDK in windows, linux, and mac are basically the same)

As shown above, these directories are mainly used. The gcc library needed to compile FFmpeg is in the aarch64, arm, x86_64, x86 folders. , here we first introduce the connection between these names in different platform libraries in Android.

aarch64: Directories with this prefix are related to the arm64-v8a library
arm: Directories with this prefix are related to the armeabi-v7a library
x86_64: Directories with this prefix are related to the x86_64 library
x86: Directories with this prefix are related to x86 libraries

1.clang compilation tool

Enter the directory llvm->prebuilt->darwin-x86_64->bin. It contains files related to cross-compilation. We compile with clang, so the main focus is on Files ending with clang, clang + + , clang is used to compile c files, clang + + is used to compile c++ files.

  • What needs to be noted here is: 21 and i686
  • 21: Indicates the minimum Android version supported by the compiled library
  • i686: Indicates the compilation tool that compiles the x86 library platform

The corresponding form of the above NDK directory name on each system:
mac:darwin-x86_64
linux: linux-x86_64
windows: windows-x86_64
Note: The following is introduced using the NDK directory under the mac system.

2. Compilation environment and libraries needed

The directory where the library and header files are located is in the sysroot directory under darwin-x86_64. The header files are in the include directory and the library is in lib directory, after understanding this, you can start compiling.

2. Use clang to cross-compile the libffmpeg.so library that can be used on the Android platform

Enter the FFmpeg source code root directory

1. Create a compilation script: build_ffmpeg_android.sh

The main content of the script is as follows:

#!/bin/sh
# The path where NDK is located
NDK=/Users/mac/Library/Android/sdk/ndk/21.4.7075529
# The platform that needs to be compiled, here is arm64-v8a
ARCH=aarch64
# Minimum supported Android API
API=21
# Output directory after compilation, /android/arm64-v8a in the ffmpeg source code directory
OUTPUT=$(pwd)/android/arm64-v8a
# The path to the NDK cross-compilation tool chain
TOOLCHAIN=/Users/mac/Library/Android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64

build() {
  ./configure \
  --target-os=android \
  --prefix=$OUTPUT \
  --arch=$ARCH \
  --sysroot=$TOOLCHAIN/sysroot \
  --disable-static \
  --disable-ffmpeg \
  --disable-ffplay \
  --disable-ffprobe \
  --disable-debug \
  --disable-doc \
  --disable-avdevice \
  --enable-shared \
  --enable-cross-compile \
  --cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
  --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang \
  --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang + + \
  --extra-cflags="-fpic"

  make clean all
  make -j12
  make install
}

build

This shell script is actually very easy to understand in general, such as
--disabble-static disables the output of static libraries
--enable-shared Output dynamic library
--arch What is the architecture of the so library used to configure output?
--prefix The storage path of the so library used to configure the output
enable-cross-compile enables multi-platform compilation, which means that libraries for multiple platforms can be compiled.
For more options, you can check the introduction on the official website, so I won’t go into details here.

The following will focus on a few options:

  • target-os --target-os=android: In the old version of FFmpeg, the support for the Android platform is not very complete, and there is no android code> is the target, so it will be mentioned in some older articles. To compile the so library for the Android platform, you need to modify configure, otherwise it will follow the linux standard. The method outputs the so library. Its naming method is different from Android’s so. Android cannot load it, so when compiling, it is best to choose the source code version of FFmpeg consistent with the author’s.

Problem 1: The so library output under Linux cannot be loaded under Android

  • sysroot --sysroot=$TOOLCHAIN/sysroot: used to configure the root path of the cross-compilation environment. When compiling, it will search for usr/include from this path by default. usr/lib These two paths, and then find the relevant header files and library files.
    NDK20-NDK22 The header files and library files of the system are in $SYSYROOT/usr/include and $SYSYROOT/usr/lib.

extra-cflags specifies some compilation flags to the compiler, for example:
Set header file path: Format -I header file path
Set the compiled binary file as a position-independent code file: format -fpic
As for why it is necessary to compile a position-independent code file, it is because the so library produced by Packaging is composed of multiple binary files that are position-independent codes.

extra-ldflags specifies some link flags to the linker, for example:
Set the path of the library to be linked: Format -L library file path
Export library and set name: Format -o library name
Set the library to be linked: Format -l library name
Things to note here:
Assume the library name is: a
-o library name needs to be prefixed with lib and suffixed with .so/.a, such as -o liba.so
-l library name is the part without the lib prefix and the .so/.a suffix, such as -la

Regarding the issue of compilation and link flags, if you want to know more details, you can check here.

  • cross-prefix configures the prefix of the cross-compilation compilation tool, which is the prefix of the file name in the directory where the cross-compilation related files introduced above are located. For example: compiling the arm64-v8a platform is aarch64-linux-android-, and the one that compiles the armeabi-v7a platform is arm-linux-androideabi-. For details, you can check it in the bin directory under the cross-compilation tool chain directory. .
  • cc
  • cxx These two items are the specific path to configure the clang tool that comes with Android as mentioned above.

2. Start compiling

Go to the FFmpeg source code root directory through the terminal and run the compilation script you just wrote.

sh build_ffmpeg_android.sh

The running results are as follows

As shown in the picture above, the files in the red box are all the files we compiled. However, with so many so files, it is troublesome to use, so we have to package them into one so file.

3. Package multiple libraries into one library

  • Modify the compilation script as follows:
#!/bin/sh

# ...omitting the unchanged parts
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android
GCC_L=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/lib/gcc/aarch64-linux-android/4.9.x

build() {
  # ...omitting the unchanged parts
  --disable-shared \
  --enable-static \
  --extra-cflags="-fpic -I$OUTPUT/include" \
  --extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L$OUTPUT/lib"
  
  # ...omitting the unchanged parts
}

package_library() {
  $TOOLCHAIN/bin/aarch64-linux-android-ld -L$OUTPUT/lib -L$GCC_L \
    -rpath-link=$SYSROOT_L/$API -L$SYSROOT_L/$API -soname libffmpeg.so \
    -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $OUTPUT/libffmpeg.so \
    -lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
    -lc -ldl -lm -lz -llog \
    --dynamic-linker=/system/bin/linker
    # Set up the dynamic linker, which varies on different platforms. Android uses /system/bin/linker
}

build
package_library

The above list is only a snippet of code, and only the parts that have been changed are listed. The general process is:
clang compiles a static library -> Use Android's built-in linker to package the compiled static library into a dynamic library

The following focuses on a few options:

  • -rpath-link
When using ELF or SunOS, one shared library may require another. This happens when
an `"ld -shared"` link includes a shared library as one of the input files.

When the linker encounters such a dependency when doing a non-shared, non-relocatable
link, it will automatically try to locate the required shared library and include it in
the link, if it is not included explicitly. In such a case, the **-rpath-link** option
specifies the first set of directories to search. The **-rpath-link** option may
specify a sequence of directory names either by specifying a list of names separated by
colons, or by appearing multiple times.

The above is the official introduction. Here I would venture to summarize it in one sentence. It may not be accurate, but it will be understandable:
This is a flag passed to the linker. When the library we use has dependencies, packaging needs to be carried out according to dependencies, otherwise an error will be reported. Use this flag , we only need to set the directory of the library and do not need to worry about dependencies. The linker will handle it for us when linking.

  • -soname The explanation of this option is: add an alias to the library, that is, can reference the library through the alias. Why do we need an alias? Because we typed multiple static libraries at the beginning, the static libraries already have their own names. If you don’t do alias mapping for them uniformly, you will find that when you load the library, you will always get an error, saying that the one you specified cannot be found. Library file, if you don’t believe it, you can try it ^^.
  • –dynamic-linker This option is used to set the dynamic linker. Remember the Question 1 raised above. In order to solve this problem, set it here to the linker used by Android, which is /system /bin/linker, about this, you can look here

4. Compile again

The running results are as follows

At this point, we have completed the compilation of FFmpeg.

3. Introduction to script usage
The author has encapsulated the compilation script to facilitate the compilation of so libraries for various Android platforms. The script link is at the beginning of the article. The usage steps are introduced below:

  1. Place the script in the FFmpeg source code root directory
  2. Open the script in text mode and simply modify the parameters listed below
# Minimum supported API level for builds
API=21
# What system to build on, mac: darwin, linux: linux, windows: windows
OS_TYPE=darwin
# The directory where your own local NDK is located
NDK=/Users/mac/Library/Android/sdk/ndk/21.4.7075529
# Target file output directory, the default is the android directory under the current directory
OUTPUT=$(pwd)/android/$ABI
  1. Open the terminal, enter the FFmpeg source code directory, and execute the script: sh build_ffmpeg_android.sh 1

enforce rules
sh build_ffmpeg_android.sh can be followed by four items: 1, 2, 3, and 4. The meaning of these four items is explained below.
1: Build the library file of arm64-v8a architecture
2: Build the library file of armeabi-v7a architecture
3: Build the library file of x86_64 architecture
4: Build the library file for x86 architecture
If you want to build multiple platforms, you can attach multiple items and separate them with spaces. For example, to build a full platform:
sh build_ffmpeg_android.sh 1 2 3 4

4. Problems encountered

  1. Running the script shows no permissions

Modify the file permissions and run again: chmod 777 build_ffmpeg_android.sh

  1. Running the script shows that there are unrecognizable characters in the script and cannot be run.

Solution one:
Visual Studio Code replaces Notepad and re-edits
Solution two:
Install dos2unix software
Under mac: brew install dos2unix
Under ubuntu: sudo apt install dos2unix
Use: dos2unix build_ffmpeg_android.sh
Then just run it again

An error is reported when compiling the x86 library. The error is as follows

At this time, you only need to add: –disable-asm after ./configure, and then recompile will be no problem, because the x86 platform removes the register. If you do not disable this item, An error will be reported, the detailed reason is here.

  1. The gcc library used when packaging multiple libraries is also available in other directories.

Allow me to complain here, I think it is a huge pitfall… Because I used gcc in another directory at the beginning when packaging. The packaging of some platforms is normal, but the packaging of the armeabi-v7a platform has been unsuccessful. After trying for a long time, I found that the directory I am using now also exists and there is no problem. If you also encounter the same problem, then change to gcc in the directory I introduced and there will be no problem.

5. Summary

  1. Compilation problems on the Android side are often caused by unfamiliarity with the directory of the compilation tool chain and the corresponding library cannot be found.
  2. When compiling, if you encounter a missing library, just go to the directory introduced above to find it and add it during compilation.
  3. With more hands-on practice, you will discover the true meaning of the saying “What you learn from books will eventually become shallow.” Finally, if you think this article is helpful to you, then give it a like

Reference article

FFmpeg so library compilation
How to cross-platform compile files that can be executed on Android
FFmpeg x86 compilation issues
Introduction to linker-rpath
Compile FFmpeg into a libffmpeg.so library