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:
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user