perf: Remove FrameProcessorPlugin HybridClass (#467)

* Rename `JImageProxyHostObject` -> `FrameHostObject`

* `FrameProcessorPlugin` -> `JFrameProcessorPlugin` 1/2

* `FrameProcessorPlugin` -> `JFrameProcessorPlugin` 2/2

* Make `const`

* Make `getName()` instance based

* Update JFrameProcessorPlugin.h

* Update JImageProxy.h

* `T`

* T

* Remove default ctor

* Use `TSelf` again

* Return `local_ref<CameraView*>` instead of `CameraView*`

* Make `findCameraViewById` return a raw pointer again...

* Extract `setFrameProcessor` and `unsetFrameProcessor`

* Use `global_ref`

* Use `static_cast` for `FrameHostObject`

* Update FrameProcessorRuntimeManager.cpp

* Fix reference lint error

* linelength

* Fix `unsetFrameProcessor` call
This commit is contained in:
Marc Rousavy 2021-09-29 12:30:50 +02:00 committed by GitHub
parent 8f65427391
commit 42e791b4bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 193 additions and 185 deletions

View File

@ -15,12 +15,12 @@ add_library(
SHARED SHARED
src/main/cpp/VisionCamera.cpp src/main/cpp/VisionCamera.cpp
src/main/cpp/JSIJNIConversion.cpp src/main/cpp/JSIJNIConversion.cpp
src/main/cpp/FrameHostObject.cpp
src/main/cpp/FrameProcessorRuntimeManager.cpp src/main/cpp/FrameProcessorRuntimeManager.cpp
src/main/cpp/FrameProcessorPlugin.cpp
src/main/cpp/CameraView.cpp src/main/cpp/CameraView.cpp
src/main/cpp/VisionCameraScheduler.cpp src/main/cpp/VisionCameraScheduler.cpp
src/main/cpp/java-bindings/JFrameProcessorPlugin.cpp
src/main/cpp/java-bindings/JImageProxy.cpp src/main/cpp/java-bindings/JImageProxy.cpp
src/main/cpp/java-bindings/JImageProxyHostObject.cpp
src/main/cpp/java-bindings/JHashMap.cpp src/main/cpp/java-bindings/JHashMap.cpp
) )

View File

@ -15,7 +15,7 @@ namespace vision {
using namespace facebook; using namespace facebook;
using namespace jni; using namespace jni;
using TSelf = local_ref<HybridClass<vision::CameraView>::jhybriddata>; using TSelf = local_ref<CameraView::jhybriddata>;
TSelf CameraView::initHybrid(alias_ref<HybridClass::jhybridobject> jThis) { TSelf CameraView::initHybrid(alias_ref<HybridClass::jhybridobject> jThis) {
return makeCxxInstance(jThis); return makeCxxInstance(jThis);
@ -42,7 +42,7 @@ void CameraView::frameProcessorCallback(const alias_ref<JImageProxy::javaobject>
} }
} }
void CameraView::setFrameProcessor(const FrameProcessor&& frameProcessor) { void CameraView::setFrameProcessor(const TFrameProcessor&& frameProcessor) {
frameProcessor_ = frameProcessor; frameProcessor_ = frameProcessor;
} }

View File

@ -14,7 +14,7 @@
namespace vision { namespace vision {
using namespace facebook; using namespace facebook;
using FrameProcessor = std::function<void(jni::alias_ref<JImageProxy::javaobject>)>; using TFrameProcessor = std::function<void(jni::alias_ref<JImageProxy::javaobject>)>;
class CameraView : public jni::HybridClass<CameraView> { class CameraView : public jni::HybridClass<CameraView> {
public: public:
@ -24,13 +24,13 @@ class CameraView : public jni::HybridClass<CameraView> {
static void registerNatives(); static void registerNatives();
// TODO: Use template<> to avoid heap allocation for std::function<> // TODO: Use template<> to avoid heap allocation for std::function<>
void setFrameProcessor(const FrameProcessor&& frameProcessor); void setFrameProcessor(const TFrameProcessor&& frameProcessor);
void unsetFrameProcessor(); void unsetFrameProcessor();
private: private:
friend HybridBase; friend HybridBase;
jni::global_ref<CameraView::javaobject> javaPart_; jni::global_ref<CameraView::javaobject> javaPart_;
FrameProcessor frameProcessor_; TFrameProcessor frameProcessor_;
void frameProcessorCallback(const jni::alias_ref<JImageProxy::javaobject>& frame); void frameProcessorCallback(const jni::alias_ref<JImageProxy::javaobject>& frame);

View File

@ -2,7 +2,7 @@
// Created by Marc on 19/06/2021. // Created by Marc on 19/06/2021.
// //
#include "JImageProxyHostObject.h" #include "FrameHostObject.h"
#include <android/log.h> #include <android/log.h>
#include <fbjni/fbjni.h> #include <fbjni/fbjni.h>
#include <jni.h> #include <jni.h>
@ -13,9 +13,9 @@ namespace vision {
using namespace facebook; using namespace facebook;
JImageProxyHostObject::JImageProxyHostObject(jni::alias_ref<JImageProxy::javaobject> image): frame(make_local(image)) { } FrameHostObject::FrameHostObject(jni::alias_ref<JImageProxy::javaobject> image): frame(make_local(image)) { }
JImageProxyHostObject::~JImageProxyHostObject() { FrameHostObject::~FrameHostObject() {
// Hermes' Garbage Collector (Hades GC) calls destructors on a separate Thread // Hermes' Garbage Collector (Hades GC) calls destructors on a separate Thread
// which might not be attached to JNI. Ensure that we use the JNI class loader when // which might not be attached to JNI. Ensure that we use the JNI class loader when
// deallocating the `frame` HybridClass, because otherwise JNI cannot call the Java // deallocating the `frame` HybridClass, because otherwise JNI cannot call the Java
@ -23,7 +23,7 @@ JImageProxyHostObject::~JImageProxyHostObject() {
jni::ThreadScope::WithClassLoader([=] { frame.reset(); }); jni::ThreadScope::WithClassLoader([=] { frame.reset(); });
} }
std::vector<jsi::PropNameID> JImageProxyHostObject::getPropertyNames(jsi::Runtime& rt) { std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) {
std::vector<jsi::PropNameID> result; std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid")));
@ -35,7 +35,7 @@ std::vector<jsi::PropNameID> JImageProxyHostObject::getPropertyNames(jsi::Runtim
return result; return result;
} }
jsi::Value JImageProxyHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
auto name = propNameId.utf8(runtime); auto name = propNameId.utf8(runtime);
if (name == "toString") { if (name == "toString") {
@ -84,14 +84,14 @@ jsi::Value JImageProxyHostObject::get(jsi::Runtime& runtime, const jsi::PropName
return jsi::Value::undefined(); return jsi::Value::undefined();
} }
void JImageProxyHostObject::assertIsFrameStrong(jsi::Runtime& runtime, const std::string& accessedPropName) const { void FrameHostObject::assertIsFrameStrong(jsi::Runtime& runtime, const std::string& accessedPropName) const {
if (!this->frame) { if (!this->frame) {
auto message = "Cannot get `" + accessedPropName + "`, frame is already closed!"; auto message = "Cannot get `" + accessedPropName + "`, frame is already closed!";
throw jsi::JSError(runtime, message.c_str()); throw jsi::JSError(runtime, message.c_str());
} }
} }
void JImageProxyHostObject::close() { void FrameHostObject::close() {
if (this->frame) { if (this->frame) {
this->frame->close(); this->frame->close();
} }

View File

@ -10,16 +10,16 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "JImageProxy.h" #include "java-bindings/JImageProxy.h"
namespace vision { namespace vision {
using namespace facebook; using namespace facebook;
class JSI_EXPORT JImageProxyHostObject : public jsi::HostObject { class JSI_EXPORT FrameHostObject : public jsi::HostObject {
public: public:
explicit JImageProxyHostObject(jni::alias_ref<JImageProxy::javaobject> image); explicit FrameHostObject(jni::alias_ref<JImageProxy::javaobject> image);
~JImageProxyHostObject(); ~FrameHostObject();
public: public:
jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;

View File

@ -1,38 +0,0 @@
//
// Created by Marc Rousavy on 22.06.21.
//
#include "FrameProcessorPlugin.h"
#include <string>
namespace vision {
using namespace facebook;
using namespace jni;
using TSelf = local_ref<HybridClass<FrameProcessorPlugin>::jhybriddata>;
using TFrameProcessorPlugin = jobject(alias_ref<JImageProxy::javaobject>, alias_ref<JArrayClass<jobject>>);
TSelf vision::FrameProcessorPlugin::initHybrid(alias_ref<HybridClass::jhybridobject> jThis, const std::string& name) {
return makeCxxInstance(jThis, name);
}
void FrameProcessorPlugin::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid",
FrameProcessorPlugin::initHybrid),
});
}
local_ref<jobject> FrameProcessorPlugin::callback(alias_ref<JImageProxy::javaobject> image, alias_ref<JArrayClass<jobject>> params) {
auto func = javaPart_->getClass()->getMethod<TFrameProcessorPlugin>("callback");
auto result = func(javaPart_.get(), image, params);
return make_local(result);
}
std::string FrameProcessorPlugin::getName() {
return name;
}
} // namespace vision

View File

@ -1,39 +0,0 @@
//
// Created by Marc Rousavy on 22.06.21.
//
#pragma once
#include <jni.h>
#include <fbjni/fbjni.h>
#include <string>
#include "java-bindings/JImageProxy.h"
namespace vision {
using namespace facebook;
using namespace jni;
class FrameProcessorPlugin: public HybridClass<FrameProcessorPlugin> {
public:
static auto constexpr kJavaDescriptor = "Lcom/mrousavy/camera/frameprocessor/FrameProcessorPlugin;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis,
const std::string& name);
static void registerNatives();
local_ref<jobject> callback(alias_ref<JImageProxy::javaobject> image, alias_ref<JArrayClass<jobject>> params);
std::string getName();
private:
friend HybridBase;
jni::global_ref<FrameProcessorPlugin::javaobject> javaPart_;
std::string name;
FrameProcessorPlugin(alias_ref<FrameProcessorPlugin::jhybridobject> jThis,
std::string name): javaPart_(make_global(jThis)),
name(name)
{}
};
} // namespace vision

View File

@ -15,17 +15,18 @@
#include "MakeJSIRuntime.h" #include "MakeJSIRuntime.h"
#include "CameraView.h" #include "CameraView.h"
#include "java-bindings/JImageProxy.h" #include "FrameHostObject.h"
#include "java-bindings/JImageProxyHostObject.h"
#include "JSIJNIConversion.h" #include "JSIJNIConversion.h"
#include "VisionCameraScheduler.h" #include "VisionCameraScheduler.h"
#include "java-bindings/JImageProxy.h"
#include "java-bindings/JFrameProcessorPlugin.h"
namespace vision { namespace vision {
// type aliases // type aliases
using TSelf = local_ref<HybridClass<vision::FrameProcessorRuntimeManager>::jhybriddata>; using TSelf = local_ref<HybridClass<vision::FrameProcessorRuntimeManager>::jhybriddata>;
using JSCallInvokerHolder = jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>; using TJSCallInvokerHolder = jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>;
using AndroidScheduler = jni::alias_ref<VisionCameraScheduler::javaobject>; using TAndroidScheduler = jni::alias_ref<VisionCameraScheduler::javaobject>;
// JNI binding // JNI binding
void vision::FrameProcessorRuntimeManager::registerNatives() { void vision::FrameProcessorRuntimeManager::registerNatives() {
@ -44,18 +45,19 @@ void vision::FrameProcessorRuntimeManager::registerNatives() {
// JNI init // JNI init
TSelf vision::FrameProcessorRuntimeManager::initHybrid( TSelf vision::FrameProcessorRuntimeManager::initHybrid(
alias_ref<jhybridobject> jThis, alias_ref<jhybridobject> jThis,
jlong jsContext, jlong jsRuntimePointer,
JSCallInvokerHolder jsCallInvokerHolder, TJSCallInvokerHolder jsCallInvokerHolder,
AndroidScheduler androidScheduler) { TAndroidScheduler androidScheduler) {
__android_log_write(ANDROID_LOG_INFO, TAG, __android_log_write(ANDROID_LOG_INFO, TAG,
"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 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); scheduler->setJSCallInvoker(jsCallInvoker);
return makeCxxInstance(jThis, reinterpret_cast<jsi::Runtime *>(jsContext), jsCallInvoker, scheduler); return makeCxxInstance(jThis, runtime, jsCallInvoker, scheduler);
} }
void vision::FrameProcessorRuntimeManager::initializeRuntime() { void vision::FrameProcessorRuntimeManager::initializeRuntime() {
@ -78,10 +80,10 @@ void vision::FrameProcessorRuntimeManager::initializeRuntime() {
"Initialized Vision JS-Runtime!"); "Initialized Vision JS-Runtime!");
} }
CameraView* FrameProcessorRuntimeManager::findCameraViewById(int viewId) { global_ref<CameraView::javaobject> FrameProcessorRuntimeManager::findCameraViewById(int viewId) {
static const auto func = javaPart_->getClass()->getMethod<CameraView*(jint)>("findCameraViewById"); static const auto findCameraViewByIdMethod = javaPart_->getClass()->getMethod<CameraView(jint)>("findCameraViewById");
auto result = func(javaPart_.get(), viewId); auto weakCameraView = findCameraViewByIdMethod(javaPart_.get(), viewId);
return result->cthis(); return make_global(weakCameraView);
} }
void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) { void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) {
@ -103,6 +105,63 @@ void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) {
}); });
} }
void FrameProcessorRuntimeManager::setFrameProcessor(jsi::Runtime& runtime,
int viewTag,
const jsi::Value& frameProcessor) {
__android_log_write(ANDROID_LOG_INFO, TAG,
"Setting new Frame Processor...");
if (!_runtimeManager || !_runtimeManager->runtime) {
throw jsi::JSError(runtime,
"setFrameProcessor(..): VisionCamera's RuntimeManager is not yet initialized!");
}
// find camera view
auto cameraView = findCameraViewById(viewTag);
__android_log_write(ANDROID_LOG_INFO, TAG, "Found CameraView!");
// convert jsi::Function to a ShareableValue (can be shared across runtimes)
__android_log_write(ANDROID_LOG_INFO, TAG,
"Adapting Shareable value from function (conversion to worklet)...");
auto worklet = reanimated::ShareableValue::adapt(runtime,
frameProcessor,
_runtimeManager.get());
__android_log_write(ANDROID_LOG_INFO, TAG, "Successfully created worklet!");
scheduler_->scheduleOnUI([=]() {
// cast worklet to a jsi::Function for the new runtime
auto& rt = *_runtimeManager->runtime;
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 {
// create HostObject which holds the Frame (JImageProxy)
auto hostObject = std::make_shared<FrameHostObject>(frame);
function->callWithThis(rt, *function, jsi::Object::createFromHostObject(rt, hostObject));
} catch (jsi::JSError& jsError) {
auto message = "Frame Processor threw an error: " + jsError.getMessage();
__android_log_write(ANDROID_LOG_ERROR, TAG, message.c_str());
this->logErrorToJS(message);
}
});
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor set!");
});
}
void FrameProcessorRuntimeManager::unsetFrameProcessor(int viewTag) {
__android_log_write(ANDROID_LOG_INFO, TAG, "Removing Frame Processor...");
// find camera view
auto cameraView = findCameraViewById(viewTag);
// call Java method to unset frame processor
cameraView->cthis()->unsetFrameProcessor();
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor removed!");
}
// actual JSI installer // actual JSI installer
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...");
@ -113,7 +172,7 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
return; return;
} }
auto &jsiRuntime = *runtime_; auto& jsiRuntime = *runtime_;
auto setFrameProcessor = [this](jsi::Runtime &runtime, auto setFrameProcessor = [this](jsi::Runtime &runtime,
const jsi::Value &thisValue, const jsi::Value &thisValue,
@ -130,43 +189,10 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
throw jsi::JSError(runtime, throw jsi::JSError(runtime,
"Camera::setFrameProcessor: Second argument ('frameProcessor') must be a function!"); "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!");
}
// find camera view double viewTag = arguments[0].asNumber();
auto viewTag = arguments[0].asNumber(); const jsi::Value& frameProcessor = arguments[1];
auto cameraView = findCameraViewById(static_cast<int>(viewTag)); this->setFrameProcessor(runtime, static_cast<int>(viewTag), frameProcessor);
__android_log_write(ANDROID_LOG_INFO, TAG, "Found CameraView!");
// convert jsi::Function to a ShareableValue (can be shared across runtimes)
__android_log_write(ANDROID_LOG_INFO, TAG, "Adapting Shareable value from function (conversion to worklet)...");
auto worklet = reanimated::ShareableValue::adapt(runtime, arguments[1],
_runtimeManager.get());
__android_log_write(ANDROID_LOG_INFO, TAG, "Successfully created worklet!");
scheduler_->scheduleOnUI([=]() {
// cast worklet to a jsi::Function for the new runtime
auto &rt = *_runtimeManager->runtime;
auto function = std::make_shared<jsi::Function>(worklet->getValue(rt).asObject(rt).asFunction(rt));
// assign lambda to frame processor
cameraView->setFrameProcessor([this, &rt, function](jni::alias_ref<JImageProxy::javaobject> frame) {
try {
// create HostObject which holds the Frame (JImageProxy)
auto hostObject = std::make_shared<JImageProxyHostObject>(frame);
function->callWithThis(rt, *function, jsi::Object::createFromHostObject(rt, hostObject));
} catch (jsi::JSError& jsError) {
auto message = "Frame Processor threw an error: " + jsError.getMessage();
__android_log_write(ANDROID_LOG_ERROR, TAG, message.c_str());
this->logErrorToJS(message);
}
});
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor set!");
});
return jsi::Value::undefined(); return jsi::Value::undefined();
}; };
@ -190,14 +216,8 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
"Camera::unsetFrameProcessor: First argument ('viewTag') must be a number!"); "Camera::unsetFrameProcessor: First argument ('viewTag') must be a number!");
} }
// find camera view
auto viewTag = arguments[0].asNumber(); auto viewTag = arguments[0].asNumber();
auto cameraView = findCameraViewById(static_cast<int>(viewTag)); this->unsetFrameProcessor(static_cast<int>(viewTag));
// call Java method to unset frame processor
cameraView->unsetFrameProcessor();
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor removed!");
return jsi::Value::undefined(); return jsi::Value::undefined();
}; };
@ -213,7 +233,7 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
__android_log_write(ANDROID_LOG_INFO, TAG, "Finished installing JSI bindings!"); __android_log_write(ANDROID_LOG_INFO, TAG, "Finished installing JSI bindings!");
} }
void FrameProcessorRuntimeManager::registerPlugin(alias_ref<FrameProcessorPlugin::javaobject> plugin) { void FrameProcessorRuntimeManager::registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin) {
// _runtimeManager might never be null, but we can never be too sure. // _runtimeManager might never be null, but we can never be too sure.
if (!_runtimeManager || !_runtimeManager->runtime) { if (!_runtimeManager || !_runtimeManager->runtime) {
throw std::runtime_error("Tried to register plugin before initializing JS runtime! Call `initializeRuntime()` first."); throw std::runtime_error("Tried to register plugin before initializing JS runtime! Call `initializeRuntime()` first.");
@ -224,7 +244,7 @@ void FrameProcessorRuntimeManager::registerPlugin(alias_ref<FrameProcessorPlugin
// 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 (__) // name is always prefixed with two underscores (__)
auto name = "__" + pluginGlobal->cthis()->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\"...", name.c_str());
@ -234,7 +254,7 @@ void FrameProcessorRuntimeManager::registerPlugin(alias_ref<FrameProcessorPlugin
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 = dynamic_cast<JImageProxyHostObject*>(boxedHostObject.get()); auto frameHostObject = static_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);
@ -243,7 +263,7 @@ void FrameProcessorRuntimeManager::registerPlugin(alias_ref<FrameProcessorPlugin
} }
// call implemented virtual method // call implemented virtual method
auto result = pluginGlobal->cthis()->callback(frameHostObject->frame, params); auto result = pluginGlobal->callback(frameHostObject->frame, params);
// convert result from JNI to JSI value // convert result from JNI to JSI value
return JSIJNIConversion::convertJNIObjectToJSIValue(runtime, result); return JSIJNIConversion::convertJNIObjectToJSIValue(runtime, result);

View File

@ -14,8 +14,8 @@
#include "reanimated-headers/AndroidScheduler.h" #include "reanimated-headers/AndroidScheduler.h"
#include "CameraView.h" #include "CameraView.h"
#include "FrameProcessorPlugin.h"
#include "VisionCameraScheduler.h" #include "VisionCameraScheduler.h"
#include "java-bindings/JFrameProcessorPlugin.h"
namespace vision { namespace vision {
@ -31,7 +31,6 @@ class FrameProcessorRuntimeManager : public jni::HybridClass<FrameProcessorRunti
jni::alias_ref<vision::VisionCameraScheduler::javaobject> androidScheduler); jni::alias_ref<vision::VisionCameraScheduler::javaobject> androidScheduler);
static void registerNatives(); static void registerNatives();
FrameProcessorRuntimeManager() {}
explicit FrameProcessorRuntimeManager(jni::alias_ref<FrameProcessorRuntimeManager::jhybridobject> jThis, explicit FrameProcessorRuntimeManager(jni::alias_ref<FrameProcessorRuntimeManager::jhybridobject> jThis,
jsi::Runtime* runtime, jsi::Runtime* runtime,
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker, std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker,
@ -50,11 +49,16 @@ class FrameProcessorRuntimeManager : public jni::HybridClass<FrameProcessorRunti
std::shared_ptr<reanimated::RuntimeManager> _runtimeManager; std::shared_ptr<reanimated::RuntimeManager> _runtimeManager;
std::shared_ptr<vision::VisionCameraScheduler> scheduler_; std::shared_ptr<vision::VisionCameraScheduler> scheduler_;
CameraView* findCameraViewById(int viewId); jni::global_ref<CameraView::javaobject> findCameraViewById(int viewId);
void initializeRuntime(); void initializeRuntime();
void installJSIBindings(); void installJSIBindings();
void registerPlugin(alias_ref<FrameProcessorPlugin::javaobject> plugin); void registerPlugin(alias_ref<JFrameProcessorPlugin::javaobject> plugin);
void logErrorToJS(const std::string& message); void logErrorToJS(const std::string& message);
void setFrameProcessor(jsi::Runtime& runtime, // NOLINT(runtime/references)
int viewTag,
const jsi::Value& frameProcessor);
void unsetFrameProcessor(int viewTag);
}; };
} // namespace vision } // namespace vision

View File

@ -19,7 +19,7 @@
#include <jsi/JSIDynamic.h> #include <jsi/JSIDynamic.h>
#include <folly/dynamic.h> #include <folly/dynamic.h>
#include "java-bindings/JImageProxyHostObject.h" #include "FrameHostObject.h"
#include "java-bindings/JImageProxy.h" #include "java-bindings/JImageProxy.h"
#include "java-bindings/JArrayList.h" #include "java-bindings/JArrayList.h"
#include "java-bindings/JHashMap.h" #include "java-bindings/JHashMap.h"
@ -68,7 +68,7 @@ jobject JSIJNIConversion::convertJSIValueToJNIObject(jsi::Runtime &runtime, cons
// jsi::HostObject // jsi::HostObject
auto boxedHostObject = object.getHostObject(runtime); auto boxedHostObject = object.getHostObject(runtime);
auto hostObject = dynamic_cast<JImageProxyHostObject*>(boxedHostObject.get()); auto hostObject = dynamic_cast<FrameHostObject*>(boxedHostObject.get());
if (hostObject != nullptr) { if (hostObject != nullptr) {
// return jni local_ref to the JImageProxy // return jni local_ref to the JImageProxy
return hostObject->frame.get(); return hostObject->frame.get();

View File

@ -1,14 +1,12 @@
#include <jni.h> #include <jni.h>
#include <fbjni/fbjni.h> #include <fbjni/fbjni.h>
#include "FrameProcessorRuntimeManager.h" #include "FrameProcessorRuntimeManager.h"
#include "FrameProcessorPlugin.h"
#include "CameraView.h" #include "CameraView.h"
#include "VisionCameraScheduler.h" #include "VisionCameraScheduler.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] { return facebook::jni::initialize(vm, [] {
vision::FrameProcessorRuntimeManager::registerNatives(); vision::FrameProcessorRuntimeManager::registerNatives();
vision::FrameProcessorPlugin::registerNatives();
vision::CameraView::registerNatives(); vision::CameraView::registerNatives();
vision::VisionCameraScheduler::registerNatives(); vision::VisionCameraScheduler::registerNatives();
}); });

View File

@ -0,0 +1,30 @@
//
// Created by Marc Rousavy on 29.09.21.
//
#include "JFrameProcessorPlugin.h"
#include <jni.h>
#include <fbjni/fbjni.h>
namespace vision {
using namespace facebook;
using namespace jni;
using TCallback = jobject(alias_ref<JImageProxy::javaobject>, alias_ref<JArrayClass<jobject>>);
local_ref<jobject> JFrameProcessorPlugin::callback(alias_ref<JImageProxy::javaobject> image,
alias_ref<JArrayClass<jobject>> params) const {
auto callbackMethod = getClass()->getMethod<TCallback>("callback");
auto result = callbackMethod(self(), image, params);
return make_local(result);
}
std::string JFrameProcessorPlugin::getName() const {
auto getNameMethod = getClass()->getMethod<jstring()>("getName");
return getNameMethod(self())->toStdString();
}
} // namespace vision

View File

@ -0,0 +1,33 @@
//
// Created by Marc Rousavy on 29.09.21
//
#pragma once
#include <jni.h>
#include <fbjni/fbjni.h>
#include <string>
#include "JImageProxy.h"
namespace vision {
using namespace facebook;
using namespace jni;
struct JFrameProcessorPlugin : public JavaClass<JFrameProcessorPlugin> {
static constexpr auto kJavaDescriptor = "Lcom/mrousavy/camera/frameprocessor/FrameProcessorPlugin;";
public:
/**
* Call the plugin.
*/
local_ref<jobject> callback(alias_ref<JImageProxy::javaobject> image,
alias_ref<JArrayClass<jobject>> params) const;
/**
* Get the user-defined name of the Frame Processor Plugin
*/
std::string getName() const;
};
} // namespace vision

View File

@ -12,12 +12,12 @@ namespace vision {
using namespace facebook; using namespace facebook;
using namespace jni; using namespace jni;
int JImageProxy::getWidth() { int JImageProxy::getWidth() const {
static const auto getWidthMethod = getClass()->getMethod<jint()>("getWidth"); static const auto getWidthMethod = getClass()->getMethod<jint()>("getWidth");
return getWidthMethod(self()); return getWidthMethod(self());
} }
int JImageProxy::getHeight() { int JImageProxy::getHeight() const {
static const auto getWidthMethod = getClass()->getMethod<jint()>("getHeight"); static const auto getWidthMethod = getClass()->getMethod<jint()>("getHeight");
return getWidthMethod(self()); return getWidthMethod(self());
} }
@ -27,19 +27,19 @@ alias_ref<JClass> getUtilsClass() {
return ImageProxyUtilsClass; return ImageProxyUtilsClass;
} }
bool JImageProxy::getIsValid() { bool JImageProxy::getIsValid() const {
auto utilsClass = getUtilsClass(); auto utilsClass = getUtilsClass();
static const auto isImageProxyValidMethod = utilsClass->getStaticMethod<jboolean(JImageProxy::javaobject)>("isImageProxyValid"); static const auto isImageProxyValidMethod = utilsClass->getStaticMethod<jboolean(JImageProxy::javaobject)>("isImageProxyValid");
return isImageProxyValidMethod(utilsClass, self()); return isImageProxyValidMethod(utilsClass, self());
} }
int JImageProxy::getPlanesCount() { int JImageProxy::getPlanesCount() const {
auto utilsClass = getUtilsClass(); auto utilsClass = getUtilsClass();
static const auto getPlanesCountMethod = utilsClass->getStaticMethod<jint(JImageProxy::javaobject)>("getPlanesCount"); static const auto getPlanesCountMethod = utilsClass->getStaticMethod<jint(JImageProxy::javaobject)>("getPlanesCount");
return getPlanesCountMethod(utilsClass, self()); return getPlanesCountMethod(utilsClass, self());
} }
int JImageProxy::getBytesPerRow() { int JImageProxy::getBytesPerRow() const {
auto utilsClass = getUtilsClass(); auto utilsClass = getUtilsClass();
static const auto getBytesPerRowMethod = utilsClass->getStaticMethod<jint(JImageProxy::javaobject)>("getBytesPerRow"); static const auto getBytesPerRowMethod = utilsClass->getStaticMethod<jint(JImageProxy::javaobject)>("getBytesPerRow");
return getBytesPerRowMethod(utilsClass, self()); return getBytesPerRowMethod(utilsClass, self());

View File

@ -9,15 +9,18 @@
namespace vision { namespace vision {
struct JImageProxy : public facebook::jni::JavaClass<JImageProxy> { using namespace facebook;
using namespace jni;
struct JImageProxy : public JavaClass<JImageProxy> {
static constexpr auto kJavaDescriptor = "Landroidx/camera/core/ImageProxy;"; static constexpr auto kJavaDescriptor = "Landroidx/camera/core/ImageProxy;";
public: public:
int getWidth(); int getWidth() const;
int getHeight(); int getHeight() const;
bool getIsValid(); bool getIsValid() const;
int getPlanesCount(); int getPlanesCount() const;
int getBytesPerRow(); int getBytesPerRow() const;
void close(); void close();
}; };

View File

@ -4,21 +4,13 @@ import androidx.annotation.Keep;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy; import androidx.camera.core.ImageProxy;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.proguard.annotations.DoNotStrip;
/** /**
* Declares a Frame Processor Plugin. * Declares a Frame Processor Plugin.
*/ */
@SuppressWarnings("JavaJniMissingFunction")
public abstract class FrameProcessorPlugin { public abstract class FrameProcessorPlugin {
static { private final @NonNull String mName;
System.loadLibrary("VisionCamera");
}
@DoNotStrip
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final HybridData mHybridData;
/** /**
* The actual Frame Processor plugin callback. Called for every frame the ImageAnalyzer receives. * The actual Frame Processor plugin callback. Called for every frame the ImageAnalyzer receives.
@ -37,10 +29,15 @@ public abstract class FrameProcessorPlugin {
* The actual name in the JS Runtime will be prefixed with two underscores (`__`) * The actual name in the JS Runtime will be prefixed with two underscores (`__`)
*/ */
protected FrameProcessorPlugin(@NonNull String name) { protected FrameProcessorPlugin(@NonNull String name) {
mHybridData = initHybrid(name); mName = name;
} }
private native @NonNull HybridData initHybrid(@NonNull String name); /**
* Get the user-defined name of the Frame Processor Plugin.
*/
public @NonNull String getName() {
return mName;
}
/** /**
* Registers the given plugin in the Frame Processor Runtime. * Registers the given plugin in the Frame Processor Runtime.