Linking FFmpeg 8.1 CLI into an Android NDK Shared Library: Header Resolution and Symbol Errors

5 days ago 11
ARTICLE AD BOX

I am attempting to build a custom video processing engine for Android using FFmpeg 8.1. Instead of re-implementing the muxing logic using libavformat/libavcodec APIs, I want to "library-ify" the FFmpeg CLI tool and call it via JNI also I am intentionally avoiding ffmpeg-kit to keep the binary minimal.

I have successfully cross-compiled FFmpeg on a macOS (M5 chip) for arm64-v8a (NDK r28), targeting API 24.

The Strategy:

I modified fftools/ffmpeg.c by renaming main to int sm_engine_exec(int argc, char** argv).

I am building FFmpeg with --enable-pic to ensure compatibility with an Android .so.

My JNI Implementation:

extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> // My custom prototype from fftools/ffmpeg.c int sm_engine_exec(int argc, char** argv); } extern "C" JNIEXPORT jint JNICALL Java_com_harrshbermann_SocialMate_google_logEvent2(JNIEnv *env, jobject thiz, jstring video_path, jstring audio_path, jstring out_path) { const char *v_path = env->GetStringUTFChars(video_path, nullptr); const char *a_path = env->GetStringUTFChars(audio_path, nullptr); const char *o_path = env->GetStringUTFChars(out_path, nullptr); const char *argv[] = { "ffmpeg", "-i", v_path, "-i", a_path, "-c", "copy", "-map", "0:v:0", "-map", "1:a:0", "-shortest", "-y", o_path, NULL }; int argc = 14; int result = sm_engine_exec(argc, (char**)argv); // Release strings... return result; }

The Problem:

Header Issues: When compiling the JNI wrapper, I get "file not found" errors for headers used within ffmpeg.c (like cmdutils.h) because they aren't in the standard include/ folder after make install.

Linker Errors: Even when I fix the headers, the linker fails to find sm_engine_exec or its dependencies (like stdin_interaction, dec_free, term_init) because they reside in fftools/*.o, which aren't included in the standard .a libraries.

My Build Configuration:

./configure \ --prefix=$OUTPUT \ --target-os=android \ --arch=aarch64 \ --cpu=armv8-a \ --enable-cross-compile \ --sysroot=$TOOLCHAIN/sysroot \ --cc=$CC \ --cxx=$CXX \ --ar=$AR \ --ranlib=$RANLIB \ --nm=$NM \ --strip=$STRIP \ --extra-cflags="-fPIC -fPIE" \ --extra-ldflags="-fPIC -fPIE" \ --enable-static \ --disable-shared \ --enable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --disable-doc \ --disable-debug \ --disable-programs \ --enable-small

Questions:

Correct Pathing: How should I structure my CMake/NDK build to include non-public headers from the fftools/ source directory?

Bundling CLI Logic: Is there a standard way to force FFmpeg to output a libffmpeg_cli.a static library that encapsulates ffmpeg.c and its dependencies (ffmpeg_opt.c, ffmpeg_dec.c, etc.)?

FFmpeg-Kit Internals: How does ffmpeg-kit (or similar projects) successfully wrap the CLI into a shared library? Do they manually create a static archive of all fftools/*.o objects?

CLI via .so: Is it architecturally sound to use FFmpeg's CLI code as a static library within an Android .so, or are there hidden pitfalls with global state/threading?

Read Entire Article