Android mac 交叉编译与ffmpeg编译踩坑记 (v7a 与 v8a and 动态库与静态库)

Android mac 交叉编译与ffmpeg编译踩坑记

环境:

system: mac

NDK: android-ndk-r17c

Fffmpeg: ffmpeg-4.0.2

Cmake: 3.10.2

Gradle: 4.1.3

tips: 本文记录踩坑过程,具体细节如果感兴趣可以在评论区留言交流讨论!

mac 编译 (动态库(so))

首先来回顾一下,mac原始库是如何编译的

先来创建一个最简单的so文件:

image-20230711093344318

只需要输入:

gcc -fPIC -shared main.c -o libmain.so

image-20230711093507390

这样就可以将c代码编译成so库

mac 编译 (静态库(a))

静态库稍微麻烦一点,需要先生成 .o文件 [ gcc -fPIC -c main.c -o main.o ]

然后通过 .o文件生成 .a文件 [ar -r libmain.a main.o]

image-20230711094503924

这样一来,就很轻易的编译成了静态库

android 交叉编译(动态库 arm-v7a)

这里就有疑问了, 为什么需要交叉编译 ?

因为刚才我们的代码是在mac上编译的 ,android并不认识, 要想android认识,就引出了交叉编译

就好像越南人来china买东西,必须吧越南盾换成人民币一样,必须要认识,才能够使用

在mac上,编译的方式是采用gcc

那么NDK也为我们提供了GCC

image-20230711095455235

那么只能这样:

image-20230711100232342

此时就会出现第一个坑:

找不到头文件

他的头文件都在这里, 只需要再次配置一下就OK

image-20230711101202396

配置完成头文件后,就会有第二个坑:

找不到 asm/types.h

image-20230711101338201

asm/types.h 在这里:

image-20230711101835708

那么这个也需要配置一下:

image-20230711102217655

最终编译, 还是不成功! .

最后还需要告诉gcc,当前编译的是什么android架构,当前编译的是v7a,那么只需要告诉gcc即可

image-20230711102511048

最终编译:

image-20230711102731233

完整命令:

/Users/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi -fPIC -shared main.c -o libmain-ndk-32.so

这样一来, 打包成的so库就可以在android v7a的手机上运行了!

但是问题就来了,我不可能每次打包都这么麻烦,输这么长的指令,实在是太丑了, 所以说我将它配置成了全局变量,并写成了shell

配置全局变量:

# .bash_profile# NDK路径
export NDK="/Users/ndk/android-ndk-r17c"# NDK的命令操作 配置到 Linux环境变量里面去
export PATH=$NDK:$PATH# 下面是交叉编译相关
# ================= GCC ==================================
export NDK_GCC_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-gcc"export NDK_GCC_x64="$NDK/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-gcc"export NDK_GCC_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc"export NDK_GCC_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-gcc"# ================================== config 配置架构 ==================================
export NDK_CFIG_x86="--sysroot=$NDK/platforms/android-21/arch-x86 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/i686-linux-android"export NDK_CFIG_x64="--sysroot=$NDK/platforms/android-21/arch-x86_64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/x86_64-linux-android"export NDK_CFIG_arm="--sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi"export NDK_CFIG_arm_64="--sysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/aarch64-linux-android"

记得保存:

source ~/.bash_profile

Shell 命令

image-20230711103651463

重新编译v7a 动态库(so):

image-20230711103822045

android 交叉编译(动态库 arm-v8a)

v8a 的方式也是一样的,只是选择架构的时候选择v8a即可

image-20230711103915648

为了在android端演示, 写一个util方法,里面有加减乘除函数, 通过JNI调用试试

image-20230711112017150

可以直接采用sh命令来直接生成交叉编译后的so,

然后可以通过 file. XXX.so 来查看编译结果!

在android中测试打包后的so库:

创建一个C++工程, 配置CMake

image-20230711112611458

记得在gradle中设置NDK架构:

image-20230711112702104

最后直接来调用即可:

image-20230711112850894

android 交叉编译 (静态库 arm-v7a)

静态库与动态库类似,只需要将linux的编译方式换成NDK的编译方式即可

Linux编译方式:
1. 生成.o文件
gcc -fPIC -c main.c -o main.o 2. 通过.o文件来构建 静态库
ar -r libmain.a main.o

先来找到NDK对应v7a架构的ar, 因为这里是通过ar来生成.a 文件的

image-20230711130713079

同样为了方便使用,我把它配置成了全局变量,并且写成了shell命令

# .bash_profile# 下面是交叉编译相关
# ================= GCC ==================================
export NDK_GCC_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-gcc"export NDK_GCC_x64="$NDK/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-gcc"export NDK_GCC_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc"export NDK_GCC_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-gcc"# ================================== config 配置架构 ==================================
export NDK_CFIG_x86="--sysroot=$NDK/platforms/android-21/arch-x86 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/i686-linux-android"export NDK_CFIG_x64="--sysroot=$NDK/platforms/android-21/arch-x86_64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/x86_64-linux-android"export NDK_CFIG_arm="--sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi"export NDK_CFIG_arm_64="--sysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/aarch64-linux-android"# ================================== ar配置 用来生成静态库 ==================================
export NDK_AR_x86="$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-ar"export NDK_AR_x64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"export NDK_AR_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar"export NDK_AR_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"

同样记得保存:

source ~/.bash_profile	

shell命令:

#!bin/bashif [  $1 != 'v7a' ] && [ $1 != 'v8a' ]; thenecho "请选择平台 正确指令: sh $0 v7a或v8a(生成架构) xxx"exit 0
fiecho 您选择编译的平台是: $1if [ -e $NDK ]; thenif [ "v7a" = $1 ]; then# 交叉编译生成.o文件$NDK_GCC_arm $NDK_CFIG_arm -fPIC -c $2.c -o $2.oecho "生成$2.o中..."sleep 1$NDK_AR_arm rcs -o lib-$2.a $2.oelse $NDK_GCC_arm_64 $NDK_CFIG_arm_64 -fPIC -c $2.c -o $2.oecho "生成$2.o中..."sleep 1$NDK_AR_arm_64 rcs -o lib-$2.a $2.ofiecho "执行成功"elseecho "执行失败"
fi

使用:

只需要简单的:

sh static_build.sh v7a util 即可

image-20230711132834051

android 交叉编译 (静态库 arm-v8a)

Arm-v8a编译静态库与动态库编译方式是一样的; 使用同一个shell命令

image-20230711132718889

在android中使用:

image-20230711133200529

使用效果和动态库类似,只是配置的方式不同

动态库与静态库的区别

  1. 静态库不会显示在apk中,在编译过程中,静态库的太吗都会copy到主库中

image-20230711133500901

相反动态库则全会打包到apk中

image-20230711133745838

ffmpeg编译(armv7a)

ffmpeg如何下载我就不说了, 写了一个脚本,我测试了很多次,只要NDK地址给对 80%都是可以编译成功的. 坑我全踩了 花了2天时间

build32.sh

#!/bin/bash# NDK_PATH
NDK_ROOT=/Users/ndk/android-ndk-r17c# NDK中gcc地址 arm-v7a地址
TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"
INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"# 1.定义编译后,所存放的目录
PREFIX=./android/arm32# 2.--enable-small 优化大小 非常重要,必须优化才行的哦
# 3.--disable-programs 不编译ffmpeg程序(命令行工具),我们是需要获取静态、动态库
# 4.--disable-avdevice 关闭avdevice模块,此模块在android中无用
# 5.--disable-encoders 关闭所有编码器(播放不需要编码)
# 6.--disable-muxers 关闭所有复用器(封装器),不需要生成mp4这样的文件,所有关闭
# 7.--disable-filters 关闭所有滤镜
# 8.--enable-cross-compile 开启交叉编译(ffmpeg是跨平台的,注意:并不是所有库都有这么happy的选项)
# 9.--cross-prefix 看右边的值就知道是干嘛的,gcc的前缀..
# 10.disable-shared / enable-static 这个不写也可以,默认就是这样的,(代表关闭动态库,开启静态库)
# 11.--sysroot
# 12.--extra-cflags 会传给gcc的参数
# 13.--arch  --target-os 编译版本 v7a=arm; v8a=arm64;./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \
--arch=arm \
--target-os=androidmake cleanmake install

如果你想编译v7a的,直接在这里创建build32.sh 吧NDK路径换成你自己的

image-20230711134705544

最好是手动创建好编译后存放的目录 [android/arm32]

编译大约3-5分钟就OK了

sh build32.sh

切记,编译时候切换到root环境编译

ffmpeg编译(armv8a)

还是同样的套路,写一个shell脚本:

build64.sh

#!/bin/bash# NDK位置
NDK_ROOT=/Users/ndk/android-ndk-r17c# GCC
TOOLCHAIN=$NDK_ROOT/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv8-a -mcpu=generic -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"
INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"# 1.定义编译后,所存放的目录
PREFIX=./android/arm64# 2.--enable-small 优化大小 非常重要,必须优化才行的哦
# 3.--disable-programs 不编译ffmpeg程序(命令行工具),我们是需要获取静态、动态库
# 4.--disable-avdevice 关闭avdevice模块,此模块在android中无用
# 5.--disable-encoders 关闭所有编码器(播放不需要编码)
# 6.--disable-muxers 关闭所有复用器(封装器),不需要生成mp4这样的文件,所有关闭
# 7.--disable-filters 关闭所有滤镜
# 8.--enable-cross-compile 开启交叉编译(ffmpeg是跨平台的,注意:并不是所有库都有这么happy的选项)
# 9.--cross-prefix 看右边的值就知道是干嘛的,gcc的前缀..
# 10.disable-shared / enable-static 这个不写也可以,默认就是这样的,(代表关闭动态库,开启静态库)
# 11.--sysroot
# 12.--extra-cflags 会传给gcc的参数
# 13.--arch  --target-os ./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm64 \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \
--arch=arm64 \
--target-os=androidmake cleanmake install

只需要吧NDK地址换成自己的即可:

image-20230711134959290

sh build64.sh

最终编译后,这就是编译结果:

image-20230711135314188

导入到android中使用:

image-20230711135700846

调用看看结果:

image-20230711135810027

我将本篇用到的所有命令已经全局配置放到项目中了,有需要的亲下载源码自取!

image-20230711140131262

完整代码

原创不易,您的点赞就是对我最大的支持!

往期文章:

  • android kotlin 协程(六) 源码浅析

  • android kotlin 协程(五) suspend与continuation

  • android kotlin 协程(四) 协程间的通信

  • android 自定义View:仿QQ拖拽效果

  • android MD 进阶[五] CoordinatorLayout 从源码到实战…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18520.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

沉浸式翻译(immersive-translate):解决谷歌翻译无法使用的问题

前言: 在如今的全球化时代,语言不再是隔离人们交流的障碍。然而,在浏览外语网页时,我们常常遇到一个问题:无法准确理解其中的内容。 谷歌翻译是一款强大的翻译工具,但由于各种原因,我们可能无法…

负载均衡的知识点

目录 1.负载均衡是什么 2.负载均衡的分类 客户端负载均衡: 服务端负载均衡: 软件实现:根据OSI模型可以分为四层负载均衡和七层负载均衡 硬件实现: 附1:客户端和服务端: 附2:OSI…

nginx基本使用

这是一份完整的nginx配置文件: #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024; }http {include mi…

Markdown基本用法

目录 1 字体倾斜 1.1 加* 1.2 加_ 2 字体加粗 2.1 加** 2.2 加__ 3 字体上带删除线 4 文字变标题 5 超链接 5.1 直接输入地址 5.2 将超链接改成文字 5.2.1 同行写法 5.2.2 不同行写法 6 文字前加 6.1 号 6.2 *号 6.3 -号 7 有序列表 8 …

2023数学建模国赛常用算法-Topsis优劣解距离法

更多国赛数学建模资料思路,关注文末! 1 优劣解距离法(TOPSIS)简介 1.1 概念 TOPSIS 法是一种常用的组内综合评价方法,能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距。基本过程为基于…

客户端是win10远程连接ubuntu后图形化执行程序verdi

一、安装xlaunch 1.1 搜索vcxsrv安装即可 1.2 对vcx进行配置,参考 将ubuntu终端打开的GUI图形界面窗口显示在windows上_heusjh的博客-CSDN博客https://blog.csdn.net/m0_51194302/article/details/128768284#:~:text%E5%B0%86ubuntu%E7%BB%88%E7%AB%AF%E6%89%93%…

DataFun: ChatGPT背后的模型详解

ChatGPT背后的模型详解 Overview Transofrmer 各个构件都有一定的作用 Multi-head self attention 每个字的重要性不一样,学习QKV三个矩阵(query,key,value) 多组QKV RLHF ChatGPT训练过程 思维链 COT

Unreal 5 游戏框架

之前,只是简单的实现了一些特定的功能,这几天一直在学习官方的案例,学习了Lyra初学者项目和Action RPG的项目,也从中学习到了很多功能的使用,并对基础的架构设计有了初步的认识。 接下来,将对基础的一些设置…

41.RocketMQ之高频面试题大全

消息重复消费 影响消息正常发送和消费的重要原因是网络的不确定性。 引起重复消费的原因 ACK 正常情况下在consumer真正消费完消息后应该发送ack,通知broker该消息已正常消费,从queue中剔除。当ack因为网络原因无法发送到broker,broker会认为…

SpringBoot源码分析(4)--Environment(下)/配置文件加载原理

SpringBoot源码分析 SpringBoot源码分析(1)–SpringBootApplication注解使用和原理/SpringBoot的自动配置原理详解SpringBoot源码分析(2)–SpringBoot启动源码(万字图文源码debug讲解springboot启动原理)SpringBoot源码分析(3)–Environment简介/prepareEnvironment准备环境&a…

已解决‘mongo‘ 不是内部或外部命令,也不是可运行的程序

已解决(MongoDB安装报错)‘mongo’ 不是内部或外部命令,也不是可运行的程序 报错代码 粉丝群里的一个小伙伴安装完MongoDB后,在cmd中启动,却说不是可运行的命令? 报错原因 报错原因:由于没有配置环境变量的…

《TCP/IP网络编程》第3,4章学习记录

基础知识: struct sockaddr_in {sa_family_t sin_family; //地址族(Address Family)uint16_t sin_port; //16位TCP/UDP端口号struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用 }sa_family_t包括: (1)AF_INET,IPv4网络协议…