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:
parent
61f19df500
commit
622d3830f1
@ -41,29 +41,35 @@ For reference see the [CLI's docs](https://github.com/mateusz1913/vision-camera-
|
|||||||
2. Create an Objective-C source file, for the QR Code Plugin this will be called `QRCodeFrameProcessorPlugin.m`.
|
2. Create an Objective-C source file, for the QR Code Plugin this will be called `QRCodeFrameProcessorPlugin.m`.
|
||||||
3. Add the following code:
|
3. Add the following code:
|
||||||
|
|
||||||
```objc {12}
|
```objc
|
||||||
#import <VisionCamera/FrameProcessorPlugin.h>
|
#import <VisionCamera/FrameProcessorPlugin.h>
|
||||||
#import <VisionCamera/Frame.h>
|
#import <VisionCamera/Frame.h>
|
||||||
|
|
||||||
@interface QRCodeFrameProcessorPlugin : NSObject
|
@interface QRCodeFrameProcessorPlugin : FrameProcessorPlugin
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation QRCodeFrameProcessorPlugin
|
@implementation QRCodeFrameProcessorPlugin
|
||||||
|
|
||||||
static inline id scanQRCodes(Frame* frame, NSArray* args) {
|
- (NSString *)name {
|
||||||
|
return @"scanQRCodes";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)callback:(Frame *)frame withArguments:(NSArray<id> *)arguments {
|
||||||
CMSampleBufferRef buffer = frame.buffer;
|
CMSampleBufferRef buffer = frame.buffer;
|
||||||
UIImageOrientation orientation = frame.orientation;
|
UIImageOrientation orientation = frame.orientation;
|
||||||
// code goes here
|
// code goes here
|
||||||
return @[];
|
return @[];
|
||||||
}
|
}
|
||||||
|
|
||||||
VISION_EXPORT_FRAME_PROCESSOR(scanQRCodes)
|
+ (void) load {
|
||||||
|
[self registerPlugin:[[ExampleFrameProcessorPlugin alloc] init]];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
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(...)`.
|
The Frame Processor Plugin will be exposed to JS through the `FrameProcessorPlugins` object using the name returned from the `name` getter. 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.
|
||||||
@ -83,27 +89,16 @@ The Frame Processor Plugin will be exposed to JS through the `FrameProcessorPlug
|
|||||||
#import <VisionCamera/Frame.h>
|
#import <VisionCamera/Frame.h>
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Create an Objective-C source file with the same name as the Swift file, for the QR Code Plugin this will be `QRCodeFrameProcessorPlugin.m`. Add the following code:
|
4. In the Swift file, add the following code:
|
||||||
|
|
||||||
```objc
|
```swift
|
||||||
#import <VisionCamera/FrameProcessorPlugin.h>
|
|
||||||
|
|
||||||
@interface VISION_EXPORT_SWIFT_FRAME_PROCESSOR(scanQRCodes, QRCodeFrameProcessorPlugin)
|
|
||||||
@end
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note
|
|
||||||
The first parameter in the Macro specifies the JS function name. Make sure it is unique across other Frame Processors.
|
|
||||||
:::
|
|
||||||
|
|
||||||
5. In the Swift file, add the following code:
|
|
||||||
|
|
||||||
```swift {8}
|
|
||||||
@objc(QRCodeFrameProcessorPlugin)
|
@objc(QRCodeFrameProcessorPlugin)
|
||||||
public class QRCodeFrameProcessorPlugin: NSObject, FrameProcessorPluginBase {
|
public class QRCodeFrameProcessorPlugin: FrameProcessorPlugin {
|
||||||
|
override public func name() -> String! {
|
||||||
|
return "scanQRCodes"
|
||||||
|
}
|
||||||
|
|
||||||
@objc
|
public override func callback(_ frame: Frame!, withArguments arguments: [Any]!) -> Any! {
|
||||||
public static func callback(_ frame: Frame!, withArgs _: [Any]!) -> Any! {
|
|
||||||
let buffer = frame.buffer
|
let buffer = frame.buffer
|
||||||
let orientation = frame.orientation
|
let orientation = frame.orientation
|
||||||
// code goes here
|
// code goes here
|
||||||
@ -112,7 +107,27 @@ public class QRCodeFrameProcessorPlugin: NSObject, FrameProcessorPluginBase {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
6. **Implement your frame processing.** See [Example Plugin (Swift)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/Example%20Plugin%20%28Swift%29) for reference.
|
5. In your `AppDelegate.m`, add the following imports (you can skip this if your AppDelegate is in Swift):
|
||||||
|
|
||||||
|
```objc
|
||||||
|
#import "YOUR_XCODE_PROJECT_NAME-Swift.h"
|
||||||
|
#import <VisionCamera/FrameProcessorPlugin.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
6. In your `AppDelegate.m`, add the following code to `application:didFinishLaunchingWithOptions:`:
|
||||||
|
|
||||||
|
```objc {5}
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
...
|
||||||
|
|
||||||
|
[FrameProcessorPlugin registerPlugin:[[QRCodeFrameProcessorPlugin alloc] init]];
|
||||||
|
|
||||||
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Implement your frame processing.** See [Example Plugin (Swift)](https://github.com/mrousavy/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/Example%20Plugin%20%28Swift%29) for reference.
|
||||||
|
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
@ -11,12 +11,16 @@
|
|||||||
|
|
||||||
// Example for an Objective-C Frame Processor plugin
|
// Example for an Objective-C Frame Processor plugin
|
||||||
|
|
||||||
@interface ExampleFrameProcessorPlugin : NSObject
|
@interface ExampleFrameProcessorPlugin : FrameProcessorPlugin
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ExampleFrameProcessorPlugin
|
@implementation ExampleFrameProcessorPlugin
|
||||||
|
|
||||||
static inline id example_plugin(Frame* frame, NSArray* arguments) {
|
- (NSString *)name {
|
||||||
|
return @"example_plugin";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)callback:(Frame *)frame withArguments:(NSArray<id> *)arguments {
|
||||||
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
|
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
|
||||||
NSLog(@"ExamplePlugin: %zu x %zu Image. Logging %lu parameters:", CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer), (unsigned long)arguments.count);
|
NSLog(@"ExamplePlugin: %zu x %zu Image. Logging %lu parameters:", CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer), (unsigned long)arguments.count);
|
||||||
|
|
||||||
@ -36,6 +40,8 @@ static inline id example_plugin(Frame* frame, NSArray* arguments) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
VISION_EXPORT_FRAME_PROCESSOR(example_plugin)
|
+ (void) load {
|
||||||
|
[self registerPlugin:[[ExampleFrameProcessorPlugin alloc] init]];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// ExamplePluginSwift.m
|
|
||||||
// VisionCamera
|
|
||||||
//
|
|
||||||
// Created by Marc Rousavy on 01.05.21.
|
|
||||||
// Copyright © 2021 mrousavy. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <VisionCamera/FrameProcessorPlugin.h>
|
|
||||||
|
|
||||||
@interface VISION_EXPORT_SWIFT_FRAME_PROCESSOR(example_plugin_swift, ExamplePluginSwift)
|
|
||||||
@end
|
|
@ -9,16 +9,20 @@
|
|||||||
import AVKit
|
import AVKit
|
||||||
import Vision
|
import Vision
|
||||||
|
|
||||||
@objc(ExamplePluginSwift)
|
|
||||||
public class ExamplePluginSwift: NSObject, FrameProcessorPluginBase {
|
|
||||||
@objc
|
@objc
|
||||||
public static func callback(_ frame: Frame!, withArgs args: [Any]!) -> Any! {
|
public class ExamplePluginSwift : FrameProcessorPlugin {
|
||||||
|
|
||||||
|
override public func name() -> String! {
|
||||||
|
return "example_plugin_swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func callback(_ frame: Frame!, withArguments arguments: [Any]!) -> Any! {
|
||||||
guard let imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer) else {
|
guard let imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
NSLog("ExamplePlugin: \(CVPixelBufferGetWidth(imageBuffer)) x \(CVPixelBufferGetHeight(imageBuffer)) Image. Logging \(args.count) parameters:")
|
NSLog("ExamplePlugin: \(CVPixelBufferGetWidth(imageBuffer)) x \(CVPixelBufferGetHeight(imageBuffer)) Image. Logging \(arguments.count) parameters:")
|
||||||
|
|
||||||
args.forEach { arg in
|
arguments.forEach { arg in
|
||||||
var string = "\(arg)"
|
var string = "\(arg)"
|
||||||
if let array = arg as? NSArray {
|
if let array = arg as? NSArray {
|
||||||
string = (array as Array).description
|
string = (array as Array).description
|
||||||
|
@ -436,7 +436,7 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- RNVectorIcons (9.2.0):
|
- RNVectorIcons (9.2.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- VisionCamera (3.0.0-rc.1):
|
- VisionCamera (3.0.0-rc.2):
|
||||||
- React
|
- React
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
@ -644,7 +644,7 @@ SPEC CHECKSUMS:
|
|||||||
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
|
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
|
||||||
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
|
||||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||||
VisionCamera: 0d154cd0ab9043a3c8a4908fb57ad65c9e1f3baf
|
VisionCamera: e4f19a6c22cfa146736bdfe8df057e2ed5ca8dd3
|
||||||
Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
|
Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
|
||||||
|
|
||||||
PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636
|
PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636
|
||||||
|
@ -11,10 +11,9 @@
|
|||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||||
B8DB3BD5263DE8B7004C18D7 /* BuildFile in Sources */ = {isa = PBXBuildFile; };
|
B8DB3BD5263DE8B7004C18D7 /* (null) in Sources */ = {isa = PBXBuildFile; };
|
||||||
B8DB3BDC263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BD8263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m */; };
|
B8DB3BDC263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BD8263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m */; };
|
||||||
B8DB3BDD263DEA31004C18D7 /* ExamplePluginSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */; };
|
B8DB3BDD263DEA31004C18D7 /* ExamplePluginSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */; };
|
||||||
B8DB3BDE263DEA31004C18D7 /* ExamplePluginSwift.m in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BDB263DEA31004C18D7 /* ExamplePluginSwift.m */; };
|
|
||||||
B8F0E10825E0199F00586F16 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F0E10725E0199F00586F16 /* File.swift */; };
|
B8F0E10825E0199F00586F16 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F0E10725E0199F00586F16 /* File.swift */; };
|
||||||
C0B129659921D2EA967280B2 /* libPods-VisionCameraExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CDCFE89C25C89320B98945E /* libPods-VisionCameraExample.a */; };
|
C0B129659921D2EA967280B2 /* libPods-VisionCameraExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CDCFE89C25C89320B98945E /* libPods-VisionCameraExample.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@ -31,7 +30,6 @@
|
|||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
B8DB3BD8263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleFrameProcessorPlugin.m; sourceTree = "<group>"; };
|
B8DB3BD8263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleFrameProcessorPlugin.m; sourceTree = "<group>"; };
|
||||||
B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExamplePluginSwift.swift; sourceTree = "<group>"; };
|
B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExamplePluginSwift.swift; sourceTree = "<group>"; };
|
||||||
B8DB3BDB263DEA31004C18D7 /* ExamplePluginSwift.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExamplePluginSwift.m; sourceTree = "<group>"; };
|
|
||||||
B8F0E10625E0199F00586F16 /* VisionCameraExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VisionCameraExample-Bridging-Header.h"; sourceTree = "<group>"; };
|
B8F0E10625E0199F00586F16 /* VisionCameraExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VisionCameraExample-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
B8F0E10725E0199F00586F16 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
B8F0E10725E0199F00586F16 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||||
C1D342AD8210E7627A632602 /* Pods-VisionCameraExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample.debug.xcconfig"; sourceTree = "<group>"; };
|
C1D342AD8210E7627A632602 /* Pods-VisionCameraExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
@ -138,7 +136,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */,
|
B8DB3BDA263DEA31004C18D7 /* ExamplePluginSwift.swift */,
|
||||||
B8DB3BDB263DEA31004C18D7 /* ExamplePluginSwift.m */,
|
|
||||||
);
|
);
|
||||||
path = "Example Plugin (Swift)";
|
path = "Example Plugin (Swift)";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -383,11 +380,10 @@
|
|||||||
files = (
|
files = (
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||||
B8DB3BDC263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m in Sources */,
|
B8DB3BDC263DEA31004C18D7 /* ExampleFrameProcessorPlugin.m in Sources */,
|
||||||
B8DB3BD5263DE8B7004C18D7 /* BuildFile in Sources */,
|
B8DB3BD5263DE8B7004C18D7 /* (null) in Sources */,
|
||||||
B8DB3BDD263DEA31004C18D7 /* ExamplePluginSwift.swift in Sources */,
|
B8DB3BDD263DEA31004C18D7 /* ExamplePluginSwift.swift in Sources */,
|
||||||
B8F0E10825E0199F00586F16 /* File.swift in Sources */,
|
B8F0E10825E0199F00586F16 /* File.swift in Sources */,
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||||
B8DB3BDE263DEA31004C18D7 /* ExamplePluginSwift.m in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import "VisionCameraExample-Swift.h"
|
||||||
|
#import <VisionCamera/FrameProcessorPlugin.h>
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
@ -11,6 +13,8 @@
|
|||||||
// They will be passed down to the ViewController used by React Native.
|
// They will be passed down to the ViewController used by React Native.
|
||||||
self.initialProps = @{};
|
self.initialProps = @{};
|
||||||
|
|
||||||
|
[FrameProcessorPlugin registerPlugin:[[ExamplePluginSwift alloc] init]];
|
||||||
|
|
||||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,57 +6,27 @@
|
|||||||
// Copyright © 2021 mrousavy. All rights reserved.
|
// Copyright © 2021 mrousavy. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef FrameProcessorPlugin_h
|
#pragma once
|
||||||
#define FrameProcessorPlugin_h
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "FrameProcessorPluginRegistry.h"
|
|
||||||
#import "Frame.h"
|
#import "Frame.h"
|
||||||
|
|
||||||
@protocol FrameProcessorPluginBase
|
/// The base class for a Frame Processor Plugin which can be called synchronously from a JS Frame Processor.
|
||||||
+ (id) callback:(Frame*)frame withArgs:(NSArray<id>*)args;
|
///
|
||||||
|
/// 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
|
@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 <Foundation/Foundation.h>
|
||||||
#import "Frame.h"
|
#import "Frame.h"
|
||||||
|
#import "FrameProcessorPlugin.h"
|
||||||
typedef id (^FrameProcessorPlugin) (Frame* frame, NSArray<id>* arguments);
|
|
||||||
|
|
||||||
@interface FrameProcessorPluginRegistry : NSObject
|
@interface FrameProcessorPluginRegistry : NSObject
|
||||||
|
|
||||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins;
|
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin*>*)frameProcessorPlugins;
|
||||||
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback;
|
+ (void) addFrameProcessorPlugin:(FrameProcessorPlugin*)plugin;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -11,19 +11,19 @@
|
|||||||
|
|
||||||
@implementation FrameProcessorPluginRegistry
|
@implementation FrameProcessorPluginRegistry
|
||||||
|
|
||||||
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins {
|
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin*>*)frameProcessorPlugins {
|
||||||
static NSMutableDictionary<NSString*, FrameProcessorPlugin>* plugins = nil;
|
static NSMutableDictionary<NSString*, FrameProcessorPlugin*>* plugins = nil;
|
||||||
if (plugins == nil) {
|
if (plugins == nil) {
|
||||||
plugins = [[NSMutableDictionary alloc] init];
|
plugins = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback {
|
+ (void) addFrameProcessorPlugin:(FrameProcessorPlugin*)plugin {
|
||||||
BOOL alreadyExists = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:name] != nil;
|
BOOL alreadyExists = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:plugin.name] != nil;
|
||||||
NSAssert(!alreadyExists, @"Tried to two Frame Processor Plugins with the same name! Either choose unique names, or remove the unused plugin.");
|
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
|
@end
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "FrameProcessorRuntimeManager.h"
|
#import "FrameProcessorRuntimeManager.h"
|
||||||
#import "FrameProcessorPluginRegistry.h"
|
#import "FrameProcessorPluginRegistry.h"
|
||||||
|
#import "FrameProcessorPlugin.h"
|
||||||
#import "FrameHostObject.h"
|
#import "FrameHostObject.h"
|
||||||
|
|
||||||
#import <memory>
|
#import <memory>
|
||||||
@ -83,11 +84,11 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
|||||||
auto pluginName = [pluginKey UTF8String];
|
auto pluginName = [pluginKey UTF8String];
|
||||||
|
|
||||||
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
|
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
|
||||||
// Get the Plugin callback func
|
// Get the Plugin
|
||||||
FrameProcessorPlugin callback = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
|
FrameProcessorPlugin* plugin = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
|
||||||
|
|
||||||
// Create the JSI host function
|
// Create the JSI host function
|
||||||
auto function = [callback, callInvoker](jsi::Runtime& runtime,
|
auto function = [plugin, callInvoker](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 {
|
||||||
@ -101,7 +102,7 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
|
|||||||
count - 1, // use smaller count
|
count - 1, // use smaller count
|
||||||
callInvoker);
|
callInvoker);
|
||||||
// Call the FP Plugin, which might return something.
|
// 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
|
// Convert the return value (or null) to a JS Value and return it to JS
|
||||||
return convertObjCObjectToJSIValue(runtime, result);
|
return convertObjCObjectToJSIValue(runtime, result);
|
||||||
|
@ -110,6 +110,7 @@
|
|||||||
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>"; };
|
||||||
B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVAudioSession.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
B882720F26AEB1A100B14107 /* AVCaptureConnection+setInterfaceOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureConnection+setInterfaceOrientation.swift"; sourceTree = "<group>"; };
|
||||||
@ -285,6 +286,7 @@
|
|||||||
B80C0DFE260BDD97001699AB /* FrameProcessorPluginRegistry.h */,
|
B80C0DFE260BDD97001699AB /* FrameProcessorPluginRegistry.h */,
|
||||||
B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */,
|
B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */,
|
||||||
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */,
|
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */,
|
||||||
|
B86F803429A90DBD00205E48 /* FrameProcessorPlugin.m */,
|
||||||
);
|
);
|
||||||
path = "Frame Processor";
|
path = "Frame Processor";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
Loading…
Reference in New Issue
Block a user