fix: Fix UI Thread race condition in setFrameProcessor(...)
(#265)
* fix: Fix UI Thread race condition in `setFrameProcessor(...)` * Revert "fix: Fix UI Thread race condition in `setFrameProcessor(...)`" This reverts commit 9c524e123cff6843d7d11db602a5027d1bb06b4b. * Use `setImmediate` to call `setFrameProcessor(...)` * Fix frame processor order of applying * Add `enableFrameProcessor` prop that defines if a FP is added * rename constant * Implement `enableFrameProcessor` prop for Android and make `frameProcessorFps` faster * link to troubleshooting guide * Update TROUBLESHOOTING.mdx * Add logs for use-cases * fix log * set initial frame processor in `onLayout` instead of `componentDidMount`
This commit is contained in:
@@ -30,8 +30,7 @@ void CameraView::registerNatives() {
|
||||
|
||||
void CameraView::frameProcessorCallback(const alias_ref<JImageProxy::javaobject>& frame) {
|
||||
if (frameProcessor_ == nullptr) {
|
||||
__android_log_write(ANDROID_LOG_WARN, TAG, "Frame Processor is null!");
|
||||
setEnableFrameProcessor(false);
|
||||
__android_log_write(ANDROID_LOG_WARN, TAG, "Called Frame Processor callback, but `frameProcessor` is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,24 +44,12 @@ void CameraView::frameProcessorCallback(const alias_ref<JImageProxy::javaobject>
|
||||
}
|
||||
}
|
||||
|
||||
void CameraView::setEnableFrameProcessor(bool enable) {
|
||||
if (enable) {
|
||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Enabling Frame Processor Callback...");
|
||||
} else {
|
||||
__android_log_write(ANDROID_LOG_INFO, TAG, "Disabling Frame Processor Callback...");
|
||||
}
|
||||
static const auto javaMethod = javaPart_->getClass()->getMethod<void(bool)>("setEnableFrameProcessor");
|
||||
javaMethod(javaPart_.get(), enable);
|
||||
}
|
||||
|
||||
void CameraView::setFrameProcessor(const FrameProcessor&& frameProcessor) {
|
||||
frameProcessor_ = frameProcessor;
|
||||
setEnableFrameProcessor(true);
|
||||
}
|
||||
|
||||
void vision::CameraView::unsetFrameProcessor() {
|
||||
frameProcessor_ = nullptr;
|
||||
setEnableFrameProcessor(false);
|
||||
}
|
||||
|
||||
} // namespace vision
|
||||
|
@@ -26,7 +26,6 @@ class CameraView : public jni::HybridClass<CameraView> {
|
||||
// TODO: Use template<> to avoid heap allocation for std::function<>
|
||||
void setFrameProcessor(const FrameProcessor&& frameProcessor);
|
||||
void unsetFrameProcessor();
|
||||
void setEnableFrameProcessor(bool enable);
|
||||
|
||||
private:
|
||||
friend HybridBase;
|
||||
|
@@ -71,6 +71,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
var photo: Boolean? = null
|
||||
var video: Boolean? = null
|
||||
var audio: Boolean? = null
|
||||
var enableFrameProcessor = false
|
||||
// props that require format reconfiguring
|
||||
var format: ReadableMap? = null
|
||||
var fps: Int? = null
|
||||
@@ -88,8 +89,6 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
private val reactContext: ReactContext
|
||||
get() = context as ReactContext
|
||||
|
||||
private var enableFrameProcessor = false
|
||||
|
||||
@Suppress("JoinDeclarationAndAssignment")
|
||||
internal val previewView: PreviewView
|
||||
private val cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
@@ -99,7 +98,10 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
internal var camera: Camera? = null
|
||||
internal var imageCapture: ImageCapture? = null
|
||||
internal var videoCapture: VideoCapture? = null
|
||||
internal var imageAnalysis: ImageAnalysis? = null
|
||||
private var imageAnalysis: ImageAnalysis? = null
|
||||
|
||||
private var lastFrameProcessorCall = System.currentTimeMillis()
|
||||
|
||||
private var extensionsManager: ExtensionsManager? = null
|
||||
|
||||
private val scaleGestureListener: ScaleGestureDetector.SimpleOnScaleGestureListener
|
||||
@@ -191,26 +193,6 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
private external fun initHybrid(): HybridData
|
||||
private external fun frameProcessorCallback(frame: ImageProxy)
|
||||
|
||||
@Suppress("unused")
|
||||
@DoNotStrip
|
||||
fun setEnableFrameProcessor(enable: Boolean) {
|
||||
Log.d(TAG, "Set enable frame processor: $enable")
|
||||
val before = enableFrameProcessor
|
||||
enableFrameProcessor = enable
|
||||
|
||||
if (before != enable) {
|
||||
// reconfigure session if frame processor was added/removed to adjust use-cases.
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
try {
|
||||
configureSession()
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "Failed to configure session after setting frame processor! ${e.message}")
|
||||
invokeOnError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
return lifecycleRegistry
|
||||
}
|
||||
@@ -383,6 +365,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
// Bind use cases to camera
|
||||
val useCases = ArrayList<UseCase>()
|
||||
if (video == true) {
|
||||
Log.i(TAG, "Adding VideoCapture use-case...")
|
||||
videoCapture = videoCaptureBuilder.build()
|
||||
useCases.add(videoCapture!!)
|
||||
}
|
||||
@@ -391,18 +374,19 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
Log.i(TAG, "Tried to add photo use-case (`photo={true}`) but the Camera device only supports " +
|
||||
"a single use-case at a time. Falling back to Snapshot capture.")
|
||||
} else {
|
||||
Log.i(TAG, "Adding ImageCapture use-case...")
|
||||
imageCapture = imageCaptureBuilder.build()
|
||||
useCases.add(imageCapture!!)
|
||||
}
|
||||
}
|
||||
if (enableFrameProcessor) {
|
||||
var lastCall = System.currentTimeMillis() - 1000
|
||||
val intervalMs = (1.0 / frameProcessorFps) * 1000.0
|
||||
Log.i(TAG, "Adding ImageAnalysis use-case...")
|
||||
imageAnalysis = imageAnalysisBuilder.build().apply {
|
||||
setAnalyzer(cameraExecutor, { image ->
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - lastCall > intervalMs) {
|
||||
lastCall = now
|
||||
val intervalMs = (1.0 / frameProcessorFps) * 1000.0
|
||||
if (now - lastFrameProcessorCall > intervalMs) {
|
||||
lastFrameProcessorCall = now
|
||||
frameProcessorCallback(image)
|
||||
}
|
||||
image.close()
|
||||
@@ -477,7 +461,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
||||
const val TAG = "CameraView"
|
||||
const val TAG_PERF = "CameraView.performance"
|
||||
|
||||
private val propsThatRequireSessionReconfiguration = arrayListOf("cameraId", "format", "fps", "hdr", "lowLightBoost", "photo", "video", "frameProcessorFps")
|
||||
private val propsThatRequireSessionReconfiguration = arrayListOf("cameraId", "format", "fps", "hdr", "lowLightBoost", "photo", "video", "enableFrameProcessor")
|
||||
private val arrayListOfZoom = arrayListOf("zoom")
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,13 @@ class CameraViewManager : SimpleViewManager<CameraView>() {
|
||||
view.audio = audio
|
||||
}
|
||||
|
||||
@ReactProp(name = "enableFrameProcessor")
|
||||
fun setEnableFrameProcessor(view: CameraView, enableFrameProcessor: Boolean) {
|
||||
if (view.enableFrameProcessor != enableFrameProcessor)
|
||||
addChangedPropToTransaction(view, "enableFrameProcessor")
|
||||
view.enableFrameProcessor = enableFrameProcessor
|
||||
}
|
||||
|
||||
@ReactProp(name = "enableDepthData")
|
||||
fun setEnableDepthData(view: CameraView, enableDepthData: Boolean) {
|
||||
if (view.enableDepthData != enableDepthData)
|
||||
|
Reference in New Issue
Block a user