Cross Compile FFmpeg

Cross compile FFmpeg on MacOS for Android

1. Prepare FFmpeg and Android NDK

FFmpeg

1
2
3
4
5
6
7
8
// 1. Download FFMpeg (4.2.2 for this tutotial)
https://ffmpeg.org/releases/

// 2. unzip
tar -jxvf ffmpeg-4.2.2.tar.bz2

// 3. configure
./configure --disable-x86asm

Android NDK

1
https://developer.android.google.cn/ndk/downloads

2. Build

In the unzipped folder ffmpeg-4.2.2, create a new shell script build_android.sh.

build_android.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/bin/sh

# NDK path, android studio has downloaded sdk, inside the sdk folder, there are multiple ndk versions. I am using 26.1.10909125 here.
NDK=~/Library/Android/sdk/ndk/26.1.10909125
# Target OS
ARCH=aarch64
CPU=arm64-v8a
# Android API
API=21
# Output path: /android/arm64-v8a
OUTPUT=$(pwd)/android/$CPU
# NDK toolchain path
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
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_arm64() {
echo "\033[32m Compiling FFmpeg for $CPU \033[0m"
./configure \
--target-os=android \
--prefix=$OUTPUT \
--arch=$ARCH \
--sysroot=$TOOLCHAIN/sysroot \
--enable-neon \
--enable-hwaccels \
--enable-gpl \
--disable-postproc \
--disable-debug \
--enable-small \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-static \
--enable-shared \
--disable-doc \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--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 -I$OUTPUT/include" \
--extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L$OUTPUT/lib"

make clean all
make -j8
make install
echo "\033[32m The Compilation of FFmpeg for $CPU is completed \033[0m"
}

build_arm64

To build

1
2
chmod +x build_android.sh
./build_android.sh

After successful build, we will have needed libs and headers under android/arm64-v8a

3. Test FFmpeg on Android

Build a simple demo in Android studio to test if FFmpeg works.

We place the ffmpeg headers under cpp/include and all the libraries under jniLibs/arm64-v8a.

Java client code

MainActivity.java
1
2
3
4
5
6
7
8
9
10
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
((TextView)findViewById(R.id.text_view)).setText(FFmpegPlay.getFFmpegVersion());
}
}
FFmpegplay.java
1
2
3
4
5
6
7
8
9
10
11
public class FFmpegPlay {
static {
System.loadLibrary("ffmpegtest");
}

public static String getFFmpegVersion() {
return nativeGetFFmpegVersion();
}

private static native String nativeGetFFmpegVersion();
}

Native code

ffmpegtest.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <jni.h>
#include <cstring>
#include "util/log.h"

extern "C" {
#include <libavcodec/version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <libavfilter/version.h>
#include <libswresample/version.h>
#include <libswscale/version.h>
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_ffmpegtest_FFmpegPlay_nativeGetFFmpegVersion(JNIEnv *env, jclass clazz) {
char strBuffer[1024 * 4] = {0};
strcat(strBuffer, "libavcodec : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
strcat(strBuffer, "\nlibavformat : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
strcat(strBuffer, "\nlibavutil : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
strcat(strBuffer, "\nlibavfilter : ");
strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
strcat(strBuffer, "\nlibswresample : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
strcat(strBuffer, "\nlibswscale : ");
strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
strcat(strBuffer, "\navcodec_configure : \n");
strcat(strBuffer, avcodec_configuration());
strcat(strBuffer, "\navcodec_license : ");
strcat(strBuffer, avcodec_license());
LOGCATE("GetFFmpegVersion\n%s", strBuffer);
return env->NewStringUTF(strBuffer);
}
CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

set(jnilibs ${CMAKE_SOURCE_DIR}/../jniLibs)
set(libname ffmpegtest)

include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/util
)

link_directories(
${jnilibs}/${ANDROID_ABI})

file(GLOB src-files
${CMAKE_SOURCE_DIR}/*.cpp)

add_library(
${libname}
SHARED
${src-files}
)

set(third-party-libs
avformat
avcodec
avfilter
swresample
swscale
avutil
)

set(native-libs
android
mediandk
EGL
GLESv3
OpenSLES
log
m
z
)

target_link_libraries(
${libname}

${log-lib}
${third-party-libs}
${native-libs}
)

Result

Author

Joe Chu

Posted on

2025-01-06

Updated on

2025-01-09

Licensed under

Comments