FFmpeg In Android - 移植
移植FFmpeg到Android:
編譯指令碼
其實編譯指令碼網上也挺多的了,不過一開始可能沒那麼容易成功,需要耐心,細心, 還需要一點運氣,比如有時NDK的版本編譯通過不了,換另外一個NDK版本又可以了.下面記錄一下自己的編譯過程:
Linux 環境(Ubuntu 18.04.1 ,x86_64)
NDK 版本(android-ndk-r13b)
FFmpeg 版本(ffmpeg-3.3.8): 官網下載連結:https://ffmpeg.org/download.html
下載ffmpeg解壓後放到資料夾/ffmpeg3.3.8_build下.
- 首先需要對原始碼中的configure檔案進行修改。由於編譯出來的動態庫檔名的版本號在.so之後(例如“libavcodec.so.5.100.1”),而android平臺不能識別這樣檔名,所以需要修改這種檔名。在configure檔案中找到下面幾行程式碼:
LIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)' SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)' ***************** 替換為下面內容 *************** SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' SLIB_INSTALL_LINKS='$(SLIBNAME)'
- 在資料夾ffmpeg3.3.8_build下新建個指令碼build_ffmpeg.sh,指令碼內容如下,NDK路徑需修改為自己的本地路徑:
#!/bin/bash cd ffmpeg-3.3.8 export NDK=/home/king/Android/android-ndk-r13b export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 export PLATFORM=$NDK/platforms/android-9/arch-arm/ export PREFIX=../simplefflib build_one(){ ./configure --target-os=linux\ --prefix=$PREFIX \ --enable-cross-compile \ --enable-runtime-cpudetect \ --extra-libs="-lgcc" \ --disable-asm \ --arch=arm \ --cpu=armv7-a \ --cc=$PREBUILT/bin/arm-linux-androideabi-gcc \ --cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \ --disable-stripping \ --nm=$PREBUILT/bin/arm-linux-androideabi-nm \ --sysroot=$PLATFORM \ --enable-gpl \ --enable-shared \ --disable-static \ --enable-small \ --disable-debug \ --enable-parsers \ --disable-encoders \ --enable-decoders \ --disable-muxers \ --enable-demuxers \ --enable-swscale \ --disable-ffplay \ --disable-ffprobe \ --disable-ffserver \ --disable-ffmpeg \ --disable-network \ --enable-indevs \ --disable-bsfs \ --disable-filters \ --disable-protocols \ --enable-protocol=file \ --extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" make clean make make install } build_one cd ..
.
├── build_ffmpeg.sh
├── ffmpeg-3.3.8
└── simplefflib
執行指令碼,編譯成功後,simplefflib目錄下得到標頭檔案和動態庫:
└── simplefflib
├── include
│ ├── libavcodec
│ ├── libavdevice
│ ├── libavfilter
│ ├── libavformat
│ ├── libavutil
│ ├── libpostproc
│ ├── libswresample
│ └── libswscale
└── lib
├── libavcodec-57.so
├── libavcodec.so -> libavcodec-57.so
├── libavdevice-57.so
├── libavdevice.so -> libavdevice-57.so
├── libavfilter-6.so
├── libavfilter.so -> libavfilter-6.so
├── libavformat-57.so
├── libavformat.so -> libavformat-57.so
├── libavutil-55.so
├── libavutil.so -> libavutil-55.so
├── libpostproc-54.so
├── libpostproc.so -> libpostproc-54.so
├── libswresample-2.so
├── libswresample.so -> libswresample-2.so
├── libswscale-4.so
├── libswscale.so -> libswscale-4.so
└── pkgconfig
將include目錄下的標頭檔案和lib目錄下後面有數字的so庫拷貝到自己的Android專案使用.
新建一個Android Studio C++專案,並配置好CMakeLists.txt,然後將標頭檔案和動態庫拷貝進專案:
.
├── app
│ ├── app.iml
│ ├── build.gradle
│ ├── CMakeLists.txt
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ │ └── java
│ │ └── com
│ │ └── example
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── cpp
│ │ │ ├── include
│ │ │ │ ├── libavcodec
│ │ │ │ ├── libavdevice
│ │ │ │ ├── libavfilter
│ │ │ │ ├── libavformat
│ │ │ │ ├── libavutil
│ │ │ │ ├── libpostproc
│ │ │ │ ├── libswresample
│ │ │ │ └── libswscale
│ │ │ └── ndk-lib.cpp
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ ├── jniLibs
│ │ │ └── armeabi-v7a
│ │ │ ├── libavcodec-57.so
│ │ │ ├── libavdevice-57.so
│ │ │ ├── libavfilter-6.so
│ │ │ ├── libavformat-57.so
│ │ │ ├── libavutil-55.so
│ │ │ ├── libpostproc-54.so
│ │ │ ├── libswresample-2.so
│ │ │ └── libswscale-4.so
│ │ └── res
│ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
CMakeLists.txt配置內容如下:
cmake_minimum_required(VERSION 3.4.1)
set(shared_libs ${CMAKE_SOURCE_DIR}/src/main/jniLibs)
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library(libavcodec-57 SHARED IMPORTED)
set_target_properties(libavcodec-57 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libavcodec-57.so)
add_library(libavdevice-57 SHARED IMPORTED)
set_target_properties(libavdevice-57 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libavdevice-57.so)
add_library(libavfilter-6 SHARED IMPORTED)
set_target_properties(libavfilter-6 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libavfilter-6.so)
add_library(libavformat-57 SHARED IMPORTED)
set_target_properties(libavformat-57 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libavformat-57.so)
add_library(libavutil-55 SHARED IMPORTED)
set_target_properties(libavutil-55 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libavutil-55.so)
add_library(libpostproc-54 SHARED IMPORTED)
set_target_properties(libpostproc-54 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libpostproc-54.so)
add_library(libswresample-2 SHARED IMPORTED)
set_target_properties(libswresample-2 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libswresample-2.so)
add_library(libswscale-4 SHARED IMPORTED)
set_target_properties(libswscale-4 PROPERTIES
IMPORTED_LOCATION ${shared_libs}/${ANDROID_ABI}/libswscale-4.so)
add_library(
native-lib
SHARED
src/main/cpp/ndk-lib.cpp)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries(native-lib
libavcodec-57
libavdevice-57
libavfilter-6
libavformat-57
libavutil-55
libpostproc-54
libswresample-2
libswscale-4
# Links the target library to the log library
# included in the NDK.
log)
要注意的是動態庫不能隨便放,而要放在armeabi,armeabi-v7a這樣的目錄下.
Java類程式碼如下:
static {
System.loadLibrary("avcodec-57");
System.loadLibrary("avdevice-57");
System.loadLibrary("avfilter-6");
System.loadLibrary("avformat-57");
System.loadLibrary("avutil-55");
System.loadLibrary("postproc-54");
System.loadLibrary("swresample-2");
System.loadLibrary("swscale-4");
System.loadLibrary("native-lib");
}
public native void stringFromJNI();
測試一下呼叫ffmpeg的函式是否成功:
#include <jni.h>
#include <string>
#include <android/log.h>
#define ANDROID_LOG_TAG "native"
#define ANDROID_LOG_ERR "native"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,ANDROID_LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,ANDROID_LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,ANDROID_LOG_ERR,__VA_ARGS__)
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#ifdef __cplusplus
};
#endif
extern "C" JNIEXPORT void JNICALL
Java_com_example_king_cmakedemo_NativeUtil_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
unsigned version = avformat_version();
const char* license = avformat_license();
LOGD("ffmpeg version %u, license:%s", version, license);
const char * hello = "Hello from C++";
LOGD(hello);
}
日誌輸出如下,證明呼叫ffmpeg函式成功了:
2018-10-23 10:48:17.823 1442-1442/com.example.king.cmakedemo D/native: ffmpeg version 3753828, license:GPL version 2 or later