环境

开发环境

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性能不行



硬件      JNI JNI 调瑞为SDK JNI 总结

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!