2023-07-21 09:52:30 -06:00
|
|
|
//
|
|
|
|
// VisionCameraProxy.mm
|
|
|
|
// VisionCamera
|
|
|
|
//
|
|
|
|
// Created by Marc Rousavy on 20.07.23.
|
|
|
|
// Copyright © 2023 mrousavy. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "VisionCameraProxy.h"
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#import <jsi/jsi.h>
|
|
|
|
|
2023-09-01 04:58:32 -06:00
|
|
|
#import "../../cpp/JSITypedArray.h"
|
2023-07-21 09:52:30 -06:00
|
|
|
#import "FrameHostObject.h"
|
2023-09-01 04:58:32 -06:00
|
|
|
#import "FrameProcessor.h"
|
|
|
|
#import "FrameProcessorPluginHostObject.h"
|
|
|
|
#import "FrameProcessorPluginRegistry.h"
|
2023-07-21 09:52:30 -06:00
|
|
|
#import "JSINSObjectConversion.h"
|
|
|
|
#import "WKTJsiWorklet.h"
|
|
|
|
|
|
|
|
#import <React/RCTBridge+Private.h>
|
2023-09-01 04:58:32 -06:00
|
|
|
#import <React/RCTBridge.h>
|
2023-07-21 09:52:30 -06:00
|
|
|
#import <React/RCTUIManager.h>
|
2023-09-01 04:58:32 -06:00
|
|
|
#import <React/RCTUtils.h>
|
2023-07-21 09:52:30 -06:00
|
|
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
|
|
|
|
|
|
|
// Swift forward-declarations
|
|
|
|
__attribute__((objc_runtime_name("_TtC12VisionCamera12CameraQueues")))
|
2023-09-01 04:58:32 -06:00
|
|
|
@interface CameraQueues : NSObject
|
|
|
|
@property(nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull videoQueue;
|
2023-07-21 09:52:30 -06:00
|
|
|
@end
|
|
|
|
|
|
|
|
__attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
2023-09-01 04:58:32 -06:00
|
|
|
@interface CameraView : UIView
|
|
|
|
@property(nonatomic, copy) FrameProcessor* _Nullable frameProcessor;
|
2023-07-21 09:52:30 -06:00
|
|
|
@end
|
|
|
|
|
|
|
|
using namespace facebook;
|
|
|
|
|
2023-09-01 11:39:25 -06:00
|
|
|
VisionCameraProxy::VisionCameraProxy(jsi::Runtime& runtime, std::shared_ptr<react::CallInvoker> callInvoker) {
|
2023-07-21 09:52:30 -06:00
|
|
|
_callInvoker = callInvoker;
|
|
|
|
|
|
|
|
NSLog(@"VisionCameraProxy: Creating Worklet Context...");
|
|
|
|
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
|
2023-09-01 04:58:32 -06:00
|
|
|
dispatch_async(CameraQueues.videoQueue, [f = std::move(f)]() { f(); });
|
2023-07-21 09:52:30 -06:00
|
|
|
};
|
|
|
|
|
2023-09-01 11:39:25 -06:00
|
|
|
_workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera", &runtime, runOnJS, runOnWorklet);
|
2023-07-21 09:52:30 -06:00
|
|
|
NSLog(@"VisionCameraProxy: Worklet Context Created!");
|
|
|
|
}
|
|
|
|
|
|
|
|
VisionCameraProxy::~VisionCameraProxy() {
|
|
|
|
NSLog(@"VisionCameraProxy: Destroying context...");
|
|
|
|
// Destroy ArrayBuffer cache for both the JS and the Worklet Runtime.
|
|
|
|
vision::invalidateArrayBufferCache(*_workletContext->getJsRuntime());
|
|
|
|
vision::invalidateArrayBufferCache(_workletContext->getWorkletRuntime());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<jsi::PropNameID> VisionCameraProxy::getPropertyNames(jsi::Runtime& runtime) {
|
|
|
|
std::vector<jsi::PropNameID> result;
|
|
|
|
result.push_back(jsi::PropNameID::forUtf8(runtime, std::string("setFrameProcessor")));
|
|
|
|
result.push_back(jsi::PropNameID::forUtf8(runtime, std::string("removeFrameProcessor")));
|
2023-10-19 03:19:47 -06:00
|
|
|
result.push_back(jsi::PropNameID::forUtf8(runtime, std::string("initFrameProcessorPlugin")));
|
2023-07-21 09:52:30 -06:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-09-01 11:39:25 -06:00
|
|
|
void VisionCameraProxy::setFrameProcessor(jsi::Runtime& runtime, int viewTag, const jsi::Object& object) {
|
2023-07-21 09:52:30 -06:00
|
|
|
auto frameProcessorType = object.getProperty(runtime, "type").asString(runtime).utf8(runtime);
|
2023-09-01 11:39:25 -06:00
|
|
|
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, object.getProperty(runtime, "frameProcessor"));
|
2023-07-21 09:52:30 -06:00
|
|
|
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
|
|
auto currentBridge = [RCTBridge currentBridge];
|
2023-09-01 11:39:25 -06:00
|
|
|
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
|
2023-07-21 09:52:30 -06:00
|
|
|
auto view = static_cast<CameraView*>(anonymousView);
|
|
|
|
if (frameProcessorType == "frame-processor") {
|
2023-09-01 11:39:25 -06:00
|
|
|
view.frameProcessor = [[FrameProcessor alloc] initWithWorklet:worklet context:_workletContext];
|
2023-07-21 09:52:30 -06:00
|
|
|
} else {
|
2023-09-01 11:39:25 -06:00
|
|
|
throw std::runtime_error("Unknown FrameProcessor.type passed! Received: " + frameProcessorType);
|
2023-07-21 09:52:30 -06:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisionCameraProxy::removeFrameProcessor(jsi::Runtime& runtime, int viewTag) {
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
|
|
auto currentBridge = [RCTBridge currentBridge];
|
2023-09-01 11:39:25 -06:00
|
|
|
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
|
2023-07-21 09:52:30 -06:00
|
|
|
auto view = static_cast<CameraView*>(anonymousView);
|
|
|
|
view.frameProcessor = nil;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-10-19 03:19:47 -06:00
|
|
|
jsi::Value VisionCameraProxy::initFrameProcessorPlugin(jsi::Runtime& runtime, std::string name, const jsi::Object& options) {
|
2023-07-21 09:52:30 -06:00
|
|
|
NSString* key = [NSString stringWithUTF8String:name.c_str()];
|
2023-09-01 11:39:25 -06:00
|
|
|
NSDictionary* optionsObjc = JSINSObjectConversion::convertJSIObjectToNSDictionary(runtime, options, _callInvoker);
|
2024-01-12 08:00:36 -07:00
|
|
|
VisionCameraProxyHolder* proxy = [[VisionCameraProxyHolder alloc] initWithProxy:this];
|
|
|
|
FrameProcessorPlugin* plugin = [FrameProcessorPluginRegistry getPlugin:key withProxy:proxy withOptions:optionsObjc];
|
2023-07-21 09:52:30 -06:00
|
|
|
if (plugin == nil) {
|
|
|
|
return jsi::Value::undefined();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto pluginHostObject = std::make_shared<FrameProcessorPluginHostObject>(plugin, _callInvoker);
|
|
|
|
return jsi::Object::createFromHostObject(runtime, pluginHostObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
jsi::Value VisionCameraProxy::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
|
|
|
|
auto name = propName.utf8(runtime);
|
|
|
|
|
|
|
|
if (name == "setFrameProcessor") {
|
2023-09-01 04:58:32 -06:00
|
|
|
return jsi::Function::createFromHostFunction(
|
|
|
|
runtime, jsi::PropNameID::forUtf8(runtime, "setFrameProcessor"), 1,
|
2023-09-01 11:39:25 -06:00
|
|
|
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
|
2023-09-01 04:58:32 -06:00
|
|
|
auto viewTag = arguments[0].asNumber();
|
|
|
|
auto object = arguments[1].asObject(runtime);
|
|
|
|
this->setFrameProcessor(runtime, static_cast<int>(viewTag), object);
|
|
|
|
return jsi::Value::undefined();
|
|
|
|
});
|
2023-07-21 09:52:30 -06:00
|
|
|
}
|
|
|
|
if (name == "removeFrameProcessor") {
|
2023-09-01 04:58:32 -06:00
|
|
|
return jsi::Function::createFromHostFunction(
|
|
|
|
runtime, jsi::PropNameID::forUtf8(runtime, "removeFrameProcessor"), 1,
|
2023-09-01 11:39:25 -06:00
|
|
|
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
|
2023-09-01 04:58:32 -06:00
|
|
|
auto viewTag = arguments[0].asNumber();
|
|
|
|
this->removeFrameProcessor(runtime, static_cast<int>(viewTag));
|
|
|
|
return jsi::Value::undefined();
|
|
|
|
});
|
2023-07-21 09:52:30 -06:00
|
|
|
}
|
2023-10-19 03:19:47 -06:00
|
|
|
if (name == "initFrameProcessorPlugin") {
|
2023-09-01 04:58:32 -06:00
|
|
|
return jsi::Function::createFromHostFunction(
|
2023-10-19 03:19:47 -06:00
|
|
|
runtime, jsi::PropNameID::forUtf8(runtime, "initFrameProcessorPlugin"), 1,
|
2023-09-01 11:39:25 -06:00
|
|
|
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
|
2023-09-01 04:58:32 -06:00
|
|
|
if (count < 1 || !arguments[0].isString()) {
|
|
|
|
throw jsi::JSError(runtime, "First argument needs to be a string (pluginName)!");
|
|
|
|
}
|
|
|
|
auto pluginName = arguments[0].asString(runtime).utf8(runtime);
|
|
|
|
auto options = count > 1 ? arguments[1].asObject(runtime) : jsi::Object(runtime);
|
|
|
|
|
2023-10-19 03:19:47 -06:00
|
|
|
return this->initFrameProcessorPlugin(runtime, pluginName, options);
|
2023-09-01 04:58:32 -06:00
|
|
|
});
|
2023-07-21 09:52:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return jsi::Value::undefined();
|
|
|
|
}
|
|
|
|
|
2024-01-12 08:00:36 -07:00
|
|
|
@implementation VisionCameraProxyHolder {
|
|
|
|
VisionCameraProxy* _proxy;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)initWithProxy:(void*)proxy {
|
|
|
|
if (self = [super init]) {
|
|
|
|
_proxy = (VisionCameraProxy*)proxy;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (VisionCameraProxy*)proxy {
|
|
|
|
return _proxy;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2023-07-21 09:52:30 -06:00
|
|
|
@implementation VisionCameraInstaller
|
2024-01-12 08:00:36 -07:00
|
|
|
|
2023-07-21 09:52:30 -06:00
|
|
|
+ (BOOL)installToBridge:(RCTBridge* _Nonnull)bridge {
|
|
|
|
RCTCxxBridge* cxxBridge = (RCTCxxBridge*)[RCTBridge currentBridge];
|
|
|
|
if (!cxxBridge.runtime) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsi::Runtime& runtime = *(jsi::Runtime*)cxxBridge.runtime;
|
|
|
|
|
|
|
|
// global.VisionCameraProxy
|
|
|
|
auto visionCameraProxy = std::make_shared<VisionCameraProxy>(runtime, bridge.jsCallInvoker);
|
2023-09-01 11:39:25 -06:00
|
|
|
runtime.global().setProperty(runtime, "VisionCameraProxy", jsi::Object::createFromHostObject(runtime, visionCameraProxy));
|
2023-07-21 09:52:30 -06:00
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
2024-01-12 08:00:36 -07:00
|
|
|
|
2023-07-21 09:52:30 -06:00
|
|
|
@end
|