1. 程式人生 > >FFmpeg In Android - 移植

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