音视频开发(二十六):交叉编译android使用的FFmpeg(3.x和4.x)_android 安装交叉编译工具链-程序员宅基地

技术标签: 实时互动  webrtc  实时音视频  音视频  视频编解码  音视频开发进阶  

目录

  1. 配置安装android交叉编译工具链

  2. 手写FFmpeg编译脚本 进行编译(针对ffmpeg3.x和ffmpeg4.x版本)

  3. androidStudio中引用使用ffmpeg

这篇我们来学习实践ffmpeg的交叉编译,其中会涉及到ffmpeg的版本、NDK的版本、编译脚本的编写、Gradler ABI处理 以及 CMakeLists.txt的针对不同ndk版本脚步的编写
在交叉编译的时候由于平台差异性大,需要工具来解决这一问题,就出现了各种工具链,即Toolchains。而NDK提供了交叉编译的一整套工具的集合,不同版本和配置会有不同差异,很容易就会出现各种问题,是一个不断折腾的过程,),本文记录学习实践的过程,ffmpeg3.3.9+ndk16,以及ffmpeg4.3.1或者ffmpeg4.2.4+ndk21 欢迎交流讨论。

一、配置安装android交叉编译工具链

  1. 下载NDK
    NDK有很多版本 14,16,21等等,我们该选择什么版本比较好呐?
    现在一般都选用16以上的,16时编译工具链做了不少变化。而不同的ffmpeg版本支持的NDK版本也不同,比如,ffmpeg3.x的版本使用android-ndk-r16b,但是ffmpeg4.x的版本使用16就编译不过,这里选用的是android-ndk-r21e.

  2. 配置NDK环境
    android-ndk-r21e

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

配置环境变量
vim ~/.bash_profile

export NDK_PATH=/Users/xxx/tools/android-ndk-r21e
export PATH=${PATH}:$NDK_PATH

编辑保存好之后执行下下面语句使之生效
. ~/.bash_profile

3.安装android交叉编译工具链(也可省略)
/Users/xxx/tools/android-ndk-r 21e/build/tools路径下有make-standalone-toolchain.sh和make_standalone_toolchain.py,我们使用py版本来配置android交叉编译工具链

用到make_standalone_toolchain.py

先创建交叉编译工具链输出的路径
/Users/xxx/tools/android-ndk-r21e/android-toolchains/android-19

mkdir android-toolchains
cd android-toolchains
mkdir android-19
cd android-19

with open(src, 'rb') as fsrc:

到android-ndk-r16b/build/tools
下执行
sudo ./make_standalone_toolchain.py --arch arm --api 19 --install-dir ../../android-toolchains/android-19/arm-arch

这个步骤相当于把配置的arch和api等相关信息在指定文件夹下面做个copy(个人理解),方便后续编写ffmpeg编译脚本时用指定toolchain。

之所以说这个步骤可以省略,是因为,也可以直接在后续编写ffmpeg编译脚本中指定不同ABI和API对应的toolchain路径。

二、手写FFmpeg编译脚本 进行编译(针对ffmpeg3.x和ffmpeg4.x版本)

下载ffmpeg

不同的ffmpeg版本使用编译时需要的ndk版本也会有不同,如果在上一步下载配置的NDK是16,则下载3.x的ffmpeg。如果NDK是21,则下载4.x的ffmpeg。

这个编译的过程折腾了自己两三天的时间,为了搞清楚原因,避免后续踩坑,针对ffmpeg3.x和ffmpeg4.x都进行记录。

我们先来看下编译第三方库的一般通用步骤

  1. 看READMME.md

  2. 编译项目需要Makefile,如果有尝试使用make编译,如果没有,需要写makefile或者cmake构建

  3. 如果报错需要解决,一般都是Makefile的一些配置文件没有生成,运行下configure

  4. 生成配置文件后,再次运行make,但是编译后的文件(elf,so,a)只能在当前系统下运行。

  5. 如果需要需要跑到android或者ios需要交叉编译

  6. 需要给configure传一些脚本编译参数(关键!)

不要去copy,一定要手写,原因有三:

  1. 通过手写,搞清楚每个配置的含义

  2. 避免路径不同导致编译失败。

  3. 避免TAB和空格的转换导致编译失败(这个很坑!!)

下面我们分别编写下ffmpeg3.x和ffmpeg4.x对应的配置编译脚本。

2.1 针对ffmpeg3.x的编译脚本**

ffmpeg3.x_build.sh

#用于编译android平台的脚本
#!/bin/bash

#定义几个变量

ARCH=arm
CPU=armv7-a

#so生成的对应路径
PREFIX = $(pwd)/android 

#注意,这里就是上一小节1.3中说的 安装android交叉编译工具链对应的路径
ANDROID_TOOLCHAINS_PATH=$NDK_PATH/android-toolchains/android-19/arch-arm
CROSS_PREFIX=$ANDROID_TOOLCHAINS_PATH/bin/arm-linux-androideabi-
SYSROOT=$NDROID_TOOLCHAINS_PATH/sysroot


#执行.configure文件 (./configure)
#运行./configure --help查看配置 裁剪
#换行后不要用空格,可以使用tab

echo "start build ffmpeg for $ARCH"
./configure --prefix =${PREFIX} \
            #编译动态库
            --disable-static \
            --enable-shared \
            #根据需要进行裁剪
            --enable-samall \
            --disable-programs \
            --disable-ffmpeg \
            --disable-ffplay \
            --disable-ffprobe \
            --disable-doc \
            --disable-ffserver \
            #指定cpu架构
            --arch=$ARCH \
            指定cpu类型
            --cpu=$CPU \
            #指定交叉编译工具目录
            --cross-prefix=${CROSS_PREFIX} \
            #开启交叉编译
            --enable-crosss-compile \
            #指定目标平台为linux(android内核也是基于linux的ffmpeg早期版本目标平台还不可以指定为android)
            --target-os=linux \
            #配置编译环境c语言的头文件环境
            --sysroot=$SYSROOT \
            --extra-cflags="-Os -fpic" \ #生成与位置无关的代码

make clean
make
make install

echo "end build ffmpeg for $ARCH"

编译生成的so的名称版本号在后面,这不符合在android中使用so的命名规范,为此修改configure脚本,替换以下四行内容,重新编译生成对应的so。

修改生成so的文件名称,andorid方便使用

在configure文件中,搜索build set找到对应的位置

# SLIBNAME_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)'

2.2 针对ffmpeg4.x的版本

#!/bin/bash

#这里每个使用上一小节1.3中安装android交叉编译工具链对应的路径,我们采用手动指定的toolchain,注意要根据不同的API和不同的ABI进行不同的配置。
#另外注意 darwin-x86_64是mac上对应的文件夹名称,linux上对应的是linux-x86_64,需要根据自己编译的环境具体确认。

API=21
TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64/

#armv8-a
ARCH=arm64
CPU=armv8-a
#r21版本的ndk中所有的编译器都在/android-ndk-r21e/toolchains/llvm/prebuilt/darwin-x86_64/目录下(clang)
#配置.c文件的编译工具
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang

#.cpp文件的编译工具
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++

#头文件环境用的不是/android-ndk-r21e/sysroot,而是编译器//android-ndk-r21e/toolchains/llvm/prebuilt/darwin-x86_64/sysroot

SYSROOT=$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64/sysroot

#交叉编译工具目录,对应关系如下(我们这里使用的是armv8a,根据自己的需要进行配置)
# armv8a -> arm64 -> aarch64-linux-android-
# armv7a -> arm -> arm-linux-androideabi-
# x86 -> x86 -> i686-linux-android-
# x86_64 -> x86_64 -> x86_64-linux-android-
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-

#输出目录
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"

# 把configure,make写到一个function中(方便编译不同ABI时复用)
# 运行./configure --help查看配置 裁剪
# 换行后不要用空格,可以使用tab

function build_android
{
echo "CC is $CC"
echo "CROSS_PREFIX is $CROSS_PREFIX"
./configure \
    #指定输出路径
    --prefix=$PREFIX \
    #编译动态库
    --enable-shared \
    --disable-static \
    #根据需要进行裁剪
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-avdevice \
    --disable-doc \
    --disable-symver \
    #指定交叉编译工具目录
    --cross-prefix=$CROSS_PREFIX \
    #指定目标平台为android
    --target-os=android \
    #指定cpu架构
    --arch=$ARCH \
    #指定cpu类型
    --cpu=$CPU \
    #指定c、cpp语言编译器
    --cc=$CC \
    --cxx=$CXX \
    #开启交叉编译
    --enable-cross-compile \
    #配置编译环境c语言的头文件环境
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \

make clean
make
make install

}

build_android

Ffmpeg4.x的版本编译出来命名直接是正确的(但不带版本号),可以不用修改configure直接使用生成的so。

三、AS中引入使用ffmpeg动态库

首先在local.properties中指定对应的ndk.dir,之所以没有用AS自动配置的ndk版本,是因为通过自己手动配置更加灵活,指定不同的版本。

不同的camkelist版本以及gradle版本有不同的API

针对ffmpeg3.x和android-ndk-r16b

cmake_minimum_required(VERSION 3.4.1)

project("myffmpegdemo")
MESSAGE(STATUS "PROJECT_SOURCE_DIR =" ${PROJECT_SOURCE_DIR})
MESSAGE(STATUS "CMAKE_SOURCE_DIR =" ${CMAKE_SOURCE_DIR})

include_directories(${PROJECT_SOURCE_DIR}/ffmpeg/include)

link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a)

add_library(
             native-lib
             SHARED
             native-lib.cpp )

find_library(
        log-lib
        log)

target_link_libraries(
        native-lib
        avcodec-57
        avfilter-6
        avformat-57
        avutil-55
        swresample-2
        swscale-4
        avdevice-57
        postproc-54

        ${log-lib})

针对ffmpeg4.x和 android-ndk-r21e

cmake_minimum_required(VERSION 3.4.2)

project("myffmpeg42demo")

#指定ffmpeg对应的头文件,根据自己的具体情况进行设置
include_directories(${PROJECT_SOURCE_DIR}/ffmpeg/include)



message("CMAKE_CXX_FLAGS:" + ${CMAKE_CXX_FLAGS})
message("CMAKE_SOURCE_DIR:" + ${CMAKE_SOURCE_DIR})

message("CMAKE_ANDROID_ARCH_ABI版本是:" + ${CMAKE_ANDROID_ARCH_ABI})

#4.x采用这种方式引入ffmpeg库不行,改下下面的方式
#link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/armeabi)

# 引入FFmpeg的库文件,设置内部的方式引入,指定库的目录是 -L  指定具体的库-l
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")


add_library( # Sets the name of the library.
        native-lib
        SHARED
        native-lib.cpp)


find_library( # Sets the name of the path variable.
        log-lib

        log)


target_link_libraries( # Specifies the target library.
        native-lib

        avformat avcodec avfilter avutil swresample swscale

        ${log-lib})

 如何适配so架构
每一种架构对应一种ABI,是可以向下兼容的

比如:项目里面没有armeabi-v7a的文件夹,如果你有两个文件夹armeabi和armeabi-v7a两个文件夹,armeabi里面有a.so 和 b.so, armeabi-v7a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有armeabi-v7a的文件夹,发现里面没有b.so,就报错了,如果没有armeabi-v7a文件夹,运行时发现没有适配armeabi-v7a,就会直接去找armeabi的so库,所以要么你别加armeabi-v7a,要么armeabi里面有的so库,armeabi-v7a里面也必须有。

四、遇到的问题

1. ffbuild/config.mak: No such file or directory

./ffmpeg_build3.sh 
Unknown option "--disable-ffserver".
See ./configure --help for available options.
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile'.  Stop.
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile'.  Stop.
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile'.  Stop.

解决方案:一般是configure配置出错了,或者换行带有空格或者copy的tab被转为了空格导致,解决方案搞清楚ffmpeg编译脚本的意义,按照规范手写。

2. 4.x的版本编译的so,在编译时报错 incompatible target

/Users/yabin/tools/android-ndk-r21e/toolchains/llvm/prebuilt/darwin-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /Users/yabin/work/tmp/myffmpegDemo/app/src/main/cpp/../jniLibs/armeabi-v7a/libswscale.so: incompatible target

把ndk的版本从21换成16也一样的错误

ndk.dir=/Users/yabin/tools/android-ndk-r21e

ndk.dir=/Users/yabin/tools/android-ndk-r16b

/Users/yabin/tools/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /Users/yabin/work/tmp/myffmpegDemo/app/src/main/cpp/../jniLibs/armeabi-v7a/libswscale.so: incompatible target

解决方案:
换成了3.3.9版本的ffmpeg的编译产物,可以了
根本原因在于不同NDK版本对应的Cmakelist中引入so的方式不同。

3. AS引入so后在armeabi中加入so,正确配置后,编译报错“armeabi is no longer supported. Use armeabi-v7a”

CMake Error at /Users/yabin/tools/android-ndk-r21e/build/cmake/android.toolchain.cmake:174 (message):
armeabi is no longer supported.  Use armeabi-v7a.


Error while executing process cmake/3.10.2.4988404/bin/cmake with arguments 
  Unknown argument -


  abiFilters Collection contains no element matching the predicate.
  No valid Native abi found to request!

4. 【ffmpeg】编译时报错:error: undefined reference to 'av_codec_next(AVCodec const)’**

解决方案:ffmpeg库的接口都是c函数,其头文件也没有extern "C"的声明,所以在cpp文件里调用ffmpeg函数要加extern “C” 。

其中ffmpeg_build.sh放入到下载的ffmpeg文件夹中,进行配置编译。

感谢你的阅读

下一篇我们开始进入ffmpeg的使用篇,以结果为导向,通过ffmpeg实现一个视频解码器

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_60259116/article/details/124124492

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签