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:
parent
8f65427391
commit
42e791b4bd
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
@ -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;
|
@ -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
|
|
@ -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
|
|
@ -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...");
|
||||||
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
|
30
android/src/main/cpp/java-bindings/JFrameProcessorPlugin.cpp
Normal file
30
android/src/main/cpp/java-bindings/JFrameProcessorPlugin.cpp
Normal 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
|
33
android/src/main/cpp/java-bindings/JFrameProcessorPlugin.h
Normal file
33
android/src/main/cpp/java-bindings/JFrameProcessorPlugin.h
Normal 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
|
@ -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());
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user