FFmpeg编译与集成

Java是 write once,run anywhre,但 C 不相同,各渠道均有差异,无法只写一次,并且各个渠道的编译都不相同。比方android的ndk东西链,不同渠道的库都是不相同的

本文首要讲解下 ffmpeg 在 win 渠道下的编译以及集成

1、穿插编译

穿插编译:穿插编译便是程序的编译环境和实践运转环境不一致,即在一个渠道上生成另一个渠道上的可履行代码。

为什么要穿插编译,其实之前原因现已说过了,由于不同渠道的差异,指令集都不相同,比方win上面是intel的指令集,但android手机上简直百分百都是arm的指令集,所以直接拿win上编译出来的库给android用,肯定无法运用的,所以需求穿插编译。

穿插编译首要是凭借android 的ndk东西包

下面大致列举了一下经常会用到的组件。

  • ARM 穿插编译器
  • 构建东西
  • Java 原生接口头文件
  • C 库
  • Math 库
  • 最小的 C++ 库
  • ZLib 紧缩库
  • POSIX 线程
  • Android 日志库
  • Android 原生运用 API
  • OpenGL ES 库
  • OpenSL ES 库

下面来看一下 Android 所提供的 NDK 跟目录下的结构。

  • ndk-build: 该 Shell 脚本是 Android NDK 构建体系的起始点,一般在项目中只是履行这一个命令就能够编译出对应的动态链接库了。
  • ndk-gdb: 该 Shell 脚本允许用 GUN 调试器调试 Native 代码,并且能够装备到 AS 中,能够做到像调试 Java 代码相同调试 Native 代码。
  • ndk-stack: 该 Shell 脚本能够帮组分析 Native 代码溃散时的仓库信息。
  • build: 该目录包括 NDK 构建体系的所有模块。
  • platforms: 该目录包括支撑不同 Android 方针版别的头文件和库文件, NDK 构建体系会依据详细的装备来引证指定渠道下的头文件和库文件。
  • toolchains: 该目录包括现在 NDK 所支撑的不同渠道下的穿插编译器 – ARM 、X86、MIPS ,现在比较常用的是 ARM 。构建体系会依据详细的装备选择不同的穿插编译器。

toolchains里一般会提供这么一些东西:

  • CC:编译器,对C源文件进行编译处理,生成汇编文件。
  • AS:将汇编文件生成方针文件(汇编文件运用的是指令助记符,AS将它翻译成机器码)。
  • AR:打包器,用于库操作,能够经过该东西从一个库中删去或许添加方针代码模块。
  • LD:链接器,为前面生成的方针代码分配地址空间,将多个方针文件链接成一个库或许是可履行文件。
  • GDB:调试东西,能够对运转过程中的程序进行代码调试工作。
  • STRIP:以终究生成的可履行文件或许库文件作为输入,然后消除去其中的源码。
  • NM:检查静态库文件中的符号表。
  • Objdump:检查静态库或许动态库的办法签名。

不过不同版别的ndk,里面的东西不相同,部分新的ndk里或许就没有ar 、strip 之类的,或许在新的ndk里这些东西命名不相同或许是放在其它当地了,比方自己发现的21.1.6352462(win)中包括 strip 和 ar,但 24.0.8215888 版别中没有相关库,并且这几个版别中都没有 nm 库,在编译 ffmpeg时必定会提示找不到nm,幸亏 nm不是有必要的,不慌,假如遇到找不到相关东西,阐明途径设置的有问题,或许根本便是当前版别的ndk中没有此类东西或许现已改名,需求去找找资料看看新版别的东西叫啥或许干脆下载旧版别ndk

2、FFmpeg编译

一名优异的c++开发,有必要得对c++编译有必定了解。前文现已介绍了穿插编译,那现在就来学习怎么编译 ffmpeg 吧

在ffmpeg官网下载源码:

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

依据自己需求,切换自己想要的版别。

ffmpeg的编译其实现已十分简略了,由于牛逼的ffmpeg开发者提供了一个脚本,叫 configure,其实咱们写的编译脚本便是在指定编译东西的位置,然后调用 configure 脚本编译

自己是在win11上编译 ffmpeg,需求下载msys2东西并装备相关环境,有必要以管理员运转msys2之后才能来装备环境,不然就会报异常

pacman -S make yasm diffutils pkg-config #在msys2上装置必要软件

然后在ffmpeg文件夹内建脚本文件,并把如下内容贴上:

 #!/bin/sh
NDK_PATH=/c/workspace/android_sdk/ndk/21.1.6352462
BUILD_PLATFORM=windows-x86_64
API=21
ANDROID_ARMV5_CFLAGS="-march=armv5te"
ANDROID_ARMV7_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon"
ANDROID_ARMV8_CFLAGS="-march=armv8-a"
ANDROID_X86_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
ANDROID_X86_64_CFLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"
# params($1:arch,$2:arch_abi,$3:compiler,$4:cross_prefix,$5:cflags)
build_bin() {
    echo "-------------------star build $2-------------------------"
    ARCH=$1         # arm arm64 x86 x86_64
    # CPU
    ANDROID_ARCH_ABI=$2     # armeabi armeabi-v7a x86 mips
    COMPILER=$3
    PREFIX=$(pwd)/dist/${ANDROID_ARCH_ABI}/
    TOOLCHAIN=${NDK_PATH}/toolchains/llvm/prebuilt/${BUILD_PLATFORM}
    CC=${TOOLCHAIN}/bin/${COMPILER}-clang
    CXX=${TOOLCHAIN}/bin/${COMPILER}-clang++
    SYSROOT=${TOOLCHAIN}/sysroot
    CROSS_PREFIX=${TOOLCHAIN}/bin/$4-
    CFLAGS=$5
    echo "pwd==$(pwd)"
    echo "ARCH==${ARCH}"
    echo "PREFIX==${PREFIX}"
    echo "SYSROOT=${SYSROOT}"
    echo "CFLAGS=${CFLAGS}"
    echo "CC==${CC}"
    echo "CROSS_PREFIX=${CROSS_PREFIX}"
	sh ./configure \
        --prefix=${PREFIX} \
        --enable-neon \
        --enable-hwaccels \
        --enable-gpl \
        --disable-postproc \
        --disable-debug \
        --enable-small \
        --enable-jni \
        --enable-mediacodec \
        --enable-decoder=h264_mediacodec \
        --disable-static \
        --enable-shared \
        --disable-doc \
        --enable-ffmpeg \
        --disable-ffplay \
        --disable-ffprobe \
        --disable-avdevice \
        --disable-doc \
        --disable-symver \
        --target-os=android \
        --arch=${ARCH} \
        --cc=$CC \
        --sysroot=$SYSROOT \
        --enable-cross-compile \
        --cross-prefix=${CROSS_PREFIX} \
        --extra-cflags="-Os -fPIC -DANDROID -Wfatal-errors -Wno-deprecated $CFLAGS" \
        --extra-cxxflags="-D__thumb__ -fexceptions -frtti" \
        --extra-ldflags="-L${SYSROOT}/usr/lib" \
    make clean
    make -j8
    make install
    echo "-------------------$2 build end-------------------------"
}
# build armeabi
# build_bin arm armeabi arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV5_CFLAGS"
#build armeabi-v7a
#build_bin arm armeabi-v7a armv7a-linux-androideabi${API} arm-linux-androideabi "$ANDROID_ARMV7_CFLAGS"
#build arm64-v8a
 build_bin arm64 arm64-v8a aarch64-linux-android${API} aarch64-linux-android "$ANDROID_ARMV8_CFLAGS"
#build x86
# build_bin x86 x86 i686-linux-android${API} i686-linux-android "$ANDROID_X86_CFLAGS"
#build x86_64
# build_bin x86_64 x86_64 x86_64-linux-android${API} x86_64-linux-android "$ANDROID_X86_64_CFLAGS"

相关解释:

  • CC:指定c编译器途径
  • CROSS_PREFIX:指定穿插编译东西文件途径的一致前缀。各个东西的终究文件途径为:cross-prefix + 东西名,比方上面脚本的prefix为TOOLCHAIN/bin/arm-linux-androideabi-,那么ar东西的途径即为TOOLCHAIN/bin/arm-linux-androideabi-ar
  • target-os:指定方针渠道,由于 ffmpeg 能够在各渠道上运转的,各渠道上一些装备不太相同,所以需求指定的

别的编译脚本里面还有很多的 enable disable ,这些都是 configure 脚本里的编译选项,比方说 –enable-shared 意思便是编译动态库,所以上面的脚本终究会生成 so 文件,而不会生成 a 文件。

这些编译选项都能够运用 configure –help,能够查询到,我们能够试试

不管是这些 enable 编译选项,仍是像 CC 一类的选项,都是在装备 configure 脚本,经过文本方式打开 configure 文件,能够看到:

--cc=CC                  use C compiler CC [$cc_default]
--target-os=OS           compiler targets OS [$target_os]
--enable-shared          build shared libraries [no]

运转编译脚本之后,假如编译成功了就会看到相关so库了,so库在lib文件夹中

FFmpeg编译与集成

3、FFmpeg集成

首先看cmakelist怎么写:

# 设置最小运用版别
cmake_minimum_required(VERSION 3.18.1)
project("demo")
include_directories(include)
# 添加本地so库 native-lib:这个是声明引证so库的称号 SHARED:表明共享so库文件
# 构建so库的源文件
add_library(
        demo
        SHARED
        native-lib.cpp
)
set(SO_DIR ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
# 运用体系ndk 提供的库,如 log库
# log-lib 这个指定的是在NDK库中每个类型的库会寄存一个特定的位置,而log库寄存
# 在log-lib中
# log 指定运用log库
find_library(
        log-lib
        log
)
message("c_CMAKE_SOURCE_DIR:" ${CMAKE_SOURCE_DIR} )
# 加载avcodec-57add_library( avcodec
        SHARED
        IMPORTED)
set_target_properties( avcodec
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libavcodec.so)
add_library( avutil
        SHARED
        IMPORTED)
set_target_properties( avutil
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libavutil.so)
add_library( swresample
        SHARED
        IMPORTED)
set_target_properties( swresample
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libswresample.so)
add_library( avfilter
        SHARED
        IMPORTED)
set_target_properties( avfilter
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libavfilter.so)
add_library( avformat
        SHARED
        IMPORTED)
set_target_properties( avformat
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libavformat.so)
add_library( swscale
        SHARED
        IMPORTED)
set_target_properties( swscale
        PROPERTIES IMPORTED_LOCATION
        ${SO_DIR}/libswscale.so)
#----------------------end-----------------------
# 假如你本地的库(native-lib)想要调用log库的办法,
# 那么就需求装备这个特点,意思是把NDK库相关到本地库。
# 第一个参数表明本地的库 native-lib 要调用到log库的办法,即要被相关的库称号,log-lib 要相关的库称号
target_link_libraries(
        demo
        #ffmpeg------start----------
        avcodec
        avutil
        swresample
        avfilter
        avformat
        swscale
        #ffmpeg------end------------
        ${log-lib}
)

其实这些写法都十分简略,假如犯错肯定是没写对,留意下相关细节即可。 所有代码均已上传到自己github中