Building/Cross compiling FFmpeg for android

Once again in my journey of exploring android, I came across the hurdle of cross compiling multiple libraries example — live555, ffmpeg, libevent etc. Of all the libraries what stumped me the most was ffmpeg. Despite there being so many details on the ffmpeg builds, I was always struggling, sometimes for hours, to find my way through all the errors. The errors were so subtle and common that it would have been sinful of me not to write about them.

Most of the problems arise when using the latest ffmpeg build or the latest ndk version. The use of unified headers in ndk 14 and above makes it difficult to use the deprecated ones which give rise to compile errors.

After spending a fair deal of time with ffmpeg I found that due to some issues in the header files of the ffmpeg and the support of unified libc headers in ndk the configurations proposed by different blogs or tutorials for cross compiling ffmpeg will lead to compile errors. In order to bridge the gap I will write the complete process for successfully cross compiling ffmpeg for android.


NDK Toolchains

As directed by the link in the previous paragraph, we will need to disable the use of unified headers or in other words make sure to use deprecated headers. In ndk r15 or greater the support of deprecated headers will be removed; if you need to revert to the deprecated headers, make sure you’re working on fixing your build or filing bugs. This can be by passed easily in ndk r14 or less, by passing the arguments deprecated-headers for the ndk14 or using the ndk nversion ≤ 13 for making tool-chains.

$NDK/build/tools/make_standalone_toolchain.py --deprecated-headers ...

Cross compiling

After you have successfully made your toolchains, download the ffmpeg from releases:-

You can use any of stable releases with version< 3.4.2 . I personally used 3.0.11 and was able to successfully build the ffmpeg. If you use 3.4 versions then you will definitely come across the following error

libavcodec/aaccoder.c: In function 'search_for_ms':
libavcodec/aaccoder.c:803:25: error: expected identifier or '(' before numeric constant
int B0 = 0, B1 = 0;
^
libavcodec/aaccoder.c:865:28: error: lvalue required as left operand of assignment
B0 += b1+b2;
^
libavcodec/aaccoder.c:866:25: error: 'B1' undeclared (first use in this function)
B1 += b3+b4;
^
libavcodec/aaccoder.c:866:25: note: each undeclared identifier is reported only once for each function it appears in
make: *** [libavcodec/aaccoder.o] Error 1
make: *** Waiting for unfinished jobs....

Now if you still want to use 3.4.x ffmpeg versions you will have to rectify this error. In order to do that you first need to understand the source of the error.

Basically what happens is sysroot/usr/include/asm-generic/termbits.h defines a macro definition of

#define B0 0000000000

and FFMPEG uses variables such as

is_available_B0 = AVAILABLE(cand_up_right, B0) &&
xb0 < s->ps.sps->width &&
PRED_BLOCK_AVAILABLE(B0) &&
!is_diff_mer(s, xB0, yB0, x0, y0);

And then replaces them with 0000000000 as

is_available_0000000000  = AVAILABLE(cand_up_right, 0000000000 ) &&
x0000000000 < s->ps.sps->width &&
PRED_BLOCK_AVAILABLE(0000000000 ) &&
!is_diff_mer(s, x0000000000 , y0000000000 , x0, y0);

Which then leads to compile issues.

Solution :- Replace B0 with a different variable like b0 in all the files very carefully using source insight or Visual Studio. If you really want to go down this road that you have my respect and if are successful in building ffmpeg please do make sure to comment or write a post on it.

After all this hassle I hope you would have understood that cross compiling is no easy task especially in this case. Damn you FFMPEG !!

Configure

Running the configure script will require a few arguments, to be honest more than a few :P . However, you can just copy paste the following command (make sure, to change the path of prefix, cross prefix and sysroot according to your system).

./configure \
--target-os=linux \
--cross-prefix=$NDK/toolchains/$NDK/platforms/android-14/arch-arm//bin/arm-linux-androideabi- \
--arch="$target_arch" \
--cpu=armv5te \
--enable-runtime-cpudetect \
--sysroot=$NDK/toolchain/$target_arc/ \
--enable-pic \
--enable-libx264 \
--enable-libass \
--enable-libfreetype \
--enable-libfribidi \
--enable-fontconfig \
--enable-pthreads \
--disable-debug \
--disable-ffserver \
--enable-version3 \
--enable-hardcoded-tables \
--disable-ffplay \
--disable-ffprobe \
--enable-gpl \
--enable-yasm \
--disable-doc \
--disable-shared \
--enable-static \
--prefix="$installationg_directory" \
--extra-cflags="--O3 -Wall -pipe -std=c99 -ffast-math -fstrict-aliasing -Werror=strict-aliasing -Wno-psabi -Wa,--noexecstack -DANDROID -DNDEBUG-march=armv5te -mtune=arm9tdmi -msoft-float" \
--extra-libs="-lpng -lexpat -lm" \
--extra-cxxflags="$CXX_FLAGS"

Make sure your environment variable does not have ldflags = “pie”, unset it if it is present, otherwise there would be build errors with the make command.

If you have reached this far successfully, give yourself a pat on your shoulder and a clap to this post. We finally move into making the shared objects.

make -j2 && make install

If things move smoothly, you will find your *.so files in the folder specified in the prefix directory during the configuration.

Done with it!

If you survived up to this point — you successfully embedded FFmpeg into your application. You can use these .so files for either making executables or unity or jni sdk.

In case you faced some different errors or are stuck somewhere, please do reach me out and comments on the section below. I will be there to help you out :).