react-native-vision-camera-spoof-detector 1.1.0 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of react-native-vision-camera-spoof-detector might be problematic. Click here for more details.

Files changed (43) hide show
  1. package/.npmignore +16 -0
  2. package/android/CMakeLists.txt +29 -0
  3. package/android/build.gradle +26 -10
  4. package/android/fix-prefab.gradle +4 -0
  5. package/android/src/main/AndroidManifest.xml +1 -1
  6. package/android/src/main/cpp/CMakeLists.txt +18 -86
  7. package/android/src/main/cpp/FaceAntiSpoofJSI.cpp +80 -6
  8. package/android/src/main/cpp/cpp-adapter.cpp +9 -0
  9. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofModule.kt +53 -103
  10. package/android/src/main/java/com/faceantispoof/FaceAntiSpoofPackage.kt +15 -8
  11. package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorFactory.kt +15 -0
  12. package/android/src/main/java/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorOutput.kt +107 -0
  13. package/index.js +23 -42
  14. package/nitro.json +20 -0
  15. package/nitrogen/generated/.gitattributes +1 -0
  16. package/nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake +81 -0
  17. package/nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.gradle +27 -0
  18. package/nitrogen/generated/android/VisionCameraSpoofDetectorOnLoad.cpp +58 -0
  19. package/nitrogen/generated/android/VisionCameraSpoofDetectorOnLoad.hpp +34 -0
  20. package/nitrogen/generated/android/c++/JAntiSpoofingResult.hpp +77 -0
  21. package/nitrogen/generated/android/c++/JFunc_void_AntiSpoofingResult.hpp +78 -0
  22. package/nitrogen/generated/android/c++/JFunc_void_std__exception_ptr.hpp +76 -0
  23. package/nitrogen/generated/android/c++/JHybridSpoofDetectorFactorySpec.cpp +75 -0
  24. package/nitrogen/generated/android/c++/JHybridSpoofDetectorFactorySpec.hpp +63 -0
  25. package/nitrogen/generated/android/c++/JSize.hpp +61 -0
  26. package/nitrogen/generated/android/c++/JSpoofDetectorOutputOptions.hpp +91 -0
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/AntiSpoofingResult.kt +76 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Func_void_AntiSpoofingResult.kt +78 -0
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Func_void_std__exception_ptr.kt +78 -0
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/HybridSpoofDetectorFactorySpec.kt +57 -0
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/Size.kt +56 -0
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/SpoofDetectorOutputOptions.kt +65 -0
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/spoofdetector/VisionCameraSpoofDetectorOnLoad.kt +35 -0
  34. package/nitrogen/generated/shared/c++/AntiSpoofingResult.hpp +103 -0
  35. package/nitrogen/generated/shared/c++/HybridSpoofDetectorFactorySpec.cpp +21 -0
  36. package/nitrogen/generated/shared/c++/HybridSpoofDetectorFactorySpec.hpp +67 -0
  37. package/nitrogen/generated/shared/c++/Size.hpp +87 -0
  38. package/nitrogen/generated/shared/c++/SpoofDetectorOutputOptions.hpp +98 -0
  39. package/package.json +10 -8
  40. package/src/specs/SpoofDetectorFactory.nitro.ts +23 -0
  41. package/android/src/main/assets/spoof_model_scale_2_7.tflite +0 -0
  42. package/android/src/main/assets/spoof_model_scale_4_0.tflite +0 -0
  43. /package/{android/src/main → lib}/assets/FaceAntiSpoofing.tflite +0 -0
package/.npmignore ADDED
@@ -0,0 +1,16 @@
1
+ **/build/
2
+ **/.cxx/
3
+ **/.gradle/
4
+ **/node_modules/
5
+ **/Pods/
6
+ **/.swiftpm/
7
+ **/DerivedData/
8
+ **/xcuserdata/
9
+ **/.idea/
10
+ **/coverage/
11
+ **/dist/
12
+ **/tmp/
13
+ **/.DS_Store
14
+ **/local.properties
15
+ **/*.iml
16
+ *.log
@@ -0,0 +1,29 @@
1
+ project(VisionCameraSpoofDetector)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME VisionCameraSpoofDetector)
5
+ set(CMAKE_SUPPRESS_REGENERATION ON)
6
+ set(CMAKE_VERBOSE_MAKEFILE ON)
7
+ set(CMAKE_CXX_STANDARD 20)
8
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
9
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
10
+ set(CMAKE_ANDROID_STL_TYPE c++_shared)
11
+
12
+ add_library(${PACKAGE_NAME} SHARED
13
+ src/main/cpp/cpp-adapter.cpp
14
+ )
15
+
16
+ # Nitrogen-generated specs: this path is valid because the project root is android/
17
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake)
18
+
19
+ find_library(LOG_LIB log)
20
+ find_package(react-native-vision-camera REQUIRED)
21
+
22
+ target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
23
+
24
+ target_link_libraries(
25
+ ${PACKAGE_NAME}
26
+ ${LOG_LIB}
27
+ android
28
+ react-native-vision-camera::VisionCamera
29
+ )
@@ -19,8 +19,15 @@ buildscript {
19
19
 
20
20
  apply plugin: 'com.android.library'
21
21
  apply plugin: 'kotlin-android'
22
+ apply from: '../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.gradle'
23
+
24
+ def reactNativeArchitectures() {
25
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
26
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
27
+ }
22
28
 
23
29
  android {
30
+ namespace "com.margelo.nitro.camera.spoofdetector"
24
31
  compileSdkVersion rootProject.ext.compileSdkVersion
25
32
 
26
33
  defaultConfig {
@@ -35,7 +42,9 @@ android {
35
42
  // Needed for JSI / C++ build
36
43
  externalNativeBuild {
37
44
  cmake {
38
- cppFlags "-std=c++17"
45
+ cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
46
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
47
+ abiFilters (*reactNativeArchitectures())
39
48
  }
40
49
  }
41
50
  }
@@ -61,7 +70,7 @@ android {
61
70
 
62
71
  sourceSets {
63
72
  main {
64
- assets.srcDirs = ['src/main/assets']
73
+ assets.srcDirs = ['../lib/assets']
65
74
  }
66
75
  }
67
76
 
@@ -91,10 +100,14 @@ android {
91
100
 
92
101
  externalNativeBuild {
93
102
  cmake {
94
- path "src/main/cpp/CMakeLists.txt"
95
- version "3.22.1" // Optional: matches your installed CMake
103
+ path "CMakeLists.txt"
104
+ version "3.22.1"
96
105
  }
97
106
  }
107
+
108
+ buildFeatures {
109
+ prefab true
110
+ }
98
111
  }
99
112
 
100
113
  def isNewArchitectureEnabled() {
@@ -109,8 +122,10 @@ repositories {
109
122
  }
110
123
 
111
124
  dependencies {
112
- // React Native
113
- implementation "com.facebook.react:react-native:+"
125
+ implementation "androidx.camera:camera-core:1.7.0-alpha03"
126
+
127
+ // React Native and VisionCamera provide their native Prefab libraries.
128
+ compileOnly "com.facebook.react:react-android"
114
129
 
115
130
  // Kotlin
116
131
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
@@ -130,8 +145,9 @@ dependencies {
130
145
  // AndroidX - EXIF Support for image orientation
131
146
  implementation "androidx.exifinterface:exifinterface:1.3.6"
132
147
 
133
- // VisionCamera 5 uses Nitro frame outputs backed by react-native-worklets.
134
- implementation project(':react-native-vision-camera')
135
- implementation project(':react-native-vision-camera-worklets')
136
- implementation project(':react-native-worklets')
148
+ // VisionCamera 5 and Nitro provide these native libraries to the consuming app.
149
+ compileOnly project(':react-native-vision-camera')
150
+ compileOnly project(':react-native-vision-camera-worklets')
151
+ compileOnly project(':react-native-worklets')
152
+ compileOnly project(':react-native-nitro-modules')
137
153
  }
@@ -0,0 +1,4 @@
1
+ // This package does not need the forced prefab reconfigure workaround.
2
+ // Leaving the task outputs as a normal Gradle up-to-date state prevents the
3
+ // infinite CMake regeneration loop that was triggering
4
+ // "ninja: error: manifest 'build.ninja' still dirty after 100 tries".
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="utf-8"?>
2
2
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
- package="com.faceantispoof">
3
+ >
4
4
  <application>
5
5
  <meta-data
6
6
  android:name="com.google.mlkit.vision.DEPENDENCIES"
@@ -1,101 +1,33 @@
1
- cmake_minimum_required(VERSION 3.4.1)
2
-
3
- project(fastyuv)
4
-
5
- set(SOURCE_FILES
6
- FastYuvJni.cpp
7
- fast_yuv.cpp
8
- )
9
-
10
- add_library(fastyuv SHARED ${SOURCE_FILES})
11
-
12
- # Enable NEON SIMD optimizations for ARM architectures
13
- if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
14
- # ARM NEON optimization flags
15
- target_compile_options(fastyuv PRIVATE
16
- -fPIC
17
- -O3
18
- )
19
-
20
- # ARM-specific NEON enablement (clang doesn't support -march=native for cross-compilation)
21
- if(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7")
22
- # ARMv7 with NEON (32-bit ARM)
23
- target_compile_options(fastyuv PRIVATE -march=armv7-a -mfpu=neon -mfloat-abi=softfp)
24
- elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
25
- # ARMv8 with full SIMD support (64-bit ARM)
26
- target_compile_options(fastyuv PRIVATE -march=armv8-a)
27
- endif()
28
- endif()
29
-
30
- # Always add libyuv include dir so headers like libyuv/convert.h are found.
31
- target_include_directories(fastyuv PRIVATE ${CMAKE_SOURCE_DIR}/libyuv/include)
32
-
33
- # Prefer using libyuv subproject if available.
34
- if (EXISTS ${CMAKE_SOURCE_DIR}/libyuv/CMakeLists.txt)
35
- add_subdirectory(libyuv)
36
- # Link against the libyuv target produced by that CMake. The lib creates
37
- # targets named 'yuv' (static) and 'yuv_shared' (shared) whose output
38
- # library name is 'libyuv'. Prefer the shared target when available.
39
- if (TARGET yuv_shared)
40
- target_link_libraries(fastyuv PRIVATE yuv_shared)
41
- elseif (TARGET yuv)
42
- target_link_libraries(fastyuv PRIVATE yuv)
43
- endif()
44
- endif()
45
-
46
- # 支持 Android 15+ 的 16KB 页面大小
47
- # https://developer.android.com/guide/practices/page-sizes
48
- target_link_options(fastyuv PRIVATE "-Wl,-z,max-page-size=16384")
49
-
50
- # Android log
51
- find_library(log-lib log)
52
- target_link_libraries(fastyuv PRIVATE ${log-lib})
53
1
  cmake_minimum_required(VERSION 3.18)
54
- project(faceantispoof)
2
+ project(VisionCameraSpoofDetector)
55
3
 
56
4
  set(CMAKE_CXX_STANDARD 17)
57
5
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_SUPPRESS_REGENERATION ON)
58
7
  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
8
+ set(CMAKE_ANDROID_STL_TYPE c++_shared)
59
9
 
60
- get_filename_component(CMAKE_SOURCE_ABS "${CMAKE_SOURCE_DIR}" ABSOLUTE)
61
- # .../react-native-vision-camera-spoof-detector/android/src/main/cpp -> ../../../.. = .../node_modules
62
- get_filename_component(PARENT_1 "${CMAKE_SOURCE_ABS}/.." ABSOLUTE) # main
63
- get_filename_component(PARENT_2 "${PARENT_1}/.." ABSOLUTE) # src
64
- get_filename_component(PARENT_3 "${PARENT_2}/.." ABSOLUTE) # android
65
- get_filename_component(PARENT_4 "${PARENT_3}/.." ABSOLUTE) # react-native-vision-camera-spoof-detector
66
- get_filename_component(NODE_MODULES_ABS "${PARENT_4}/.." ABSOLUTE) # node_modules
67
-
68
- set(REACT_NATIVE_DIR "${NODE_MODULES_ABS}/react-native")
69
- set(REACT_COMMON_DIR "${REACT_NATIVE_DIR}/ReactCommon")
70
-
71
- message(STATUS "NODE_MODULES_ABS: ${NODE_MODULES_ABS}")
72
- message(STATUS "REACT_NATIVE_DIR: ${REACT_NATIVE_DIR}")
73
- message(STATUS "REACT_COMMON_DIR: ${REACT_COMMON_DIR}")
10
+ set(PACKAGE_NAME VisionCameraSpoofDetector)
11
+ set(CMAKE_VERBOSE_MAKEFILE ON)
74
12
 
75
- # Compile JSI implementation
76
- file(GLOB JSI_CPP "${REACT_COMMON_DIR}/jsi/jsi/jsi.cpp")
77
-
78
- add_library(faceantispoof SHARED FaceAntiSpoofJSI.cpp ${JSI_CPP})
79
-
80
- target_include_directories(faceantispoof PRIVATE
81
- "${REACT_COMMON_DIR}/jsi"
82
- "${REACT_COMMON_DIR}/cxxreact"
83
- "${REACT_COMMON_DIR}/callinvoker/ReactCommon"
84
- "${REACT_COMMON_DIR}/runtimeexecutor/ReactCommon"
85
- "${REACT_COMMON_DIR}"
13
+ add_library(${PACKAGE_NAME} SHARED
14
+ cpp-adapter.cpp
86
15
  )
87
16
 
88
- find_library(log-lib log)
17
+ # Nitrogen-generated hybrid bindings
18
+ include(${CMAKE_SOURCE_DIR}/../../../../nitrogen/generated/android/VisionCameraSpoofDetector+autolinking.cmake)
19
+
20
+ find_library(LOG_LIB log)
21
+ find_package(react-native-vision-camera REQUIRED)
89
22
 
90
- # 支持 Android 15+ 16KB 页面大小
91
- target_link_options(faceantispoof PRIVATE "-Wl,-z,max-page-size=16384")
23
+ # Android 15+ 16KB page support
24
+ target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
92
25
 
93
26
  target_link_libraries(
94
- faceantispoof
95
- "${log-lib}"
96
- c++_shared
97
- atomic
98
- m
27
+ ${PACKAGE_NAME}
28
+ ${LOG_LIB}
29
+ android
30
+ react-native-vision-camera::VisionCamera
99
31
  )
100
32
 
101
33
 
@@ -1,6 +1,8 @@
1
1
  #include <jni.h>
2
2
  #include <jsi/jsi.h>
3
3
  #include <string>
4
+ #include <vector>
5
+ #include <algorithm>
4
6
  #include <android/log.h>
5
7
 
6
8
  using namespace facebook;
@@ -8,18 +10,90 @@ using namespace facebook;
8
10
  // Logging macro
9
11
  #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FaceAntiSpoofJSI", __VA_ARGS__))
10
12
 
11
- // Dummy frame processing function
13
+ static JavaVM *javaVm = nullptr;
14
+
12
15
  jsi::Value faceAntiSpoof(jsi::Runtime &runtime, const jsi::Value &frame) {
13
- LOGI("FaceAntiSpoofJSI: frame received");
16
+ if (!frame.isObject()) {
17
+ throw jsi::JSError(runtime, "FaceAntiSpoof expected a Frame object");
18
+ }
14
19
 
15
- jsi::Object result(runtime);
16
- result.setProperty(runtime, "isLive", true);
17
- result.setProperty(runtime, "label", "Live");
18
- result.setProperty(runtime, "confidence", 0.99);
20
+ auto frameObject = frame.asObject(runtime);
21
+ auto planesValue = frameObject.getProperty(runtime, "getPlanes").asObject(runtime)
22
+ .asFunction(runtime).call(runtime, frameObject);
23
+ auto planes = planesValue.asObject(runtime);
24
+ auto length = static_cast<size_t>(planes.getProperty(runtime, "length").asNumber());
25
+ if (length < 3) {
26
+ throw jsi::JSError(runtime, "FaceAntiSpoof expected Y, U and V planes");
27
+ }
19
28
 
29
+ std::vector<std::vector<uint8_t>> planeData(3);
30
+ jint rowStrides[3] = {};
31
+ jint pixelStrides[3] = {};
32
+ for (size_t index = 0; index < 3; index++) {
33
+ auto plane = planes.getProperty(runtime, jsi::Value(static_cast<double>(index))).asObject(runtime);
34
+ auto buffer = plane.getProperty(runtime, "getPixelBuffer").asObject(runtime)
35
+ .asFunction(runtime).call(runtime, plane).asObject(runtime);
36
+ auto arrayBuffer = std::move(buffer).getArrayBuffer(runtime);
37
+ auto *data = static_cast<const uint8_t *>(arrayBuffer.data(runtime));
38
+ planeData[index].assign(data, data + arrayBuffer.size(runtime));
39
+ rowStrides[index] = static_cast<jint>(plane.getProperty(runtime, "bytesPerRow").asNumber());
40
+ auto planeWidth = plane.getProperty(runtime, "width").asNumber();
41
+ pixelStrides[index] = std::max(1, static_cast<jint>(rowStrides[index] / planeWidth));
42
+ }
43
+
44
+ JNIEnv *env = nullptr;
45
+ bool attached = false;
46
+ if (javaVm == nullptr) {
47
+ throw jsi::JSError(runtime, "FaceAntiSpoof Android runtime is unavailable");
48
+ }
49
+ if (javaVm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
50
+ if (javaVm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
51
+ throw jsi::JSError(runtime, "FaceAntiSpoof could not attach to Android runtime");
52
+ }
53
+ attached = true;
54
+ }
55
+ auto moduleClass = env->FindClass("com/faceantispoof/FaceAntiSpoofModule");
56
+ auto process = env->GetStaticMethodID(moduleClass, "processFrameFromJSI", "([[BII[I[I)[D");
57
+ auto javaPlanes = env->NewObjectArray(3, env->FindClass("[B"), nullptr);
58
+ auto javaRowStrides = env->NewIntArray(3);
59
+ auto javaPixelStrides = env->NewIntArray(3);
60
+ env->SetIntArrayRegion(javaRowStrides, 0, 3, rowStrides);
61
+ env->SetIntArrayRegion(javaPixelStrides, 0, 3, pixelStrides);
62
+ for (int index = 0; index < 3; index++) {
63
+ auto bytes = env->NewByteArray(static_cast<jsize>(planeData[index].size()));
64
+ env->SetByteArrayRegion(bytes, 0, static_cast<jsize>(planeData[index].size()),
65
+ reinterpret_cast<const jbyte *>(planeData[index].data()));
66
+ env->SetObjectArrayElement(javaPlanes, index, bytes);
67
+ env->DeleteLocalRef(bytes);
68
+ }
69
+ auto values = static_cast<jdoubleArray>(env->CallStaticObjectMethod(
70
+ moduleClass, process, javaPlanes, static_cast<jint>(frameObject.getProperty(runtime, "width").asNumber()),
71
+ static_cast<jint>(frameObject.getProperty(runtime, "height").asNumber()),
72
+ javaRowStrides, javaPixelStrides));
73
+ jdouble output[5] = {};
74
+ if (values != nullptr) env->GetDoubleArrayRegion(values, 0, 5, output);
75
+ env->DeleteLocalRef(values);
76
+ env->DeleteLocalRef(javaPixelStrides);
77
+ env->DeleteLocalRef(javaRowStrides);
78
+ env->DeleteLocalRef(javaPlanes);
79
+ env->DeleteLocalRef(moduleClass);
80
+ if (attached) javaVm->DetachCurrentThread();
81
+
82
+ jsi::Object result(runtime);
83
+ result.setProperty(runtime, "isLive", output[0] != 0.0);
84
+ result.setProperty(runtime, "label", output[0] != 0.0 ? "Live Face" : "Spoof Face");
85
+ result.setProperty(runtime, "neuralNetworkScore", output[1]);
86
+ result.setProperty(runtime, "laplacianScore", output[2]);
87
+ result.setProperty(runtime, "combinedScore", output[3]);
88
+ result.setProperty(runtime, "confidence", output[4]);
20
89
  return result;
21
90
  }
22
91
 
92
+ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
93
+ javaVm = vm;
94
+ return JNI_VERSION_1_6;
95
+ }
96
+
23
97
  // JSI registration function
24
98
  extern "C"
25
99
  JNIEXPORT void JNICALL
@@ -0,0 +1,9 @@
1
+ #include "VisionCameraSpoofDetectorOnLoad.hpp"
2
+ #include <fbjni/fbjni.h>
3
+ #include <jni.h>
4
+
5
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
6
+ return facebook::jni::initialize(vm, []() {
7
+ margelo::nitro::camera::spoofdetector::registerAllNatives();
8
+ });
9
+ }
@@ -13,17 +13,27 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
13
13
 
14
14
  companion object {
15
15
  const val NAME = "FaceAntiSpoof"
16
+ private var activeModule: FaceAntiSpoofModule? = null
17
+
18
+ @JvmStatic
19
+ fun processFrameFromJSI(
20
+ planes: Array<ByteArray>,
21
+ width: Int,
22
+ height: Int,
23
+ rowStrides: IntArray,
24
+ pixelStrides: IntArray
25
+ ): DoubleArray {
26
+ val module = activeModule
27
+ ?: return doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
28
+ return module.processNativeFrame(planes, width, height, rowStrides, pixelStrides)
29
+ }
16
30
  }
17
31
 
18
32
  private var faceAntiSpoofing: FaceAntiSpoofingAdvanced? = null
19
33
  private var isModuleInitialized = false
20
34
 
21
35
  init {
22
- try {
23
- System.loadLibrary("faceantispoof")
24
- } catch (e: Exception) {
25
- android.util.Log.e("FaceAntiSpoof", "Error loading native library", e)
26
- }
36
+ activeModule = this
27
37
 
28
38
  try {
29
39
  // Initialize face anti-spoofing with app assets
@@ -35,57 +45,6 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
35
45
  isModuleInitialized = false
36
46
  }
37
47
 
38
- try {
39
- tryRegisterJSI()
40
- } catch (e: Exception) {
41
- android.util.Log.e("FaceAntiSpoof", "Error starting JSI registration", e)
42
- }
43
- }
44
-
45
- private fun tryRegisterJSI() {
46
- try {
47
- Thread {
48
- try {
49
- Thread.sleep(500)
50
- val holder = reactApplicationContext.javaScriptContextHolder ?: return@Thread
51
-
52
- // Try nativePointer
53
- try {
54
- val field = holder.javaClass.getDeclaredField("nativePointer")
55
- field.isAccessible = true
56
- val jsContextPtr = field.getLong(holder)
57
- if (jsContextPtr != 0L) {
58
- try {
59
- registerJSIFunction(jsContextPtr)
60
- } catch (e: Exception) {
61
- android.util.Log.d("FaceAntiSpoof", "JSI registration not available: ${e.message}")
62
- }
63
- return@Thread
64
- }
65
- } catch (_: Exception) { }
66
-
67
- // Try mContext
68
- try {
69
- val field = holder.javaClass.getDeclaredField("mContext")
70
- field.isAccessible = true
71
- val jsContextPtr = field.getLong(holder)
72
- if (jsContextPtr != 0L) {
73
- try {
74
- registerJSIFunction(jsContextPtr)
75
- } catch (e: Exception) {
76
- android.util.Log.d("FaceAntiSpoof", "JSI registration not available: ${e.message}")
77
- }
78
- return@Thread
79
- }
80
- } catch (_: Exception) { }
81
-
82
- } catch (e: Exception) {
83
- android.util.Log.d("FaceAntiSpoof", "Error in JSI registration thread: ${e.message}")
84
- }
85
- }.start()
86
- } catch (e: Exception) {
87
- android.util.Log.d("FaceAntiSpoof", "Failed to start thread for JSI registration: ${e.message}")
88
- }
89
48
  }
90
49
 
91
50
  override fun getName(): String = NAME
@@ -181,6 +140,41 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
181
140
  }
182
141
  }
183
142
 
143
+ @Synchronized
144
+ private fun processNativeFrame(
145
+ planes: Array<ByteArray>,
146
+ width: Int,
147
+ height: Int,
148
+ rowStrides: IntArray,
149
+ pixelStrides: IntArray
150
+ ): DoubleArray {
151
+ if (!isModuleInitialized || faceAntiSpoofing == null || planes.size < 3) {
152
+ return doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
153
+ }
154
+
155
+ return try {
156
+ val result = faceAntiSpoofing!!.advancedAntiSpoofing(
157
+ planes,
158
+ width,
159
+ height,
160
+ rowStrides,
161
+ pixelStrides
162
+ )
163
+ val isLive = result.neuralNetworkScore < FaceAntiSpoofingAdvanced.BASE_THRESHOLD &&
164
+ result.laplacianScore > FaceAntiSpoofingAdvanced.LAPLACIAN_THRESHOLD
165
+ doubleArrayOf(
166
+ if (isLive) 1.0 else 0.0,
167
+ result.neuralNetworkScore.toDouble(),
168
+ result.laplacianScore.toDouble(),
169
+ result.combinedScore.toDouble(),
170
+ result.confidence.toDouble()
171
+ )
172
+ } catch (error: Exception) {
173
+ android.util.Log.e("FaceAntiSpoof", "Native frame processing failed", error)
174
+ doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0)
175
+ }
176
+ }
177
+
184
178
  @ReactMethod
185
179
  fun isAvailable(promise: Promise) {
186
180
  try {
@@ -218,51 +212,9 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
218
212
 
219
213
  @ReactMethod
220
214
  fun install(promise: Promise) {
221
- try {
222
- val holder = reactApplicationContext.javaScriptContextHolder
223
-
224
- if (holder != null) {
225
- // Try nativePointer
226
- try {
227
- val f = holder.javaClass.getDeclaredField("nativePointer")
228
- f.isAccessible = true
229
- val ptr = f.getLong(holder)
230
- if (ptr != 0L) {
231
- try {
232
- registerJSIFunction(ptr)
233
- promise.resolve(true)
234
- return
235
- } catch (e: Exception) {
236
- android.util.Log.d("FaceAntiSpoof", "JSI not available: ${e.message}")
237
- }
238
- }
239
- } catch (_: Exception) {}
240
-
241
- // Try mContext
242
- try {
243
- val f = holder.javaClass.getDeclaredField("mContext")
244
- f.isAccessible = true
245
- val ptr = f.getLong(holder)
246
- if (ptr != 0L) {
247
- try {
248
- registerJSIFunction(ptr)
249
- promise.resolve(true)
250
- return
251
- } catch (e: Exception) {
252
- android.util.Log.d("FaceAntiSpoof", "JSI not available: ${e.message}")
253
- }
254
- }
255
- } catch (_: Exception) {}
256
-
257
- promise.resolve(false)
258
- } else {
259
- promise.resolve(false)
260
- }
261
-
262
- } catch (e: Exception) {
263
- android.util.Log.e("FaceAntiSpoof", "install() error", e)
264
- promise.reject("INSTALL_ERROR", "Failed to install JSI function", e)
265
- }
215
+ // VisionCamera 5 uses the Nitro CameraOutput. The old reflective JSI
216
+ // installer targets a native library that is no longer part of this module.
217
+ promise.resolve(false)
266
218
  }
267
219
 
268
220
  @ReactMethod
@@ -292,6 +244,4 @@ class FaceAntiSpoofModule(reactContext: ReactApplicationContext) : ReactContextB
292
244
  super.onCatalystInstanceDestroy()
293
245
  }
294
246
 
295
- // Native JSI registration - optional, may not be available
296
- private external fun registerJSIFunction(jsContext: Long)
297
247
  }
@@ -1,15 +1,22 @@
1
- package com.faceantispoof
1
+ package com.margelo.nitro.camera.spoofdetector
2
2
 
3
- import com.facebook.react.ReactPackage
3
+ import com.facebook.react.BaseReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
- import com.facebook.react.uimanager.ViewManager
6
+ import com.facebook.react.module.model.ReactModuleInfoProvider
7
+ import com.faceantispoof.FaceAntiSpoofModule
7
8
 
8
- class FaceAntiSpoofPackage : ReactPackage {
9
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10
- listOf(FaceAntiSpoofModule(reactContext))
9
+ class FaceAntiSpoofPackage : BaseReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
11
+ if (name == FaceAntiSpoofModule.NAME) FaceAntiSpoofModule(reactContext) else null
11
12
 
12
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
13
- return emptyList()
13
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
14
+ HashMap()
15
+ }
16
+
17
+ companion object {
18
+ init {
19
+ VisionCameraSpoofDetectorOnLoad.initializeNative()
20
+ }
14
21
  }
15
22
  }
@@ -0,0 +1,15 @@
1
+ package com.margelo.nitro.camera.spoofdetector
2
+
3
+ import androidx.annotation.Keep
4
+ import com.facebook.proguard.annotations.DoNotStrip
5
+ import com.margelo.nitro.camera.HybridCameraOutputSpec
6
+
7
+ @DoNotStrip
8
+ @Keep
9
+ class HybridSpoofDetectorFactory : HybridSpoofDetectorFactorySpec() {
10
+ @DoNotStrip
11
+ @Keep
12
+ override fun createSpoofDetectorOutput(options: SpoofDetectorOutputOptions): HybridCameraOutputSpec {
13
+ return HybridSpoofDetectorOutput(options)
14
+ }
15
+ }