环境
开发环境
win10 64位 + vs2015 + jdk1.8 32位
目标运行环境
win7 64位 + jre1.8 32 位
代码实现
瑞为SDK接口
#ifndef _RECONOVA_FACE_CAMERA_SDK_H_
#define _RECONOVA_FACE_CAMERA_SDK_H_
#ifdef WIN32
#ifdef RECONOVAFACECAMERASDK_EXPORTS
#define RECONOVAFACECAMERASDK_API __declspec(dllexport)
#else
#define RECONOVAFACECAMERASDK_API __declspec(dllimport)
#pragma comment(lib, "ReconovaFaceCameraSdk")
#endif
#else
#define RECONOVAFACECAMERASDK_API
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/**
* 图片信息定义,图片格式BGR24
*/
typedef struct ReconovaBGR24Image_s {
int width; /* 宽 */
int height; /* 高 */
int widthStep; /* 步长,四字节对齐 */
int imageSize; /* 图片数据大小 */
char *imageData; /* 图片数据 */
} ReconovaBGR24Image_t;
/**
* 人脸区域定义,从眉毛到下巴的矩形框
*/
typedef struct ReconovaFaceRectInfo_s {
int left; /* 左边界值 */
int top; /* 上边界值 */
int right; /* 右边界值 */
int bottom; /* 下边界值 */
} ReconovaFaceRectInfo_t;
/**
* 人脸特征信息
*/
typedef struct ReconovaFaceFeature_s {
int featLength; /* 特征数据长度 */
char *featData; /* 特征数据 */
} ReconovaFaceFeature_t;
/**
* 人员跟踪信息
*/
typedef struct ReconovaPersonTraceInfo_s {
unsigned int personId; /* 人员ID */
ReconovaBGR24Image_t frameImage; /* 当前人脸对应的现场图片 */
ReconovaFaceRectInfo_t faceRect; /* 人脸位置 */
ReconovaBGR24Image_t faceImage; /* 人脸图片 */
ReconovaFaceFeature_t faceFeature; /* 人脸特征信息 */
} ReconovaPersonTraceInfo_t;
/**
* 人员跟踪状态
*/
typedef enum ReconovaPersonTraceStatus_e {
RECONOVA_PERSON_TRACE_STATUS_ENTER, /* 人员进去 */
RECONOVA_PERSON_TRACE_STATUS_UPDATE,/* 人员更新 */
RECONOVA_PERSON_TRACE_STATUS_LEAVE /* 人员离开 */
} ReconovaPersonTraceStatus_t;
/**
* 视频帧回调
* @param image 视频帧信息
* @parma faceList 人脸框列表
* @parma faceNum 人脸个数
* @parma data 用户自定义数据
*/
typedef int (*ReconovaFrameCallBack_t)(ReconovaBGR24Image_t *image, ReconovaFaceRectInfo_t *faceList, int faceNum, void *data);
/**
* 人员跟踪信息回调
* @param personTraceInfo 人员跟踪信息
* @param personTraceStatus 人员跟踪状态
* @param data 用户自定义数据
*/
typedef int (*ReconovaPersonTraceInfoCallBack_t)(ReconovaPersonTraceInfo_t *personTraceInfo, ReconovaPersonTraceStatus_t personTraceStatus, void *data);
/**
* 模块初始化
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkInit(void);
/**
* 模块反初始化,释放资源
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkUnInit(void);
/**
* 注册视频帧回调
* @param callBack[in] 回调函数
* @param data[in] 自定义数据
*
* @note 该接口需要在启动USB像机之前调用
* 该接口推送的是BGR24的图片原始数据
*
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkRegisterFrameCallBack(ReconovaFrameCallBack_t callBack, void *data);
/**
* 注册人员跟踪信息回调
* @param callBack[in] 回调函数
* @param data[in] 自定义数据
*
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkRegisterPersonTraceInfoCallBack(ReconovaPersonTraceInfoCallBack_t callBack, void *data);
/**
* 启动人脸摄像头
*
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkFaceCameraStart(void);
/**
* 停止人脸摄像头
*
* @return 成功返回0,失败返回负数
*/
RECONOVAFACECAMERASDK_API int ReconovaFaceCameraSdkFaceCameraStop(void);
#ifdef __cplusplus
}
#endif
#endif /* _RECONOVA_FACE_CAMERA_SDK_H_ */
JAVA 代码
数据结构定义
ReconovaBGR24Image
package com.cares.reconova.model;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:11
**/
public class ReconovaBGR24Image {
public int width; /* 宽 */
public int height; /* 高 */
public int widthStep; /* 步长,四字节对齐 */
public int imageSize; /* 图片数据大小 */
public byte[] imageData; /* 图片数据 */
}
ReconovaFaceFeature
package com.cares.reconova.model;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:12
**/
public class ReconovaFaceFeature {
public int featLength; /* 特征数据长度 */
public byte[] featData; /* 特征数据 */
}
ReconovaFaceRectInfo
package com.cares.reconova.model;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:12
**/
public class ReconovaFaceRectInfo {
public int left; /* 左边界值 */
public int top; /* 上边界值 */
public int right; /* 右边界值 */
public int bottom; /* 下边界值 */
}
ReconovaPersonTraceInfo
package com.cares.reconova.model;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:13
**/
public class ReconovaPersonTraceInfo {
public int personId; /* 人员ID */
public ReconovaBGR24Image frameImage; /* 当前人脸对应的现场图片 */
public ReconovaFaceRectInfo faceRect; /* 人脸位置 */
public ReconovaBGR24Image faceImage; /* 人脸图片 */
public ReconovaFaceFeature faceFeature; /* 人脸特征信息 */
}
ReconovaPersonTraceStatus
package com.cares.reconova.model;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:14
**/
public enum ReconovaPersonTraceStatus {
RECONOVA_PERSON_TRACE_STATUS_ENTER, /* 人员进去 */
RECONOVA_PERSON_TRACE_STATUS_UPDATE,/* 人员更新 */
RECONOVA_PERSON_TRACE_STATUS_LEAVE /* 人员离开 */
}
回调接口定义
视频帧回调
package com.cares.reconova.service;
import com.cares.reconova.model.ReconovaBGR24Image;
import com.cares.reconova.model.ReconovaFaceRectInfo;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:16
**/
public interface ReconovaFrameCallBack {
/**
* 视频帧回调
* @param image 视频帧信息
* @parma faceList 人脸框列表
* @parma faceNum 人脸个数
* @parma data 用户自定义数据
*/
int invoke(ReconovaBGR24Image image, ReconovaFaceRectInfo[] faceList, int faceNum, byte[] data);
}
人脸跟踪回调
package com.cares.reconova.service;
import com.cares.reconova.model.ReconovaPersonTraceInfo;
import com.cares.reconova.model.ReconovaPersonTraceStatus;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:16
**/
public interface ReconovaPersonTraceInfoCallBack {
/**
* 人员跟踪信息回调
* @param personTraceInfo 人员跟踪信息
* @param personTraceStatus 人员跟踪状态
* @param data 用户自定义数据
*/
int invoke(ReconovaPersonTraceInfo personTraceInfo, ReconovaPersonTraceStatus personTraceStatus, byte[] data);
}
回调接口实现
视频帧回调
package com.cares.reconova.service;
import com.cares.reconova.model.ReconovaBGR24Image;
import com.cares.reconova.model.ReconovaFaceRectInfo;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.opencv_core;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_highgui.*;
import static org.bytedeco.javacpp.opencv_imgproc.cvRectangle;
public class ReconovaFrameCallBackImpl implements ReconovaFrameCallBack{
@Override
public int invoke(ReconovaBGR24Image image, ReconovaFaceRectInfo[] faceList, int faceNum, byte[] data) {
opencv_core.IplImage iplImage = cvCreateImageHeader(cvSize(image.width, image.height), IPL_DEPTH_8U, 3);
BytePointer bytePointer = new BytePointer(image.imageData);
cvSetData(iplImage, bytePointer, ((image.width * 3) + 3) >> 2 << 2);
for (int i = 0; i < faceNum; i++) {
cvRectangle(iplImage,
cvPoint(faceList[i].left, faceList[i].top),
cvPoint(faceList[i].right, faceList[i].bottom),
CV_RGB(0, 255, 0),
1,
4,
0
);
}
cvNamedWindow("test",CV_WINDOW_FULLSCREEN);
cvResizeWindow("test", image.width, image.height);
cvShowImage("test", iplImage);
cvWaitKey(30);
cvReleaseImageHeader(iplImage);
return 0;
}
}
人脸跟踪回调
package com.cares.reconova.service;
import com.cares.reconova.model.ReconovaPersonTraceInfo;
import com.cares.reconova.model.ReconovaPersonTraceStatus;
import lombok.extern.slf4j.Slf4j;
/**
* @author wangcj
* @desc
* @date 2021/4/20 21:33
**/
@Slf4j
public class ReconovaPersonTraceInfoCallBackImpl implements ReconovaPersonTraceInfoCallBack{
@Override
public int invoke(ReconovaPersonTraceInfo personTraceInfo, ReconovaPersonTraceStatus personTraceStatus, byte[] data) {
log.info("personTraceStatus={}",personTraceStatus);
return 0;
}
}
JAVA JNI 接口定义
package com.cares.reconova;
import com.cares.reconova.service.ReconovaFrameCallBack;
import com.cares.reconova.service.ReconovaPersonTraceInfoCallBack;
/**
* @author wangcj
* @desc
* @date 2021/4/20 10:10
**/
public class ReconovaFaceCameraSdk {
public ReconovaFaceCameraSdk() {
System.loadLibrary("ReconovaFaceCameraSdkJNI");
}
/**
* 模块初始化
*
* @return 成功返回0,失败返回负数
*/
public native int ReconovaFaceCameraSdkInit();
/**
* 模块反初始化,释放资源
*
* @return 成功返回0,失败返回负数
*/
public native int ReconovaFaceCameraSdkUnInit();
/**
* 注册视频帧回调
*
* @param callBack[in] 回调函数
* @param data[in] 自定义数据
* @return 成功返回0,失败返回负数
* @note 该接口需要在启动USB像机之前调用
* 该接口推送的是BGR24的图片原始数据
*/
public native int ReconovaFaceCameraSdkRegisterFrameCallBack(ReconovaFrameCallBack callBack, byte[] data);
/**
* 注册人员跟踪信息回调
*
* @param callBack[in] 回调函数
* @param data[in] 自定义数据
* @return 成功返回0,失败返回负数
*/
public native int ReconovaFaceCameraSdkRegisterPersonTraceInfoCallBack(ReconovaPersonTraceInfoCallBack callBack, byte[] data);
/**
* 启动人脸摄像头
*
* @return 成功返回0,失败返回负数
*/
public native int ReconovaFaceCameraSdkFaceCameraStart();
/**
* 停止人脸摄像头
*
* @return 成功返回0,失败返回负数
*/
public native int ReconovaFaceCameraSdkFaceCameraStop();
}
C++ 代码
工程建立
1.新建空白win 32 console application,类型为dll,非exe,编译目标为x86
2.将工程和解决方案命名为 ReconovaFaceCameraSdkJNI或者将targetName 环境变量修改为 ReconovaFaceCameraSdkJNI
3.VC++ directories 添加jdk目录下的 include 和include/win32,瑞为的sdk inc目录到工程headers 目录
4.VC++ directories 添加瑞为的 ReconovaFaceCameraSdk.lib文件夹 到工程lib文件夹,或者拷贝ReconovaFaceCameraSdk.lib到工程目录下,用#pragma 处理(#pragma comment(lib, “ReconovaFaceCameraSdk”))
5.使用javah 将定义的java jni 接口转成头文件
6.添加实现代码
JNI 实现
工程添加Source.cpp
#include "com_cares_reconova_ReconovaFaceCameraSdk.h"
#include <ReconovaFaceCameraSdk.h>
#include <string.h>
#include <memory.h>
#pragma comment(lib, "ReconovaFaceCameraSdk")
JavaVM * g_jvm = NULL;
jobject g_framecallbackobj = NULL;
jobject g_traceInfocallbackobj = NULL;
JNIEnv* AttachJava()
{
JavaVMAttachArgs args = { JNI_VERSION_1_8, 0, 0 };
JNIEnv* java;
g_jvm->AttachCurrentThread((void**)&java, &args);
return java;
}
jobject createReconovaBGR24ImageObjAndSetValue(JNIEnv* g_env, ReconovaBGR24Image_t *image) {
// 创建对象
jclass bgrimgclass = g_env->FindClass("com/cares/reconova/model/ReconovaBGR24Image");
jmethodID bgrimgclass_noarg_constructer = g_env->GetMethodID(bgrimgclass, "<init>", "()V");
jobject obj = g_env->NewObject(bgrimgclass, bgrimgclass_noarg_constructer);
// 设置第一个参数 ReconovaBGR24Image
jfieldID width = g_env->GetFieldID(bgrimgclass, "width", "I");
jfieldID height = g_env->GetFieldID(bgrimgclass, "height", "I");
jfieldID widthStep = g_env->GetFieldID(bgrimgclass, "widthStep", "I");
jfieldID imageSize = g_env->GetFieldID(bgrimgclass, "imageSize", "I");
jfieldID imageData = g_env->GetFieldID(bgrimgclass, "imageData", "[B");
g_env->SetIntField(obj, width, image->width);
g_env->SetIntField(obj, height, image->height);
g_env->SetIntField(obj, widthStep, image->widthStep);
g_env->SetIntField(obj, imageSize, image->imageSize);
jbyteArray data = g_env->NewByteArray(image->imageSize);
g_env->SetByteArrayRegion(data, 0, image->imageSize, (jbyte*)image->imageData);
g_env->SetObjectField(obj, imageData, data);
g_env->DeleteLocalRef(bgrimgclass);
return obj;
}
jobject createReconovaFaceRectInfoAndSetValue(JNIEnv* g_env, ReconovaFaceRectInfo_t *faceRect) {
// 创建对象
jclass faceRectObjclass = g_env->FindClass("com/cares/reconova/model/ReconovaFaceRectInfo");
jmethodID faceRectObjclass_noarg_constructer = g_env->GetMethodID(faceRectObjclass, "<init>", "()V");
jobject obj = g_env->NewObject(faceRectObjclass, faceRectObjclass_noarg_constructer);
jfieldID left = g_env->GetFieldID(faceRectObjclass, "left", "I");
jfieldID right = g_env->GetFieldID(faceRectObjclass, "right", "I");
jfieldID top = g_env->GetFieldID(faceRectObjclass, "top", "I");
jfieldID bottom = g_env->GetFieldID(faceRectObjclass, "bottom", "I");
g_env->SetIntField(obj, left, faceRect->left);
g_env->SetIntField(obj, right, faceRect->right);
g_env->SetIntField(obj, top, faceRect->top);
g_env->SetIntField(obj, bottom, faceRect->bottom);
g_env->DeleteLocalRef(faceRectObjclass);
return obj;
}
jobjectArray createReconovaFaceRectInfoArrayAndSetValue(JNIEnv* g_env, ReconovaFaceRectInfo_t *faceList, int faceNum) {
// 创建对象
jclass faceRectObjclass = g_env->FindClass("com/cares/reconova/model/ReconovaFaceRectInfo");
jobjectArray faceListObj = g_env->NewObjectArray(faceNum, faceRectObjclass, nullptr);
for (int i = 0; i < faceNum; i++) {
g_env->SetObjectArrayElement(faceListObj, i, createReconovaFaceRectInfoAndSetValue(g_env,faceList + i));
}
// 设置第一个参数 ReconovaBGR24Image
g_env->DeleteLocalRef(faceRectObjclass);
return faceListObj;
}
jobject createFaceFetureAndSetValue(JNIEnv* g_env, ReconovaFaceFeature_t faceFeature) {
jclass faceFetureClass = g_env->FindClass("com/cares/reconova/model/ReconovaFaceFeature");
jmethodID faceFetureClass_noarg_constructer = g_env->GetMethodID(faceFetureClass, "<init>", "()V");
jobject obj = g_env->NewObject(faceFetureClass, faceFetureClass_noarg_constructer);
jfieldID featLength = g_env->GetFieldID(faceFetureClass, "featLength", "I");
jfieldID featData = g_env->GetFieldID(faceFetureClass, "featData", "[B");
jbyteArray data = g_env->NewByteArray(faceFeature.featLength);
g_env->SetByteArrayRegion(data, 0, faceFeature.featLength, (jbyte*)faceFeature.featData);
g_env->SetObjectField(obj, featData, data);
g_env->SetIntField(obj, featLength, faceFeature.featLength);
g_env->DeleteLocalRef(faceFetureClass);
return obj;
}
jobject createReconovaPersonTraceInfoAndSetValue(JNIEnv* g_env, ReconovaPersonTraceInfo_t *personTraceInfo) {
jclass traceInfoClass = g_env->FindClass("com/cares/reconova/model/ReconovaPersonTraceInfo");
jmethodID traceInfoClass_noarg_constructer = g_env->GetMethodID(traceInfoClass, "<init>", "()V");
jobject obj = g_env->NewObject(traceInfoClass, traceInfoClass_noarg_constructer);
jfieldID personId = g_env->GetFieldID(traceInfoClass, "personId", "I");
jfieldID frameImage = g_env->GetFieldID(traceInfoClass, "frameImage", "Lcom/cares/reconova/model/ReconovaBGR24Image;");
jfieldID faceRect = g_env->GetFieldID(traceInfoClass, "faceRect", "Lcom/cares/reconova/model/ReconovaFaceRectInfo;");
jfieldID faceImage = g_env->GetFieldID(traceInfoClass, "faceImage", "Lcom/cares/reconova/model/ReconovaBGR24Image;");
jfieldID faceFeature = g_env->GetFieldID(traceInfoClass, "faceFeature", "Lcom/cares/reconova/model/ReconovaFaceFeature;");
jobject frameImageObj = createReconovaBGR24ImageObjAndSetValue(g_env, &(personTraceInfo->frameImage));
jobject faceRectObj = createReconovaFaceRectInfoAndSetValue(g_env, &(personTraceInfo->faceRect));
jobject faceImageObj = createReconovaBGR24ImageObjAndSetValue(g_env, &(personTraceInfo->faceImage));
jobject faceFeatureObj = createFaceFetureAndSetValue(g_env, personTraceInfo->faceFeature);
g_env->SetIntField(obj, personId, personTraceInfo->personId);
g_env->SetObjectField(obj, frameImage, frameImageObj);
g_env->SetObjectField(obj, faceRect, faceRectObj);
g_env->SetObjectField(obj, faceImage, faceImageObj);
g_env->SetObjectField(obj, faceFeature, faceFeatureObj);
g_env->DeleteLocalRef(traceInfoClass);
return obj;
}
jobject createStatus(JNIEnv* g_env, ReconovaPersonTraceStatus_t personTraceStatus) {
jclass statusClass = g_env->FindClass("com/cares/reconova/model/ReconovaPersonTraceStatus");
jobject statusobj;
jfieldID jfieldID_RECONOVA_PERSON_TRACE_STATUS_ENTER = g_env->GetStaticFieldID(statusClass, "RECONOVA_PERSON_TRACE_STATUS_ENTER", "Lcom/cares/reconova/model/ReconovaPersonTraceStatus;");
jfieldID jfieldID_RECONOVA_PERSON_TRACE_STATUS_UPDATE = g_env->GetStaticFieldID(statusClass, "RECONOVA_PERSON_TRACE_STATUS_UPDATE", "Lcom/cares/reconova/model/ReconovaPersonTraceStatus;");
jfieldID jfieldID_RECONOVA_PERSON_TRACE_STATUS_LEAVE = g_env->GetStaticFieldID(statusClass, "RECONOVA_PERSON_TRACE_STATUS_LEAVE", "Lcom/cares/reconova/model/ReconovaPersonTraceStatus;");
switch (personTraceStatus) {
case RECONOVA_PERSON_TRACE_STATUS_ENTER:
statusobj = g_env->GetStaticObjectField(statusClass, jfieldID_RECONOVA_PERSON_TRACE_STATUS_ENTER);
break;
case RECONOVA_PERSON_TRACE_STATUS_UPDATE:
statusobj = g_env->GetStaticObjectField(statusClass, jfieldID_RECONOVA_PERSON_TRACE_STATUS_UPDATE);
break;
case RECONOVA_PERSON_TRACE_STATUS_LEAVE:
statusobj = g_env->GetStaticObjectField(statusClass, jfieldID_RECONOVA_PERSON_TRACE_STATUS_LEAVE);
break;
default:
statusobj = nullptr;
}
g_env->DeleteLocalRef(statusClass);
return statusobj;
}
int ReconovaFrameCallBack(ReconovaBGR24Image_t *image, ReconovaFaceRectInfo_t *faceList, int faceNum, void *data) {
JNIEnv* g_env = AttachJava();
// 调用 g_traceInfocallbackobj 中的方法
jclass javacallbakimplclass = g_env->GetObjectClass(g_framecallbackobj);
jmethodID invoke = g_env->GetMethodID(javacallbakimplclass, "invoke", "(Lcom/cares/reconova/model/ReconovaBGR24Image;[Lcom/cares/reconova/model/ReconovaFaceRectInfo;I[B)I");
jobject ReconovaBGR24Image = createReconovaBGR24ImageObjAndSetValue(g_env,image);
jobjectArray ReconovaFaceRectInfo = createReconovaFaceRectInfoArrayAndSetValue(g_env, faceList, faceNum);
g_env->CallObjectMethod(g_framecallbackobj, invoke, ReconovaBGR24Image, ReconovaFaceRectInfo, faceNum, data);
g_jvm->DetachCurrentThread();
return 0;
}
int ReconovaPersonTraceInfoCallBack(ReconovaPersonTraceInfo_t *personTraceInfo, ReconovaPersonTraceStatus_t personTraceStatus, void *data) {
JNIEnv* g_env = AttachJava();
// 调用 g_framecallbackobj 中的方法
jclass traceInfocallbackobjimplclass = g_env->GetObjectClass(g_traceInfocallbackobj);
jmethodID invoke = g_env->GetMethodID(traceInfocallbackobjimplclass, "invoke", "(Lcom/cares/reconova/model/ReconovaPersonTraceInfo;Lcom/cares/reconova/model/ReconovaPersonTraceStatus;[B)I");
jobject personTraceInfoObj = createReconovaPersonTraceInfoAndSetValue(g_env, personTraceInfo);
jobject status = createStatus(g_env, personTraceStatus);
g_env->CallObjectMethod(g_traceInfocallbackobj, invoke, personTraceInfoObj, status, data);
g_jvm->DetachCurrentThread();
return 0;
}
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkInit
(JNIEnv *, jobject) {
return ReconovaFaceCameraSdkInit();
}
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkUnInit
(JNIEnv *, jobject) {
return ReconovaFaceCameraSdkUnInit();
}
// 帧回调
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkRegisterFrameCallBack
(JNIEnv *env, jobject sdkclass, jobject framecallbackobj, jbyteArray data) {
JNIEnv* g_env = AttachJava();
g_framecallbackobj = env->NewGlobalRef(framecallbackobj);
int res = ReconovaFaceCameraSdkRegisterFrameCallBack(ReconovaFrameCallBack, NULL);
return res;
}
// 跟踪信息回调
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkRegisterPersonTraceInfoCallBack
(JNIEnv *env, jobject sdkclass, jobject traceInfocallbackobj, jbyteArray data) {
g_traceInfocallbackobj = env->NewGlobalRef(traceInfocallbackobj);
int res= ReconovaFaceCameraSdkRegisterPersonTraceInfoCallBack(ReconovaPersonTraceInfoCallBack, NULL);
return res;
}
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkFaceCameraStart
(JNIEnv *, jobject) {
return ReconovaFaceCameraSdkFaceCameraStart();
}
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkFaceCameraStop
(JNIEnv *, jobject) {
return ReconovaFaceCameraSdkFaceCameraStop();
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
if (jvm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
g_jvm = jvm;
result = JNI_VERSION_1_8;
printf("JNI_OnLoad sucess\n");
fflush(stdout);
return result;
}
JNI 调用总结
回调函数的全局对象变量
java将回调函数作为jobject 传到jni,在瑞为的本地回调函数我们要去调用这个对象里面的方法,必须要将这个回调类对象保存为全局变量,直接赋值是不行的,会被jvm回收掉。
jobject g_framecallbackobj = NULL; // 全局变量
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkRegisterFrameCallBack
(JNIEnv *env, jobject sdkclass, jobject framecallbackobj, jbyteArray data) {
g_framecallbackobj = env->NewGlobalRef(framecallbackobj); // 正确方式全局引用
// g_framecallbackobj=framecallbackobj; // 错误方式
...
}
JNI 打印信息到控制台
printf("JNI_OnLoad sucess\n");
fflush(stdout);
多线程获取 JNIEnv *
瑞为本地sdk回调是在一个线程内调用,注册回调时把 JNIEnv * 保存为全局变量是不对的,会导致jvm crash
JNIEnv* g_env = NULL; // 全局变量
JNIEXPORT jint JNICALL Java_com_cares_reconova_ReconovaFaceCameraSdk_ReconovaFaceCameraSdkRegisterFrameCallBack
(JNIEnv *env, jobject sdkclass, jobject framecallbackobj, jbyteArray data) {
// g_env =env; // 错误方式
...
}
正确的方法
1、代码实现添加 JNI_OnLoad,jvm在加载dll成功后会调用这个函数,这时我们可以吧 jvm作为全局变量保存起来,以便获取到
JNIEnv*
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
if (jvm->GetEnv((void **)&env, JNI_VERSION_1_8) != JNI_OK) {
return -1;
}
g_jvm = jvm;
result = JNI_VERSION_1_8;
printf("JNI_OnLoad sucess\n");
fflush(stdout);
return result;
}
2、获取
JNIEnv* AttachJava()
{
JavaVMAttachArgs args = { JNI_VERSION_1_8, 0, 0 };
JNIEnv* java;
g_jvm->AttachCurrentThread((void**)&java, &args); // g_jvm 全局变量
return java;
}
3、重点,JNIEnv* 获取后在不用的时候要 释放。以本列来说,本地回调函数的开始获取JNIEnv* ,本地回调函数调用完java回调函数后需要释放 JNIEnv*
g_jvm->DetachCurrentThread();
C++枚举到JAVA枚举的转换
参考 c++ 实现代码 createStatus函数
查看 java的方法签名和字段签名
JNI在获取方法、设置字段值的时候需要签名
javap -s "your java class full packagename.your classname"
javap 会把类中的所有方法和字段的签名打印出来,我们赋值粘贴就好。
基本类型参考官方文档
jni 调试
调试可以加快效率
本地调试
1、在java代码打断点,启动程序
2、打开vs工程 ,选择 attach 到本地进程,由于java进程都是java.exe 分不清,可以用jvisualvm 或者 jps命令查看
3、在vs要调试的代码打断点,打好后放开java断点
远程调试
由于java代码运行在虚拟机,本列采用远程调试,
1、远程机器开启 C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe
这时vs的一个工具,设置为不需要验证,在options设置任意用户可以连接
2、调试端选择attach to process,transport 在弹出框选择远程,然后点击find(注意防火墙)
3、剩余步骤参考本地调试
异常检查
每一次JNI 方法调用完,都要去检查异常
检查-》描述-》清除异常-》抛出异常
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();//清除引发的异常,在Java层不会打印异常堆栈信息,如果不清除,后面的调用ThrowNew抛出的异常堆栈信息会
//覆盖前面的异常信息
jclass cls_exception = env->FindClass("java/lang/Exception");
env->ThrowNew(cls_exception, "call java static method ndk error");
return;
}
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
jclass cls_exception = env->FindClass("java/lang/Exception");
env->ThrowNew(cls_exception, "call java static method ndk error");
return;
}
FindClass 找不到类的问题
比如springboot 打包jar后,FindClass 会找不到除jdk类外的任何类,应为springboot jar是用户自定义classloader
解决办法
1、JNI找到加载的classloader,调用classloader的方法去加载jar包中的类
2、将java中的对象作为object 传入 JNI,这样JNI就不用去findClass 了
3、将springboot 打包成资源分离方式 通过java -classpath mainClass 的方式启动。这样所有的jar和类都会通过系统AppClassLoader 去加载,FindClass 就不会返回空值(推荐,资源分离方式打包,更新比较方便,springboot jar包可能会很大)
最后
如果是小库使用JNA去调用即可,如果像是人脸sdk库文件很大最好用jni。JNA性能不行
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!