NDK 编译(二)—— NDK 编译与集成 FFmpeg

news2024/11/16 9:39:29

NDK 编译系列文章共三篇,目录如下:

NDK 编译(一)—— Linux 知识汇总
NDK 编译(二)—— NDK 编译与集成 FFmpeg
NDK 编译(三)—— CMake 原生构建工具

在使用 NDK 进行音视频开发时,势必会用到 FFmpeg,因此我们要知道如何编译 FFmpeg 并将其集成到 Android 项目中。

1、准备工作

1.1 下载 FFmpeg

下载 Linux 版的 FFmpeg 4.0.6 版本并解压:

[root@frank AndroidNDK]# wget https://ffmpeg.org/releases/ffmpeg-4.0.6.tar.bz2
[root@frank AndroidNDK]# tar -xjf ffmpeg-4.0.6.tar.bz2

解压好了后将进入 FFmpeg 的目录,将用户手册导出到文件中方便查看:

[root@frank AndroidNDK]# cd ffmpeg-4.0.6/
[root@frank ffmpeg-4.3.6]# ./configure --help -> ffmpeg_help.txt

1.2 下载 NDK

下载 Linux 版本的 NDK r17c 版本并解压:

[root@frank AndroidNDK]# wget https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip?hl=zh_cn
[root@frank AndroidNDK]# unzip android-ndk-r17c-linux-x86_64.zip

2、编译 FFmpeg

实际上在 FFmpeg 根目录下有一个编译脚本 configure.sh,运行该脚本就可以编译出完整的 FFmpeg 的库文件和头文件,但是这样编译出的库在 Android 上用不了。所以我们才需要通过 NDK 做交叉编译,编译出 Android 可以使用的静态库或动态库。由于编译 FFmpeg 的参数众多,因此我们把编译指令写入一个脚本文件中:

#!/bin/bash
# NDK 根目录
NDK_ROOT=/root/AndroidNDK/android-ndk-r17c
# 定义 NDK 交叉编译时使用的编译器(所在的目录)
TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
# 编译参数的 FLAGS 可以从 AS 的 app\.cxx\...\x86_64\build.ninja(不同版本路径不同)
# 与 4.0.2 版本相比去掉了 -fstackprotector-strong 参数,因为 4.0.6 不识别
FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -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"

# 执行 configure 脚本,用于生成 makefile,参数详解:
#--prefix: 安装目录
#--enable-small: 启用小型优化,以减小生成的库文件的大小
#--disable-programs:不编译 ffmpeg 自带的程序(命令行工具),我们是需要获得静态(动态)库
#--disable-avdevice:关闭 avdevice 模块(音视频设备支持),此模块在 android 中无用
#--disable-encoders:关闭所有编码器(播放不需要编码)
#--disable-muxers:关闭所有复用器(封装器),不需要生成 mp4 这样的文件,所以关闭
#--disable-filters:关闭视频滤镜
#--enable-cross-compile:开启交叉编译,用于在一个平台上为另一个平台生成可执行文件(ffmpeg 支持跨平台)
#--cross-prefix:指定交叉编译工具链的前缀,例如 gcc 的前缀 xxx/xxx/xxx-gcc 则给 xxx/xxx/xxx-
#--disable-shared 和 enable-static:禁用生成动态库和启用生成静态库。不写也可以,默认就是这样的
#--sysroot: 指定系统根目录,用于定位 Android 平台的头文件和库文件
#--extra-cflags: 会传给 gcc 的参数
#--arch:指定目标平台的体系结构,一般真机为 ARM,模拟器为 x86
#--target-os: 指定目标操作系统,这里为 Android

PREFIX=./output/android
./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=android

make clean
make install

编译 FFmpeg 过程:

  1. 定义两个变量,这两个变量在后续脚本中会被多次用到
    • NDK 的根目录:/root/AndroidNDK/android-ndk-r17c
    • 交叉编译的编译器所在的目录:TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
  2. 定义编译产物输出路径的变量:PREFIX=./output/android
  3. 定义 gcc 的编译参数,需要两个变量:
    • FLAGS:
      • -isystem <directory>:添加 directory 到编译器的系统包含路径中。我们要添加系统级别的头文件供编译器和系统使用,这里我们添加的路径是 $TOOLCHAIN/sysroot/usr/include/arm-linux-androideabi,它包含 iotcl.h 和 ioctls.h 等头文件
      • -D <macro>=<value>:-D 用来指定宏的值,这里我们指定 ANDROID_API__=33
      • -g:会生成源码级别的 debug 信息,
      • -DANDROID:-D 是一个预处理器定义的参数,用于定义预处理器宏。在编译过程中使用该参数会定义一个名为 ANDROID 的宏,并指定为 true,以便在代码中进行条件编译
      • -fdata-sections:GCC 的编译选项之一,用于将每个数据(变量)放置在独立的数据段(section)中
      • -ffunction-sections:GCC 编译选项之一,用于在生成目标文件时将每个函数放置在独立的代码段(section)中,作用是对生成的目标文件进行优化,以便在链接(linking)过程中更好地进行代码优化和减少最终可执行文件的大小
      • -funwind-tables:GCC 的编译选项之一,用于生成用于异常处理和堆栈展开(stack unwinding)的 unwind 表格(unwind tables)。该参数会为每个函数生成 unwind 表格,这些表格描述了函数中的异常处理逻辑和堆栈展开过程所需的信息
      • -fstack-protector-strong:GCC 的编译选项,用于启用堆栈保护机制,以防止栈溢出攻击
      • -no-canonical-prefixes:GCC 的编译选项之一,用于禁用规范化的前缀(canonical prefixes)
      • -D_FORTIFY_SOURCE=2:用于启用强化源代码(fortify source)的功能,以增强程序的安全性和防范某些类型的常见漏洞。具体而言,可以开启缓冲区溢出保护、安全字符串处理函数和编译时警告功能
      • -W:GCC 的编译选项之一,会启用指定的警告。-Wformat 就会启用格式字符串相关的警告,而 -Werror=format-security 会将格式字符串安全性警告视为错误
      • -fno-limit-debug-info:GCC 的编译选项之一,用于禁用调试信息的大小限制
      • -fPIC:是 GCC 的编译选项之一,用于生成位置无关代码(Position Independent Code,PIC)。位置无关代码是一种可执行代码或共享库的形式,其加载和执行的位置不依赖于内存的绝对地址。这种代码的特点是可以在内存中的任何位置加载并正确执行,因此适用于共享库的使用场景。使用位置无关代码可以使共享库在不同的进程地址空间中共享,并减少地址冲突的可能性。
    • INCLUDES:包含 gcc 编译时所需要的系统头文件
  4. 通过 ./configure 运行 FFmpeg 提供的编译脚本,其下面配置的参数主要分为两大类,一是对 FFmpeg 进行剪裁,因为 Android 开发用不到 FFmpeg 所有的功能,因此禁用掉不用的功能可以减小编译产物的大小;二是启用交叉编译,并配置编译相关的参数,具体详见脚本注释

编译完成之后会在我们指定的 /ffmpeg-4.0.6//output/android 目录下生成三个目录:include、lib、share:

2023-12-28.FFmpeg4.0.6编译产物

include 包含的是头文件,lib 是库文件,share 则是一些示例代码。

我们将它们打包准备从 Linux 服务器上下载到本地:

[root@frank output]# zip -r ffmpeg.zip ./*

3、为 AS 配置 FFmpeg

在本地解压上一步得到的 ffmpeg.zip,将 include 和 lib 文件夹内的文件拷贝到 Android Studio 的项目中,具体步骤如下:

  1. 将 include 文件夹拷贝到 /src/main/cpp 目录下

  2. 将 lib 文件夹内的 6 个静态库(.a 结尾)文件拷贝到 /src/main/cpp/armeabi-v7a 目录下(因为我们后续要在 CPU 架构为 armeabi-v7a 的真机上使用这些静态库)

  3. 配置 CMakeLists 文件:

    # 1、引入头文件
    include_directories(${CMAKE_SOURCE_DIR}/include)
    
    # 2、引入库文件
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}")
    
    find_library(
            log-lib
            log)
    
    # 3、链接 FFmpeg 的静态库到目标库,注意顺序,比如 avformat 要在
    # avcodec 的前面
    target_link_libraries(
            ffmpeg
            ${log-lib}
            avformat avcodec avfilter avutil swresample swscale)
    

    配置 target_link_libraries 时要注意 6 个静态库的顺序,因为 avcodec 是依赖 avformat 的,被依赖的库要写在前面。如果想排除掉顺序影响,可以写成下面这样:

    target_link_libraries(
            ffmpeg
            ${log-lib}
            -Wl,--start-group
            avformat avcodec avfilter avutil swresample swscale
            -Wl,--end-group)
    
  4. 配置 build.gradle 添加 CPU 架构过滤:

    android {
        defaultConfig {
    		externalNativeBuild {
                cmake {
                    abiFilters 'armeabi-v7a'
                }
            }
            ndk {
                abiFilters 'armeabi-v7a'
            }
        }
    }
    
  5. 最后调用 avutil 库中的函数,输出 FFmpeg 的版本号,测试配置是否成功:

    // 因为 FFmpeg 是用纯 C 写的,所以要用 C 的方式编译
    extern "C" {
    #include <libavutil/avutil.h>
    }
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_linux_ffmpeg_NativeLib_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "FFmpeg 版本:";
        hello.append(av_version_info());
        return env->NewStringUTF(hello.c_str());
    }
    

    调用 JNI 函数,我写在了 Android 单元测试中:

    @RunWith(AndroidJUnit4::class)
    class ExampleInstrumentedTest {
        @Test
        fun test() {
            // Logcat 输出 test: FFmpeg 版本:4.0.6
            Log.d("Test", "test: ${NativeLib().stringFromJNI()}")
        }
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Taro引入echarts【兼容多端小程序(飞书/微信/支付宝小程序)】

近期接到公司新需求&#xff0c;开发飞书小程序&#xff0c;并且原型中含有大量的图表&#xff0c;本想使用飞书内置图表组件 —— chart-space&#xff0c;但官方表示已经停止维护了&#xff0c;无奈之下&#xff0c;只能另寻他路&#xff0c;于是乎&#xff0c;图表之王&…

CRM客户关系管理-客户资源企业化销售管理平台

什么是CRM 客户关系管理&#xff08;Customer Relationship Management&#xff0c;简称CRM&#xff09;&#xff0c;是指企业为提高核心竞争力&#xff0c;利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互&#xff0c;从而提升其管理方式&#…

dockerfile 搭建lamp 实验模拟

一 实验目的 二 实验 环境 1, 实验环境 192.168.217.88一台机器安装docker 并做mysql nginx php 三台容器 2&#xff0c; 大致框架 3&#xff0c; php php:Nginx服务器不能处理动态页面&#xff0c;需要由 Nginx 把动态请求交给 php-fpm 进程进行解析 php有三…

记录些AI Agents设计模式和NL2SQL知识

吴恩达分享的四种 自我反思&#xff08;Reflection&#xff09;&#xff1a;可以自我修正&#xff1b;使用工具&#xff08;Tool Use&#xff09;&#xff1a;链接其他系统去做一些事情&#xff0c;比如把电脑里面的未归档文件做好归档&#xff1b;规划&#xff08;Planning&a…

【炼金术士】BatchSize对网络训练的影响

文章目录 1 BatchSize对于网络训练的影响2 调整学习率可以提高大BatchSize的性能3 实际训练时的建议3.1 设置初始学习率的方法3.2 多卡训练时学习率的设置 参考资料&#xff1a; 【深度学习】Batch Size对神经网络训练的影响【AI不惑境】学习率和batchsize如何影响模型的性能&…

Windows使用SSH登录本机Linux虚拟机

SSH&#xff08;Secure Shell&#xff09;&#xff0c;一种网络协议&#xff0c;可以在安全外壳下实现数据传输通信&#xff0c;所以主要用于计算机间加密登录&#xff0c;可以简单理解为远程控制。除了计算机间直接互联&#xff0c;在git中也可以看到&#xff0c;常见的协议有…

购买 DDoS 高防 IP 防护哪家好?

DDoS 高防 IP 哪里买会比较好?在这场攻与守的游戏里&#xff0c;DDoS 高防 IP 是一种针对 DDoS 攻击的防护措施&#xff0c;通过将网站或应用的 IP 地址映射到高防 IP 上&#xff0c;实现对流量的清洗和过滤&#xff0c;从而有效抵御 DDoS 攻击。在选择 DDoS 高防 IP 服务提供…

Ubuntu下部署gerrit+报错分析(超详细)

Ubuntu下部署gerrit代码平台 之前安装过几次 最后都在Apache代理这里失败了&#xff0c;如下图&#xff0c;总是gerrit.config与Apache2.config配置有问题&#xff0c;后面换了使用ngnix代理&#xff0c;简单多了 安装Mysql、gerrit、jdk、git 这一步也是非必须得&#xff0…

无监督学习的评价指标

轮廓系数&#xff08;Silhouette Coefficient&#xff09; 轮廓系数用于判断聚类结果的紧密度和分离度。轮廓系数综合了样本与其所属簇内的相似度以及最近的其他簇间的不相似度。 其计算方法如下&#xff1a; 1、计算簇中的每个样本i 1.计算a&#xff08;i&#xff09; &#x…

实时采集麦克风并播放(springboot+webscoekt+webrtc)

项目技术 springbootwebscoektwebrtc 项目介绍 项目通过前端webrtc采集麦克风声音&#xff0c;通过websocket发送后台&#xff0c;然后处理成g711-alaw字节数据发生给广播UDP并播放。 后台处理项目使用线程池(5个线程)接受webrtc数据并处理g711-alaw字节数组放到Map容器中&…

opencv基础篇 ——(九)图像几何变换

图像几何变换是通过对图像的几何结构进行变换来改变图像的形状、大小、方向或者透视关系。常见的图像几何变换包括缩放、旋转、平移、仿射变换和透视变换等。下面对这些几何变换进行简要介绍&#xff1a; 矩阵的转置&#xff08;transpose &#xff09;&#xff1a; 对于图像来…

吴恩达2022机器学习专项课程(一) 7.1 逻辑回归的成本函数第三周课后实验:Lab4逻辑回归的损失函数

问题预览/关键词 上节课回顾逻辑回归模型使用线性回归模型的平方误差成本函数单个训练样本的损失损失函数&#xff0c;成本函数&#xff0c;代价函数的区别线性回归损失函数和逻辑回归损失函数的区别逻辑回归模型的成本函数是什么&#xff1f;逻辑回归模型的损失函数实验逻辑回…

STL——List常用接口模拟实现及其使用

认识list list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素…

linux tcpdump的交叉编译以及使用

一、源码下载 官网&#xff1a;点击跳转 二、编译 1、解压 tar -xf libpcap-1.10.4.tar.xz tar -xf tcpdump-4.99.4.tar.xz 2、配置及编译 //libpcap&#xff1a; ./configure --hostarm-linux --targetarm-linux CCarm-linux-gcc --with-pcaplinux --prefix$PWD/build//t…

对象与JSON字符串互转

1、JSON字符串转化成JSON对象 JSONObject jsonobject JSON.parseObject(str); 或者 JSONObject jsonobject JSONObject.parseObject(str); 功能上是一样的&#xff0c;都是将JSON字符串&#xff08;str&#xff09;转换成JSON对象 jsonobject 。注意str一定得是以键值对存在…

STM32之HAL开发——电容按键

电容按键原理 电容器 (简称为电容) 就是可以容纳电荷的器件&#xff0c;两个金属块中间隔一层绝缘体就可以构成一个最简单的电容。如图 32_1 (俯视图)&#xff0c;有两个金属片&#xff0c;之间有一个绝缘介质&#xff0c;这样就构成了一个电容。这样一个电容在电路板上非常容…

二维数组求最大值(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i, j, max 0, row 0, colum 0;int arr[3][4] { {1, 2, 3}, {4, 5, 16}, {7, 8, 9} …

线上办理离婚快速离婚,无需双方见面异地可办

现在离婚有两种方式 一种是协议离婚&#xff0c;双方都同意的情况下&#xff0c;可以去民政局协议离婚&#xff0c;有30天冷静期&#xff0c;冷静期过后需要双方再次去民政局办理离婚手续。 另一种是诉讼离婚&#xff0c;一方不同意离婚&#xff0c;可以选择诉讼离婚。可以全…

Vue 3 路由机制详解与实践

一、路由的理解 路由是指导用户界面导航的一种机制。它通过映射 URL 到应用程序的不同视图组件来实现页面间的切换和导航。 二、路由基本切换效果 路由基本切换效果指的是当用户在应用程序中进行页面导航时&#xff0c;通过路由可以实现页面的切换&#xff0c;从而展示不同的…

ICMP详解

3 ICMP ICMP&#xff08;Internet Control Message Protocol&#xff0c;因特网控制报文协议&#xff09;是一个差错报告机制&#xff0c;是TCP/IP协议簇中的一个重要子协议&#xff0c;通常被IP层或更高层协议&#xff08;TCP或UDP&#xff09;使用&#xff0c;属于网络层协议…