macos NDK Clang 编译FFmpeg +Android工程集成

为什么要学 FFmpeg 开发

FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发。

FFmpeg官网

image-20210411210746456
image-20210411210836107

FFmpeg 有六个常用的功能模块:

  • libavformat:多媒体文件或协议的封装和解封装库,RTMP、RTSP 等网络协议封装格式;
  • libavcodec:音视频编解码库;
  • libavfilter:音视频、字幕滤镜库;
  • libswscale:图像格式转换库;
  • libswresample:音频重采样库;
  • libavutil:工具库;

编译环境:

  • MacOS 10.15.7
  • android-ndk-21.3.6528147
  • ffmpeg-4.3.2

交叉编译工具链

image-20210411211702967

第一步,编译ffmpeg


首先下载并解压

wget https://ffmpeg.org/releases/ffmpeg-4.3.2.tar.bz2
tar xvf ffmpeg-4.3.2.tar.bz2
image-20210411211132021

然后编写编译脚本

build_ffmpeg_clang_all.sh

#!/usr/bin/env bash

NDK_PATH=/Volumes/CodeApp/Applications/AndroidSDK/ndk/21.3.6528147

HOST_PLATFORM_WIN=darwin-x86_64
HOST_PLATFORM=$HOST_PLATFORM_WIN
API=21

TOOLCHAINS="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM"
SYSROOT="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot"
CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID "
LDFLAG="-lc -lm -ldl -llog "

PREFIX=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2

CONFIG_LOG_PATH=${PREFIX}/log
COMMON_OPTIONS=
CONFIGURATION=

build() {
  APP_ABI=$1
  echo "======== > Start build $APP_ABI"
  case ${APP_ABI} in
  armeabi-v7a)
    ARCH="arm"
    CPU="armv7-a"
    MARCH="armv7-a"
    TARGET=armv7a-linux-androideabi
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
    EXTRA_CFLAGS="$CFLAG -mfloat-abi=softfp -mfpu=vfp -marm -march=$MARCH "
    EXTRA_LDFLAGS="$LDFLAG"
    EXTRA_OPTIONS="--enable-neon --cpu=$CPU "
    ;;
  arm64-v8a)
    ARCH="aarch64"
    TARGET=$ARCH-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
    EXTRA_CFLAGS="$CFLAG"
    EXTRA_LDFLAGS="$LDFLAG"
    EXTRA_OPTIONS=""
    ;;
  x86)
    ARCH="x86"
    CPU="i686"
    MARCH="i686"
    TARGET=i686-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
    EXTRA_CFLAGS="$CFLAG -march=$MARCH -mtune=intel -mssse3 -mfpmath=sse -m32"
    EXTRA_LDFLAGS="$LDFLAG"
    EXTRA_OPTIONS="--cpu=$CPU "
    ;;
  x86_64)
    ARCH="x86_64"
    CPU="x86-64"
    MARCH="x86_64"
    TARGET=$ARCH-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
    EXTRA_CFLAGS="$CFLAG -march=$CPU -mtune=intel -msse4.2 -mpopcnt -m64"
    EXTRA_LDFLAGS="$LDFLAG"
    EXTRA_OPTIONS="--cpu=$CPU "
    ;;
  esac

  echo "-------- > Start clean workspace"
  make clean

  echo "-------- > Start build configuration"
  CONFIGURATION="$COMMON_OPTIONS"
  CONFIGURATION="$CONFIGURATION --logfile=$CONFIG_LOG_PATH/config_$APP_ABI.log"
  CONFIGURATION="$CONFIGURATION --prefix=$PREFIX"
  CONFIGURATION="$CONFIGURATION --libdir=$PREFIX/libs/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --incdir=$PREFIX/includes/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --pkgconfigdir=$PREFIX/pkgconfig/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --cross-prefix=$CROSS_PREFIX"
  CONFIGURATION="$CONFIGURATION --arch=$ARCH"
  CONFIGURATION="$CONFIGURATION --sysroot=$SYSROOT"
  CONFIGURATION="$CONFIGURATION --cc=$CC"
  CONFIGURATION="$CONFIGURATION --cxx=$CXX"
  CONFIGURATION="$CONFIGURATION --ld=$LD"
  CONFIGURATION="$CONFIGURATION $EXTRA_OPTIONS"

  echo "-------- > Start config makefile with $CONFIGURATION --extra-cflags=${EXTRA_CFLAGS} --extra-ldflags=${EXTRA_LDFLAGS}"
  ./configure ${CONFIGURATION} \
  --extra-cflags="$EXTRA_CFLAGS" \
  --extra-ldflags="$EXTRA_LDFLAGS"

  echo "-------- > Start make $APP_ABI with -j8"
  make -j8

  echo "-------- > Start install $APP_ABI"
  make install
  echo "++++++++ > make and install $APP_ABI complete."

}

build_all() {

  COMMON_OPTIONS="$COMMON_OPTIONS --target-os=android"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-static"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-shared"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-protocols"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-cross-compile"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-optimizations"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-debug"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-small"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-doc"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-programs"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffmpeg"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffplay"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffprobe"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-symver"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-network"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-x86asm"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-asm"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-pthreads"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-mediacodec"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-jni"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-zlib"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-pic"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-avresample"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=h264"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mpeg4"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mjpeg"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=png"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=vorbis"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=opus"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=flac"

  echo "COMMON_OPTIONS=$COMMON_OPTIONS"
  echo "PREFIX=$PREFIX"
  echo "CONFIG_LOG_PATH=$CONFIG_LOG_PATH"

  mkdir -p ${CONFIG_LOG_PATH}

  build "armeabi-v7a"
  # build "arm64-v8a"
  # build "x86"
  # build "x86_64"
}

echo "-------- Start --------"

build_all

echo "-------- End --------"

cd ffmpeg-4.3.2
./configure --help
image-20210411212134628

交叉工具编译链,通过该交叉工具编译链,开发者就能在 PC上编译出可以运行在ARM平台下的程序了。

无论是自行安装PC上的编译器,还是下载其他平台(Android或者 iOS)的交叉工具编译链,它们都会提供以下几个工具:CCASARLDNMGDB。那么,这几个工具到底是做什么用的呢?

  • CC 编译器,对C源文件进行编译处理,生成汇编文件。
  • AS 将汇编文件生成目标文件(汇编文件使用的是指令助记符,AS将它翻译成机器码)。
  • AR 打包器,用于库操作,可以通过该工具从一个库中删除或者增加目标代码模块。
  • LD 链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者是可执行文件。
  • GDB 调试工具,可以对运行过程中的程序进行代码调试工作。
  • STRIP 以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码。
  • NM 查看静态库文件中的符号表。
  • Objdump 查看静态库或者动态库的方法签名。

在NDK目录中,交叉编译的工具有

image-20210411212521515

aarch64-linux-android-对应armv8 arm-linux-androideabi-对应armv7a

# armeabi-v7a
CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
# arm64-v8a
CROSS_PREFIX=aarch64-linux-android-

--sysroot 查找库,有点类似-L 使用

image-20210411213127703

--cc=CC use C compiler CC [gcc]
--cxx=CXX use C compiler CXX [g++]
--ld=LD use linker LD []

image-20210411213426031

上面对交叉编译参数进行了部分的解释,其实就是配置到对应的本台的交叉编译工具链上使用,替换原来系统的gcc g++等工具,链接了NDK的lib库进行编译。对 FFmpeg 项目的编译配置细节就不过多阐述。

此脚本实现了 armeabi-v7a,arm64-v8a,x86,x86_64 4个平台的编译。

  • 需要设置环境变量 $NDK_PATH PREFIX路径

执行编译

image-20210411213936093
chmod +x build_ffmpeg_clang_all.sh
./build_ffmpeg_clang_all.sh
image-20210411214158000
image-20210411214258259

FFmpeg 集成

基于上节编译好的 FFmpeg 动态库,我们在 Android Studio 上进行简单的集成测试

image-20210411214912320

把编译好目录的libs 和Include分别导入工程

image-20210411215417654

这里因为只导入armeabi-v7a平台的库,所以先配置Android abi

image-20210411215808585
externalNativeBuild {
            cmake {
                abiFilters 'armeabi-v7a'
                cppFlags ""
            }
        }
        ndk{
            abiFilters 'armeabi-v7a'
        }

修改cmakeList.txt

导入头文件

include_directories(include)

配置动态库

#添加系统环境变量
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")

加入链接

target_link_libraries( # Specifies the target library.
                       native-lib
                        avcodec
                        avdevice
                        avfilter
                        avformat
                        avutil
                        swresample
                        swscale
                       # Links the target library to the log library
                       # included in the NDK.
                        android
                       ${log-lib} )

修改C++代码

#include 
#include 
#include 

#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,"test",__VA_ARGS__)

//由于 FFmpeg 库是 C 语言实现的,告诉编译器按照 C 的规则进行编译
extern "C" {
#include 
#include 
#include 
#include 
#include 
#include 
#include 
};

extern "C" JNIEXPORT jstring JNICALL
Java_com_lecture_av_helloffmpeg_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    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());
    LOGD("GetFFmpegVersion\n%s", strBuffer);

    return env->NewStringUTF(hello.c_str());
}

run->

2021-04-11 22:55:29.337 2388-2388/com.lecture.av.helloffmpeg I/test: GetFFmpegVersion
    libavcodec : 58.135.100
    libavformat : 58.77.100
    libavutil : 56.71.100
    libavfilter : 7.111.100
    libswresample : 3.10.100
    libswscale : 5.10.100
    avcodec_configure : 
    --target-os=android --disable-static --enable-shared --enable-protocols --enable-cross-compile --enable-optimizations --disable-debug --enable-small --disable-doc --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-symver --disable-network --disable-x86asm --disable-asm --enable-pthreads --enable-mediacodec --enable-jni --enable-zlib --enable-pic --enable-avresample --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mjpeg --enable-decoder=png --enable-decoder=vorbis --enable-decoder=opus --enable-decoder=flac --logfile=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/log/config_armeabi-v7a.log --prefix=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2 --libdir=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/libs/armeabi-v7a --incdir=/Volumes/CodeApp/C_Build_Work/libs/ffmpeg4-3-2/includes/armeabi-v7a

image-20210411225731691

FFmpeg集成完成!

Android 工程代码

你可能感兴趣的:(macos NDK Clang 编译FFmpeg +Android工程集成)