如图直接上步骤:
1.需要先弄一个nativeLib module
2.整理目录层级
3.导入算法库。
4.根据头文件写入对应的方法。
nativeLib module
可以看到以及有了基础模型
同时我们需要在
settings.gradle 中添加
include ':nativelib'
将当前模块编译到。
settings.gradle 是 Gradle 构建系统中的一个配置文件,用于配置构建环境的设置,特别是在多项目构建(multi-project build)中。它主要用于定义哪些项目包含在构建中,并设置一些全局属性。
settings.gradle 的作用(可以跳过,就是简单了解它是导入model的就可)
1.包含子项目
- 在多项目构建中,settings.gradle 文件用于指定包含哪些子项目。使用 include 方法将子项目添加到构建中。
- 例如,如果你的项目结构如下:
- settings.gradle 的作用(可以跳过,就是简单了解它是导入model的就可)
1.包含子项目 - 在多项目构建中,settings.gradle 文件用于指定包含哪些子项目。使用 include 方法将子项目添加到构建中。
- 例如,如果你的项目结构如下:
├── settings.gradle
├── build.gradle
├── app/
│ └── build.gradle
└── library/
└── build.gradle
你可以在 settings.gradle 中包含子项目:
include 'app', 'library'
2.设置项目的名称
使用 rootProject.name 设置根项目的名称:
rootProject.name = 'MyProject'
3.配置子项目路径
如果子项目的目录结构不在默认位置,可以通过 projectDir 属性配置子项目的路径:
include 'app'
project(':app').projectDir = new File(settingsDir, 'custom/app-directory')
4.插件管理
从 Gradle 6.0 开始,settings.gradle 支持插件管理块,用于配置插件仓库和应用插件:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
google()
}
}
5.初始化脚本
- 可以在 settings.gradle 中添加一些初始化代码,配置构建脚本执行前的行为。
nativeLib层级编写整理
我们打开CMakeLists.txt。初始的时候是一个写好的示例。我们不建议这样,因为不可能只放一个算法。
1.修改最外层的CMakeLists(通过ADD_SUBDIRECTORY添加子模块)
2.添加子目录及写子目录的CMakeLists.txt(简单的copy一下,下面有讲解里面关键字的含义)
3.我们在子目录的SDK文件中放入算法商提供的文件(库,头文件,示例代码)
4.创建JNI对应的C++文件。
# 设置项目名称project(MyProject)
project("nativelib")
#INCLUDE_DIRECTORIES 可以确保你的项目在编译时能够正确地找到所需的头文件。
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/SDK/inc")
#file(GLOB ...) 命令来收集当前目录中所有 .cpp 文件
file(GLOB src-files
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加可执行文件
add_library( # Sets the name of the library.
nativelib
# Sets the library as a shared library.
SHARED
${src-files}
)
add_library 命令可以创建一个库(静态库或共享库)。你提供的代码片段显示了如何设置一个共享库(shared library),并将源文件添加到库中。
- 是库的名称。
- STATIC、SHARED 或 MODULE 指定库的类型。
- STATIC:静态库。
- SHARED:共享库(动态库)。
- MODULE:模块库,不会被链接到其它目标,通常在插件系统中使用。
- EXCLUDE_FROM_ALL(可选):表示该库不会被构建为默认目标的一部分。
- source1 source2 … sourceN 是源文件列表。
# 添加导入的共享库
add_library(sdk SHARED IMPORTED)
# 根据目标架构设置共享库的实际位置
set_target_properties(sdk PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/SDK/lib/${ANDROID_ABI}/zhongmin_capture.so)
# 链接导入的共享库到可执行文件
target_link_libraries( # Specifies the target library.
nativelib
# Links the target library to the log library
# included in the NDK.
sdk
)
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("nativelib")
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/SDK/inc")
file(GLOB src-files
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
nativelib
# Sets the library as a shared library.
SHARED
${src-files}
)
add_library(sdk SHARED IMPORTED)
set_target_properties(sdk PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/SDK/lib/${ANDROID_ABI}/zhongmin_capture.so)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
nativelib
# Links the target library to the log library
# included in the NDK.
sdk
)
Java代码中可以快速导入生成方法
大体框架基本搭建成功,然后导入对应算法。我们将算法放在SDK 目录下。
原理流程:
此时,算法库与jni层基础连接已完成。我们需要根据算法商提供的smaplecode来参考如何写JNI层。
我们以init 为例子。(算法不同,编写JNI不同,简单以Init 举例)
它需要一个MHandle 类型的指针数据,创建一个int 类型的hanlde传给算法。
java 中提供public 给其他类调用。
大体流程如此。根据算法要的数据来传递处理。
JNI 中的数据类型
基本数据类型
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
/* "cardinal indices and sizes" */
typedef jint jsize;
#ifdef __cplusplus
// 内部的数据结构由虚拟机实现,只能从虚拟机源码看
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
...
// 说明我们接触到到 jobject、jclass 其实是一个指针
typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
Java 类型 | JNI 类型 | 描述 | 长度(字节) |
---|---|---|---|
boolean | jboolean | unsigned char | 1 |
byte | jbyte | signed char | 1 |
char | jchar | unsigned short | 2 |
short | jshort | signed short | 2 |
int | jint、jsize | signed int | 4 |
long | jlong | signed long | 8 |
float | jfloat | signed float | 4 |
double | jdouble | signed double | 8 |
Class | jclass | Class 类对象 | 1 |
String | jstrting | 字符串对象 | / |
Object | jobject | 对象 | / |
Throwable | jthrowable | 异常对象 | / |
boolean[] | jbooleanArray | 布尔数组 | / |
byte[] | jbyteArray | byte 数组 | / |
char[] | jcharArray | char 数组 | / |
short[] | jshortArray | short 数组 | / |
int[] | jinitArray | int 数组 | / |
long[] | jlongArray | long 数组 | / |
float[] | jfloatArray | float 数组 | / |
double[] | jdoubleArray | double 数组 | / |
private static native boolean nativeSetInputImage(long engine, ByteBuffer yBuffer,
ByteBuffer uBuffer, ByteBuffer vBuffer,
int index, int yStride, int uvStride,
int width, int height, float evValue,string str);
extern "C"
JNIEXPORT jboolean JNICALL
Java_*_*_*_nativeSetInputImage(JNIEnv *env, jclass clazz,jlong engine,jobject y_buffer,jobject u_buffer,jobject v_buffer,jint index,jint y_stride,jint uv_stride,
jint width,jint height,jfloat ev_value,jString jStr) {
```
auto *yBuffer = static_cast<uint8_t *>(env->GetDirectBufferAddress(y_buffer));
char *fileName = new char[200];
sprintf(fileName, "%s_in_%s_index_%d_ev_%.2f_%dx%d.%s", getCameraTypeStr(mCameraType), getSameTimeStamp(),
index, ev_value,
y_stride, height, nv12 ? "nv12" : "nv21");
const char *str = env->GetStringUTFChars(jStr, JNI_FALSE);
std::string hello = "Hello from C++";
jstring jStr1 = env->NewStringUTF(hello.c_str());
jstring jStr2 = env->NewStringUTF("Hello 2 from C++");
}
ByteBuffer -> jobject
auto 通常是 C++ 的关键字,用于在编译时自动推断变量类型。
void* GetDirectBufferAddress(jobject buf)
{ return functions->GetDirectBufferAddress(this, buf); } //用于获取 Direct ByteBuffer 对象的地址
int sprintf(char *str, const char *format, …);
- str:目标字符数组,用于接收格式化后的字符串。
- format:格式控制字符串,规定了如何将后续参数格式化为字符串。
- …:可变参数列表,用于提供给格式控制字符串中的格式化标识符指定的数据。
获取指向 Java 字符串中 UTF-16 数据的指针。
const jchar* GetStringChars(jstring string, jboolean* isCopy)
{ return functions->GetStringChars(this, string, isCopy); }
获取指向 Java 字符串中 UTF-8 数据的指针。
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
{ return functions->GetStringUTFChars(this, string, isCopy); }
根据传入的 UTF-8 字符串创建一个 Java 字符串对象。
jstring NewStringUTF(const char* bytes)
{ return functions->NewStringUTF(this, bytes); }
private static native int nativeSetFaceInfo(long handle, Rect[] face);
extern "C"
JNIEXPORT jint JNICALL
Java_*_*_*_*_nativeSetFaceInfo(JNIEnv *env,
jclass clazz,
jlong handle,
jobjectArray face) {
int len = env->GetArrayLength(face);
}
获取 Java 数组的长度。array:Java 数组对象(jarray),可以是基本类型数组或对象数组。
jsize GetArrayLength(jarray array)
{ return functions->GetArrayLength(this, array); }
以yuvutils实战
具体不做过多介绍 可以https://github.com/hzl123456/LibyuvDemo/yuvUtils 作为三方算法示例导入
CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("yuvutils")
# jniLibs
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
include_directories(${PROJECT_SOURCE_DIR}/libyuv/include)
#add_subdirectory(${PROJECT_SOURCE_DIR}/libyuv)
add_library( # Sets the name of the library.
yuvutils
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
yuvutils.cpp)
add_library(yuv STATIC IMPORTED)
set_target_properties(
yuv
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/libyuv/jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libyuv.a)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
yuvutils
# Links the target library to the log library
# included in the NDK.
${log-lib}
yuv)
以NV12ToI420为例,写对应的JNI。
extern "C"
void
Java_com_yuvutils_YuvUtils_convertNV12ToI420(JNIEnv *env, jclass clazz,
jobject y, jint y_row_stride,
jobject uv, jint uv_row_stride,
jobject out,
jint width, jint height, jint rotation) {
uint8_t *src_y = static_cast<uint8_t *>(env->GetDirectBufferAddress(y));
uint8_t *src_uv = static_cast<uint8_t *>(env->GetDirectBufferAddress(uv));
uint8_t *output = static_cast<uint8_t *>(env->GetDirectBufferAddress(out));
rotation = rotation % 360;
RotationMode rotationMode = getRotationMode(rotation);
int outW = width;
int outH = height;
if (rotation % 180 != 0) {
outW = height;
outH = width;
}
uint8_t *out_y = output;
uint8_t *out_u = output + outW * outH;
uint8_t *out_v = out_u + (outW * outH >> 2);
NV12ToI420Rotate(src_y, y_row_stride,
src_uv, uv_row_stride,
out_y, outW,
out_u, outW >> 1,
out_v, outW >> 1,
width, height, rotationMode);
}