feat: Replace Reanimated with RN Worklets (#1468)

* Setup RN Worklets

* Use RN Worklets on iOS

* Fix console

* Add `installFrameProcessorBindings()` function

* Add `FrameProcessorPlugins` proxy (BREAKING CHANGE)

* Clean up docs

* Update FRAME_PROCESSORS.mdx

* Use RN Worklets 0.2.5

* feat: Android build setup

* Rewrite Android Frame Processor Part

* Update CMakeLists.txt

* fix: Add react-native-worklets Gradle dependency

* Update Podfile.lock

* fix build

* gradle:7.4.1

* Init JSI Bindings in method on Android

* Fix Folly flags

* fix: Init `FrameProcessorRuntimeManager` later

* fix: Wrap in `<GestureHandlerRootView>`

* Refactor plugins

* fix: Remove enableFrameProcessors

* Install RN Worklets from current GH master

* Update babel.config.js

* Update CameraViewModule.kt

* Update ImageProxyUtils.java

* feat: Upgrade to Reanimated v3

* fix: Fix crash on Worklet init

* Update RN Worklets to latest master

* fix: Simplify FP Plugins Proxy
This commit is contained in:
Marc Rousavy
2023-02-13 15:22:45 +01:00
committed by GitHub
parent 11d1e7178d
commit a0590dccb5
55 changed files with 469 additions and 861 deletions

View File

@@ -210,9 +210,7 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
}
init {
if (FrameProcessorRuntimeManager.enableFrameProcessors) {
mHybridData = initHybrid()
}
mHybridData = initHybrid()
previewView = PreviewView(context)
previewView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)

View File

@@ -20,9 +20,6 @@ import com.facebook.react.modules.core.PermissionAwareActivity
import com.facebook.react.modules.core.PermissionListener
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
import com.mrousavy.camera.CameraView
import com.mrousavy.camera.ViewNotFoundError
import java.util.concurrent.ExecutorService
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
import com.mrousavy.camera.parsers.*
@@ -55,17 +52,6 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
if (coroutineScope.isActive) {
coroutineScope.cancel("CameraViewModule has been destroyed.")
}
frameProcessorManager = null
}
override fun initialize() {
super.initialize()
if (frameProcessorManager == null) {
frameProcessorThread.execute {
frameProcessorManager = FrameProcessorRuntimeManager(reactApplicationContext, frameProcessorThread)
}
}
}
override fun onCatalystInstanceDestroy() {
@@ -165,6 +151,18 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
}
}
@ReactMethod(isBlockingSynchronousMethod = true)
fun installFrameProcessorBindings(): Boolean {
try {
frameProcessorManager = FrameProcessorRuntimeManager(reactApplicationContext, frameProcessorThread)
frameProcessorManager!!.installBindings()
return true
} catch (e: Error) {
Log.e(TAG, "Failed to install Frame Processor JSI Bindings!", e)
return false
}
}
// TODO: This uses the Camera2 API to list all characteristics of a camera device and therefore doesn't work with Camera1. Find a way to use CameraX for this
// https://issuetracker.google.com/issues/179925896
@ReactMethod

View File

@@ -48,6 +48,6 @@ public abstract class FrameProcessorPlugin {
* @param plugin An instance of a plugin.
*/
public static void register(@NonNull FrameProcessorPlugin plugin) {
FrameProcessorRuntimeManager.Companion.getPlugins().add(plugin);
FrameProcessorRuntimeManager.Companion.addPlugin(plugin);
}
}

View File

@@ -16,18 +16,20 @@ import java.util.concurrent.ExecutorService
class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProcessorThread: ExecutorService) {
companion object {
const val TAG = "FrameProcessorRuntime"
val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
var enableFrameProcessors = true
private val Plugins: ArrayList<FrameProcessorPlugin> = ArrayList()
init {
try {
System.loadLibrary("reanimated")
System.loadLibrary("VisionCamera")
} catch (e: UnsatisfiedLinkError) {
Log.w(TAG, "Failed to load Reanimated/VisionCamera C++ library. Frame Processors are disabled!")
enableFrameProcessors = false
Log.e(TAG, "Failed to load VisionCamera C++ library!", e)
throw e
}
}
fun addPlugin(plugin: FrameProcessorPlugin) {
Plugins.add(plugin)
}
}
@DoNotStrip
@@ -36,24 +38,11 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProces
private var mScheduler: VisionCameraScheduler? = null
init {
if (enableFrameProcessors) {
val holder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
mScheduler = VisionCameraScheduler(frameProcessorThread)
mContext = WeakReference(context)
mHybridData = initHybrid(context.javaScriptContextHolder.get(), holder, mScheduler!!)
initializeRuntime()
Log.i(TAG, "Installing Frame Processor Plugins...")
Plugins.forEach { plugin ->
registerPlugin(plugin)
}
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
Log.i(TAG, "Installing JSI Bindings on JS Thread...")
context.runOnJSQueueThread {
installJSIBindings()
}
}
val jsCallInvokerHolder = context.catalystInstance.jsCallInvokerHolder as CallInvokerHolderImpl
val jsRuntimeHolder = context.javaScriptContextHolder.get()
mScheduler = VisionCameraScheduler(frameProcessorThread)
mContext = WeakReference(context)
mHybridData = initHybrid(jsRuntimeHolder, jsCallInvokerHolder, mScheduler!!)
}
@Suppress("unused")
@@ -67,13 +56,22 @@ class FrameProcessorRuntimeManager(context: ReactApplicationContext, frameProces
return view ?: throw ViewNotFoundError(viewId)
}
fun installBindings() {
Log.i(TAG, "Installing JSI Bindings on JS Thread...")
installJSIBindings()
Log.i(TAG, "Installing Frame Processor Plugins...")
Plugins.forEach { plugin ->
registerPlugin(plugin)
}
Log.i(TAG, "Successfully installed ${Plugins.count()} Frame Processor Plugins!")
}
// private C++ funcs
private external fun initHybrid(
jsContext: Long,
jsCallInvokerHolder: CallInvokerHolderImpl,
scheduler: VisionCameraScheduler
): HybridData
private external fun initializeRuntime()
private external fun registerPlugin(plugin: FrameProcessorPlugin)
private external fun installJSIBindings()
}

View File

@@ -19,7 +19,7 @@ public class ImageProxyUtils {
Image image = imageProxy.getImage();
if (image == null) return false;
// will throw an exception if the image is already closed
imageProxy.getImage().getCropRect();
image.getCropRect();
// no exception thrown, image must still be valid.
return true;
} catch (Exception e) {

View File

@@ -17,11 +17,11 @@ public class VisionCameraScheduler {
}
private native HybridData initHybrid();
private native void triggerUI();
private native void trigger();
@SuppressWarnings("unused")
@DoNotStrip
private void scheduleTrigger() {
frameProcessorThread.submit(this::triggerUI);
frameProcessorThread.submit(this::trigger);
}
}