feat: Make Frame Processor Plugins object-oriented on iOS as well (#1496)
* feat: Make Frame Processor Plugins object-oriented on iOS as well * Add Plugin in AppDelegate
This commit is contained in:
@@ -6,57 +6,27 @@
|
||||
// Copyright © 2021 mrousavy. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef FrameProcessorPlugin_h
|
||||
#define FrameProcessorPlugin_h
|
||||
#pragma once
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FrameProcessorPluginRegistry.h"
|
||||
#import "Frame.h"
|
||||
|
||||
@protocol FrameProcessorPluginBase
|
||||
+ (id) callback:(Frame*)frame withArgs:(NSArray<id>*)args;
|
||||
/// The base class for a Frame Processor Plugin which can be called synchronously from a JS Frame Processor.
|
||||
///
|
||||
/// Subclass this class in a Swift or Objective-C class and override the `callback:withArguments:` method, and
|
||||
/// implement your Frame Processing there.
|
||||
/// Then, in your App's startup (AppDelegate.m), call `FrameProcessorPluginBase.registerPlugin(YourNewPlugin())`
|
||||
@interface FrameProcessorPlugin : NSObject
|
||||
|
||||
/// Get the name of the Frame Processor Plugin.
|
||||
/// This will be exposed to JS under the `FrameProcessorPlugins` Proxy object.
|
||||
- (NSString *)name;
|
||||
|
||||
/// The actual callback when calling this plugin. Any Frame Processing should be handled there.
|
||||
/// Make sure your code is optimized, as this is a hot path.
|
||||
- (id) callback:(Frame*)frame withArguments:(NSArray<id>*)arguments;
|
||||
|
||||
/// Register the given plugin in the Plugin Registry. This should be called on App Startup.
|
||||
+ (void) registerPlugin:(FrameProcessorPlugin*)plugin;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#define VISION_CONCAT2(A, B) A##B
|
||||
#define VISION_CONCAT(A, B) VISION_CONCAT2(A, B)
|
||||
|
||||
/**
|
||||
* Use this Macro to register the given function as a Frame Processor.
|
||||
* * Make sure the given function is a C-style function with the following signature: static inline id callback(Frame* frame, NSArray* args)
|
||||
* * Make sure the given function's name is unique across other frame processor plugins
|
||||
* * 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define VISION_EXPORT_FRAME_PROCESSOR(frame_processor) \
|
||||
\
|
||||
+(void)load \
|
||||
{ \
|
||||
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@ #frame_processor callback:^id(Frame* frame, NSArray<id>* args) { \
|
||||
return frame_processor(frame, args); \
|
||||
}]; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Same as VISION_EXPORT_FRAME_PROCESSOR, but uses __attribute__((constructor)) for
|
||||
* registration. Useful for registering swift classes that forbids use of +(void)load.
|
||||
*/
|
||||
#define VISION_EXPORT_SWIFT_FRAME_PROCESSOR(name, objc_name) \
|
||||
objc_name : NSObject<FrameProcessorPluginBase> \
|
||||
@end \
|
||||
\
|
||||
@interface objc_name (FrameProcessorPlugin) \
|
||||
@end \
|
||||
@implementation objc_name (FrameProcessorPlugin) \
|
||||
\
|
||||
+(void)load \
|
||||
{ \
|
||||
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@ #name callback:^id(Frame* frame, NSArray<id>* args) { \
|
||||
return [objc_name callback:frame withArgs:args]; \
|
||||
}]; \
|
||||
}
|
||||
|
||||
#endif /* FrameProcessorPlugin_h */
|
||||
|
31
ios/Frame Processor/FrameProcessorPlugin.m
Normal file
31
ios/Frame Processor/FrameProcessorPlugin.m
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// FrameProcessorPlugin.m
|
||||
// VisionCamera
|
||||
//
|
||||
// Created by Marc Rousavy on 24.02.23.
|
||||
// Copyright © 2023 mrousavy. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FrameProcessorPlugin.h"
|
||||
#import "FrameProcessorPluginRegistry.h"
|
||||
|
||||
@implementation FrameProcessorPlugin
|
||||
|
||||
- (NSString *)name {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Frame Processor Plugin \"%@\" does not override the `name` getter!", [self name]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)callback:(Frame *)frame withArguments:(NSArray<id> *)arguments {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Frame Processor Plugin \"%@\" does not override the `callback(frame:withArguments:)` method!", [self name]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (void)registerPlugin:(FrameProcessorPlugin *)plugin {
|
||||
[FrameProcessorPluginRegistry addFrameProcessorPlugin:plugin];
|
||||
}
|
||||
|
||||
@end
|
@@ -10,12 +10,11 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Frame.h"
|
||||
|
||||
typedef id (^FrameProcessorPlugin) (Frame* frame, NSArray<id>* arguments);
|
||||
#import "FrameProcessorPlugin.h"
|
||||
|
||||
@interface FrameProcessorPluginRegistry : NSObject
|
||||
|
||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins;
|
||||
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback;
|
||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin*>*)frameProcessorPlugins;
|
||||
+ (void) addFrameProcessorPlugin:(FrameProcessorPlugin*)plugin;
|
||||
|
||||
@end
|
||||
|
@@ -11,19 +11,19 @@
|
||||
|
||||
@implementation FrameProcessorPluginRegistry
|
||||
|
||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins {
|
||||
static NSMutableDictionary<NSString*, FrameProcessorPlugin>* plugins = nil;
|
||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin*>*)frameProcessorPlugins {
|
||||
static NSMutableDictionary<NSString*, FrameProcessorPlugin*>* plugins = nil;
|
||||
if (plugins == nil) {
|
||||
plugins = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback {
|
||||
BOOL alreadyExists = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:name] != nil;
|
||||
NSAssert(!alreadyExists, @"Tried to two Frame Processor Plugins with the same name! Either choose unique names, or remove the unused plugin.");
|
||||
+ (void) addFrameProcessorPlugin:(FrameProcessorPlugin*)plugin {
|
||||
BOOL alreadyExists = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:plugin.name] != nil;
|
||||
NSAssert(!alreadyExists, @"Tried to add a Frame Processor Plugin with a name that already exists! Either choose unique names, or remove the unused plugin. Name: %@", plugin.name);
|
||||
|
||||
[[FrameProcessorPluginRegistry frameProcessorPlugins] setValue:callback forKey:name];
|
||||
[[FrameProcessorPluginRegistry frameProcessorPlugins] setValue:plugin forKey:plugin.name];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FrameProcessorRuntimeManager.h"
|
||||
#import "FrameProcessorPluginRegistry.h"
|
||||
#import "FrameProcessorPlugin.h"
|
||||
#import "FrameHostObject.h"
|
||||
|
||||
#import <memory>
|
||||
@@ -83,14 +84,14 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
||||
auto pluginName = [pluginKey UTF8String];
|
||||
|
||||
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
|
||||
// Get the Plugin callback func
|
||||
FrameProcessorPlugin callback = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
|
||||
// Get the Plugin
|
||||
FrameProcessorPlugin* plugin = [[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 {
|
||||
auto function = [plugin, 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());
|
||||
@@ -101,7 +102,7 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
||||
count - 1, // use smaller count
|
||||
callInvoker);
|
||||
// Call the FP Plugin, which might return something.
|
||||
id result = callback(frame->frame, args);
|
||||
id result = [plugin callback:frame->frame withArguments:args];
|
||||
|
||||
// Convert the return value (or null) to a JS Value and return it to JS
|
||||
return convertObjCObjectToJSIValue(runtime, result);
|
||||
|
@@ -110,6 +110,7 @@
|
||||
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAudioSession+trySetAllowHaptics.swift"; sourceTree = "<group>"; };
|
||||
B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVAudioSession.swift"; sourceTree = "<group>"; };
|
||||
B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVCaptureSession.swift"; sourceTree = "<group>"; };
|
||||
B86F803429A90DBD00205E48 /* FrameProcessorPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrameProcessorPlugin.m; sourceTree = "<group>"; };
|
||||
B8805065266798AB00EAD7F2 /* JSConsoleHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSConsoleHelper.h; sourceTree = "<group>"; };
|
||||
B8805066266798B600EAD7F2 /* JSConsoleHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSConsoleHelper.mm; sourceTree = "<group>"; };
|
||||
B882720F26AEB1A100B14107 /* AVCaptureConnection+setInterfaceOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureConnection+setInterfaceOrientation.swift"; sourceTree = "<group>"; };
|
||||
@@ -285,6 +286,7 @@
|
||||
B80C0DFE260BDD97001699AB /* FrameProcessorPluginRegistry.h */,
|
||||
B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */,
|
||||
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */,
|
||||
B86F803429A90DBD00205E48 /* FrameProcessorPlugin.m */,
|
||||
);
|
||||
path = "Frame Processor";
|
||||
sourceTree = "<group>";
|
||||
|
Reference in New Issue
Block a user