feat: Replace Reanimated with RN Worklets (#1468)
* Setup RN Worklets * Use RN Worklets on iOS * Fix console * Add `installFrameProcessorBindings()` function * Add `FrameProcessorPlugins` proxy (BREAKING CHANGE) * Clean up docs * Update FRAME_PROCESSORS.mdx * Use RN Worklets 0.2.5 * feat: Android build setup * Rewrite Android Frame Processor Part * Update CMakeLists.txt * fix: Add react-native-worklets Gradle dependency * Update Podfile.lock * fix build * gradle:7.4.1 * Init JSI Bindings in method on Android * Fix Folly flags * fix: Init `FrameProcessorRuntimeManager` later * fix: Wrap in `<GestureHandlerRootView>` * Refactor plugins * fix: Remove enableFrameProcessors * Install RN Worklets from current GH master * Update babel.config.js * Update CameraViewModule.kt * Update ImageProxyUtils.java * feat: Upgrade to Reanimated v3 * fix: Fix crash on Worklet init * Update RN Worklets to latest master * fix: Simplify FP Plugins Proxy
This commit is contained in:
parent
11d1e7178d
commit
a0590dccb5
2
.github/workflows/validate-cpp.yml
vendored
2
.github/workflows/validate-cpp.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.github_token }}
|
github_token: ${{ secrets.github_token }}
|
||||||
reporter: github-pr-review
|
reporter: github-pr-review
|
||||||
flags: --linelength=230 --exclude "android/src/main/cpp/reanimated-headers"
|
flags: --linelength=230
|
||||||
targets: --recursive cpp android/src/main/cpp
|
targets: --recursive cpp android/src/main/cpp
|
||||||
filter: "-legal/copyright\
|
filter: "-legal/copyright\
|
||||||
,-readability/todo\
|
,-readability/todo\
|
||||||
|
@ -64,4 +64,5 @@ Pod::Spec.new do |s|
|
|||||||
s.dependency "React-callinvoker"
|
s.dependency "React-callinvoker"
|
||||||
s.dependency "React"
|
s.dependency "React"
|
||||||
s.dependency "React-Core"
|
s.dependency "React-Core"
|
||||||
|
s.dependency "react-native-worklets"
|
||||||
end
|
end
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
project(VisionCamera)
|
project(VisionCamera)
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.9.0)
|
||||||
|
|
||||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
set (CMAKE_CXX_STANDARD 14)
|
set(PACKAGE_NAME "VisionCamera")
|
||||||
|
set(BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# Folly
|
||||||
include("${NODE_MODULES_DIR}/react-native/ReactAndroid/cmake-utils/folly-flags.cmake")
|
include("${NODE_MODULES_DIR}/react-native/ReactAndroid/cmake-utils/folly-flags.cmake")
|
||||||
add_compile_options(${folly_FLAGS})
|
add_compile_options(${folly_FLAGS})
|
||||||
|
|
||||||
|
# Third party libraries (Prefabs)
|
||||||
set (PACKAGE_NAME "VisionCamera")
|
|
||||||
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
|
|
||||||
# Consume shared libraries and headers from prefabs
|
|
||||||
find_package(fbjni REQUIRED CONFIG)
|
|
||||||
find_package(ReactAndroid REQUIRED CONFIG)
|
find_package(ReactAndroid REQUIRED CONFIG)
|
||||||
# VisionCamera shared
|
find_package(fbjni REQUIRED CONFIG)
|
||||||
|
find_package(react-native-worklets REQUIRED CONFIG)
|
||||||
|
find_library(LOG_LIB log)
|
||||||
|
|
||||||
|
# Add react-native-vision-camera sources
|
||||||
add_library(
|
add_library(
|
||||||
${PACKAGE_NAME}
|
${PACKAGE_NAME}
|
||||||
SHARED
|
SHARED
|
||||||
@ -29,87 +32,24 @@ add_library(
|
|||||||
src/main/cpp/java-bindings/JHashMap.cpp
|
src/main/cpp/java-bindings/JHashMap.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# includes
|
# Header Search Paths (includes)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PACKAGE_NAME}
|
${PACKAGE_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule"
|
"src/main/cpp"
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon"
|
"${NODE_MODULES_DIR}/react-native/ReactCommon"
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
|
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
|
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule" # <-- CallInvokerHolder JNI wrapper
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon/react/renderer/graphics/platform/cxx"
|
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon/runtimeexecutor"
|
|
||||||
"${NODE_MODULES_DIR}/react-native/ReactCommon/yoga"
|
|
||||||
# --- Reanimated ---
|
|
||||||
# New
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/AnimatedSensor"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/Tools"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/SpecTools"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/SharedItems"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/Registries"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/LayoutAnimations"
|
|
||||||
"${NODE_MODULES_DIR}/react-native-reanimated/Common/cpp/hidden_headers"
|
|
||||||
"src/main/cpp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Link everything together
|
||||||
# find libraries
|
|
||||||
|
|
||||||
file (GLOB LIBRN_DIR "${BUILD_DIR}/react-native-0*/jni/${ANDROID_ABI}")
|
|
||||||
|
|
||||||
if(${FOR_HERMES})
|
|
||||||
string(APPEND CMAKE_CXX_FLAGS " -DFOR_HERMES=1")
|
|
||||||
|
|
||||||
find_package(hermes-engine REQUIRED CONFIG)
|
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PACKAGE_NAME}
|
${PACKAGE_NAME}
|
||||||
"hermes-engine::libhermes"
|
${LOG_LIB} # <-- Logcat logger
|
||||||
)
|
android # <-- Android JNI core
|
||||||
file (GLOB LIBREANIMATED_DIR "${BUILD_DIR}/react-native-reanimated-*-hermes.aar/jni/${ANDROID_ABI}")
|
ReactAndroid::jsi # <-- RN: JSI
|
||||||
else()
|
ReactAndroid::reactnativejni # <-- RN: React Native JNI bindings
|
||||||
file (GLOB LIBJSC_DIR "${BUILD_DIR}/android-jsc*.aar/jni/${ANDROID_ABI}")
|
ReactAndroid::folly_runtime # <-- RN: For casting JSI <> Java objects
|
||||||
|
fbjni::fbjni # <-- fbjni
|
||||||
set(JS_ENGINE_LIB ReactAndroid::jscexecutor)
|
react-native-worklets::rnworklets # <-- RN Worklets
|
||||||
target_link_libraries(
|
|
||||||
${PACKAGE_NAME}
|
|
||||||
${JS_ENGINE_LIB}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Use Reanimated JSC
|
|
||||||
file (GLOB LIBREANIMATED_DIR "${BUILD_DIR}/react-native-reanimated-*-jsc.aar/jni/${ANDROID_ABI}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(
|
|
||||||
${PACKAGE_NAME}
|
|
||||||
ReactAndroid::folly_runtime
|
|
||||||
ReactAndroid::glog
|
|
||||||
ReactAndroid::jsi
|
|
||||||
ReactAndroid::reactnativejni
|
|
||||||
fbjni::fbjni
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(
|
|
||||||
REANIMATED_LIB
|
|
||||||
reanimated
|
|
||||||
PATHS ${LIBREANIMATED_DIR}
|
|
||||||
NO_CMAKE_FIND_ROOT_PATH
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(
|
|
||||||
LOG_LIB
|
|
||||||
log
|
|
||||||
)
|
|
||||||
|
|
||||||
# linking
|
|
||||||
message(WARNING "VisionCamera linking: FOR_HERMES=${FOR_HERMES}")
|
|
||||||
target_link_libraries(
|
|
||||||
${PACKAGE_NAME}
|
|
||||||
${LOG_LIB}
|
|
||||||
${JSI_LIB}
|
|
||||||
${REANIMATED_LIB}
|
|
||||||
${REACT_NATIVE_JNI_LIB}
|
|
||||||
${FBJNI_LIB}
|
|
||||||
${FOLLY_LIB}
|
|
||||||
android
|
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VisionCamera_kotlinVersion']
|
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VisionCamera_kotlinVersion']
|
||||||
|
|
||||||
@ -16,7 +18,6 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def ENABLE_FRAME_PROCESSORS = false // TODO: fix
|
|
||||||
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VisionCamera_kotlinVersion']
|
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VisionCamera_kotlinVersion']
|
||||||
|
|
||||||
def resolveBuildType() {
|
def resolveBuildType() {
|
||||||
@ -49,6 +50,23 @@ def reactNativeArchitectures() {
|
|||||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static def findNodeModules(baseDir) {
|
||||||
|
def basePath = baseDir.toPath().normalize()
|
||||||
|
// Node's module resolution algorithm searches up to the root directory,
|
||||||
|
// after which the base path will be null
|
||||||
|
while (basePath) {
|
||||||
|
def nodeModulesPath = Paths.get(basePath.toString(), "node_modules")
|
||||||
|
def reactNativePath = Paths.get(nodeModulesPath.toString(), "react-native")
|
||||||
|
if (nodeModulesPath.toFile().exists() && reactNativePath.toFile().exists()) {
|
||||||
|
return nodeModulesPath.toString()
|
||||||
|
}
|
||||||
|
basePath = basePath.getParent()
|
||||||
|
}
|
||||||
|
throw new GradleException("react-native-worklets: Failed to find node_modules/ path!")
|
||||||
|
}
|
||||||
|
|
||||||
|
def nodeModules = findNodeModules(projectDir)
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -77,29 +95,26 @@ android {
|
|||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||||
|
|
||||||
if (ENABLE_FRAME_PROCESSORS) {
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
cppFlags "-O2 -frtti -fexceptions -Wall -Wno-unused-variable -fstack-protector-all"
|
cppFlags "-O2 -frtti -fexceptions -Wall -Wno-unused-variable -fstack-protector-all"
|
||||||
arguments "-DANDROID_STL=c++_shared"
|
arguments "-DANDROID_STL=c++_shared",
|
||||||
|
"-DNODE_MODULES_DIR=${nodeModules}"
|
||||||
abiFilters (*reactNativeArchitectures())
|
abiFilters (*reactNativeArchitectures())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ENABLE_FRAME_PROCESSORS) {
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path "CMakeLists.txt"
|
path "CMakeLists.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
doNotStrip resolveBuildType() == 'debug' ? "**/**/*.so" : ''
|
doNotStrip resolveBuildType() == 'debug' ? "**/**/*.so" : ''
|
||||||
excludes = [
|
excludes = [
|
||||||
@ -128,6 +143,8 @@ dependencies {
|
|||||||
implementation "androidx.camera:camera-extensions:1.1.0-beta02"
|
implementation "androidx.camera:camera-extensions:1.1.0-beta02"
|
||||||
|
|
||||||
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
||||||
|
|
||||||
|
implementation project(":react-native-worklets")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves "LOCAL_SRC_FILES points to a missing file, Check that libfb.so exists or that its path is correct".
|
// Resolves "LOCAL_SRC_FILES points to a missing file, Check that libfb.so exists or that its path is correct".
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
rootProject.name = 'VisionCamera'
|
rootProject.name = 'VisionCamera'
|
||||||
|
|
||||||
include ':react-native-reanimated'
|
|
||||||
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android/')
|
|
||||||
|
|
||||||
include ':VisionCamera'
|
include ':VisionCamera'
|
||||||
|
@ -7,17 +7,11 @@
|
|||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <JsiWorklet.h>
|
||||||
|
|
||||||
#include "RuntimeDecorator.h"
|
|
||||||
#include "RuntimeManager.h"
|
|
||||||
#include "reanimated-headers/AndroidScheduler.h"
|
|
||||||
#include "reanimated-headers/AndroidErrorHandler.h"
|
|
||||||
|
|
||||||
#include "MakeJSIRuntime.h"
|
|
||||||
#include "CameraView.h"
|
#include "CameraView.h"
|
||||||
#include "FrameHostObject.h"
|
#include "FrameHostObject.h"
|
||||||
#include "JSIJNIConversion.h"
|
#include "JSIJNIConversion.h"
|
||||||
#include "VisionCameraScheduler.h"
|
|
||||||
#include "java-bindings/JImageProxy.h"
|
#include "java-bindings/JImageProxy.h"
|
||||||
#include "java-bindings/JFrameProcessorPlugin.h"
|
#include "java-bindings/JFrameProcessorPlugin.h"
|
||||||
|
|
||||||
@ -28,6 +22,28 @@ using TSelf = local_ref<HybridClass<vision::FrameProcessorRuntimeManager>::jhybr
|
|||||||
using TJSCallInvokerHolder = jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>;
|
using TJSCallInvokerHolder = jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>;
|
||||||
using TAndroidScheduler = jni::alias_ref<VisionCameraScheduler::javaobject>;
|
using TAndroidScheduler = jni::alias_ref<VisionCameraScheduler::javaobject>;
|
||||||
|
|
||||||
|
FrameProcessorRuntimeManager::FrameProcessorRuntimeManager(jni::alias_ref<FrameProcessorRuntimeManager::jhybridobject> jThis,
|
||||||
|
jsi::Runtime* jsRuntime,
|
||||||
|
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
|
||||||
|
std::shared_ptr<vision::VisionCameraScheduler> scheduler) :
|
||||||
|
javaPart_(jni::make_global(jThis)),
|
||||||
|
_jsRuntime(jsRuntime) {
|
||||||
|
auto runOnJS = [jsCallInvoker](std::function<void()>&& f) {
|
||||||
|
// Run on React JS Runtime
|
||||||
|
jsCallInvoker->invokeAsync(std::move(f));
|
||||||
|
};
|
||||||
|
auto runOnWorklet = [scheduler](std::function<void()>&& f) {
|
||||||
|
// Run on Frame Processor Worklet Runtime
|
||||||
|
scheduler->dispatchAsync(std::move(f));
|
||||||
|
};
|
||||||
|
|
||||||
|
_workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera");
|
||||||
|
_workletContext->initialize("VisionCamera",
|
||||||
|
jsRuntime,
|
||||||
|
runOnJS,
|
||||||
|
runOnWorklet);
|
||||||
|
}
|
||||||
|
|
||||||
// JNI binding
|
// JNI binding
|
||||||
void vision::FrameProcessorRuntimeManager::registerNatives() {
|
void vision::FrameProcessorRuntimeManager::registerNatives() {
|
||||||
registerHybrid({
|
registerHybrid({
|
||||||
@ -35,8 +51,6 @@ void vision::FrameProcessorRuntimeManager::registerNatives() {
|
|||||||
FrameProcessorRuntimeManager::initHybrid),
|
FrameProcessorRuntimeManager::initHybrid),
|
||||||
makeNativeMethod("installJSIBindings",
|
makeNativeMethod("installJSIBindings",
|
||||||
FrameProcessorRuntimeManager::installJSIBindings),
|
FrameProcessorRuntimeManager::installJSIBindings),
|
||||||
makeNativeMethod("initializeRuntime",
|
|
||||||
FrameProcessorRuntimeManager::initializeRuntime),
|
|
||||||
makeNativeMethod("registerPlugin",
|
makeNativeMethod("registerPlugin",
|
||||||
FrameProcessorRuntimeManager::registerPlugin),
|
FrameProcessorRuntimeManager::registerPlugin),
|
||||||
});
|
});
|
||||||
@ -52,32 +66,11 @@ TSelf vision::FrameProcessorRuntimeManager::initHybrid(
|
|||||||
"Initializing FrameProcessorRuntimeManager...");
|
"Initializing FrameProcessorRuntimeManager...");
|
||||||
|
|
||||||
// cast from JNI hybrid objects to C++ instances
|
// cast from JNI hybrid objects to C++ instances
|
||||||
auto runtime = reinterpret_cast<jsi::Runtime*>(jsRuntimePointer);
|
auto jsRuntime = reinterpret_cast<jsi::Runtime*>(jsRuntimePointer);
|
||||||
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
|
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
|
||||||
auto scheduler = std::shared_ptr<VisionCameraScheduler>(androidScheduler->cthis());
|
auto scheduler = std::shared_ptr<VisionCameraScheduler>(androidScheduler->cthis());
|
||||||
scheduler->setJSCallInvoker(jsCallInvoker);
|
|
||||||
|
|
||||||
return makeCxxInstance(jThis, runtime, jsCallInvoker, scheduler);
|
return makeCxxInstance(jThis, jsRuntime, jsCallInvoker, scheduler);
|
||||||
}
|
|
||||||
|
|
||||||
void vision::FrameProcessorRuntimeManager::initializeRuntime() {
|
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG,
|
|
||||||
"Initializing Vision JS-Runtime...");
|
|
||||||
|
|
||||||
// create JSI runtime and decorate it
|
|
||||||
auto runtime = makeJSIRuntime();
|
|
||||||
reanimated::RuntimeDecorator::decorateRuntime(*runtime, "FRAME_PROCESSOR");
|
|
||||||
runtime->global().setProperty(*runtime, "_FRAME_PROCESSOR",
|
|
||||||
jsi::Value(true));
|
|
||||||
|
|
||||||
// create REA runtime manager
|
|
||||||
auto errorHandler = std::make_shared<reanimated::AndroidErrorHandler>(scheduler_);
|
|
||||||
_runtimeManager = std::make_unique<reanimated::RuntimeManager>(std::move(runtime),
|
|
||||||
errorHandler,
|
|
||||||
scheduler_);
|
|
||||||
|
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG,
|
|
||||||
"Initialized Vision JS-Runtime!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
global_ref<CameraView::javaobject> FrameProcessorRuntimeManager::findCameraViewById(int viewId) {
|
global_ref<CameraView::javaobject> FrameProcessorRuntimeManager::findCameraViewById(int viewId) {
|
||||||
@ -87,16 +80,11 @@ global_ref<CameraView::javaobject> FrameProcessorRuntimeManager::findCameraViewB
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) {
|
void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) {
|
||||||
if (!this->jsCallInvoker_) {
|
if (!_workletContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Call console.error() on JS Thread
|
||||||
this->jsCallInvoker_->invokeAsync([this, message]() {
|
_workletContext->invokeOnJsThread([message](jsi::Runtime& runtime) {
|
||||||
if (this->runtime_ == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& runtime = *this->runtime_;
|
|
||||||
auto consoleError = runtime
|
auto consoleError = runtime
|
||||||
.global()
|
.global()
|
||||||
.getPropertyAsObject(runtime, "console")
|
.getPropertyAsObject(runtime, "console")
|
||||||
@ -111,42 +99,38 @@ void FrameProcessorRuntimeManager::setFrameProcessor(jsi::Runtime& runtime,
|
|||||||
__android_log_write(ANDROID_LOG_INFO, TAG,
|
__android_log_write(ANDROID_LOG_INFO, TAG,
|
||||||
"Setting new Frame Processor...");
|
"Setting new Frame Processor...");
|
||||||
|
|
||||||
if (!_runtimeManager || !_runtimeManager->runtime) {
|
if (!_workletContext) {
|
||||||
throw jsi::JSError(runtime,
|
throw jsi::JSError(runtime,
|
||||||
"setFrameProcessor(..): VisionCamera's RuntimeManager is not yet initialized!");
|
"setFrameProcessor(..): VisionCamera's Worklet Context is not yet initialized!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// find camera view
|
// find camera view
|
||||||
auto cameraView = findCameraViewById(viewTag);
|
auto cameraView = findCameraViewById(viewTag);
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Found CameraView!");
|
__android_log_write(ANDROID_LOG_INFO, TAG, "Found CameraView!");
|
||||||
|
|
||||||
// convert jsi::Function to a ShareableValue (can be shared across runtimes)
|
// convert jsi::Function to a Worklet (can be shared across runtimes)
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG,
|
__android_log_write(ANDROID_LOG_INFO, TAG, "Creating Worklet...");
|
||||||
"Adapting Shareable value from function (conversion to worklet)...");
|
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, frameProcessor);
|
||||||
auto worklet = reanimated::ShareableValue::adapt(runtime,
|
auto workletInvoker = std::make_shared<RNWorklet::WorkletInvoker>(worklet);
|
||||||
frameProcessor,
|
|
||||||
_runtimeManager.get());
|
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Successfully created worklet!");
|
__android_log_write(ANDROID_LOG_INFO, TAG, "Successfully created worklet!");
|
||||||
|
|
||||||
scheduler_->scheduleOnUI([=]() {
|
_workletContext->invokeOnWorkletThread([=](RNWorklet::JsiWorkletContext*, jsi::Runtime& rt) {
|
||||||
// cast worklet to a jsi::Function for the new runtime
|
// Set Frame Processor as callable C++ lambda - this will then call the Worklet
|
||||||
auto& rt = *_runtimeManager->runtime;
|
cameraView->cthis()->setFrameProcessor([this, workletInvoker, &rt](jni::alias_ref<JImageProxy::javaobject> frame) {
|
||||||
auto function = std::make_shared<jsi::Function>(worklet->getValue(rt).asObject(rt).asFunction(rt));
|
|
||||||
|
|
||||||
// assign lambda to frame processor
|
|
||||||
cameraView->cthis()->setFrameProcessor([this, &rt, function](jni::alias_ref<JImageProxy::javaobject> frame) {
|
|
||||||
try {
|
try {
|
||||||
// create HostObject which holds the Frame (JImageProxy)
|
// create HostObject which holds the Frame (JImageProxy)
|
||||||
auto hostObject = std::make_shared<FrameHostObject>(frame);
|
auto frameHostObject = std::make_shared<FrameHostObject>(frame);
|
||||||
function->callWithThis(rt, *function, jsi::Object::createFromHostObject(rt, hostObject));
|
auto argument = jsi::Object::createFromHostObject(rt, frameHostObject);
|
||||||
|
jsi::Value jsValue(std::move(argument));
|
||||||
|
// Call the Worklet on the Worklet Runtime
|
||||||
|
workletInvoker->call(rt, jsi::Value::undefined(), &jsValue, 1);
|
||||||
} catch (jsi::JSError& jsError) {
|
} catch (jsi::JSError& jsError) {
|
||||||
|
// Worklet threw a JS Error, catch it and log it to JS.
|
||||||
auto message = "Frame Processor threw an error: " + jsError.getMessage();
|
auto message = "Frame Processor threw an error: " + jsError.getMessage();
|
||||||
__android_log_write(ANDROID_LOG_ERROR, TAG, message.c_str());
|
__android_log_write(ANDROID_LOG_ERROR, TAG, message.c_str());
|
||||||
this->logErrorToJS(message);
|
this->logErrorToJS(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor set!");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,13 +150,13 @@ void FrameProcessorRuntimeManager::unsetFrameProcessor(int viewTag) {
|
|||||||
void FrameProcessorRuntimeManager::installJSIBindings() {
|
void FrameProcessorRuntimeManager::installJSIBindings() {
|
||||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Installing JSI bindings...");
|
__android_log_write(ANDROID_LOG_INFO, TAG, "Installing JSI bindings...");
|
||||||
|
|
||||||
if (runtime_ == nullptr) {
|
if (_jsRuntime == nullptr) {
|
||||||
__android_log_write(ANDROID_LOG_ERROR, TAG,
|
__android_log_write(ANDROID_LOG_ERROR, TAG,
|
||||||
"JS-Runtime was null, Frame Processor JSI bindings could not be installed!");
|
"JS-Runtime was null, Frame Processor JSI bindings could not be installed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& jsiRuntime = *runtime_;
|
auto& jsiRuntime = *_jsRuntime;
|
||||||
|
|
||||||
auto setFrameProcessor = [this](jsi::Runtime &runtime,
|
auto setFrameProcessor = [this](jsi::Runtime &runtime,
|
||||||
const jsi::Value &thisValue,
|
const jsi::Value &thisValue,
|
||||||
@ -234,27 +218,26 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FrameProcessorRuntimeManager::registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin) {
|
void FrameProcessorRuntimeManager::registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin) {
|
||||||
// _runtimeManager might never be null, but we can never be too sure.
|
auto& runtime = *_jsRuntime;
|
||||||
if (!_runtimeManager || !_runtimeManager->runtime) {
|
|
||||||
throw std::runtime_error("Tried to register plugin before initializing JS runtime! Call `initializeRuntime()` first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& runtime = *_runtimeManager->runtime;
|
|
||||||
|
|
||||||
// we need a strong reference on the plugin, make_global does that.
|
// we need a strong reference on the plugin, make_global does that.
|
||||||
auto pluginGlobal = make_global(plugin);
|
auto pluginGlobal = make_global(plugin);
|
||||||
// name is always prefixed with two underscores (__)
|
auto pluginName = pluginGlobal->getName();
|
||||||
auto name = "__" + pluginGlobal->getName();
|
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, TAG, "Installing Frame Processor Plugin \"%s\"...", name.c_str());
|
__android_log_print(ANDROID_LOG_INFO, TAG, "Installing Frame Processor Plugin \"%s\"...", pluginName.c_str());
|
||||||
|
|
||||||
auto callback = [pluginGlobal](jsi::Runtime& runtime,
|
if (!runtime.global().hasProperty(runtime, "FrameProcessorPlugins")) {
|
||||||
|
runtime.global().setProperty(runtime, "FrameProcessorPlugins", jsi::Object(runtime));
|
||||||
|
}
|
||||||
|
jsi::Object frameProcessorPlugins = runtime.global().getPropertyAsObject(runtime, "FrameProcessorPlugins");
|
||||||
|
|
||||||
|
auto function = [pluginGlobal](jsi::Runtime& runtime,
|
||||||
const jsi::Value& thisValue,
|
const jsi::Value& thisValue,
|
||||||
const jsi::Value* arguments,
|
const jsi::Value* arguments,
|
||||||
size_t count) -> jsi::Value {
|
size_t count) -> jsi::Value {
|
||||||
// Unbox object and get typed HostObject
|
// Unbox object and get typed HostObject
|
||||||
auto boxedHostObject = arguments[0].asObject(runtime).asHostObject(runtime);
|
auto boxedHostObject = arguments[0].asObject(runtime).asHostObject(runtime);
|
||||||
auto frameHostObject = static_cast<FrameHostObject*>(boxedHostObject.get());
|
auto frameHostObject = dynamic_cast<FrameHostObject*>(boxedHostObject.get());
|
||||||
|
|
||||||
// parse params - we are offset by `1` because the frame is the first parameter.
|
// parse params - we are offset by `1` because the frame is the first parameter.
|
||||||
auto params = JArrayClass<jobject>::newArray(count - 1);
|
auto params = JArrayClass<jobject>::newArray(count - 1);
|
||||||
@ -269,10 +252,14 @@ void FrameProcessorRuntimeManager::registerPlugin(alias_ref<JFrameProcessorPlugi
|
|||||||
return JSIJNIConversion::convertJNIObjectToJSIValue(runtime, result);
|
return JSIJNIConversion::convertJNIObjectToJSIValue(runtime, result);
|
||||||
};
|
};
|
||||||
|
|
||||||
runtime.global().setProperty(runtime, name.c_str(), jsi::Function::createFromHostFunction(runtime,
|
// Assign it to the Proxy.
|
||||||
jsi::PropNameID::forAscii(runtime, name),
|
// A FP Plugin called "example_plugin" can be now called from JS using "FrameProcessorPlugins.example_plugin(frame)"
|
||||||
|
frameProcessorPlugins.setProperty(runtime,
|
||||||
|
pluginName.c_str(),
|
||||||
|
jsi::Function::createFromHostFunction(runtime,
|
||||||
|
jsi::PropNameID::forAscii(runtime, pluginName),
|
||||||
1, // frame
|
1, // frame
|
||||||
callback));
|
function));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vision
|
} // namespace vision
|
||||||
|
@ -9,9 +9,7 @@
|
|||||||
#include <ReactCommon/CallInvokerHolder.h>
|
#include <ReactCommon/CallInvokerHolder.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <JsiWorkletContext.h>
|
||||||
#include "RuntimeManager.h"
|
|
||||||
#include "reanimated-headers/AndroidScheduler.h"
|
|
||||||
|
|
||||||
#include "CameraView.h"
|
#include "CameraView.h"
|
||||||
#include "VisionCameraScheduler.h"
|
#include "VisionCameraScheduler.h"
|
||||||
@ -32,25 +30,17 @@ class FrameProcessorRuntimeManager : public jni::HybridClass<FrameProcessorRunti
|
|||||||
static void registerNatives();
|
static void registerNatives();
|
||||||
|
|
||||||
explicit FrameProcessorRuntimeManager(jni::alias_ref<FrameProcessorRuntimeManager::jhybridobject> jThis,
|
explicit FrameProcessorRuntimeManager(jni::alias_ref<FrameProcessorRuntimeManager::jhybridobject> jThis,
|
||||||
jsi::Runtime* runtime,
|
jsi::Runtime* jsRuntime,
|
||||||
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
|
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
|
||||||
std::shared_ptr<vision::VisionCameraScheduler> scheduler) :
|
std::shared_ptr<vision::VisionCameraScheduler> scheduler);
|
||||||
javaPart_(jni::make_global(jThis)),
|
|
||||||
runtime_(runtime),
|
|
||||||
jsCallInvoker_(jsCallInvoker),
|
|
||||||
scheduler_(scheduler)
|
|
||||||
{}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend HybridBase;
|
friend HybridBase;
|
||||||
jni::global_ref<FrameProcessorRuntimeManager::javaobject> javaPart_;
|
jni::global_ref<FrameProcessorRuntimeManager::javaobject> javaPart_;
|
||||||
jsi::Runtime* runtime_;
|
jsi::Runtime* _jsRuntime;
|
||||||
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
|
std::shared_ptr<RNWorklet::JsiWorkletContext> _workletContext;
|
||||||
std::shared_ptr<reanimated::RuntimeManager> _runtimeManager;
|
|
||||||
std::shared_ptr<vision::VisionCameraScheduler> scheduler_;
|
|
||||||
|
|
||||||
jni::global_ref<CameraView::javaobject> findCameraViewById(int viewId);
|
jni::global_ref<CameraView::javaobject> findCameraViewById(int viewId);
|
||||||
void initializeRuntime();
|
|
||||||
void installJSIBindings();
|
void installJSIBindings();
|
||||||
void registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin);
|
void registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin);
|
||||||
void logErrorToJS(const std::string& message);
|
void logErrorToJS(const std::string& message);
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Marc Rousavy on 06.07.21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jsi/jsi.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#if FOR_HERMES
|
|
||||||
// Hermes
|
|
||||||
#include <hermes/hermes.h>
|
|
||||||
#else
|
|
||||||
// JSC
|
|
||||||
#include <jsi/JSCRuntime.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace vision {
|
|
||||||
|
|
||||||
using namespace facebook;
|
|
||||||
|
|
||||||
static std::unique_ptr<jsi::Runtime> makeJSIRuntime() {
|
|
||||||
#if FOR_HERMES
|
|
||||||
return facebook::hermes::makeHermesRuntime();
|
|
||||||
#else
|
|
||||||
return facebook::jsc::makeJSCRuntime();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace vision
|
|
@ -14,9 +14,9 @@ TSelf VisionCameraScheduler::initHybrid(jni::alias_ref<jhybridobject> jThis) {
|
|||||||
return makeCxxInstance(jThis);
|
return makeCxxInstance(jThis);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisionCameraScheduler::scheduleOnUI(std::function<void()> job) {
|
void VisionCameraScheduler::dispatchAsync(std::function<void()> job) {
|
||||||
// 1. add job to queue
|
// 1. add job to queue
|
||||||
uiJobs.push(job);
|
_jobs.push(job);
|
||||||
scheduleTrigger();
|
scheduleTrigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,16 +26,18 @@ void VisionCameraScheduler::scheduleTrigger() {
|
|||||||
method(javaPart_.get());
|
method(javaPart_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisionCameraScheduler::triggerUI() {
|
void VisionCameraScheduler::trigger() {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
// 3. call job we enqueued in step 1.
|
// 3. call job we enqueued in step 1.
|
||||||
auto job = uiJobs.pop();
|
auto job = _jobs.front();
|
||||||
job();
|
job();
|
||||||
|
_jobs.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisionCameraScheduler::registerNatives() {
|
void VisionCameraScheduler::registerNatives() {
|
||||||
registerHybrid({
|
registerHybrid({
|
||||||
makeNativeMethod("initHybrid", VisionCameraScheduler::initHybrid),
|
makeNativeMethod("initHybrid", VisionCameraScheduler::initHybrid),
|
||||||
makeNativeMethod("triggerUI", VisionCameraScheduler::triggerUI),
|
makeNativeMethod("trigger", VisionCameraScheduler::trigger),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,35 +4,47 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Scheduler.h"
|
|
||||||
#include <ReactCommon/CallInvokerHolder.h>
|
#include <ReactCommon/CallInvokerHolder.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <fbjni/fbjni.h>
|
#include <fbjni/fbjni.h>
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace vision {
|
namespace vision {
|
||||||
|
|
||||||
using namespace facebook;
|
using namespace facebook;
|
||||||
|
|
||||||
class VisionCameraScheduler : public reanimated::Scheduler, public jni::HybridClass<VisionCameraScheduler> {
|
/**
|
||||||
|
* A Scheduler that runs methods on the Frame Processor Thread (which is a Java Thread).
|
||||||
|
* In order to call something on the Java Frame Processor Thread, you have to:
|
||||||
|
*
|
||||||
|
* 1. Call `dispatchAsync(..)` with the given C++ Method.
|
||||||
|
* 2. Internally, `scheduleTrigger()` will get called, which is a Java Method.
|
||||||
|
* 3. The `scheduleTrigger()` Java Method will switch to the Frame Processor Java Thread and call `trigger()` on there
|
||||||
|
* 4. `trigger()` is a C++ function here that just calls the passed C++ Method from step 1.
|
||||||
|
*/
|
||||||
|
class VisionCameraScheduler : public jni::HybridClass<VisionCameraScheduler> {
|
||||||
public:
|
public:
|
||||||
static auto constexpr kJavaDescriptor = "Lcom/mrousavy/camera/frameprocessor/VisionCameraScheduler;";
|
static auto constexpr kJavaDescriptor = "Lcom/mrousavy/camera/frameprocessor/VisionCameraScheduler;";
|
||||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
||||||
static void registerNatives();
|
static void registerNatives();
|
||||||
|
|
||||||
// schedules the given job to be run on the VisionCamera FP Thread at some future point in time
|
// schedules the given job to be run on the VisionCamera FP Thread at some future point in time
|
||||||
void scheduleOnUI(std::function<void()> job) override;
|
void dispatchAsync(std::function<void()> job);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend HybridBase;
|
friend HybridBase;
|
||||||
jni::global_ref<VisionCameraScheduler::javaobject> javaPart_;
|
jni::global_ref<VisionCameraScheduler::javaobject> javaPart_;
|
||||||
|
std::queue<std::function<void()>> _jobs;
|
||||||
|
std::mutex _mutex;
|
||||||
|
|
||||||
explicit VisionCameraScheduler(jni::alias_ref<VisionCameraScheduler::jhybridobject> jThis):
|
explicit VisionCameraScheduler(jni::alias_ref<VisionCameraScheduler::jhybridobject> jThis):
|
||||||
javaPart_(jni::make_global(jThis)) {}
|
javaPart_(jni::make_global(jThis)) {}
|
||||||
|
|
||||||
// Schedules a call to `triggerUI` on the VisionCamera FP Thread
|
// Schedules a call to `trigger` on the VisionCamera FP Thread
|
||||||
void scheduleTrigger();
|
void scheduleTrigger();
|
||||||
// Calls the latest job in the job queue
|
// Calls the latest job in the job queue
|
||||||
void triggerUI() override;
|
void trigger();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace vision
|
} // namespace vision
|
@ -1,30 +0,0 @@
|
|||||||
// copied from https://github.com/software-mansion/react-native-reanimated/blob/master/android/src/main/cpp/headers/AndroidErrorHandler.h
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ErrorHandler.h"
|
|
||||||
#include "AndroidScheduler.h"
|
|
||||||
#include "Scheduler.h"
|
|
||||||
#include <jni.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <fbjni/fbjni.h>
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
namespace reanimated
|
|
||||||
{
|
|
||||||
|
|
||||||
class AndroidErrorHandler : public JavaClass<AndroidErrorHandler>, public ErrorHandler {
|
|
||||||
std::shared_ptr<ErrorWrapper> error;
|
|
||||||
std::shared_ptr<Scheduler> scheduler;
|
|
||||||
void raiseSpec() override;
|
|
||||||
public:
|
|
||||||
static auto constexpr kJavaDescriptor = "Lcom/swmansion/reanimated/AndroidErrorHandler;";
|
|
||||||
AndroidErrorHandler(
|
|
||||||
std::shared_ptr<Scheduler> scheduler);
|
|
||||||
std::shared_ptr<Scheduler> getScheduler() override;
|
|
||||||
std::shared_ptr<ErrorWrapper> getError() override;
|
|
||||||
void setError(std::string message) override;
|
|
||||||
virtual ~AndroidErrorHandler() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// copied from https://github.com/software-mansion/react-native-reanimated/blob/master/android/src/main/cpp/headers/AndroidScheduler.h
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <fbjni/fbjni.h>
|
|
||||||
#include <jsi/jsi.h>
|
|
||||||
#include <react/jni/CxxModuleWrapper.h>
|
|
||||||
#include <react/jni/JMessageQueueThread.h>
|
|
||||||
#include "Scheduler.h"
|
|
||||||
|
|
||||||
namespace reanimated {
|
|
||||||
|
|
||||||
using namespace facebook;
|
|
||||||
|
|
||||||
class AndroidScheduler : public jni::HybridClass<AndroidScheduler> {
|
|
||||||
public:
|
|
||||||
static auto constexpr kJavaDescriptor = "Lcom/swmansion/reanimated/Scheduler;";
|
|
||||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
||||||
static void registerNatives();
|
|
||||||
|
|
||||||
std::shared_ptr<Scheduler> getScheduler() { return scheduler_; }
|
|
||||||
|
|
||||||
void scheduleOnUI();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend HybridBase;
|
|
||||||
|
|
||||||
void triggerUI();
|
|
||||||
|
|
||||||
jni::global_ref<AndroidScheduler::javaobject> javaPart_;
|
|
||||||
std::shared_ptr<Scheduler> scheduler_;
|
|
||||||
|
|
||||||
explicit AndroidScheduler(jni::alias_ref<AndroidScheduler::jhybridobject> jThis);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -210,9 +210,7 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (FrameProcessorRuntimeManager.enableFrameProcessors) {
|
|
||||||
mHybridData = initHybrid()
|
mHybridData = initHybrid()
|
||||||
}
|
|
||||||
|
|
||||||
previewView = PreviewView(context)
|
previewView = PreviewView(context)
|
||||||
previewView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
previewView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||||
|
@ -20,9 +20,6 @@ import com.facebook.react.modules.core.PermissionAwareActivity
|
|||||||
import com.facebook.react.modules.core.PermissionListener
|
import com.facebook.react.modules.core.PermissionListener
|
||||||
import com.facebook.react.uimanager.UIManagerHelper
|
import com.facebook.react.uimanager.UIManagerHelper
|
||||||
import com.facebook.react.bridge.ReactApplicationContext
|
import com.facebook.react.bridge.ReactApplicationContext
|
||||||
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
||||||
import com.mrousavy.camera.CameraView
|
|
||||||
import com.mrousavy.camera.ViewNotFoundError
|
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
|
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
|
||||||
import com.mrousavy.camera.parsers.*
|
import com.mrousavy.camera.parsers.*
|
||||||
@ -55,17 +52,6 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
if (coroutineScope.isActive) {
|
if (coroutineScope.isActive) {
|
||||||
coroutineScope.cancel("CameraViewModule has been destroyed.")
|
coroutineScope.cancel("CameraViewModule has been destroyed.")
|
||||||
}
|
}
|
||||||
frameProcessorManager = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun initialize() {
|
|
||||||
super.initialize()
|
|
||||||
|
|
||||||
if (frameProcessorManager == null) {
|
|
||||||
frameProcessorThread.execute {
|
|
||||||
frameProcessorManager = FrameProcessorRuntimeManager(reactApplicationContext, frameProcessorThread)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCatalystInstanceDestroy() {
|
override fun onCatalystInstanceDestroy() {
|
||||||
@ -165,6 +151,18 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||||
|
fun installFrameProcessorBindings(): Boolean {
|
||||||
|
try {
|
||||||
|
frameProcessorManager = FrameProcessorRuntimeManager(reactApplicationContext, frameProcessorThread)
|
||||||
|
frameProcessorManager!!.installBindings()
|
||||||
|
return true
|
||||||
|
} catch (e: Error) {
|
||||||
|
Log.e(TAG, "Failed to install Frame Processor JSI Bindings!", e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This uses the Camera2 API to list all characteristics of a camera device and therefore doesn't work with Camera1. Find a way to use CameraX for this
|
// TODO: This uses the Camera2 API to list all characteristics of a camera device and therefore doesn't work with Camera1. Find a way to use CameraX for this
|
||||||
// https://issuetracker.google.com/issues/179925896
|
// https://issuetracker.google.com/issues/179925896
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
|
@ -48,6 +48,6 @@ public abstract class FrameProcessorPlugin {
|
|||||||
* @param plugin An instance of a plugin.
|
* @param plugin An instance of a plugin.
|
||||||
*/
|
*/
|
||||||
public static void register(@NonNull FrameProcessorPlugin plugin) {
|
public static void register(@NonNull FrameProcessorPlugin plugin) {
|
||||||
FrameProcessorRuntimeManager.Companion.getPlugins().add(plugin);
|
FrameProcessorRuntimeManager.Companion.addPlugin(plugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,18 +16,20 @@ import java.util.concurrent.ExecutorService
|
|||||||
class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProcessorThread: ExecutorService) {
|
class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProcessorThread: ExecutorService) {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "FrameProcessorRuntime"
|
const val TAG = "FrameProcessorRuntime"
|
||||||
val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
|
private val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
|
||||||
var enableFrameProcessors = true
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
try {
|
try {
|
||||||
System.loadLibrary("reanimated")
|
|
||||||
System.loadLibrary("VisionCamera")
|
System.loadLibrary("VisionCamera")
|
||||||
} catch (e: UnsatisfiedLinkError) {
|
} catch (e: UnsatisfiedLinkError) {
|
||||||
Log.w(TAG, "Failed to load Reanimated/VisionCamera C++ library. Frame Processors are disabled!")
|
Log.e(TAG, "Failed to load VisionCamera C++ library!", e)
|
||||||
enableFrameProcessors = false
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addPlugin(plugin: FrameProcessorPlugin) {
|
||||||
|
Plugins.add(plugin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
@ -36,24 +38,11 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProces
|
|||||||
private var mScheduler: VisionCameraScheduler? = null
|
private var mScheduler: VisionCameraScheduler? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (enableFrameProcessors) {
|
val jsCallInvokerHolder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
|
||||||
val holder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
|
val jsRuntimeHolder = context.javaScriptContextHolder.get()
|
||||||
mScheduler = VisionCameraScheduler(frameProcessorThread)
|
mScheduler = VisionCameraScheduler(frameProcessorThread)
|
||||||
mContext = WeakReference(context)
|
mContext = WeakReference(context)
|
||||||
mHybridData = initHybrid(context.javaScriptContextHolder.get(), holder, mScheduler!!)
|
mHybridData = initHybrid(jsRuntimeHolder, jsCallInvokerHolder, mScheduler!!)
|
||||||
initializeRuntime()
|
|
||||||
|
|
||||||
Log.i(TAG, "Installing Frame Processor Plugins...")
|
|
||||||
Plugins.forEach { plugin ->
|
|
||||||
registerPlugin(plugin)
|
|
||||||
}
|
|
||||||
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
|
|
||||||
|
|
||||||
Log.i(TAG, "Installing JSI Bindings on JS Thread...")
|
|
||||||
context.runOnJSQueueThread {
|
|
||||||
installJSIBindings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -67,13 +56,22 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProces
|
|||||||
return view ?: throw ViewNotFoundError(viewId)
|
return view ?: throw ViewNotFoundError(viewId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun installBindings() {
|
||||||
|
Log.i(TAG, "Installing JSI Bindings on JS Thread...")
|
||||||
|
installJSIBindings()
|
||||||
|
Log.i(TAG, "Installing Frame Processor Plugins...")
|
||||||
|
Plugins.forEach { plugin ->
|
||||||
|
registerPlugin(plugin)
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
|
||||||
|
}
|
||||||
|
|
||||||
// private C++ funcs
|
// private C++ funcs
|
||||||
private external fun initHybrid(
|
private external fun initHybrid(
|
||||||
jsContext: Long,
|
jsContext: Long,
|
||||||
jsCallInvokerHolder: CallInvokerHolderImpl,
|
jsCallInvokerHolder: CallInvokerHolderImpl,
|
||||||
scheduler: VisionCameraScheduler
|
scheduler: VisionCameraScheduler
|
||||||
): HybridData
|
): HybridData
|
||||||
private external fun initializeRuntime()
|
|
||||||
private external fun registerPlugin(plugin: FrameProcessorPlugin)
|
private external fun registerPlugin(plugin: FrameProcessorPlugin)
|
||||||
private external fun installJSIBindings()
|
private external fun installJSIBindings()
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ public class ImageProxyUtils {
|
|||||||
Image image = imageProxy.getImage();
|
Image image = imageProxy.getImage();
|
||||||
if (image == null) return false;
|
if (image == null) return false;
|
||||||
// will throw an exception if the image is already closed
|
// will throw an exception if the image is already closed
|
||||||
imageProxy.getImage().getCropRect();
|
image.getCropRect();
|
||||||
// no exception thrown, image must still be valid.
|
// no exception thrown, image must still be valid.
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -17,11 +17,11 @@ public class VisionCameraScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private native HybridData initHybrid();
|
private native HybridData initHybrid();
|
||||||
private native void triggerUI();
|
private native void trigger();
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
private void scheduleTrigger() {
|
private void scheduleTrigger() {
|
||||||
frameProcessorThread.submit(this::triggerUI);
|
frameProcessorThread.submit(this::trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,17 +48,13 @@ Frame processors are by far not limited to Hotdog detection, other examples incl
|
|||||||
* Creating scanners for **QR codes**, **Barcodes** or even custom codes such as **Snapchat's SnapCodes** or **Apple's AppClips**
|
* Creating scanners for **QR codes**, **Barcodes** or even custom codes such as **Snapchat's SnapCodes** or **Apple's AppClips**
|
||||||
* Creating **snapchat-like filters**, e.g. draw a dog-mask filter over the user's face
|
* Creating **snapchat-like filters**, e.g. draw a dog-mask filter over the user's face
|
||||||
* Creating **color filters** with depth-detection
|
* Creating **color filters** with depth-detection
|
||||||
|
* Drawing boxes, text, overlays, or colors on the screen in realtime
|
||||||
|
* Rendering filters and shaders such as Blur, inverted colors, beauty filter, or more on the screen
|
||||||
|
|
||||||
Because they are written in JS, Frame Processors are **simple**, **powerful**, **extensible** and **easy to create** while still running at **native performance**. (Frame Processors can run up to **1000 times a second**!) Also, you can use **fast-refresh** to quickly see changes while developing or publish [over-the-air updates](https://github.com/microsoft/react-native-code-push) to tweak the Hotdog detector's sensitivity in live apps without pushing a native update.
|
Because they are written in JS, Frame Processors are **simple**, **powerful**, **extensible** and **easy to create** while still running at **native performance**. (Frame Processors can run up to **1000 times a second**!) Also, you can use **fast-refresh** to quickly see changes while developing or publish [over-the-air updates](https://github.com/microsoft/react-native-code-push) to tweak the Hotdog detector's sensitivity in live apps without pushing a native update.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Frame Processors require [**react-native-reanimated**](https://github.com/software-mansion/react-native-reanimated) 2.2.0 or higher. Also make sure to add
|
Frame Processors require [**react-native-worklets**](https://github.com/chrfalch/react-native-worklets) 1.0.0 or higher.
|
||||||
|
|
||||||
```js
|
|
||||||
import 'react-native-reanimated'
|
|
||||||
```
|
|
||||||
|
|
||||||
to the top of the file when using `useFrameProcessor`.
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Interacting with Frame Processors
|
### Interacting with Frame Processors
|
||||||
@ -80,7 +76,7 @@ You can also easily read from, and assign to [**Shared Values**](https://docs.sw
|
|||||||
|
|
||||||
In this example, we detect a cat in the frame - if a cat was found, we assign the `catBounds` Shared Value to the coordinates of the cat (relative to the screen) which we can then use in a `useAnimatedStyle` hook to position the red rectangle surrounding the cat. This updates in realtime on the UI Thread, and can also be smoothly animated with `withSpring` or `withTiming`.
|
In this example, we detect a cat in the frame - if a cat was found, we assign the `catBounds` Shared Value to the coordinates of the cat (relative to the screen) which we can then use in a `useAnimatedStyle` hook to position the red rectangle surrounding the cat. This updates in realtime on the UI Thread, and can also be smoothly animated with `withSpring` or `withTiming`.
|
||||||
|
|
||||||
```tsx {6}
|
```tsx {7}
|
||||||
// represents position of the cat on the screen 🐈
|
// represents position of the cat on the screen 🐈
|
||||||
const catBounds = useSharedValue({ top: 0, left: 0, right: 0, bottom: 0 })
|
const catBounds = useSharedValue({ top: 0, left: 0, right: 0, bottom: 0 })
|
||||||
|
|
||||||
@ -108,18 +104,18 @@ return (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
And you can also call back to the React-JS thread by using [`runOnJS`](https://docs.swmansion.com/react-native-reanimated/docs/api/miscellaneous/runOnJS/):
|
And you can also call back to the React-JS thread by using `createRunInJsFn(...)`:
|
||||||
|
|
||||||
```tsx {9}
|
```tsx {1}
|
||||||
const onQRCodeDetected = useCallback((qrCode: string) => {
|
const onQRCodeDetected = Worklets.createRunInJsFn((qrCode: string) => {
|
||||||
navigation.push("ProductPage", { productId: qrCode })
|
navigation.push("ProductPage", { productId: qrCode })
|
||||||
}, [])
|
})
|
||||||
|
|
||||||
const frameProcessor = useFrameProcessor((frame) => {
|
const frameProcessor = useFrameProcessor((frame) => {
|
||||||
'worklet'
|
'worklet'
|
||||||
const qrCodes = scanQRCodes(frame)
|
const qrCodes = scanQRCodes(frame)
|
||||||
if (qrCodes.length > 0) {
|
if (qrCodes.length > 0) {
|
||||||
runOnJS(onQRCodeDetected)(qrCodes[0])
|
onQRCodeDetected(qrCodes[0])
|
||||||
}
|
}
|
||||||
}, [onQRCodeDetected])
|
}, [onQRCodeDetected])
|
||||||
```
|
```
|
||||||
@ -133,23 +129,6 @@ npm i vision-camera-image-labeler
|
|||||||
cd ios && pod install
|
cd ios && pod install
|
||||||
```
|
```
|
||||||
|
|
||||||
Then add it to your `babel.config.js`. For the Image Labeler, this will be `__labelImage`:
|
|
||||||
|
|
||||||
```js {6}
|
|
||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
'react-native-reanimated/plugin',
|
|
||||||
{
|
|
||||||
globals: ['__labelImage'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note
|
|
||||||
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
|
|
||||||
:::
|
|
||||||
|
|
||||||
That's it! 🎉 Now you can use it:
|
That's it! 🎉 Now you can use it:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@ -192,7 +171,7 @@ If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/e
|
|||||||
|
|
||||||
#### Frame Processors
|
#### Frame Processors
|
||||||
|
|
||||||
**Frame Processors** are JS functions that will be **workletized** using [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated). They are created on a **parallel camera thread** using a separate JavaScript Runtime (_"VisionCamera JS-Runtime"_) and are **invoked synchronously** (using JSI) without ever going over the bridge. In a **Frame Processor** you can write normal JS code, call back to the React-JS Thread (e.g. `setState`), use [Shared Values](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/shared-values/) and call **Frame Processor Plugins**.
|
**Frame Processors** are JS functions that will be **workletized** using [react-native-worklets](https://github.com/chrfalch/react-native-worklets). They are created on a **parallel camera thread** using a separate JavaScript Runtime (_"VisionCamera JS-Runtime"_) and are **invoked synchronously** (using JSI) without ever going over the bridge. In a **Frame Processor** you can write normal JS code, call back to the React-JS Thread (e.g. `setState`), use [Shared Values](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/shared-values/) and call **Frame Processor Plugins**.
|
||||||
|
|
||||||
> See [**the example Frame Processor**](https://github.com/mrousavy/react-native-vision-camera/blob/cf68a4c6476d085ec48fc424a53a96962e0c33f9/example/src/CameraPage.tsx#L199-L203)
|
> See [**the example Frame Processor**](https://github.com/mrousavy/react-native-vision-camera/blob/cf68a4c6476d085ec48fc424a53a96962e0c33f9/example/src/CameraPage.tsx#L199-L203)
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ Returns a `string` in JS:
|
|||||||
```js
|
```js
|
||||||
export function detectObject(frame: Frame): string {
|
export function detectObject(frame: Frame): string {
|
||||||
'worklet'
|
'worklet'
|
||||||
const result = __detectObject(frame)
|
const result = FrameProcessorPlugins.detectObject(frame)
|
||||||
console.log(result) // <-- "cat"
|
console.log(result) // <-- "cat"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -9,36 +9,17 @@ sidebar_label: Finish creating your Frame Processor Plugin
|
|||||||
To make the Frame Processor Plugin available to the Frame Processor Worklet Runtime, create the following wrapper function in JS/TS:
|
To make the Frame Processor Plugin available to the Frame Processor Worklet Runtime, create the following wrapper function in JS/TS:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { Frame } from 'react-native-vision-camera'
|
import { FrameProcessorPlugins, Frame } from 'react-native-vision-camera'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scans QR codes.
|
* Scans QR codes.
|
||||||
*/
|
*/
|
||||||
export function scanQRCodes(frame: Frame): string[] {
|
export function scanQRCodes(frame: Frame): string[] {
|
||||||
'worklet'
|
'worklet'
|
||||||
return __scanQRCodes(frame)
|
return FrameProcessorPlugins.scanQRCodes(frame)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Users will then have to add the Frame Processor Plugin's name to their `babel.config.js`.
|
|
||||||
|
|
||||||
For the QR Code Scanner, this will be `__scanQRCodes`:
|
|
||||||
|
|
||||||
```js {6}
|
|
||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
'react-native-reanimated/plugin',
|
|
||||||
{
|
|
||||||
globals: ['__scanQRCodes'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note
|
|
||||||
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Test it!
|
## Test it!
|
||||||
|
|
||||||
Simply call the wrapper Worklet in your Frame Processor:
|
Simply call the wrapper Worklet in your Frame Processor:
|
||||||
@ -64,11 +45,10 @@ If you want to distribute your Frame Processor Plugin, simply use npm.
|
|||||||
1. Create a blank Native Module using [bob](https://github.com/callstack/react-native-builder-bob) or [create-react-native-module](https://github.com/brodybits/create-react-native-module)
|
1. Create a blank Native Module using [bob](https://github.com/callstack/react-native-builder-bob) or [create-react-native-module](https://github.com/brodybits/create-react-native-module)
|
||||||
2. Name it `vision-camera-plugin-xxxxx` where `xxxxx` is the name of your plugin
|
2. Name it `vision-camera-plugin-xxxxx` where `xxxxx` is the name of your plugin
|
||||||
3. Remove the generated template code from the Example Native Module
|
3. Remove the generated template code from the Example Native Module
|
||||||
4. Add VisionCamera to `peerDependencies`: `"react-native-vision-camera": ">= 2"`
|
4. Add VisionCamera to `peerDependencies`: `"react-native-vision-camera": ">= 3"`
|
||||||
5. Implement the Frame Processor Plugin in the iOS, Android and JS/TS Codebase using the guides above
|
5. Implement the Frame Processor Plugin in the iOS, Android and JS/TS Codebase using the guides above
|
||||||
6. Add installation instructions to the `README.md` to let users know they have to add your frame processor in the `babel.config.js` configuration.
|
6. Publish the plugin to npm. Users will only have to install the plugin using `npm i vision-camera-plugin-xxxxx` and add it to their `babel.config.js` file.
|
||||||
7. Publish the plugin to npm. Users will only have to install the plugin using `npm i vision-camera-plugin-xxxxx` and add it to their `babel.config.js` file.
|
7. [Add the plugin to the **official VisionCamera plugin list**](https://github.com/mrousavy/react-native-vision-camera/edit/main/docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx) for more visibility
|
||||||
8. [Add the plugin to the **official VisionCamera plugin list**](https://github.com/mrousavy/react-native-vision-camera/edit/main/docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx) for more visibility
|
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ public class QRCodeFrameProcessorPlugin extends FrameProcessorPlugin {
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
The JS function name will be equal to the name you pass to the `super(...)` call (with a `__` prefix). Make sure it is unique across other Frame Processor Plugins.
|
The Frame Processor Plugin will be exposed to JS through the `FrameProcessorPlugins` object using the name you pass to the `super(...)` call. In this case, it would be `FrameProcessorPlugins.scanQRCodes(...)`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
4. **Implement your Frame Processing.** See the [Example Plugin (Java)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/android/app/src/main/java/com/mrousavy/camera/example/ExampleFrameProcessorPlugin.java) for reference.
|
4. **Implement your Frame Processing.** See the [Example Plugin (Java)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/android/app/src/main/java/com/mrousavy/camera/example/ExampleFrameProcessorPlugin.java) for reference.
|
||||||
@ -137,7 +137,7 @@ class ExampleFrameProcessorPluginKotlin: FrameProcessorPlugin("scanQRCodes") {
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
The JS function name will be equal to the name you pass to the `FrameProcessorPlugin(...)` call (with a `__` prefix). Make sure it is unique across other Frame Processor Plugins.
|
The Frame Processor Plugin will be exposed to JS through the `FrameProcessorPlugins` object using the name you pass to the `FrameProcessorPlugin(...)` call. In this case, it would be `FrameProcessorPlugins.scanQRCodes(...)`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
4. **Implement your Frame Processing.** See the [Example Plugin (Java)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/android/app/src/main/java/com/mrousavy/camera/example/ExampleFrameProcessorPlugin.java) for reference.
|
4. **Implement your Frame Processing.** See the [Example Plugin (Java)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/android/app/src/main/java/com/mrousavy/camera/example/ExampleFrameProcessorPlugin.java) for reference.
|
||||||
|
@ -63,7 +63,7 @@ VISION_EXPORT_FRAME_PROCESSOR(scanQRCodes)
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
The JS function name will be equal to the Objective-C function name you choose (with a `__` prefix). Make sure it is unique across other Frame Processor Plugins.
|
The Frame Processor Plugin will be exposed to JS through the `FrameProcessorPlugins` object using the same name as the Objective-C function. In this case, it would be `FrameProcessorPlugins.scanQRCodes(...)`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
4. **Implement your Frame Processing.** See the [Example Plugin (Objective-C)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/Example%20Plugin%20%28Objective%2DC%29) for reference.
|
4. **Implement your Frame Processing.** See the [Example Plugin (Objective-C)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/Example%20Plugin%20%28Objective%2DC%29) for reference.
|
||||||
|
@ -12,13 +12,10 @@ These are VisionCamera Frame Processor Plugins created by the community.
|
|||||||
|
|
||||||
```
|
```
|
||||||
npm i vision-camera-xxxxx
|
npm i vision-camera-xxxxx
|
||||||
|
cd ios && pod install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add the native function's name (the one with the `__` prefix) to your `babel.config.js`. (See their README for instructions)
|
2. Rebuild your app and use the plugin
|
||||||
|
|
||||||
:::note
|
|
||||||
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Plugin List
|
## Plugin List
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ expo install react-native-vision-camera
|
|||||||
|
|
||||||
VisionCamera requires **iOS 11 or higher**, and **Android-SDK version 21 or higher**. See [Troubleshooting](/docs/guides/troubleshooting) if you're having installation issues.
|
VisionCamera requires **iOS 11 or higher**, and **Android-SDK version 21 or higher**. See [Troubleshooting](/docs/guides/troubleshooting) if you're having installation issues.
|
||||||
|
|
||||||
> **(Optional)** If you want to use [**Frame Processors**](/docs/guides/frame-processors), you need to install [**react-native-reanimated**](https://github.com/software-mansion/react-native-reanimated) 2.2.0 or higher.
|
> **(Optional)** If you want to use [**Frame Processors**](/docs/guides/frame-processors), you need to install [**react-native-worklets**](https://github.com/chrfalch/react-native-worklets) 1.0.0 or higher.
|
||||||
|
|
||||||
## Updating manifests
|
## Updating manifests
|
||||||
|
|
||||||
|
@ -6,5 +6,5 @@ This is an internal TODO list which I am using to keep track of some of the feat
|
|||||||
* [ ] Allow camera switching (front <-> back) while recording and stich videos together
|
* [ ] Allow camera switching (front <-> back) while recording and stich videos together
|
||||||
* [ ] Make `startRecording()` async. Due to NativeModules limitations, we can only have either one callback or one promise in a native function. For `startRecording()` we need both, since you probably also want to catch any errors that occured during a `startRecording()` call (or wait until the recording has actually started, since this can also take some time)
|
* [ ] Make `startRecording()` async. Due to NativeModules limitations, we can only have either one callback or one promise in a native function. For `startRecording()` we need both, since you probably also want to catch any errors that occured during a `startRecording()` call (or wait until the recording has actually started, since this can also take some time)
|
||||||
* [ ] Return a `jsi::Value` reference for images (`UIImage`/`Bitmap`) on `takePhoto()` and `takeSnapshot()`. This way, we skip the entire file writing and reading, making image capture _a lot_ faster.
|
* [ ] Return a `jsi::Value` reference for images (`UIImage`/`Bitmap`) on `takePhoto()` and `takeSnapshot()`. This way, we skip the entire file writing and reading, making image capture _a lot_ faster.
|
||||||
* [ ] Implement frame processors. The idea here is that the user passes a small JS function (reanimated worklet) to the `Camera::frameProcessor` prop which will then get called on every frame the camera previews. (I'd say we cap it to 30 times per second, even if the camera fps is higher) This can then be used to scan QR codes, detect faces, detect depth, render something ontop of the camera such as color filters, QR code boundaries or even dog filters, possibly even use AR - all from a single, small, and highly flexible JS function!
|
* [ ] Implement frame processors. The idea here is that the user passes a small JS function (worklet) to the `Camera::frameProcessor` prop which will then get called on every frame the camera previews. (I'd say we cap it to 30 times per second, even if the camera fps is higher) This can then be used to scan QR codes, detect faces, detect depth, render something ontop of the camera such as color filters, QR code boundaries or even dog filters, possibly even use AR - all from a single, small, and highly flexible JS function!
|
||||||
* [ ] Create a custom MPEG4 encoder to allow for more customizability in `recordVideo()` (`bitRate`, `priority`, `minQuantizationParameter`, `allowFrameReordering`, `expectedFrameRate`, `realTime`, `minimizeMemoryUsage`)
|
* [ ] Create a custom MPEG4 encoder to allow for more customizability in `recordVideo()` (`bitRate`, `priority`, `minQuantizationParameter`, `allowFrameReordering`, `expectedFrameRate`, `realTime`, `minimizeMemoryUsage`)
|
||||||
|
@ -12,8 +12,6 @@ import com.facebook.react.defaults.DefaultReactNativeHost;
|
|||||||
|
|
||||||
import com.mrousavy.camera.CameraPackage;
|
import com.mrousavy.camera.CameraPackage;
|
||||||
import com.mrousavy.camera.frameprocessor.FrameProcessorPlugin;
|
import com.mrousavy.camera.frameprocessor.FrameProcessorPlugin;
|
||||||
import com.facebook.react.bridge.JSIModulePackage;
|
|
||||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
|
||||||
|
|
||||||
public class MainApplication extends Application implements ReactApplication {
|
public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
@ -47,11 +45,6 @@ public class MainApplication extends Application implements ReactApplication {
|
|||||||
protected Boolean isHermesEnabled() {
|
protected Boolean isHermesEnabled() {
|
||||||
return BuildConfig.IS_HERMES_ENABLED;
|
return BuildConfig.IS_HERMES_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected JSIModulePackage getJSIModulePackage() {
|
|
||||||
return new ReanimatedJSIModulePackage();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,7 +13,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle")
|
classpath('com.android.tools.build:gradle:7.4.1')
|
||||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,8 @@ const pak = require('../package.json');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:metro-react-native-babel-preset'],
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
plugins: [
|
plugins: [
|
||||||
[
|
['react-native-reanimated/plugin'],
|
||||||
'react-native-reanimated/plugin',
|
['react-native-worklets/plugin'],
|
||||||
{
|
|
||||||
globals: ['__example_plugin', '__example_plugin_swift'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'module-resolver',
|
'module-resolver',
|
||||||
{
|
{
|
||||||
|
@ -283,6 +283,10 @@ PODS:
|
|||||||
- react-native-video/Video (= 5.2.1)
|
- react-native-video/Video (= 5.2.1)
|
||||||
- react-native-video/Video (5.2.1):
|
- react-native-video/Video (5.2.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- react-native-worklets (0.1.0):
|
||||||
|
- React
|
||||||
|
- React-callinvoker
|
||||||
|
- React-Core
|
||||||
- React-perflogger (0.71.2)
|
- React-perflogger (0.71.2)
|
||||||
- React-RCTActionSheet (0.71.2):
|
- React-RCTActionSheet (0.71.2):
|
||||||
- React-Core/RCTActionSheetHeaders (= 0.71.2)
|
- React-Core/RCTActionSheetHeaders (= 0.71.2)
|
||||||
@ -369,7 +373,7 @@ PODS:
|
|||||||
- React-perflogger (= 0.71.2)
|
- React-perflogger (= 0.71.2)
|
||||||
- RNGestureHandler (2.9.0):
|
- RNGestureHandler (2.9.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNReanimated (2.14.4):
|
- RNReanimated (3.0.0-rc.10):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- FBLazyVector
|
- FBLazyVector
|
||||||
- FBReactNativeSpec
|
- FBReactNativeSpec
|
||||||
@ -407,6 +411,7 @@ PODS:
|
|||||||
- React
|
- React
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- react-native-worklets
|
||||||
- Yoga (1.14.0)
|
- Yoga (1.14.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
@ -437,6 +442,7 @@ DEPENDENCIES:
|
|||||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||||
- react-native-video (from `../node_modules/react-native-video`)
|
- react-native-video (from `../node_modules/react-native-video`)
|
||||||
|
- react-native-worklets (from `../node_modules/react-native-worklets`)
|
||||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||||
@ -514,6 +520,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/@react-native-community/slider"
|
:path: "../node_modules/@react-native-community/slider"
|
||||||
react-native-video:
|
react-native-video:
|
||||||
:path: "../node_modules/react-native-video"
|
:path: "../node_modules/react-native-video"
|
||||||
|
react-native-worklets:
|
||||||
|
:path: "../node_modules/react-native-worklets"
|
||||||
React-perflogger:
|
React-perflogger:
|
||||||
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
|
:path: "../node_modules/react-native/ReactCommon/reactperflogger"
|
||||||
React-RCTActionSheet:
|
React-RCTActionSheet:
|
||||||
@ -583,6 +591,7 @@ SPEC CHECKSUMS:
|
|||||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||||
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
|
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
|
||||||
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
|
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
|
||||||
|
react-native-worklets: c7576ad4ad0f030ff41e8d74ad0077c96054a6c1
|
||||||
React-perflogger: c7ccda3d1d1da837f7ff4e54e816022a6803ee87
|
React-perflogger: c7ccda3d1d1da837f7ff4e54e816022a6803ee87
|
||||||
React-RCTActionSheet: 01c125aebbad462a24228f68c584c7a921d6c28e
|
React-RCTActionSheet: 01c125aebbad462a24228f68c584c7a921d6c28e
|
||||||
React-RCTAnimation: 5277a9440acffc4a5b7baa6ae3880fe467277ae6
|
React-RCTAnimation: 5277a9440acffc4a5b7baa6ae3880fe467277ae6
|
||||||
@ -597,11 +606,11 @@ SPEC CHECKSUMS:
|
|||||||
React-runtimeexecutor: 4bf9a9086d27f74065fce1dddac274aa95216952
|
React-runtimeexecutor: 4bf9a9086d27f74065fce1dddac274aa95216952
|
||||||
ReactCommon: f697c0ac52e999aa818e43e2b6f277787c735e2d
|
ReactCommon: f697c0ac52e999aa818e43e2b6f277787c735e2d
|
||||||
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
||||||
RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128
|
RNReanimated: fbc356493970e3acddc15586b1bccb5eab3ff1ec
|
||||||
RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb
|
RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb
|
||||||
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
||||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||||
VisionCamera: b9345e7da5eff343cc1603dd19153e2b9acd0c07
|
VisionCamera: 312151eb95370d1d764720de3b7dad33d8c7fb40
|
||||||
Yoga: 5b0304b3dbef2b52e078052138e23a19c7dacaef
|
Yoga: 5b0304b3dbef2b52e078052138e23a19c7dacaef
|
||||||
|
|
||||||
PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636
|
PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
"typescript": "tsc --noEmit"
|
"typescript": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-community/blur": "^4.3.0",
|
|
||||||
"@react-native-camera-roll/camera-roll": "^5.2.3",
|
"@react-native-camera-roll/camera-roll": "^5.2.3",
|
||||||
|
"@react-native-community/blur": "^4.3.0",
|
||||||
"@react-native-community/slider": "^4.4.2",
|
"@react-native-community/slider": "^4.4.2",
|
||||||
"@react-navigation/native": "^6.1.3",
|
"@react-navigation/native": "^6.1.3",
|
||||||
"@react-navigation/native-stack": "^6.9.9",
|
"@react-navigation/native-stack": "^6.9.9",
|
||||||
@ -23,12 +23,13 @@
|
|||||||
"react-native": "^0.71.2",
|
"react-native": "^0.71.2",
|
||||||
"react-native-gesture-handler": "^2.9.0",
|
"react-native-gesture-handler": "^2.9.0",
|
||||||
"react-native-pressable-opacity": "^1.0.10",
|
"react-native-pressable-opacity": "^1.0.10",
|
||||||
"react-native-reanimated": "^2.14.4",
|
"react-native-reanimated": "^3.0.0-rc.10",
|
||||||
"react-native-safe-area-context": "^4.5.0",
|
"react-native-safe-area-context": "^4.5.0",
|
||||||
"react-native-screens": "^3.19.0",
|
"react-native-screens": "^3.19.0",
|
||||||
"react-native-static-safe-area-insets": "^2.2.0",
|
"react-native-static-safe-area-insets": "^2.2.0",
|
||||||
"react-native-vector-icons": "^9.2.0",
|
"react-native-vector-icons": "^9.2.0",
|
||||||
"react-native-video": "^5.2.1"
|
"react-native-video": "^5.2.1",
|
||||||
|
"react-native-worklets": "https://github.com/chrfalch/react-native-worklets#50950aa"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.12",
|
"@babel/core": "^7.20.12",
|
||||||
|
@ -6,6 +6,7 @@ import { MediaPage } from './MediaPage';
|
|||||||
import { CameraPage } from './CameraPage';
|
import { CameraPage } from './CameraPage';
|
||||||
import type { Routes } from './Routes';
|
import type { Routes } from './Routes';
|
||||||
import { Camera, CameraPermissionStatus } from 'react-native-vision-camera';
|
import { Camera, CameraPermissionStatus } from 'react-native-vision-camera';
|
||||||
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Routes>();
|
const Stack = createNativeStackNavigator<Routes>();
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ export function App(): React.ReactElement | null {
|
|||||||
const showPermissionsPage = cameraPermission !== 'authorized' || microphonePermission === 'not-determined';
|
const showPermissionsPage = cameraPermission !== 'authorized' || microphonePermission === 'not-determined';
|
||||||
return (
|
return (
|
||||||
<NavigationContainer>
|
<NavigationContainer>
|
||||||
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||||
<Stack.Navigator
|
<Stack.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
@ -46,6 +48,7 @@ export function App(): React.ReactElement | null {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
|
</GestureHandlerRootView>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
/* global __example_plugin __example_plugin_swift */
|
import { FrameProcessorPlugins, Frame } from 'react-native-vision-camera';
|
||||||
import type { Frame } from 'react-native-vision-camera';
|
|
||||||
|
|
||||||
declare let _WORKLET: true | undefined;
|
|
||||||
|
|
||||||
export function examplePluginSwift(frame: Frame): string[] {
|
export function examplePluginSwift(frame: Frame): string[] {
|
||||||
'worklet';
|
'worklet';
|
||||||
if (!_WORKLET) throw new Error('examplePluginSwift must be called from a frame processor!');
|
|
||||||
|
|
||||||
// @ts-expect-error because this function is dynamically injected by VisionCamera
|
// @ts-expect-error because this function is dynamically injected by VisionCamera
|
||||||
return __example_plugin_swift(frame, 'hello!', 'parameter2', true, 42, { test: 0, second: 'test' }, ['another test', 5]);
|
return FrameProcessorPlugins.example_plugin_swift(frame, 'hello!', 'parameter2', true, 42, { test: 0, second: 'test' }, [
|
||||||
|
'another test',
|
||||||
|
5,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function examplePlugin(frame: Frame): string[] {
|
export function examplePlugin(frame: Frame): string[] {
|
||||||
'worklet';
|
'worklet';
|
||||||
if (!_WORKLET) throw new Error('examplePlugin must be called from a frame processor!');
|
|
||||||
|
|
||||||
// @ts-expect-error because this function is dynamically injected by VisionCamera
|
// @ts-expect-error because this function is dynamically injected by VisionCamera
|
||||||
return __example_plugin(frame, 'hello!', 'parameter2', true, 42, { test: 0, second: 'test' }, ['another test', 5]);
|
return FrameProcessorPlugins.example_plugin(frame, 'hello!', 'parameter2', true, 42, { test: 0, second: 'test' }, ['another test', 5]);
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1190,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
"@sideway/formula@^3.0.0":
|
"@sideway/formula@^3.0.1":
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||||
@ -2259,9 +2259,9 @@ ee-first@1.1.1:
|
|||||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||||
|
|
||||||
electron-to-chromium@^1.4.284:
|
electron-to-chromium@^1.4.284:
|
||||||
version "1.4.292"
|
version "1.4.294"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.292.tgz#e3a3dca3780c8ce01e2c1866b5ec2fbe31c423e3"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.294.tgz#ad80317b85f0859a9454680fbc1c726fefa7e6fd"
|
||||||
integrity sha512-ESWOSyJy5odDlE8wvh5NNAMORv4r6assPwIPGHEMWrWD0SONXcG/xT+9aD9CQyeRwyYDPo6dJT4Bbeg5uevVQQ==
|
integrity sha512-PuHZB3jEN7D8WPPjLmBQAsqQz8tWHlkkB4n0E2OYw8RwVdmBYV0Wn+rUFH8JqYyIRb4HQhhedgxlZL163wqLrQ==
|
||||||
|
|
||||||
eme-encryption-scheme-polyfill@^2.0.1:
|
eme-encryption-scheme-polyfill@^2.0.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
@ -2884,7 +2884,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
|
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
|
||||||
integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
|
integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
|
||||||
@ -3162,11 +3162,11 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
|||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3"
|
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
||||||
integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==
|
integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.2.0"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
@ -3550,14 +3550,14 @@ jest-worker@^27.2.0:
|
|||||||
supports-color "^8.0.0"
|
supports-color "^8.0.0"
|
||||||
|
|
||||||
joi@^17.2.1:
|
joi@^17.2.1:
|
||||||
version "17.7.0"
|
version "17.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3"
|
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.1.tgz#854fc85c7fa3cfc47c91124d30bffdbb58e06cec"
|
||||||
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==
|
integrity sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
"@hapi/topo" "^5.0.0"
|
"@hapi/topo" "^5.0.0"
|
||||||
"@sideway/address" "^4.1.3"
|
"@sideway/address" "^4.1.3"
|
||||||
"@sideway/formula" "^3.0.0"
|
"@sideway/formula" "^3.0.1"
|
||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
js-sdsl@^4.1.4:
|
js-sdsl@^4.1.4:
|
||||||
@ -4444,9 +4444,9 @@ minimatch@^5.0.1:
|
|||||||
brace-expansion "^2.0.1"
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimist@^1.2.6:
|
minimist@^1.2.6:
|
||||||
version "1.2.7"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||||
|
|
||||||
mixin-deep@^1.2.0:
|
mixin-deep@^1.2.0:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
@ -4984,9 +4984,9 @@ range-parser@~1.2.1:
|
|||||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||||
|
|
||||||
react-devtools-core@^4.26.1:
|
react-devtools-core@^4.26.1:
|
||||||
version "4.27.1"
|
version "4.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3"
|
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148"
|
||||||
integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w==
|
integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g==
|
||||||
dependencies:
|
dependencies:
|
||||||
shell-quote "^1.6.1"
|
shell-quote "^1.6.1"
|
||||||
ws "^7"
|
ws "^7"
|
||||||
@ -5042,10 +5042,10 @@ react-native-pressable-opacity@^1.0.10:
|
|||||||
resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d"
|
resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d"
|
||||||
integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w==
|
integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w==
|
||||||
|
|
||||||
react-native-reanimated@^2.14.4:
|
react-native-reanimated@^3.0.0-rc.10:
|
||||||
version "2.14.4"
|
version "3.0.0-rc.10"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz#3fa3da4e7b99f5dfb28f86bcf24d9d1024d38836"
|
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.0.0-rc.10.tgz#40e5b628759aa81f94317fd0301231292b4eacf5"
|
||||||
integrity sha512-DquSbl7P8j4SAmc+kRdd75Ianm8G+IYQ9T4AQ6lrpLVeDkhZmjWI0wkutKWnp6L7c5XNVUrFDUf69dwETLCItQ==
|
integrity sha512-0P2jSO+dXHRxSzqSxNp08VaUy89nqeUIvqBS0wlI8lsli8CJcqulL3pjNqTGzBkxXjt13mGdIzJv4u9lSjHPzg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/plugin-transform-object-assign" "^7.16.7"
|
"@babel/plugin-transform-object-assign" "^7.16.7"
|
||||||
"@babel/preset-typescript" "^7.16.7"
|
"@babel/preset-typescript" "^7.16.7"
|
||||||
@ -5091,6 +5091,10 @@ react-native-video@^5.2.1:
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
shaka-player "^2.5.9"
|
shaka-player "^2.5.9"
|
||||||
|
|
||||||
|
"react-native-worklets@https://github.com/chrfalch/react-native-worklets#50950aa":
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://github.com/chrfalch/react-native-worklets#50950aa1b671a0d8a9e79878e63a3445991e7192"
|
||||||
|
|
||||||
react-native@^0.71.2:
|
react-native@^0.71.2:
|
||||||
version "0.71.2"
|
version "0.71.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9"
|
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9"
|
||||||
|
@ -19,12 +19,6 @@
|
|||||||
#import "RCTBridge+runOnJS.h"
|
#import "RCTBridge+runOnJS.h"
|
||||||
#import "JSConsoleHelper.h"
|
#import "JSConsoleHelper.h"
|
||||||
|
|
||||||
#ifdef VISION_CAMERA_DISABLE_FRAME_PROCESSORS
|
|
||||||
static bool VISION_CAMERA_ENABLE_FRAME_PROCESSORS = false;
|
|
||||||
#else
|
|
||||||
static bool VISION_CAMERA_ENABLE_FRAME_PROCESSORS = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@interface CameraBridge: RCTViewManager
|
@interface CameraBridge: RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -29,10 +29,10 @@ extension CameraView {
|
|||||||
|
|
||||||
internal func updateOrientation() {
|
internal func updateOrientation() {
|
||||||
// Updates the Orientation for all rotable
|
// Updates the Orientation for all rotable
|
||||||
let isMirrored = self.videoDeviceInput?.device.position == .front
|
let isMirrored = videoDeviceInput?.device.position == .front
|
||||||
|
|
||||||
let connectionOrientation = self.outputOrientation
|
let connectionOrientation = outputOrientation
|
||||||
self.captureSession.outputs.forEach { output in
|
captureSession.outputs.forEach { output in
|
||||||
output.connections.forEach { connection in
|
output.connections.forEach { connection in
|
||||||
if connection.isVideoMirroringSupported {
|
if connection.isVideoMirroringSupported {
|
||||||
connection.automaticallyAdjustsVideoMirroring = false
|
connection.automaticallyAdjustsVideoMirroring = false
|
||||||
|
@ -60,6 +60,8 @@ RCT_EXTERN_METHOD(stopRecording:(nonnull NSNumber *)node resolve:(RCTPromiseReso
|
|||||||
RCT_EXTERN_METHOD(takePhoto:(nonnull NSNumber *)node options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
RCT_EXTERN_METHOD(takePhoto:(nonnull NSNumber *)node options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
||||||
RCT_EXTERN_METHOD(focus:(nonnull NSNumber *)node point:(NSDictionary *)point resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
RCT_EXTERN_METHOD(focus:(nonnull NSNumber *)node point:(NSDictionary *)point resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
||||||
|
|
||||||
|
// Static Methods
|
||||||
|
RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(installFrameProcessorBindings);
|
||||||
RCT_EXTERN_METHOD(getAvailableVideoCodecs:(nonnull NSNumber *)node fileType:(NSString *)fileType resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
RCT_EXTERN_METHOD(getAvailableVideoCodecs:(nonnull NSNumber *)node fileType:(NSString *)fileType resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -15,20 +15,6 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
|
|
||||||
private var runtimeManager: FrameProcessorRuntimeManager?
|
private var runtimeManager: FrameProcessorRuntimeManager?
|
||||||
|
|
||||||
override var bridge: RCTBridge! {
|
|
||||||
didSet {
|
|
||||||
// Install Frame Processor bindings and setup Runtime
|
|
||||||
if VISION_CAMERA_ENABLE_FRAME_PROCESSORS {
|
|
||||||
CameraQueues.frameProcessorQueue.async {
|
|
||||||
self.runtimeManager = FrameProcessorRuntimeManager(bridge: self.bridge)
|
|
||||||
self.bridge.runOnJS {
|
|
||||||
self.runtimeManager!.installFrameProcessorBindings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var methodQueue: DispatchQueue! {
|
override var methodQueue: DispatchQueue! {
|
||||||
return DispatchQueue.main
|
return DispatchQueue.main
|
||||||
}
|
}
|
||||||
@ -43,6 +29,14 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
|
|
||||||
// pragma MARK: React Functions
|
// pragma MARK: React Functions
|
||||||
|
|
||||||
|
@objc
|
||||||
|
final func installFrameProcessorBindings() -> NSNumber {
|
||||||
|
// Runs on JS Thread
|
||||||
|
runtimeManager = FrameProcessorRuntimeManager()
|
||||||
|
runtimeManager!.installFrameProcessorBindings()
|
||||||
|
return NSNumber(booleanLiteral: true)
|
||||||
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
|
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
|
||||||
let component = getCameraView(withTag: node)
|
let component = getCameraView(withTag: node)
|
||||||
|
@ -28,14 +28,13 @@
|
|||||||
* * Make sure your frame processor returns a Value that can be converted to JS
|
* * Make sure your frame processor returns a Value that can be converted to JS
|
||||||
* * Make sure to use this Macro in an @implementation, not @interface
|
* * Make sure to use this Macro in an @implementation, not @interface
|
||||||
*
|
*
|
||||||
* The JS function will have the same name as the given Objective-C function, but with a "__" prefix.
|
* The JS function will have the same name as the given Objective-C function. It can be accessed through the FrameProcessorPlugins object exposed by VisionCamera.
|
||||||
* Make sure to add that function to the babel.config.js under reanimated's "globals" option, and add TypeScript type declarations.
|
|
||||||
*/
|
*/
|
||||||
#define VISION_EXPORT_FRAME_PROCESSOR(frame_processor) \
|
#define VISION_EXPORT_FRAME_PROCESSOR(frame_processor) \
|
||||||
\
|
\
|
||||||
+(void)load \
|
+(void)load \
|
||||||
{ \
|
{ \
|
||||||
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@"__" @ #frame_processor callback:^id(Frame* frame, NSArray<id>* args) { \
|
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@ #frame_processor callback:^id(Frame* frame, NSArray<id>* args) { \
|
||||||
return frame_processor(frame, args); \
|
return frame_processor(frame, args); \
|
||||||
}]; \
|
}]; \
|
||||||
}
|
}
|
||||||
@ -55,7 +54,7 @@ objc_name : NSObject<FrameProcessorPluginBase>
|
|||||||
\
|
\
|
||||||
+(void)load \
|
+(void)load \
|
||||||
{ \
|
{ \
|
||||||
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@"__" @ #name callback:^id(Frame* frame, NSArray<id>* args) { \
|
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@ #name callback:^id(Frame* frame, NSArray<id>* args) { \
|
||||||
return [objc_name callback:frame withArgs:args]; \
|
return [objc_name callback:frame withArgs:args]; \
|
||||||
}]; \
|
}]; \
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,7 @@
|
|||||||
|
|
||||||
@interface FrameProcessorRuntimeManager : NSObject
|
@interface FrameProcessorRuntimeManager : NSObject
|
||||||
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
- (instancetype) init;
|
||||||
|
|
||||||
/**
|
|
||||||
Initializes the Frame Processor Runtime Manager with the given bridge.
|
|
||||||
This init is not thread safe, so only init this on the Thread you want the runtime to run on.
|
|
||||||
*/
|
|
||||||
- (instancetype) initWithBridge:(RCTBridge*)bridge;
|
|
||||||
|
|
||||||
- (void) installFrameProcessorBindings;
|
- (void) installFrameProcessorBindings;
|
||||||
|
|
||||||
|
@ -19,25 +19,12 @@
|
|||||||
#import <React/RCTUIManager.h>
|
#import <React/RCTUIManager.h>
|
||||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||||
|
|
||||||
#ifndef VISION_CAMERA_DISABLE_FRAME_PROCESSORS
|
#import "JsiWorkletContext.h"
|
||||||
#if __has_include(<RNReanimated/NativeReanimatedModule.h>)
|
#import "JsiWorkletApi.h"
|
||||||
#if __has_include(<RNReanimated/RuntimeManager.h>)
|
#import "JsiWorklet.h"
|
||||||
#import <RNReanimated/RuntimeManager.h>
|
|
||||||
#import <RNReanimated/RuntimeDecorator.h>
|
|
||||||
#import <RNReanimated/REAIOSErrorHandler.h>
|
|
||||||
#import "VisionCameraScheduler.h"
|
|
||||||
#define ENABLE_FRAME_PROCESSORS
|
|
||||||
#else
|
|
||||||
#warning Your react-native-reanimated version is not compatible with VisionCamera, Frame Processors are disabled. Make sure you're using reanimated 2.2.0 or above!
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#warning The NativeReanimatedModule.h header could not be found, Frame Processors are disabled. If you want to use Frame Processors, make sure you install react-native-reanimated!
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import "FrameProcessorUtils.h"
|
#import "FrameProcessorUtils.h"
|
||||||
#import "FrameProcessorCallback.h"
|
#import "FrameProcessorCallback.h"
|
||||||
#import "../React Utils/MakeJSIRuntime.h"
|
|
||||||
#import "../React Utils/JSIUtils.h"
|
#import "../React Utils/JSIUtils.h"
|
||||||
|
|
||||||
// Forward declarations for the Swift classes
|
// Forward declarations for the Swift classes
|
||||||
@ -51,84 +38,101 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation FrameProcessorRuntimeManager {
|
@implementation FrameProcessorRuntimeManager {
|
||||||
#ifdef ENABLE_FRAME_PROCESSORS
|
std::shared_ptr<RNWorklet::JsiWorkletContext> workletContext;
|
||||||
std::unique_ptr<reanimated::RuntimeManager> runtimeManager;
|
|
||||||
#endif
|
|
||||||
__weak RCTBridge* weakBridge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype) initWithBridge:(RCTBridge*)bridge {
|
- (instancetype)init {
|
||||||
self = [super init];
|
if (self = [super init]) {
|
||||||
if (self) {
|
// Initialize self
|
||||||
#ifdef ENABLE_FRAME_PROCESSORS
|
|
||||||
NSLog(@"FrameProcessorBindings: Creating Runtime Manager...");
|
|
||||||
weakBridge = bridge;
|
|
||||||
|
|
||||||
auto runtime = vision::makeJSIRuntime();
|
|
||||||
reanimated::RuntimeDecorator::decorateRuntime(*runtime, "FRAME_PROCESSOR");
|
|
||||||
runtime->global().setProperty(*runtime, "_FRAME_PROCESSOR", jsi::Value(true));
|
|
||||||
|
|
||||||
auto callInvoker = bridge.jsCallInvoker;
|
|
||||||
auto scheduler = std::make_shared<vision::VisionCameraScheduler>(callInvoker);
|
|
||||||
runtimeManager = std::make_unique<reanimated::RuntimeManager>(std::move(runtime),
|
|
||||||
std::make_shared<reanimated::REAIOSErrorHandler>(scheduler),
|
|
||||||
scheduler);
|
|
||||||
NSLog(@"FrameProcessorBindings: Runtime Manager created!");
|
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugins...");
|
|
||||||
auto& visionRuntime = *runtimeManager->runtime;
|
|
||||||
auto visionGlobal = visionRuntime.global();
|
|
||||||
|
|
||||||
for (NSString* pluginKey in [FrameProcessorPluginRegistry frameProcessorPlugins]) {
|
|
||||||
auto pluginName = [pluginKey UTF8String];
|
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
|
|
||||||
FrameProcessorPlugin callback = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
|
|
||||||
|
|
||||||
auto function = [callback, callInvoker](jsi::Runtime& runtime,
|
|
||||||
const jsi::Value& thisValue,
|
|
||||||
const jsi::Value* arguments,
|
|
||||||
size_t count) -> jsi::Value {
|
|
||||||
auto frameHostObject = arguments[0].asObject(runtime).asHostObject(runtime);
|
|
||||||
auto frame = static_cast<FrameHostObject*>(frameHostObject.get());
|
|
||||||
|
|
||||||
auto args = convertJSICStyleArrayToNSArray(runtime,
|
|
||||||
arguments + 1, // start at index 1 since first arg = Frame
|
|
||||||
count - 1, // use smaller count
|
|
||||||
callInvoker);
|
|
||||||
id result = callback(frame->frame, args);
|
|
||||||
|
|
||||||
return convertObjCObjectToJSIValue(runtime, result);
|
|
||||||
};
|
|
||||||
|
|
||||||
visionGlobal.setProperty(visionRuntime, pluginName, jsi::Function::createFromHostFunction(visionRuntime,
|
|
||||||
jsi::PropNameID::forAscii(visionRuntime, pluginName),
|
|
||||||
1, // frame
|
|
||||||
function));
|
|
||||||
}
|
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Frame Processor plugins installed!");
|
|
||||||
#else
|
|
||||||
NSLog(@"Reanimated not found, Frame Processors are disabled.");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) installFrameProcessorBindings {
|
- (void) setupWorkletContext:(jsi::Runtime&)runtime {
|
||||||
#ifdef ENABLE_FRAME_PROCESSORS
|
NSLog(@"FrameProcessorBindings: Creating Worklet Context...");
|
||||||
if (!weakBridge) {
|
|
||||||
NSLog(@"FrameProcessorBindings: Failed to install Frame Processor Bindings - bridge was null!");
|
auto callInvoker = RCTBridge.currentBridge.jsCallInvoker;
|
||||||
return;
|
|
||||||
|
auto runOnJS = [callInvoker](std::function<void()>&& f) {
|
||||||
|
// Run on React JS Runtime
|
||||||
|
callInvoker->invokeAsync(std::move(f));
|
||||||
|
};
|
||||||
|
auto runOnWorklet = [](std::function<void()>&& f) {
|
||||||
|
// Run on Frame Processor Worklet Runtime
|
||||||
|
dispatch_async(CameraQueues.frameProcessorQueue, [f = std::move(f)](){
|
||||||
|
f();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera");
|
||||||
|
workletContext->initialize("VisionCamera",
|
||||||
|
&runtime,
|
||||||
|
runOnJS,
|
||||||
|
runOnWorklet);
|
||||||
|
|
||||||
|
NSLog(@"FrameProcessorBindings: Worklet Context Created!");
|
||||||
|
|
||||||
|
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugins...");
|
||||||
|
|
||||||
|
jsi::Object frameProcessorPlugins(runtime);
|
||||||
|
|
||||||
|
// Iterate through all registered plugins (+init)
|
||||||
|
for (NSString* pluginKey in [FrameProcessorPluginRegistry frameProcessorPlugins]) {
|
||||||
|
auto pluginName = [pluginKey UTF8String];
|
||||||
|
|
||||||
|
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
|
||||||
|
// Get the Plugin callback func
|
||||||
|
FrameProcessorPlugin callback = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
|
||||||
|
|
||||||
|
// Create the JSI host function
|
||||||
|
auto function = [callback, callInvoker](jsi::Runtime& runtime,
|
||||||
|
const jsi::Value& thisValue,
|
||||||
|
const jsi::Value* arguments,
|
||||||
|
size_t count) -> jsi::Value {
|
||||||
|
// Get the first parameter, which is always the native Frame Host Object.
|
||||||
|
auto frameHostObject = arguments[0].asObject(runtime).asHostObject(runtime);
|
||||||
|
auto frame = static_cast<FrameHostObject*>(frameHostObject.get());
|
||||||
|
|
||||||
|
// Convert any additional parameters to the Frame Processor to ObjC objects
|
||||||
|
auto args = convertJSICStyleArrayToNSArray(runtime,
|
||||||
|
arguments + 1, // start at index 1 since first arg = Frame
|
||||||
|
count - 1, // use smaller count
|
||||||
|
callInvoker);
|
||||||
|
// Call the FP Plugin, which might return something.
|
||||||
|
id result = callback(frame->frame, args);
|
||||||
|
|
||||||
|
// Convert the return value (or null) to a JS Value and return it to JS
|
||||||
|
return convertObjCObjectToJSIValue(runtime, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assign it to the Proxy.
|
||||||
|
// A FP Plugin called "example_plugin" can be now called from JS using "FrameProcessorPlugins.example_plugin(frame)"
|
||||||
|
frameProcessorPlugins.setProperty(runtime,
|
||||||
|
pluginName,
|
||||||
|
jsi::Function::createFromHostFunction(runtime,
|
||||||
|
jsi::PropNameID::forAscii(runtime, pluginName),
|
||||||
|
1, // frame
|
||||||
|
function));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// global.FrameProcessorPlugins Proxy
|
||||||
|
runtime.global().setProperty(runtime, "FrameProcessorPlugins", frameProcessorPlugins);
|
||||||
|
|
||||||
|
NSLog(@"FrameProcessorBindings: Frame Processor plugins installed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) installFrameProcessorBindings {
|
||||||
NSLog(@"FrameProcessorBindings: Installing Frame Processor Bindings for Bridge...");
|
NSLog(@"FrameProcessorBindings: Installing Frame Processor Bindings for Bridge...");
|
||||||
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)weakBridge;
|
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge];
|
||||||
if (!cxxBridge.runtime) {
|
if (!cxxBridge.runtime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsi::Runtime& jsiRuntime = *(jsi::Runtime*)cxxBridge.runtime;
|
jsi::Runtime& jsiRuntime = *(jsi::Runtime*)cxxBridge.runtime;
|
||||||
|
|
||||||
|
// Install the Worklet Runtime in the main React JS Runtime
|
||||||
|
[self setupWorkletContext:jsiRuntime];
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Installing global functions...");
|
NSLog(@"FrameProcessorBindings: Installing global functions...");
|
||||||
|
|
||||||
// setFrameProcessor(viewTag: number, frameProcessor: (frame: Frame) => void)
|
// setFrameProcessor(viewTag: number, frameProcessor: (frame: Frame) => void)
|
||||||
@ -139,28 +143,22 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
|||||||
NSLog(@"FrameProcessorBindings: Setting new frame processor...");
|
NSLog(@"FrameProcessorBindings: Setting new frame processor...");
|
||||||
if (!arguments[0].isNumber()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: First argument ('viewTag') must be a number!");
|
if (!arguments[0].isNumber()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: First argument ('viewTag') must be a number!");
|
||||||
if (!arguments[1].isObject()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: Second argument ('frameProcessor') must be a function!");
|
if (!arguments[1].isObject()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: Second argument ('frameProcessor') must be a function!");
|
||||||
if (!runtimeManager || !runtimeManager->runtime) throw jsi::JSError(runtime, "Camera::setFrameProcessor: The RuntimeManager is not yet initialized!");
|
|
||||||
|
|
||||||
auto viewTag = arguments[0].asNumber();
|
auto viewTag = arguments[0].asNumber();
|
||||||
NSLog(@"FrameProcessorBindings: Adapting Shareable value from function (conversion to worklet)...");
|
NSLog(@"FrameProcessorBindings: Converting JSI Function to Worklet...");
|
||||||
auto worklet = reanimated::ShareableValue::adapt(runtime, arguments[1], runtimeManager.get());
|
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, arguments[1]);
|
||||||
NSLog(@"FrameProcessorBindings: Successfully created worklet!");
|
|
||||||
|
|
||||||
RCTExecuteOnMainQueue([=]() {
|
RCTExecuteOnMainQueue([=]() {
|
||||||
auto currentBridge = [RCTBridge currentBridge];
|
auto currentBridge = [RCTBridge currentBridge];
|
||||||
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
|
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
|
||||||
auto view = static_cast<CameraView*>(anonymousView);
|
auto view = static_cast<CameraView*>(anonymousView);
|
||||||
|
|
||||||
dispatch_async(CameraQueues.frameProcessorQueue, [=]() {
|
|
||||||
NSLog(@"FrameProcessorBindings: Converting worklet to Objective-C callback...");
|
NSLog(@"FrameProcessorBindings: Converting worklet to Objective-C callback...");
|
||||||
|
|
||||||
auto& rt = *runtimeManager->runtime;
|
view.frameProcessorCallback = convertWorkletToFrameProcessorCallback(workletContext->getWorkletRuntime(), worklet);
|
||||||
auto function = worklet->getValue(rt).asObject(rt).asFunction(rt);
|
|
||||||
|
|
||||||
view.frameProcessorCallback = convertJSIFunctionToFrameProcessorCallback(rt, function);
|
|
||||||
NSLog(@"FrameProcessorBindings: Frame processor set!");
|
NSLog(@"FrameProcessorBindings: Frame processor set!");
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return jsi::Value::undefined();
|
return jsi::Value::undefined();
|
||||||
};
|
};
|
||||||
@ -198,7 +196,6 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
|||||||
unsetFrameProcessor));
|
unsetFrameProcessor));
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Finished installing bindings.");
|
NSLog(@"FrameProcessorBindings: Finished installing bindings.");
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import <jsi/jsi.h>
|
#import <jsi/jsi.h>
|
||||||
|
#import "JsiWorklet.h"
|
||||||
|
#import <memory>
|
||||||
|
|
||||||
using namespace facebook;
|
using namespace facebook;
|
||||||
|
|
||||||
FrameProcessorCallback convertJSIFunctionToFrameProcessorCallback(jsi::Runtime &runtime, const jsi::Function &value);
|
FrameProcessorCallback convertWorkletToFrameProcessorCallback(jsi::Runtime& runtime, std::shared_ptr<RNWorklet::JsiWorklet> worklet);
|
||||||
|
@ -19,14 +19,21 @@
|
|||||||
#import "JSConsoleHelper.h"
|
#import "JSConsoleHelper.h"
|
||||||
#import <ReactCommon/RCTTurboModule.h>
|
#import <ReactCommon/RCTTurboModule.h>
|
||||||
|
|
||||||
FrameProcessorCallback convertJSIFunctionToFrameProcessorCallback(jsi::Runtime& runtime, const jsi::Function& value) {
|
#import "JsiWorklet.h"
|
||||||
__block auto cb = value.getFunction(runtime);
|
|
||||||
|
|
||||||
|
FrameProcessorCallback convertWorkletToFrameProcessorCallback(jsi::Runtime& runtime, std::shared_ptr<RNWorklet::JsiWorklet> worklet) {
|
||||||
|
|
||||||
|
auto workletInvoker = std::make_shared<RNWorklet::WorkletInvoker>(worklet);
|
||||||
|
|
||||||
|
// Converts a Worklet to a callable Objective-C block function
|
||||||
return ^(Frame* frame) {
|
return ^(Frame* frame) {
|
||||||
|
|
||||||
auto frameHostObject = std::make_shared<FrameHostObject>(frame);
|
auto frameHostObject = std::make_shared<FrameHostObject>(frame);
|
||||||
try {
|
try {
|
||||||
cb.callWithThis(runtime, cb, jsi::Object::createFromHostObject(runtime, frameHostObject));
|
// Call JS Frame Processor function with boxed Frame Host Object
|
||||||
|
auto argument = jsi::Object::createFromHostObject(runtime, frameHostObject);
|
||||||
|
jsi::Value jsValue(std::move(argument));
|
||||||
|
workletInvoker->call(runtime, jsi::Value::undefined(), &jsValue, 1);
|
||||||
} catch (jsi::JSError& jsError) {
|
} catch (jsi::JSError& jsError) {
|
||||||
auto stack = std::regex_replace(jsError.getStack(), std::regex("\n"), "\n ");
|
auto stack = std::regex_replace(jsError.getStack(), std::regex("\n"), "\n ");
|
||||||
auto message = [NSString stringWithFormat:@"Frame Processor threw an error: %s\nIn: %s", jsError.getMessage().c_str(), stack.c_str()];
|
auto message = [NSString stringWithFormat:@"Frame Processor threw an error: %s\nIn: %s", jsError.getMessage().c_str(), stack.c_str()];
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
//
|
|
||||||
// VisionCameraScheduler.h
|
|
||||||
// VisionCamera
|
|
||||||
//
|
|
||||||
// Created by Marc Rousavy on 23.07.21.
|
|
||||||
// Copyright © 2021 mrousavy. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#import <React-callinvoker/ReactCommon/CallInvoker.h>
|
|
||||||
|
|
||||||
#if __has_include(<RNReanimated/RuntimeManager.h>)
|
|
||||||
#import <RNReanimated/Scheduler.h>
|
|
||||||
#else
|
|
||||||
// dummy placeholder
|
|
||||||
namespace reanimated {
|
|
||||||
class Scheduler {
|
|
||||||
public:
|
|
||||||
virtual void scheduleOnUI(std::function<void()> job);
|
|
||||||
protected:
|
|
||||||
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace vision {
|
|
||||||
|
|
||||||
class VisionCameraScheduler : public reanimated::Scheduler {
|
|
||||||
public:
|
|
||||||
VisionCameraScheduler(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
|
|
||||||
virtual ~VisionCameraScheduler();
|
|
||||||
|
|
||||||
void scheduleOnUI(std::function<void()> job) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace vision
|
|
@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// VisionCameraScheduler.mm
|
|
||||||
// VisionCamera
|
|
||||||
//
|
|
||||||
// Created by Marc Rousavy on 23.07.21.
|
|
||||||
// Copyright © 2021 mrousavy. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "VisionCameraScheduler.h"
|
|
||||||
|
|
||||||
#import <React-callinvoker/ReactCommon/CallInvoker.h>
|
|
||||||
|
|
||||||
// Forward declarations for the Swift classes
|
|
||||||
__attribute__((objc_runtime_name("_TtC12VisionCamera12CameraQueues")))
|
|
||||||
@interface CameraQueues : NSObject
|
|
||||||
@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull frameProcessorQueue;
|
|
||||||
@end
|
|
||||||
|
|
||||||
namespace vision {
|
|
||||||
|
|
||||||
using namespace facebook;
|
|
||||||
|
|
||||||
VisionCameraScheduler::VisionCameraScheduler(std::shared_ptr<react::CallInvoker> jsInvoker) {
|
|
||||||
this->jsCallInvoker_ = jsInvoker;
|
|
||||||
}
|
|
||||||
|
|
||||||
// does not schedule on UI thread but rather on Frame Processor Thread
|
|
||||||
void VisionCameraScheduler::scheduleOnUI(std::function<void()> job) {
|
|
||||||
dispatch_async(CameraQueues.frameProcessorQueue, ^{
|
|
||||||
job();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
VisionCameraScheduler::~VisionCameraScheduler(){
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace vision
|
|
@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// MakeJSIRuntime.h
|
|
||||||
// VisionCamera
|
|
||||||
//
|
|
||||||
// Created by Marc Rousavy on 06.07.21.
|
|
||||||
// Copyright © 2021 mrousavy. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jsi/jsi.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#if __has_include(<reacthermes/HermesExecutorFactory.h>)
|
|
||||||
// Hermes (https://hermesengine.dev) (RN 0.65+)
|
|
||||||
#include <reacthermes/HermesExecutorFactory.h>
|
|
||||||
#elif __has_include(<hermes/hermes.h>)
|
|
||||||
// Hermes (https://hermesengine.dev)
|
|
||||||
#include <hermes/hermes.h>
|
|
||||||
#elif __has_include(<v8runtime/V8RuntimeFactory.h>)
|
|
||||||
// V8 (https://github.com/Kudo/react-native-v8)
|
|
||||||
#include <v8runtime/V8RuntimeFactory.h>
|
|
||||||
#else
|
|
||||||
// JSC
|
|
||||||
#include <jsi/JSCRuntime.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace facebook;
|
|
||||||
|
|
||||||
namespace vision {
|
|
||||||
|
|
||||||
static std::unique_ptr<jsi::Runtime> makeJSIRuntime() {
|
|
||||||
#if __has_include(<hermes/hermes.h>) || __has_include(<reacthermes/HermesExecutorFactory.h>)
|
|
||||||
return facebook::hermes::makeHermesRuntime();
|
|
||||||
#elif __has_include(<v8runtime/V8RuntimeFactory.h>)
|
|
||||||
return facebook::createV8Runtime("");
|
|
||||||
#else
|
|
||||||
return facebook::jsc::makeJSCRuntime();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace vision
|
|
@ -94,7 +94,6 @@
|
|||||||
B84760A22608EE38004C3180 /* FrameHostObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameHostObject.h; sourceTree = "<group>"; };
|
B84760A22608EE38004C3180 /* FrameHostObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameHostObject.h; sourceTree = "<group>"; };
|
||||||
B84760A52608EE7C004C3180 /* FrameHostObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameHostObject.mm; sourceTree = "<group>"; };
|
B84760A52608EE7C004C3180 /* FrameHostObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameHostObject.mm; sourceTree = "<group>"; };
|
||||||
B84760DE2608F57D004C3180 /* CameraQueues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraQueues.swift; sourceTree = "<group>"; };
|
B84760DE2608F57D004C3180 /* CameraQueues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraQueues.swift; sourceTree = "<group>"; };
|
||||||
B84C10592694A182006EFA70 /* MakeJSIRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakeJSIRuntime.h; sourceTree = "<group>"; };
|
|
||||||
B864004F27849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIInterfaceOrientation+descriptor.swift"; sourceTree = "<group>"; };
|
B864004F27849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIInterfaceOrientation+descriptor.swift"; sourceTree = "<group>"; };
|
||||||
B86400512784A23400E9D2CA /* CameraView+Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Orientation.swift"; sourceTree = "<group>"; };
|
B86400512784A23400E9D2CA /* CameraView+Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Orientation.swift"; sourceTree = "<group>"; };
|
||||||
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAudioSession+trySetAllowHaptics.swift"; sourceTree = "<group>"; };
|
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAudioSession+trySetAllowHaptics.swift"; sourceTree = "<group>"; };
|
||||||
@ -231,7 +230,6 @@
|
|||||||
B8994E6B263F03E100069589 /* JSIUtils.mm */,
|
B8994E6B263F03E100069589 /* JSIUtils.mm */,
|
||||||
B8805065266798AB00EAD7F2 /* JSConsoleHelper.h */,
|
B8805065266798AB00EAD7F2 /* JSConsoleHelper.h */,
|
||||||
B8805066266798B600EAD7F2 /* JSConsoleHelper.mm */,
|
B8805066266798B600EAD7F2 /* JSConsoleHelper.mm */,
|
||||||
B84C10592694A182006EFA70 /* MakeJSIRuntime.h */,
|
|
||||||
);
|
);
|
||||||
path = "React Utils";
|
path = "React Utils";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -82,13 +82,13 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-native": "^0.71.2",
|
"react-native": "^0.71.2",
|
||||||
"react-native-builder-bob": "^0.20.3",
|
"react-native-builder-bob": "^0.20.3",
|
||||||
"react-native-reanimated": "^2.14.4",
|
|
||||||
"release-it": "^15.6.0",
|
"release-it": "^15.6.0",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^4.9.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*",
|
||||||
|
"react-native-worklets": "*"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if which cpplint >/dev/null; then
|
if which cpplint >/dev/null; then
|
||||||
cpplint --linelength=230 --filter=-legal/copyright,-readability/todo,-build/namespaces,-whitespace/comments,-build/include_order,-build/c++11 --quiet --recursive --exclude "android/src/main/cpp/reanimated-headers" cpp android/src/main/cpp
|
cpplint --linelength=230 --filter=-legal/copyright,-readability/todo,-build/namespaces,-whitespace/comments,-build/include_order,-build/c++11 --quiet --recursive cpp android/src/main/cpp
|
||||||
else
|
else
|
||||||
echo "warning: cpplint not installed, download from https://github.com/cpplint/cpplint"
|
echo "warning: cpplint not installed, download from https://github.com/cpplint/cpplint"
|
||||||
fi
|
fi
|
||||||
|
@ -315,6 +315,14 @@ export class Camera extends React.PureComponent<CameraProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Static Functions (NativeModule)
|
//#region Static Functions (NativeModule)
|
||||||
|
/**
|
||||||
|
* Install JSI Bindings for Frame Processors
|
||||||
|
*/
|
||||||
|
public static installFrameProcessorBindings(): void {
|
||||||
|
const result = CameraModule.installFrameProcessorBindings() as unknown;
|
||||||
|
if (result !== true) throw new Error('Failed to install Frame Processor JSI bindings!');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all available camera devices on the current phone.
|
* Get a list of all available camera devices on the current phone.
|
||||||
*
|
*
|
||||||
|
13
src/FrameProcessorPlugins.ts
Normal file
13
src/FrameProcessorPlugins.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { Frame } from './Frame';
|
||||||
|
import { Camera } from './Camera';
|
||||||
|
|
||||||
|
// Install VisionCamera Frame Processor JSI Bindings and Plugins
|
||||||
|
Camera.installFrameProcessorBindings();
|
||||||
|
|
||||||
|
type FrameProcessor = (frame: Frame, ...args: unknown[]) => unknown;
|
||||||
|
type TFrameProcessorPlugins = Record<string, FrameProcessor>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All natively installed Frame Processor Plugins.
|
||||||
|
*/
|
||||||
|
export const FrameProcessorPlugins = global.FrameProcessorPlugins as TFrameProcessorPlugins;
|
22
src/globals.d.ts
vendored
22
src/globals.d.ts
vendored
@ -1,24 +1,6 @@
|
|||||||
/* eslint-disable no-var */
|
/* eslint-disable no-var */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `true` if currently running in a Frame Processor runtime
|
* The global Frame Processor plugins registry - will be initialized after the `installFrameProcessorBindings()` call
|
||||||
*/
|
*/
|
||||||
declare var _FRAME_PROCESSOR: true | undefined;
|
declare var FrameProcessorPlugins: Record<string | symbol, unknown> | undefined;
|
||||||
/**
|
|
||||||
* `true` if currently running in a reanimated UI runtime
|
|
||||||
*/
|
|
||||||
declare var _UI: true | undefined;
|
|
||||||
/**
|
|
||||||
* `true` if currently running in a Worklet runtime (frame processor, multithreading, reanimated)
|
|
||||||
*/
|
|
||||||
declare var _WORKLET: true | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A native logging function (outputs to Xcode console/Android Logcat)
|
|
||||||
*/
|
|
||||||
declare var _log: (message: string) => void | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a Proxy for global.console in this Runtime
|
|
||||||
*/
|
|
||||||
declare var _setGlobalConsole: (console: unknown) => void;
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
/* global _setGlobalConsole */
|
|
||||||
|
|
||||||
import { DependencyList, useCallback } from 'react';
|
import { DependencyList, useCallback } from 'react';
|
||||||
import type { Frame } from '../Frame';
|
import type { Frame } from '../Frame';
|
||||||
|
// Install RN Worklets by importing it
|
||||||
|
import 'react-native-worklets/src';
|
||||||
|
|
||||||
type FrameProcessor = (frame: Frame) => void;
|
type FrameProcessor = (frame: Frame) => void;
|
||||||
|
|
||||||
const capturableConsole = console;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a memoized Frame Processor function wich you can pass to the `<Camera>`. (See ["Frame Processors"](https://mrousavy.github.io/react-native-vision-camera/docs/guides/frame-processors))
|
* Returns a memoized Frame Processor function wich you can pass to the `<Camera>`. (See ["Frame Processors"](https://mrousavy.github.io/react-native-vision-camera/docs/guides/frame-processors))
|
||||||
*
|
*
|
||||||
@ -25,34 +23,6 @@ const capturableConsole = console;
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function useFrameProcessor(frameProcessor: FrameProcessor, dependencies: DependencyList): FrameProcessor {
|
export function useFrameProcessor(frameProcessor: FrameProcessor, dependencies: DependencyList): FrameProcessor {
|
||||||
return useCallback((frame: Frame) => {
|
|
||||||
'worklet';
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
if (global.didSetConsole == null || global.didSetConsole === false) {
|
|
||||||
const console = {
|
|
||||||
// @ts-expect-error __callAsync is injected by native REA
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
debug: capturableConsole.debug.__callAsync,
|
|
||||||
// @ts-expect-error __callAsync is injected by native REA
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
log: capturableConsole.log.__callAsync,
|
|
||||||
// @ts-expect-error __callAsync is injected by native REA
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
warn: capturableConsole.warn.__callAsync,
|
|
||||||
// @ts-expect-error __callAsync is injected by native REA
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
error: capturableConsole.error.__callAsync,
|
|
||||||
// @ts-expect-error __callAsync is injected by native REA
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
info: capturableConsole.info.__callAsync,
|
|
||||||
};
|
|
||||||
_setGlobalConsole(console);
|
|
||||||
// @ts-expect-error
|
|
||||||
global.didSetConsole = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
frameProcessor(frame);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, dependencies);
|
return useCallback(frameProcessor, dependencies);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ export * from './CameraPosition';
|
|||||||
export * from './CameraPreset';
|
export * from './CameraPreset';
|
||||||
export * from './CameraProps';
|
export * from './CameraProps';
|
||||||
export * from './Frame';
|
export * from './Frame';
|
||||||
|
export * from './FrameProcessorPlugins';
|
||||||
export * from './CameraProps';
|
export * from './CameraProps';
|
||||||
export * from './PhotoFile';
|
export * from './PhotoFile';
|
||||||
export * from './Point';
|
export * from './Point';
|
||||||
|
81
yarn.lock
81
yarn.lock
@ -758,13 +758,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.18.6"
|
"@babel/helper-plugin-utils" "^7.18.6"
|
||||||
|
|
||||||
"@babel/plugin-transform-object-assign@^7.16.7":
|
|
||||||
version "7.18.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.18.6.tgz#7830b4b6f83e1374a5afb9f6111bcfaea872cdd2"
|
|
||||||
integrity sha512-mQisZ3JfqWh2gVXvfqYCAAyRs6+7oev+myBsTwW5RnPhYXOTuCEw2oe3YgxlXMViXUS53lG8koulI7mJ+8JE+A==
|
|
||||||
dependencies:
|
|
||||||
"@babel/helper-plugin-utils" "^7.18.6"
|
|
||||||
|
|
||||||
"@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.18.6":
|
"@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.18.6":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c"
|
||||||
@ -1034,7 +1027,7 @@
|
|||||||
"@babel/plugin-transform-react-jsx-development" "^7.18.6"
|
"@babel/plugin-transform-react-jsx-development" "^7.18.6"
|
||||||
"@babel/plugin-transform-react-pure-annotations" "^7.18.6"
|
"@babel/plugin-transform-react-pure-annotations" "^7.18.6"
|
||||||
|
|
||||||
"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7", "@babel/preset-typescript@^7.17.12":
|
"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.17.12":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399"
|
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399"
|
||||||
integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==
|
integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==
|
||||||
@ -1703,7 +1696,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
"@sideway/formula@^3.0.0":
|
"@sideway/formula@^3.0.1":
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||||
@ -3313,9 +3306,9 @@ ee-first@1.1.1:
|
|||||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||||
|
|
||||||
electron-to-chromium@^1.4.284:
|
electron-to-chromium@^1.4.284:
|
||||||
version "1.4.292"
|
version "1.4.294"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.292.tgz#e3a3dca3780c8ce01e2c1866b5ec2fbe31c423e3"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.294.tgz#ad80317b85f0859a9454680fbc1c726fefa7e6fd"
|
||||||
integrity sha512-ESWOSyJy5odDlE8wvh5NNAMORv4r6assPwIPGHEMWrWD0SONXcG/xT+9aD9CQyeRwyYDPo6dJT4Bbeg5uevVQQ==
|
integrity sha512-PuHZB3jEN7D8WPPjLmBQAsqQz8tWHlkkB4n0E2OYw8RwVdmBYV0Wn+rUFH8JqYyIRb4HQhhedgxlZL163wqLrQ==
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
@ -4078,7 +4071,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
|
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
|
||||||
integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
|
integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
|
||||||
@ -4611,11 +4604,11 @@ inquirer@9.1.4:
|
|||||||
wrap-ansi "^8.0.1"
|
wrap-ansi "^8.0.1"
|
||||||
|
|
||||||
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3"
|
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
||||||
integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==
|
integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.2.0"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
@ -5189,14 +5182,14 @@ jetifier@^2.0.0:
|
|||||||
integrity sha512-J4Au9KuT74te+PCCCHKgAjyLlEa+2VyIAEPNCdE5aNkAJ6FAJcAqcdzEkSnzNksIa9NkGmC4tPiClk2e7tCJuQ==
|
integrity sha512-J4Au9KuT74te+PCCCHKgAjyLlEa+2VyIAEPNCdE5aNkAJ6FAJcAqcdzEkSnzNksIa9NkGmC4tPiClk2e7tCJuQ==
|
||||||
|
|
||||||
joi@^17.2.1:
|
joi@^17.2.1:
|
||||||
version "17.7.0"
|
version "17.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3"
|
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.1.tgz#854fc85c7fa3cfc47c91124d30bffdbb58e06cec"
|
||||||
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==
|
integrity sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
"@hapi/topo" "^5.0.0"
|
"@hapi/topo" "^5.0.0"
|
||||||
"@sideway/address" "^4.1.3"
|
"@sideway/address" "^4.1.3"
|
||||||
"@sideway/formula" "^3.0.0"
|
"@sideway/formula" "^3.0.1"
|
||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
js-sdsl@^4.1.4:
|
js-sdsl@^4.1.4:
|
||||||
@ -5454,11 +5447,6 @@ lodash.debounce@^4.0.8:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||||
|
|
||||||
lodash.isequal@^4.5.0:
|
|
||||||
version "4.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
|
||||||
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
|
|
||||||
|
|
||||||
lodash.ismatch@^4.4.0:
|
lodash.ismatch@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37"
|
resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37"
|
||||||
@ -5979,9 +5967,9 @@ minimist-options@4.1.0:
|
|||||||
kind-of "^6.0.3"
|
kind-of "^6.0.3"
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
|
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
|
||||||
version "1.2.7"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||||
|
|
||||||
mixin-deep@^1.2.0:
|
mixin-deep@^1.2.0:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
@ -6830,9 +6818,9 @@ rc@1.2.8:
|
|||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
react-devtools-core@^4.26.1:
|
react-devtools-core@^4.26.1:
|
||||||
version "4.27.1"
|
version "4.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.1.tgz#167aa174383c65786cbb7e965a5b39c702f0a2d3"
|
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.27.2.tgz#d20fc57e258c656eedabafc2c851d38b33583148"
|
||||||
integrity sha512-qXhcxxDWiFmFAOq48jts9YQYe1+wVoUXzJTlY4jbaATzyio6dd6CUGu3dXBhREeVgpZ+y4kg6vFJzIOZh6vY2w==
|
integrity sha512-8SzmIkpO87alD7Xr6gWIEa1jHkMjawOZ+6egjazlnjB4UUcbnzGDf/vBJ4BzGuWWEM+pzrxuzsPpcMqlQkYK2g==
|
||||||
dependencies:
|
dependencies:
|
||||||
shell-quote "^1.6.1"
|
shell-quote "^1.6.1"
|
||||||
ws "^7"
|
ws "^7"
|
||||||
@ -6894,19 +6882,6 @@ react-native-gradle-plugin@^0.71.14:
|
|||||||
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.14.tgz#cc399662f04fbfcc0e352d03eae1d3efbd5f635a"
|
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.14.tgz#cc399662f04fbfcc0e352d03eae1d3efbd5f635a"
|
||||||
integrity sha512-nnLawTZEPPRAKq92UqDkzoGgCBl9aa9zAihFHMwmwzn4WRVdK4O6Cd4XYiyoNOiQzx3Hh9k5WOckHE80C92ivQ==
|
integrity sha512-nnLawTZEPPRAKq92UqDkzoGgCBl9aa9zAihFHMwmwzn4WRVdK4O6Cd4XYiyoNOiQzx3Hh9k5WOckHE80C92ivQ==
|
||||||
|
|
||||||
react-native-reanimated@^2.14.4:
|
|
||||||
version "2.14.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz#3fa3da4e7b99f5dfb28f86bcf24d9d1024d38836"
|
|
||||||
integrity sha512-DquSbl7P8j4SAmc+kRdd75Ianm8G+IYQ9T4AQ6lrpLVeDkhZmjWI0wkutKWnp6L7c5XNVUrFDUf69dwETLCItQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/plugin-transform-object-assign" "^7.16.7"
|
|
||||||
"@babel/preset-typescript" "^7.16.7"
|
|
||||||
convert-source-map "^1.7.0"
|
|
||||||
invariant "^2.2.4"
|
|
||||||
lodash.isequal "^4.5.0"
|
|
||||||
setimmediate "^1.0.5"
|
|
||||||
string-hash-64 "^1.0.3"
|
|
||||||
|
|
||||||
react-native@^0.71.2:
|
react-native@^0.71.2:
|
||||||
version "0.71.2"
|
version "0.71.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9"
|
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9"
|
||||||
@ -7431,11 +7406,6 @@ set-value@^2.0.0, set-value@^2.0.1:
|
|||||||
is-plain-object "^2.0.3"
|
is-plain-object "^2.0.3"
|
||||||
split-string "^3.0.1"
|
split-string "^3.0.1"
|
||||||
|
|
||||||
setimmediate@^1.0.5:
|
|
||||||
version "1.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
|
||||||
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
|
|
||||||
|
|
||||||
setprototypeof@1.2.0:
|
setprototypeof@1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
@ -7725,11 +7695,6 @@ stream-buffers@2.2.x:
|
|||||||
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
|
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
|
||||||
integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==
|
integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==
|
||||||
|
|
||||||
string-hash-64@^1.0.3:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322"
|
|
||||||
integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==
|
|
||||||
|
|
||||||
string-natural-compare@^3.0.1:
|
string-natural-compare@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||||
@ -8094,9 +8059,9 @@ type-fest@^2.13.0, type-fest@^2.5.1:
|
|||||||
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
|
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
|
||||||
|
|
||||||
type-fest@^3.0.0:
|
type-fest@^3.0.0:
|
||||||
version "3.5.6"
|
version "3.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.5.6.tgz#f8f3a630c185fb5d66ca6950c7cbc2893deb6b84"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.5.7.tgz#1ee9efc9a172f4002c40b896689928a7bba537f2"
|
||||||
integrity sha512-6bd2bflx8ed7c99tc6zSTIzHr1/QG29bQoK4Qh8MYGnlPbODUzGxklLShjwc/xWQQFHgIci+y5Arv7Rbb0LjXw==
|
integrity sha512-6J4bYzb4sdkcLBty4XW7F18VPI66M4boXNE+CY40532oq2OJe6AVMB5NmjOp6skt/jw5mRjz/hLRpuglz0U+FA==
|
||||||
|
|
||||||
typed-array-length@^1.0.4:
|
typed-array-length@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user