Android使用FFmpeg的API库

简介: Android使用FFmpeg的API库

Java 可以通过 JNI 调用原生库中的函数,原生库完全是由原生的 CPU 指令堆叠起来的,所以运行速度很快。大部分的原生库都是用 C/C++ 编译出来的。

因此,Android 里面也能通过 JNI 的方式调用 so 动态库,或者 .a 静态库里面的函数。JNI 的全称是 Java Native Interface,Java 原生接口。

Android 提供了一套 NDK 工具,来编译使用 C/C++ 代码。有关 NDK,JNI 相关的资料,本文不进行讲解,推荐阅读以下资料。

1,《Java Native Interface: Programmer's Guide and Specification》- Sheng Liang

2,《Android Studio添加C/C++代码

3,《NDK 使用入门》,这是官方文档。

4,《细说Android 4.0 NDK编程》- 王家林


掌握 JNI/NDK 基本概念之后,现在就来实战巩固一下,引入 ffmpeg 的动态库给之前的 Hello app 使用,相关代码可在 Github 下载。

首先我们需要使用 Android Studio 里面的 NDK 的 clang 编译器来编译 ffmpeg 的代码,编译出来 so 动态库。本文采用的 ffmpeg 版本是 n4.4.1。

如果没有安装 NDK,可以点击 Tools > SDK Manager 进行安装,如下:

安装完成之后,会在 SDK 的安装目录上看到这个 clang.exe 编译器,如下:

本文使用的 NDK 版本是 23.1,在进行 NDK 开发的时候,谷歌推荐使用 clang 编译器来编译 C/C++ 代码,而不是 gcc 或者 msvc。


由于我们是在 Windows10 系统上编译 C/C++ 代码,生成手机上运行的 CPU 指令,这是交叉编译,也叫跨平台编译,所以需要指定 --target,指定生成那种 CPU 架构的指令集,同时也要指定 Android 的 API 级别。

手机的 CPU 架构可以用命令查看,先通过 adb 链接上手机,然后执行 cat /proc/cpuinfo 命令,如下:

cd C:\Users\loken\AppData\Local\Android\Sdk\platform-tools
adb.exe shell
cat /proc/cpuinfo

可以看到,CPU 架构是 8 ,那 8 代表什么呢? 8 代表这个 CPU 是 ARMv8 的架构,暂时可以把 aarch64 跟 ARMv8 看成是一个东西。

我的手机是 Redmi 5 Plus,Android 版本 8.1.0,对应的 API 级别是 27。读者可以在《SDK 平台版本说明》查阅自己手机的 Android 版本对应的 API 级别。因此本文的设备是 aarch64 + API27

Android Studio 已经提前弄好一些 shell 跟 batch 脚本给我们用,如下:

cmd 后缀是用在 windows 命令行的,没有 cmd 的是 shell 脚本,用在 Linux 的命令行。

这个 aarch64-linux-android27-clang 其实就是一个 shell 脚本,里面也是调的 clang.exe 编译器,只是这个 shell 脚本预设了一些参数,它的部分内容如下:

`dirname $0`/clang.exe --target=aarch64-linux-android27 "$@"

我们不需要用到这个 shell 脚本,只是参考一下他的 --target 参数。


下面我们打开 msys2 的 shell 窗口,这里直接使用下面的命令打开即可,不需要继承 vs2019 的环境变量,因为不需要用到 msvc。

cd C:\msys64
.\msys2_shell.cmd -mingw64

在 msys2 环境下,安装一些 必要的软件:

# 刷新软件包数据
pacman -Sy  
# 安装mingw-w64。
pacman -S mingw-w64-x86_64-toolchain
pacman -S git
pacman -S make
pacman -S automake 
pacman -S autoconf
pacman -S perl
pacman -S mingw-w64-x86_64-SDL2
pacman -S libtool
pacman -S mingw-w64-x86_64-cmake
pacman -S pkg-config 
pacman -S yasm
pacman -S diffutils
# 编译x264 需要 nasm
pacman -S nasm

然后上 Github 下载 FFmpeg-n4.4.1.zip 代码,放到 下图中的目录,这样 msys2 环境也能找到。


然后我们需要设置一下 PATH 环境变量,让 clang.exe 编译器能被找到,命令如下:

export PATH=$PATH:/C/Users/loken/AppData/Local/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/windows-x86_64/bin

进入 FFmpeg-n4.4.1 目录,开始编译,命令如下:

cd /home/loken/ffmpeg/ffmpeg-n4.4.1
./configure \
--prefix=/home/loken/ffmpeg/build64/ffmpeg-n4.4.1-android \
--target-os=android \
--arch=arm64  \
--cpu=armv8-a \
--cc=clang \
--cxx=clang++ \
--strip=llvm-strip \
--extra-cflags="--target=aarch64-linux-android27" \
--extra-ldflags="--target=aarch64-linux-android27" \
--extra-cflags="-Os -fpic -march=armv8-a" \
--disable-asm \
--disable-strip \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-ffmpeg \
--enable-neon \
--enable-cross-compile \
--enable-shared
make -j8
make install

上面的命令有一个重点,就是无论是编译阶段,还是链接阶段,都需要加上 --target=aarch64-linux-android27,要不会报错的。

网上有些文章说要指定 --sysroot 的路径,但是好像我不指定也没问题。

上面的命令执行完之后,ffmpeg-n4.4.1-android 目录的结构如下:


so 动态库编译出来之后,就可以在 Android Studio 里面使用了。

先在原来的 Hello 项目里面 main 目录下创建一个 jinLibs 文件夹,然后再创建一个 arm64-v8a 文件夹,如下:

然后把之前生成的 so 库全部拷贝到 arm64-v8a 目录,如下:

要使用这些动态库里面的函数,还需要有头文件,头文件我们不需要一个一个拷贝,之前 make install 的时候,已经把所需要的头文件拷贝到 build64/ffmpeg-n4.4.1-android 的 include 目录下,如下:

现在我们在把 build64/ffmpeg-n4.4.1-android 的 include 目录拷贝到 Hello app 项目的 cpp 目录下,通常 Android 项目是没有 cpp 目录的,所以我们需要右键 app 目录,点击 Add C++ to Module,如下:

这样操作,Android Studio 就会帮我们生成 cpp 目录,以及相关的 CMakeLists.txthello.cpp

提醒:这些 cpp 目录以及文件,是可以自己手动添加的,但是我试了一下,手动添加完之后,Android Studio 没有加载出来,可能是有个配置文件负责管理的,所以保险起见,还是用 Add C++ to Module 这个功能。


现在我们在把 build64/ffmpeg-n4.4.1-android 的 include 目录拷贝到 Hello app 项目的 cpp 目录,如下:

现在我们需要对 CMakeLists.txt 做一下修改,把 FFmpeg 的 so 库引入进来,重点的修改如下:

然后再修改一下 hello.cpp 的代码,调用一下 so 库里面的一个版本函数,代码如下:

#include <jni.h>
#include <string>
#include <unistd.h>
extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavcodec/jni.h>
    JNIEXPORT jstring JNICALL
    Java_com_example_hello_FirstActivity_ffmpegVersion(JNIEnv *env, jobject  /* this */) {
        unsigned ver = avformat_version();
        char info[40000] = {0};
        sprintf(info, "avformat_version %u:", ver);
        return env->NewStringUTF(info);
    }
}

然后我们就可以在 FirstActivity 里面调用这个 ffmpegVersion() 函数了,如下:

上图中,我把 avformat 库的版本号加在后面了,然后直接编译项目把 apk 安装到手机,整个过程如果没有报错,就会生成 app-debug.apk 文件,如下:

Android Studio 提供了一个 apkanalyzer.exe 工具来分析 apk 文件,我们可以分析一下 apk,如下:

可以看到,so 库已经打包进去 apk 里面了。

手机上的运行效果如下:

Jni 完整的项目代码可以点击 helloJni 进行下载。


官网文档的 两篇文章 说要用 Android.mk 跟 Application.mk 来引入外部的库,但是我没看懂这两篇文章,貌似不创建这两个文件也可以。


本文只是做了一些简单的介绍,把 FFmpeg 的库编译进去 apk,然后调用一个函数。关于 NDK开发,料推荐阅读以下书籍 深入学习

1,《Android C++高级编程-使用NDK》

2,《细说Android 4.0 NDK编程》


结尾,分享一些本人的学习技巧,本文是参考《Android FFmpeg 编译和集成》一文进行写作的,这篇文章里面创建了一个 build_android_clang.sh 脚本,然后定义一些变量传递进去,如下:

我能看懂大部分的 shell 以及 batch(cmd)代码,我看了一下这个 shell,为了降低本文的复杂度,所以我没有创建新的 shell 脚本,而是直接把参数写在 configure 上了。

再回到之前的 Android Studio 提供的 aarch64-linux-android27-clang 脚本。

`dirname $0`/clang.exe --target=aarch64-linux-android27 "$@"

这个脚本实际上就是给 clang.exe 编译器定义一个别名,这个别名会附加上 --target 的参数。

由于 clang.exe 会被调用两次,一次是编译的时候,一次是链接的时候,所以当把 参数往 configure 上丢的时候,需要丢两次,所以就有了下面的代码:

./configure \
...省略...
--extra-cflags="--target=aarch64-linux-android27" \
--extra-ldflags="--target=aarch64-linux-android27" \
...省略...

上面的命令,--extra-cflags 是往编译阶段传递参数,--extra-ldflags 是往链接阶段传递参数。

本书的《FFmpeg编译过程分析》一章专门分析 configure 的代码。


然后,我没有使用 Ubuntu 或者 Mac 来编译 FFmpeg 的动态库,虽然也可以。我平时是在 Windows 上开发的,所以直接就采用 msys2 了,在前面的文章《用msys2与msvc编译FFmpeg》里,msys2 可以调 vs2019 的 msvc 编译器 cl.exe ,同理 msys2 肯定也能调 Android Studio 的 NDK 编译器 clang.exe,这些东西都是可以举一反三串联起来的。

最后,FFmpeg 官方的 configure 脚本非常值得称赞,这个脚本使得 很多选项可以配置,例如可以指定编译器,链接器,strip ,等等。因此 configure 的通用性非常强,在各种平台都能跑。Windows 可以用 msys2 来执行 shell 脚本,其他类 Linux 平台本身就支持 shell。


目录
相关文章
|
21天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
2天前
|
算法 Linux Windows
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
在Windows环境下为FFmpeg集成字幕渲染库libass涉及多个步骤,包括安装freetype、libxml2、gperf、fontconfig、fribidi、harfbuzz和libass。每个库的安装都需要下载源码、配置、编译和安装,并更新PKG_CONFIG_PATH环境变量。最后,重新配置并编译FFmpeg以启用libass及相关依赖。完成上述步骤后,通过`ffmpeg -version`确认libass已成功集成。
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
|
4天前
|
SQL Java 数据库连接
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
9 0
|
5天前
|
Ubuntu Linux Shell
Android-NDK-clang 编译 FFmpeg
Android-NDK-clang 编译 FFmpeg
14 0
|
6天前
|
Android开发 C++
Android S HAL库的编译
Android S HAL库的编译
10 0
|
7天前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
|
13天前
|
机器学习/深度学习 算法 数据挖掘
机器学习--K近邻算法,以及python中通过Scikit-learn库实现K近邻算法API使用技巧
机器学习--K近邻算法,以及python中通过Scikit-learn库实现K近邻算法API使用技巧
|
17天前
|
缓存 前端开发 API
toapi,一个强大的 Python Web API库!
toapi,一个强大的 Python Web API库!
27 5
|
19天前
|
JSON 测试技术 API
Python的Api自动化测试使用HTTP客户端库发送请求
【4月更文挑战第18天】在Python中进行HTTP请求和API自动化测试有多个库可选:1) `requests`是最流行的选择,支持多种请求方法和内置JSON解析;2) `http.client`是标准库的一部分,适合需要低级别控制的用户;3) `urllib`提供URL操作,适用于复杂请求;4) `httpx`拥有类似`requests`的API,提供现代特性和异步支持。根据具体需求选择,如多数情况`requests`已足够。
15 3
|
21天前
|
API Android开发
Android Framework增加API 报错 Missing nullability on parameter
Android Framework增加API 报错 Missing nullability on parameter
11 1