阅读 535

FFmpeg-Android交叉编译完全剖析(包括openssl)

背景介绍

现如今FFmpeg应用越来越广泛,应用场景也越来越多,想要在Android平台上用上FFmpeg,首先要掌握交叉编译的技巧,才能编译出Android平台上好用的FFmpeg库. FFmpeg是一个多媒体开发框架,其支持音视频一条龙的服务.

ffmpeg转码工作流程 (1).png

  • FFmpeg只提供框架以及必要的一些工具,一些工具toolkit还是依赖三方继承进来的,不过FFmpeg提供了一些通用的接口方便开发者集成的.
  • FFmpeg原生编译是不带https解析的,需要引入三方库去解决https的识别问题,这个三方库就是openssl
  • 需要集成h265吗,目前h265在Android端的软解方面性能很差,基本上没有商用的可能性,所以集成x265到FFmpeg上意义不大,本文不作讨论.

编译准备

FFmpeg源码:git.ffmpeg.org/ffmpeg.git 下载下来之后切换到一个release分支,我切换的是n4.0.3分支;每个分支的情况编译都不一样,这个分支的代码尝试编译时可以的,推荐给大家吧;

编译系统:Mac OS X 或者 Ubuntu 18.04

ndk版本:android-ndk-r14b

大家可以直接参考我的开源项目: github.com/JeffMony/An…

下载这个开源项目,其中ffmpeg目录下有具体的ffmpeg源码。当然你也可以单独下载ffmpeg源码。

FFmpeg单独编译

我们现在想生成FFmpeg的so,放在Android手机上执行,ffmpeg中有一个非常重要的文件是configure 针对Android 平台的,只需要关注armeabi-v7a 、 arm64-v8a、x86、x86_64四种平台就可以; 交叉编译主要有4点:

  • 编译架构ARCH,armeabi-v7a 是 arm , arm64-v8a 是arm64, x86架构是x86,x86_64是 x86_64
  • 编译平台PLATFORM,armeabi-v7a 是 arm-linux-androideabi, arm64-v8a 是aarch64-linux-android, x86是 i686-linux-android, x86_64是 x86_64_linux_android,这个还是和认知有点区别的。
  • 系统链接SYSROOT,armeabi-v7a 是$NDK_ROOT/platforms/android-$API/arch-arm/, arm64-v8a 是$NDK_ROOT/platforms/android-$API/arch-arm64/,x86是$NDK_ROOT/platforms/android-$API/arch-x86/,x86_64是$NDK_ROOT/platforms/android-$API/arch-x86_64/
  • 交叉编译工具,MAC平台下是$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64/bin/$PLATFORM-,Linux平台下是$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/linux-x86_64/bin/$PLATFORM

直接看一下ndk工具集的目录就行了: toolchains目录下有交叉编译的工具集:

drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 aarch64-linux-android-4.9
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 arm-linux-androideabi-4.9
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 llvm
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 mips64el-linux-android-4.9
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 mipsel-linux-android-4.9
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 renderscript
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 x86-4.9
drwxr-xr-x  3 jeffmony jeffmony 4096 3月  15  2017 x86_64-4.9
复制代码

FFmpeg 有很多configure 配置选项,通过 ./configure --help可以看到全部的FFmpeg 配置选项; 编译之前还要注意一点,如果想让生成的so是标准的so命名规范,还需要改一下configure文件中的配置; 将configure中的几行配置修改一下 1.configure修改点1

SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)
复制代码

修改为

SLIBNAME_WITH_MAJOR='$(SLIBNAME)-$(LIBMAJOR)$(SLIBSUF)'
复制代码

2.configure修改点2

SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
复制代码

修改为

SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
复制代码

下面是ffmpeg的动态库的完整的编译脚本:其中支持MAC和Linux平台

#!/bin/bash

# PLATFORM -----> MAC   LINUX
COMPILE_PLATFORM="LINUX"

build_arm() {
API=24
ARCH=$1
PLATFORM=$2
if [ "$COMPILE_PLATFORM" == "MAC" ]
then
    export NDK_ROOT=/Users/jeffmony/tools/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64/bin/$PLATFORM-
else
    export NDK_ROOT=/home/jeffmony/developer/android-ndk-r14b/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/linux-x86_64/bin/$PLATFORM-
fi
PREFIX=$(pwd)/android/openssl_lib/$ARCH #自己指定一个输出目录
rm -rf $(pwd)/android/openssl_lib/$ARCH

echo "开始编译ffmpeg $ARCH so"
./configure \
--prefix=$PREFIX \
--disable-doc \
--enable-shared \
--disable-static \
--disable-x86asm \
--disable-asm \
--disable-symver \
--disable-devices \
--disable-avdevice \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--enable-small \
--enable-nonfree \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--sysroot=$SYSROOT
}

build_arm arm arm-linux-androideabi
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_arm arm64 aarch64-linux-android
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_x86() {
API=24
ARCH=$1
PLATFORM=$2
if [ "$COMPILE_PLATFORM" == "MAC" ]
then
    export NDK_ROOT=/Users/jeffmony/tools/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$ARCH-4.9/prebuilt/darwin-x86_64/bin/$PLATFORM-
else
    export NDK_ROOT=/home/jeffmony/developer/android-ndk-r14b/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$ARCH-4.9/prebuilt/linux-x86_64/bin/$PLATFORM-
fi
PREFIX=$(pwd)/android/openssl_lib/$ARCH #自己指定一个输出目录
rm -rf $(pwd)/android/openssl_lib/$ARCH

echo "开始编译ffmpeg $ARCH so"
./configure \
--prefix=$PREFIX \
--disable-doc \
--enable-shared \
--disable-static \
--disable-x86asm \
--disable-asm \
--disable-symver \
--disable-devices \
--disable-avdevice \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--enable-small \
--enable-nonfree \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--sysroot=$SYSROOT
}

build_x86 x86 i686-linux-android
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_x86 x86_64 x86_64-linux-android
make clean
make -j4
make install
复制代码

将这个脚本放在FFmpeg 项目文件下,执行这个脚本,执行完成 ffmpeg/android 目录下生成两个文件夹,分别是 arm和arm64, 里面的文件分别是两个CPU架构下生成的动态库和头文件;

  • include 下面是生成的头文件,通过这些头文件可以引用生成so中的ffmpeg 具体模块的功能;
  • lib 下面是生成的动态库,Android下直接加载这些动态库,pkgconfig 文件夹是pc上的链接文件,我们这里可以忽略;
  • share 文件夹下面是ffmpeg中的例子应用,学习这些例子对理解ffmpeg有很大的帮助;

生成的文件目录如下:

├── arm
│   ├── include
│   ├── lib
│   └── share
├── arm64
│   ├── include
│   ├── lib
│   └── share
├── x86
│   ├── include
│   ├── lib
│   └── share
└── x86_64
    ├── include
    ├── lib
    └── share
复制代码

但是生成的这个so也是存在一定的缺陷,就是不能访问https链接,这个在Android平台当然是无法接收的,所以还是需要增加https的支持。

openssl编译

openssl是一个支持https的全面的库,ffmpeg要想支持https,需要集成openssl库,所以我们要编译openssl库生成静态库,然后集成打包到ffmpeg的so中。 先下载openssl库,www.openssl.org/source/snap… 下载最新的:openssl-1.1.1-stable-SNAP-20200215.tar.gz openssl 库中编译配置是 ./Configure 文件

Configuring OpenSSL version 1.1.1l-dev (0x101010c0L) for 
Using os-specific seed configuration
Usage: Configure [no-<cipher> ...] [enable-<cipher> ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] os/compiler[:flags]
复制代码

openssl的编译选项有点少,我们需要将openssl编译到ffmpeg中,还是选择编译静态库,方便打包; 编译脚本如下

#!/bin/bash

# PLATFORM -----> MAC   LINUX
COMPILE_PLATFORM="LINUX"

build() {
API=24
CPU=$1
PLATFORM=$2
make clean
rm -rf $(pwd)/android/$CPU
if [ "$COMPILE_PLATFORM" == "MAC" ]
then
    export NDK_ROOT=/Users/jeffmony/tools/android-ndk-r14b # 修改自己本地的ndk路径
    export ANDROID_NDK_HOME=$NDK_ROOT
    PATH=$ANDROID_NDK_HOME/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64/bin:$PATH
else
    export NDK_ROOT=/home/jeffmony/developer/android-ndk-r14b/android-ndk-r14b # 修改自己本地的ndk路径
    export ANDROID_NDK_HOME=$NDK_ROOT
    PATH=$ANDROID_NDK_HOME/toolchains/$PLATFORM-4.9/prebuilt/linux-x86_64/bin:$PATH
fi

./Configure android-$CPU -D__ANDROID_API__=24 no-shared no-ssl2 no-ssl3 no-comp no-hw no-engine --prefix=$(pwd)/android/$CPU --openssldir=$(pwd)/android/$CPU

make
make install
}

# build armv7
build arm arm-linux-androideabi

# build armv8
build arm64 aarch64-linux-android

# build x86
build x86 x86

# build x86_64
build x86_64 x86_64
复制代码

编译完成之后,会在android 文件夹下生成 arm、arm64、x86、x86_64四个文件夹;

drwxrwxr-x 2 jeffmony jeffmony  4096 6月   9 10:32 bin
drwxrwxr-x 2 jeffmony jeffmony  4096 6月   9 10:32 certs
-rw-r--r-- 1 jeffmony jeffmony   412 6月   9 10:32 ct_log_list.cnf
-rw-r--r-- 1 jeffmony jeffmony   412 6月   9 10:32 ct_log_list.cnf.dist
drwxrwxr-x 3 jeffmony jeffmony  4096 6月   9 10:32 include
drwxrwxr-x 4 jeffmony jeffmony  4096 6月   9 10:32 lib
drwxrwxr-x 2 jeffmony jeffmony  4096 6月   9 10:33 misc
-rw-r--r-- 1 jeffmony jeffmony 10909 6月   9 10:32 openssl.cnf
-rw-r--r-- 1 jeffmony jeffmony 10909 6月   9 10:32 openssl.cnf.dist
drwxrwxr-x 2 jeffmony jeffmony  4096 6月   9 10:32 private
drwxrwxr-x 4 jeffmony jeffmony  4096 6月   9 10:33 share
复制代码
  • include 下面是 openssl 的核心头文件;
  • lib 下面是编译好的 静态库;libcrypto.a和libssl.a

生成好了openssl静态库,接下来需要在编译ffmpeg的将静态库集成进去了。

FFmpeg编译引入openssl库

上面给出了编译openssl静态库的过程,ffmpeg如果想解析https的链接,必须将openssl 编译进 ffmpeg 库中;

在编译ffmpeg的基础上加一些参数:

  • 编译配置中加上 --enable-openssl \ --enable-nonfree
  • 编译链接中加上openssl的链接:-extra-cflags 加上 openssl的头文件;--extra-ldflags 加上 openssl的静态库;
#!/bin/bash

# PLATFORM -----> MAC   LINUX
COMPILE_PLATFORM="LINUX"

build_arm() {
API=24
ARCH=$1
PLATFORM=$2
OPENSSL=$(pwd)/openssl_lib/$ARCH
if [ "$COMPILE_PLATFORM" == "MAC" ]
then
    export NDK_ROOT=/Users/jeffmony/tools/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/darwin-x86_64/bin/$PLATFORM-
else
    export NDK_ROOT=/home/jeffmony/developer/android-ndk-r14b/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$PLATFORM-4.9/prebuilt/linux-x86_64/bin/$PLATFORM-
fi
PREFIX=$(pwd)/android/openssl_lib/$ARCH #自己指定一个输出目录
rm -rf $(pwd)/android/openssl_lib/$ARCH

echo "开始编译ffmpeg $ARCH so"
./configure \
--prefix=$PREFIX \
--disable-doc \
--enable-shared \
--disable-static \
--disable-x86asm \
--disable-asm \
--disable-symver \
--disable-devices \
--disable-avdevice \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--enable-small \
--enable-openssl \
--enable-nonfree \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--sysroot=$SYSROOT \
--extra-cflags="-I$OPENSSL/include -fPIE -pie" \
--extra-ldflags="-L$OPENSSL/lib"
}

build_arm arm arm-linux-androideabi
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_arm arm64 aarch64-linux-android
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_x86() {
API=24
ARCH=$1
PLATFORM=$2
OPENSSL=$(pwd)/openssl_lib/$ARCH
if [ "$COMPILE_PLATFORM" == "MAC" ]
then
    export NDK_ROOT=/Users/jeffmony/tools/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$ARCH-4.9/prebuilt/darwin-x86_64/bin/$PLATFORM-
else
    export NDK_ROOT=/home/jeffmony/developer/android-ndk-r14b/android-ndk-r14b # 修改自己本地的ndk路径
    SYSROOT=$NDK_ROOT/platforms/android-$API/arch-$ARCH/
    CROSS_PREFIX=$NDK_ROOT/toolchains/$ARCH-4.9/prebuilt/linux-x86_64/bin/$PLATFORM-
fi
PREFIX=$(pwd)/android/openssl_lib/$ARCH #自己指定一个输出目录
rm -rf $(pwd)/android/openssl_lib/$ARCH

echo "开始编译ffmpeg $ARCH so"
./configure \
--prefix=$PREFIX \
--disable-doc \
--enable-shared \
--disable-static \
--disable-x86asm \
--disable-asm \
--disable-symver \
--disable-devices \
--disable-avdevice \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--enable-small \
--enable-openssl \
--enable-nonfree \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--sysroot=$SYSROOT \
--extra-cflags="-I$OPENSSL/include -fPIE -pie" \
--extra-ldflags="-L$OPENSSL/lib"
}

build_x86 x86 i686-linux-android
make clean
make -j4
make install
echo "完成ffmpeg $ARCH 编译..."

build_x86 x86_64 x86_64-linux-android
make clean
make -j4
make install
复制代码

编译过程中发生如下的错误,找不到openssl;

test1.png 去ffmpeg/configure 文件中查看一下:

test2.png 报错的地方在这里,原因是新版本的openssl 需要在configure中新加一个检测语句:

test3.png

check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto
复制代码

老的openssl库使用‘SSL_library_init’初始化, 新版本openssl使用‘OPENSSL_init_ssl’初始化;

下面正常编译就没有问题了;生成的ffmpeg中的so是可以解析https的;

通过上面的方式编译出来的so是可以解析https协议的。 这些so怎么使用,可以参考开源项目,简单测试一下编译出来的so是否可以正常使用: github.com/JeffMony/Je…

文章分类
Android
文章标签