Mac交叉编译Android FFmpeg

最近在学习Android上的FFmpeg开发。记录下在Mac上交叉编译和使用Android FFmpeg库的过程。

  1. 交叉编译
    # 1.下载ffmpeg-4.2.2源码
    wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
    1. 解压
    1. 运行configure脚本配置项目 ./configure --disable-x86asm

      在当前文件夹下创建编译脚本build_android_arm64-v8a_clang.sh:

      #!/bin/zsh

export NDK=~/Library/Android/android-ndk-r20b #这里配置先你的 NDK 路径 TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

#arm64-v8a ARCH=arm64 CPU=armv8-a API=21 CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU"

#armv7-a #ARCH=arm #CPU=armv7-a #API=21 #CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang #CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++ #SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot #CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi- #PREFIX=$(pwd)/android/$CPU #OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "

function build_android {

./configure 
    --prefix=$PREFIX 
    --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 
    --cross-prefix=$CROSS_PREFIX 
    --target-os=android 
    --arch=$ARCH 
    --cpu=$CPU 
    --cc=$CC 
    --cxx=$CXX 
    --enable-cross-compile 
    --sysroot=$SYSROOT 
    --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" 
    --extra-ldflags="$ADDI_LDFLAGS"

  make clean
  make -j16

echo "============================ build android arm64-v8a success =========================="

}

build_android

运行脚本编译,之后会在android/armv8-a/lib/下生成动态和静态库。

# 修改 build_android_arm64-v8a_clang.sh 可执行权限  
chmod +x build_android_arm64-v8a_clang.sh  
# 运行编译脚本  
./build_android_arm64-v8a_clang.sh
  1. Android上集成测试

    新建Android项目。

    2.1 JNI接口

  • 将生成目录的头文件和库文件放入下面目录。注意库文件要放在app/src/jniLibs/arm64-v8a目录下。 image.png
  • 创建FFMediaPlayer.java,作为java层接口。
  • package com.zzakafool.androidffmpeg;
    
    public class FFMediaPlayer {
        static {
            System.loadLibrary("learn-ffmpeg");
        }
    
        public static String GetFFmpegVersion() {
            return native_GetFFmpegVersion();
        }
    
        private static native String native_GetFFmpegVersion();
    }
    
  • app/src/main/cpp/下添加对应native实现FFMediaPlayer.cpp
  • #include 
    #include 
    //#include "util/LogUtil.h"
    #include "jni.h"
    
    //由于 FFmpeg 库是 C 语言实现的,告诉编译器按照 C 的规则进行编译
    extern "C" {
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    };
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_zzakafool_androidffmpeg_FFMediaPlayer_native_1GetFFmpegVersion(JNIEnv *env, jclass clazz) {
        // TODO: implement native_GetFFmpegVersion()
        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("GetFFmpegVersionn%s", strBuffer);
        return env->NewStringUTF(strBuffer);
    }
    
  • app/src/main/cpp/下添加CMakeLists.txt
  • # 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 learn-ffmpeg)
    
    include_directories(
            include
            ${CMAKE_SOURCE_DIR}/util
    )
    
    link_directories(
            ${jnilibs}/${ANDROID_ABI})
    
    file(GLOB src-files
            ${CMAKE_SOURCE_DIR}/*.cpp)
    
    add_library( # Sets the name of the library.
            ${libname}
    
            1. Sets the library as a shared library.
            SHARED
    
            1. Provides a relative path to your source file(s).
            ${src-files}
            )
    
    set(third-party-libs
            avformat
            avcodec
            avfilter
            swresample
            swscale
            avutil
            )
    
    set(native-libs
            android
            EGL
            GLESv3
            OpenSLES
            log
            m
            z
            )
    
    target_link_libraries( # Specifies the target library.
            ${libname}
    
            1. Links the target library to the log library
            1. included in the NDK.
            ${log-lib}
            ${third-party-libs}
            ${native-libs}
            )
    
  • 在Activity中调用对应的接口。
  • import static com.zzakafool.androidffmpeg.R.id.*;
    
    import android.os.Bundle;
    import android.widget.TextView;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.zzakafool.androidffmpeg.R.id;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ((TextView)findViewById(text_view)).setText(FFMediaPlayer.GetFFmpegVersion());
        }
    }
    
  • 运行App
  • image.png

    参考资料

    FFmpeg 开发(01):FFmpeg 编译和集成