feat: Sync Frame Processors (plus runAsync and runAtTargetFps) (#1472)

Before, Frame Processors ran on a separate Thread.

After, Frame Processors run fully synchronous and always at the same FPS as the Camera.

Two new functions have been introduced:

* `runAtTargetFps(fps: number, func: () => void)`: Runs the given code as often as the given `fps`, effectively throttling it's calls.
* `runAsync(frame: Frame, func: () => void)`: Runs the given function on a separate Thread for Frame Processing. A strong reference to the Frame is held as long as the function takes to execute.

You can use `runAtTargetFps` to throttle calls to a specific API (e.g. if your Camera is running at 60 FPS, but you only want to run face detection at ~25 FPS, use `runAtTargetFps(25, ...)`.)

You can use `runAsync` to run a heavy algorithm asynchronous, so that the Camera is not blocked while your algorithm runs. This is useful if your main sync processor draws something, and your async processor is doing some image analysis on the side. 

You can also combine both functions.

Examples:

```js
const frameProcessor = useFrameProcessor((frame) => {
  'worklet'
  console.log("I'm running at 60 FPS!")
}, [])
```

```js
const frameProcessor = useFrameProcessor((frame) => {
  'worklet'
  console.log("I'm running at 60 FPS!")

  runAtTargetFps(10, () => {
    'worklet'
    console.log("I'm running at 10 FPS!")
  })
}, [])
```



```js
const frameProcessor = useFrameProcessor((frame) => {
  'worklet'
  console.log("I'm running at 60 FPS!")

  runAsync(frame, () => {
    'worklet'
    console.log("I'm running on another Thread, I can block for longer!")
  })
}, [])
```

```js
const frameProcessor = useFrameProcessor((frame) => {
  'worklet'
  console.log("I'm running at 60 FPS!")

  runAtTargetFps(10, () => {
    'worklet'
    runAsync(frame, () => {
      'worklet'
      console.log("I'm running on another Thread at 10 FPS, I can block for longer!")
    })
  })
}, [])
```
This commit is contained in:
Marc Rousavy 2023-02-15 16:47:09 +01:00 committed by GitHub
parent a0590dccb5
commit 30b56153db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 660 additions and 914 deletions

View File

@ -8,12 +8,13 @@
#include <jni.h> #include <jni.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <JsiHostObject.h>
namespace vision { namespace vision {
using namespace facebook; using namespace facebook;
FrameHostObject::FrameHostObject(jni::alias_ref<JImageProxy::javaobject> image): frame(make_global(image)) { } FrameHostObject::FrameHostObject(jni::alias_ref<JImageProxy::javaobject> image): frame(make_global(image)), _refCount(0) { }
FrameHostObject::~FrameHostObject() { FrameHostObject::~FrameHostObject() {
// Hermes' Garbage Collector (Hades GC) calls destructors on a separate Thread // Hermes' Garbage Collector (Hades GC) calls destructors on a separate Thread
@ -25,21 +26,24 @@ FrameHostObject::~FrameHostObject() {
std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) { std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) {
std::vector<jsi::PropNameID> result; std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("width"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("width")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("height"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("height")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("bytesPerRow"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("bytesPerRow")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("planesCount"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("planesCount")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("close"))); // Debugging
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
// Ref Management
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("incrementRefCount")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("decrementRefCount")));
return result; return result;
} }
jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) { jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
auto name = propNameId.utf8(runtime); auto name = propName.utf8(runtime);
if (name == "toString") { if (name == "toString") {
auto toString = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value { auto toString = JSI_HOST_FUNCTION_LAMBDA {
if (!this->frame) { if (!this->frame) {
return jsi::String::createFromUtf8(runtime, "[closed frame]"); return jsi::String::createFromUtf8(runtime, "[closed frame]");
} }
@ -50,51 +54,53 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
}; };
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString); return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
} }
if (name == "close") { if (name == "incrementRefCount") {
auto close = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value { auto incrementRefCount = JSI_HOST_FUNCTION_LAMBDA {
if (!this->frame) { // Increment retain count by one so ARC doesn't destroy the Frame Buffer.
throw jsi::JSError(runtime, "Trying to close an already closed frame! Did you call frame.close() twice?"); std::lock_guard lock(this->_refCountMutex);
} this->_refCount++;
this->close();
return jsi::Value::undefined(); return jsi::Value::undefined();
}; };
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "close"), 0, close); return jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forUtf8(runtime, "incrementRefCount"),
0,
incrementRefCount);
}
if (name == "decrementRefCount") {
auto decrementRefCount = JSI_HOST_FUNCTION_LAMBDA {
// Decrement retain count by one. If the retain count is zero, ARC will destroy the Frame Buffer.
std::lock_guard lock(this->_refCountMutex);
this->_refCount--;
if (_refCount < 1) {
this->frame->close();
}
return jsi::Value::undefined();
};
return jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forUtf8(runtime, "decrementRefCount"),
0,
decrementRefCount);
} }
if (name == "isValid") { if (name == "isValid") {
return jsi::Value(this->frame && this->frame->getIsValid()); return jsi::Value(this->frame && this->frame->getIsValid());
} }
if (name == "width") { if (name == "width") {
this->assertIsFrameStrong(runtime, name);
return jsi::Value(this->frame->getWidth()); return jsi::Value(this->frame->getWidth());
} }
if (name == "height") { if (name == "height") {
this->assertIsFrameStrong(runtime, name);
return jsi::Value(this->frame->getHeight()); return jsi::Value(this->frame->getHeight());
} }
if (name == "bytesPerRow") { if (name == "bytesPerRow") {
this->assertIsFrameStrong(runtime, name);
return jsi::Value(this->frame->getBytesPerRow()); return jsi::Value(this->frame->getBytesPerRow());
} }
if (name == "planesCount") { if (name == "planesCount") {
this->assertIsFrameStrong(runtime, name);
return jsi::Value(this->frame->getPlanesCount()); return jsi::Value(this->frame->getPlanesCount());
} }
return jsi::Value::undefined(); // fallback to base implementation
} return HostObject::get(runtime, propName);
void FrameHostObject::assertIsFrameStrong(jsi::Runtime& runtime, const std::string& accessedPropName) const {
if (!this->frame) {
auto message = "Cannot get `" + accessedPropName + "`, frame is already closed!";
throw jsi::JSError(runtime, message.c_str());
}
}
void FrameHostObject::close() {
if (this->frame) {
this->frame->close();
}
} }
} // namespace vision } // namespace vision

View File

@ -9,6 +9,7 @@
#include <fbjni/fbjni.h> #include <fbjni/fbjni.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <mutex>
#include "java-bindings/JImageProxy.h" #include "java-bindings/JImageProxy.h"
@ -25,15 +26,14 @@ class JSI_EXPORT FrameHostObject : public jsi::HostObject {
jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override; std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
void close();
public: public:
jni::global_ref<JImageProxy> frame; jni::global_ref<JImageProxy> frame;
private: private:
static auto constexpr TAG = "VisionCamera"; static auto constexpr TAG = "VisionCamera";
void assertIsFrameStrong(jsi::Runtime& runtime, const std::string& accessedPropName) const; // NOLINT(runtime/references) size_t _refCount;
std::mutex _refCountMutex;
}; };
} // namespace vision } // namespace vision

View File

@ -8,6 +8,7 @@
#include <utility> #include <utility>
#include <string> #include <string>
#include <JsiWorklet.h> #include <JsiWorklet.h>
#include <JsiHostObject.h>
#include "CameraView.h" #include "CameraView.h"
#include "FrameHostObject.h" #include "FrameHostObject.h"
@ -36,9 +37,7 @@ FrameProcessorRuntimeManager::FrameProcessorRuntimeManager(jni::alias_ref<FrameP
// Run on Frame Processor Worklet Runtime // Run on Frame Processor Worklet Runtime
scheduler->dispatchAsync(std::move(f)); scheduler->dispatchAsync(std::move(f));
}; };
_workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera",
_workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera");
_workletContext->initialize("VisionCamera",
jsRuntime, jsRuntime,
runOnJS, runOnJS,
runOnWorklet); runOnWorklet);
@ -96,8 +95,7 @@ void FrameProcessorRuntimeManager::logErrorToJS(const std::string& message) {
void FrameProcessorRuntimeManager::setFrameProcessor(jsi::Runtime& runtime, void FrameProcessorRuntimeManager::setFrameProcessor(jsi::Runtime& runtime,
int viewTag, int viewTag,
const jsi::Value& frameProcessor) { const jsi::Value& frameProcessor) {
__android_log_write(ANDROID_LOG_INFO, TAG, __android_log_write(ANDROID_LOG_INFO, TAG, "Setting new Frame Processor...");
"Setting new Frame Processor...");
if (!_workletContext) { if (!_workletContext) {
throw jsi::JSError(runtime, throw jsi::JSError(runtime,
@ -106,13 +104,10 @@ void FrameProcessorRuntimeManager::setFrameProcessor(jsi::Runtime& runtime,
// find camera view // find camera view
auto cameraView = findCameraViewById(viewTag); auto cameraView = findCameraViewById(viewTag);
__android_log_write(ANDROID_LOG_INFO, TAG, "Found CameraView!");
// convert jsi::Function to a Worklet (can be shared across runtimes) // convert jsi::Function to a Worklet (can be shared across runtimes)
__android_log_write(ANDROID_LOG_INFO, TAG, "Creating Worklet...");
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, frameProcessor); auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, frameProcessor);
auto workletInvoker = std::make_shared<RNWorklet::WorkletInvoker>(worklet); auto workletInvoker = std::make_shared<RNWorklet::WorkletInvoker>(worklet);
__android_log_write(ANDROID_LOG_INFO, TAG, "Successfully created worklet!");
_workletContext->invokeOnWorkletThread([=](RNWorklet::JsiWorkletContext*, jsi::Runtime& rt) { _workletContext->invokeOnWorkletThread([=](RNWorklet::JsiWorkletContext*, jsi::Runtime& rt) {
// Set Frame Processor as callable C++ lambda - this will then call the Worklet // Set Frame Processor as callable C++ lambda - this will then call the Worklet
@ -142,8 +137,6 @@ void FrameProcessorRuntimeManager::unsetFrameProcessor(int viewTag) {
// call Java method to unset frame processor // call Java method to unset frame processor
cameraView->cthis()->unsetFrameProcessor(); cameraView->cthis()->unsetFrameProcessor();
__android_log_write(ANDROID_LOG_INFO, TAG, "Frame Processor removed!");
} }
// actual JSI installer // actual JSI installer
@ -158,21 +151,8 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
auto& jsiRuntime = *_jsRuntime; auto& jsiRuntime = *_jsRuntime;
auto setFrameProcessor = [this](jsi::Runtime &runtime, auto setFrameProcessor = JSI_HOST_FUNCTION_LAMBDA {
const jsi::Value &thisValue, __android_log_write(ANDROID_LOG_INFO, TAG, "Setting new Frame Processor...");
const jsi::Value *arguments,
size_t count) -> jsi::Value {
__android_log_write(ANDROID_LOG_INFO, TAG,
"Setting new Frame Processor...");
if (!arguments[0].isNumber()) {
throw jsi::JSError(runtime,
"Camera::setFrameProcessor: First argument ('viewTag') must be a number!");
}
if (!arguments[1].isObject()) {
throw jsi::JSError(runtime,
"Camera::setFrameProcessor: Second argument ('frameProcessor') must be a function!");
}
double viewTag = arguments[0].asNumber(); double viewTag = arguments[0].asNumber();
const jsi::Value& frameProcessor = arguments[1]; const jsi::Value& frameProcessor = arguments[1];
@ -190,15 +170,8 @@ void FrameProcessorRuntimeManager::installJSIBindings() {
setFrameProcessor)); setFrameProcessor));
auto unsetFrameProcessor = [this](jsi::Runtime &runtime, auto unsetFrameProcessor = JSI_HOST_FUNCTION_LAMBDA {
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
__android_log_write(ANDROID_LOG_INFO, TAG, "Removing Frame Processor..."); __android_log_write(ANDROID_LOG_INFO, TAG, "Removing Frame Processor...");
if (!arguments[0].isNumber()) {
throw jsi::JSError(runtime,
"Camera::unsetFrameProcessor: First argument ('viewTag') must be a number!");
}
auto viewTag = arguments[0].asNumber(); auto viewTag = arguments[0].asNumber();
this->unsetFrameProcessor(static_cast<int>(viewTag)); this->unsetFrameProcessor(static_cast<int>(viewTag));

View File

@ -31,17 +31,6 @@ fun CameraView.invokeOnError(error: Throwable) {
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraError", event) reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraError", event)
} }
fun CameraView.invokeOnFrameProcessorPerformanceSuggestionAvailable(currentFps: Double, suggestedFps: Double) {
Log.e(CameraView.TAG, "invokeOnFrameProcessorPerformanceSuggestionAvailable(suggestedFps: $suggestedFps):")
val event = Arguments.createMap()
val type = if (suggestedFps > currentFps) "can-use-higher-fps" else "should-use-lower-fps"
event.putString("type", type)
event.putDouble("suggestedFrameProcessorFps", suggestedFps)
val reactContext = context as ReactContext
reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, "cameraPerformanceSuggestionAvailable", event)
}
fun CameraView.invokeOnViewReady() { fun CameraView.invokeOnViewReady() {
val event = Arguments.createMap() val event = Arguments.createMap()
val reactContext = context as ReactContext val reactContext = context as ReactContext

View File

@ -25,7 +25,6 @@ import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.* import com.facebook.react.bridge.*
import com.facebook.react.uimanager.events.RCTEventEmitter import com.facebook.react.uimanager.events.RCTEventEmitter
import com.mrousavy.camera.frameprocessor.FrameProcessorPerformanceDataCollector
import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager import com.mrousavy.camera.frameprocessor.FrameProcessorRuntimeManager
import com.mrousavy.camera.utils.* import com.mrousavy.camera.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -103,13 +102,6 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
field = value field = value
setOnTouchListener(if (value) touchEventListener else null) setOnTouchListener(if (value) touchEventListener else null)
} }
var frameProcessorFps = 1.0
set(value) {
field = value
actualFrameProcessorFps = if (value == -1.0) 30.0 else value
lastFrameProcessorPerformanceEvaluation = System.currentTimeMillis()
frameProcessorPerformanceDataCollector.clear()
}
// private properties // private properties
private var isMounted = false private var isMounted = false
@ -166,16 +158,6 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
private var minZoom: Float = 1f private var minZoom: Float = 1f
private var maxZoom: Float = 1f private var maxZoom: Float = 1f
private var actualFrameProcessorFps = 30.0
private val frameProcessorPerformanceDataCollector = FrameProcessorPerformanceDataCollector()
private var lastSuggestedFrameProcessorFps = 0.0
private var lastFrameProcessorPerformanceEvaluation = System.currentTimeMillis()
private val isReadyForNewEvaluation: Boolean
get() {
val lastPerformanceEvaluationElapsedTime = System.currentTimeMillis() - lastFrameProcessorPerformanceEvaluation
return lastPerformanceEvaluationElapsedTime > 1000
}
@DoNotStrip @DoNotStrip
private var mHybridData: HybridData? = null private var mHybridData: HybridData? = null
@ -480,21 +462,8 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
Log.i(TAG, "Adding ImageAnalysis use-case...") Log.i(TAG, "Adding ImageAnalysis use-case...")
imageAnalysis = imageAnalysisBuilder.build().apply { imageAnalysis = imageAnalysisBuilder.build().apply {
setAnalyzer(cameraExecutor, { image -> setAnalyzer(cameraExecutor, { image ->
val now = System.currentTimeMillis() // Call JS Frame Processor
val intervalMs = (1.0 / actualFrameProcessorFps) * 1000.0
if (now - lastFrameProcessorCall > intervalMs) {
lastFrameProcessorCall = now
val perfSample = frameProcessorPerformanceDataCollector.beginPerformanceSampleCollection()
frameProcessorCallback(image) frameProcessorCallback(image)
perfSample.endPerformanceSampleCollection()
}
image.close()
if (isReadyForNewEvaluation) {
// last evaluation was more than a second ago, evaluate again
evaluateNewPerformanceSamples()
}
}) })
} }
useCases.add(imageAnalysis!!) useCases.add(imageAnalysis!!)
@ -526,22 +495,4 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
} }
} }
} }
private fun evaluateNewPerformanceSamples() {
lastFrameProcessorPerformanceEvaluation = System.currentTimeMillis()
val maxFrameProcessorFps = 30 // TODO: Get maxFrameProcessorFps from ImageAnalyser
val averageFps = 1.0 / frameProcessorPerformanceDataCollector.averageExecutionTimeSeconds
val suggestedFrameProcessorFps = floor(min(averageFps, maxFrameProcessorFps.toDouble()))
if (frameProcessorFps == -1.0) {
// frameProcessorFps="auto"
actualFrameProcessorFps = suggestedFrameProcessorFps
} else {
// frameProcessorFps={someCustomFpsValue}
if (suggestedFrameProcessorFps != lastSuggestedFrameProcessorFps && suggestedFrameProcessorFps != frameProcessorFps) {
invokeOnFrameProcessorPerformanceSuggestionAvailable(frameProcessorFps, suggestedFrameProcessorFps)
lastSuggestedFrameProcessorFps = suggestedFrameProcessorFps
}
}
}
} }

View File

@ -27,7 +27,6 @@ class CameraViewManager(reactContext: ReactApplicationContext) : ViewGroupManage
.put("cameraViewReady", MapBuilder.of("registrationName", "onViewReady")) .put("cameraViewReady", MapBuilder.of("registrationName", "onViewReady"))
.put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized")) .put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
.put("cameraError", MapBuilder.of("registrationName", "onError")) .put("cameraError", MapBuilder.of("registrationName", "onError"))
.put("cameraPerformanceSuggestionAvailable", MapBuilder.of("registrationName", "onFrameProcessorPerformanceSuggestionAvailable"))
.build() .build()
} }
@ -108,13 +107,6 @@ class CameraViewManager(reactContext: ReactApplicationContext) : ViewGroupManage
view.fps = if (fps > 0) fps else null view.fps = if (fps > 0) fps else null
} }
@ReactProp(name = "frameProcessorFps", defaultDouble = 1.0)
fun setFrameProcessorFps(view: CameraView, frameProcessorFps: Double) {
if (view.frameProcessorFps != frameProcessorFps)
addChangedPropToTransaction(view, "frameProcessorFps")
view.frameProcessorFps = frameProcessorFps
}
@ReactProp(name = "hdr") @ReactProp(name = "hdr")
fun setHdr(view: CameraView, hdr: Boolean?) { fun setHdr(view: CameraView, hdr: Boolean?) {
if (view.hdr != hdr) if (view.hdr != hdr)

View File

@ -1,38 +0,0 @@
package com.mrousavy.camera.frameprocessor
data class PerformanceSampleCollection(val endPerformanceSampleCollection: () -> Unit)
// keep a maximum of `maxSampleSize` historical performance data samples cached.
private const val maxSampleSize = 15
class FrameProcessorPerformanceDataCollector {
private var counter = 0
private var performanceSamples: ArrayList<Double> = ArrayList()
val averageExecutionTimeSeconds: Double
get() = performanceSamples.average()
fun beginPerformanceSampleCollection(): PerformanceSampleCollection {
val begin = System.currentTimeMillis()
return PerformanceSampleCollection {
val end = System.currentTimeMillis()
val seconds = (end - begin) / 1_000.0
val index = counter % maxSampleSize
if (performanceSamples.size > index) {
performanceSamples[index] = seconds
} else {
performanceSamples.add(seconds)
}
counter++
}
}
fun clear() {
counter = 0
performanceSamples.clear()
}
}

View File

@ -161,7 +161,7 @@ This means that **the Frame Processor API only takes ~1ms longer than a fully na
### Avoiding Frame-drops ### Avoiding Frame-drops
Frame Processors will be **synchronously** called for each frame the Camera sees and have to finish executing before the next frame arrives, otherwise the next frame(s) will be dropped. For a frame rate of **30 FPS**, you have about **33ms** to finish processing frames. Use [`frameProcessorFps`](/docs/api/interfaces/CameraProps#frameprocessorfps) to throttle the frame processor's FPS. For a QR Code Scanner, **5 FPS** (200ms) might suffice, while a object tracking AI might run at the same frame rate as the Camera itself (e.g. **60 FPS** (16ms)). Frame Processors will be **synchronously** called for each frame the Camera sees and have to finish executing before the next frame arrives, otherwise the next frame(s) will be dropped. For a frame rate of **30 FPS**, you have about **33ms** to finish processing frames. For a QR Code Scanner, **5 FPS** (200ms) might suffice, while a object tracking AI might run at the same frame rate as the Camera itself (e.g. **60 FPS** (16ms)).
### ESLint react-hooks plugin ### ESLint react-hooks plugin

View File

@ -1,19 +1,19 @@
PODS: PODS:
- boost (1.76.0) - boost (1.76.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- FBLazyVector (0.71.2) - FBLazyVector (0.71.3)
- FBReactNativeSpec (0.71.2): - FBReactNativeSpec (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.71.2) - RCTRequired (= 0.71.3)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Core (= 0.71.2) - React-Core (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- fmt (6.2.1) - fmt (6.2.1)
- glog (0.3.5) - glog (0.3.5)
- hermes-engine (0.71.2): - hermes-engine (0.71.3):
- hermes-engine/Pre-built (= 0.71.2) - hermes-engine/Pre-built (= 0.71.3)
- hermes-engine/Pre-built (0.71.2) - hermes-engine/Pre-built (0.71.3)
- libevent (2.1.12) - libevent (2.1.12)
- RCT-Folly (2021.07.22.00): - RCT-Folly (2021.07.22.00):
- boost - boost
@ -32,26 +32,26 @@ PODS:
- fmt (~> 6.2.1) - fmt (~> 6.2.1)
- glog - glog
- libevent - libevent
- RCTRequired (0.71.2) - RCTRequired (0.71.3)
- RCTTypeSafety (0.71.2): - RCTTypeSafety (0.71.3):
- FBLazyVector (= 0.71.2) - FBLazyVector (= 0.71.3)
- RCTRequired (= 0.71.2) - RCTRequired (= 0.71.3)
- React-Core (= 0.71.2) - React-Core (= 0.71.3)
- React (0.71.2): - React (0.71.3):
- React-Core (= 0.71.2) - React-Core (= 0.71.3)
- React-Core/DevSupport (= 0.71.2) - React-Core/DevSupport (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.2) - React-Core/RCTWebSocket (= 0.71.3)
- React-RCTActionSheet (= 0.71.2) - React-RCTActionSheet (= 0.71.3)
- React-RCTAnimation (= 0.71.2) - React-RCTAnimation (= 0.71.3)
- React-RCTBlob (= 0.71.2) - React-RCTBlob (= 0.71.3)
- React-RCTImage (= 0.71.2) - React-RCTImage (= 0.71.3)
- React-RCTLinking (= 0.71.2) - React-RCTLinking (= 0.71.3)
- React-RCTNetwork (= 0.71.2) - React-RCTNetwork (= 0.71.3)
- React-RCTSettings (= 0.71.2) - React-RCTSettings (= 0.71.3)
- React-RCTText (= 0.71.2) - React-RCTText (= 0.71.3)
- React-RCTVibration (= 0.71.2) - React-RCTVibration (= 0.71.3)
- React-callinvoker (0.71.2) - React-callinvoker (0.71.3)
- React-Codegen (0.71.2): - React-Codegen (0.71.3):
- FBReactNativeSpec - FBReactNativeSpec
- hermes-engine - hermes-engine
- RCT-Folly - RCT-Folly
@ -62,209 +62,209 @@ PODS:
- React-jsiexecutor - React-jsiexecutor
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- React-Core (0.71.2): - React-Core (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.2) - React-Core/Default (= 0.71.3)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/CoreModulesHeaders (0.71.2): - React-Core/CoreModulesHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/Default (0.71.2): - React-Core/Default (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/DevSupport (0.71.2): - React-Core/DevSupport (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.2) - React-Core/Default (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.2) - React-Core/RCTWebSocket (= 0.71.3)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-jsinspector (= 0.71.2) - React-jsinspector (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTActionSheetHeaders (0.71.2): - React-Core/RCTActionSheetHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTAnimationHeaders (0.71.2): - React-Core/RCTAnimationHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTBlobHeaders (0.71.2): - React-Core/RCTBlobHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTImageHeaders (0.71.2): - React-Core/RCTImageHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTLinkingHeaders (0.71.2): - React-Core/RCTLinkingHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTNetworkHeaders (0.71.2): - React-Core/RCTNetworkHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTSettingsHeaders (0.71.2): - React-Core/RCTSettingsHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTTextHeaders (0.71.2): - React-Core/RCTTextHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTVibrationHeaders (0.71.2): - React-Core/RCTVibrationHeaders (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default - React-Core/Default
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-Core/RCTWebSocket (0.71.2): - React-Core/RCTWebSocket (0.71.3):
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.2) - React-Core/Default (= 0.71.3)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-hermes - React-hermes
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- Yoga - Yoga
- React-CoreModules (0.71.2): - React-CoreModules (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/CoreModulesHeaders (= 0.71.2) - React-Core/CoreModulesHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-RCTBlob - React-RCTBlob
- React-RCTImage (= 0.71.2) - React-RCTImage (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-cxxreact (0.71.2): - React-cxxreact (0.71.3):
- boost (= 1.76.0) - boost (= 1.76.0)
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.2) - React-callinvoker (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-jsinspector (= 0.71.2) - React-jsinspector (= 0.71.3)
- React-logger (= 0.71.2) - React-logger (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- React-runtimeexecutor (= 0.71.2) - React-runtimeexecutor (= 0.71.3)
- React-hermes (0.71.2): - React-hermes (0.71.3):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00) - RCT-Folly/Futures (= 2021.07.22.00)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-jsi - React-jsi
- React-jsiexecutor (= 0.71.2) - React-jsiexecutor (= 0.71.3)
- React-jsinspector (= 0.71.2) - React-jsinspector (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- React-jsi (0.71.2): - React-jsi (0.71.3):
- boost (= 1.76.0) - boost (= 1.76.0)
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-jsiexecutor (0.71.2): - React-jsiexecutor (0.71.3):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- React-jsinspector (0.71.2) - React-jsinspector (0.71.3)
- React-logger (0.71.2): - React-logger (0.71.3):
- glog - glog
- react-native-blur (4.3.0): - react-native-blur (4.3.0):
- React-Core - React-Core
@ -287,90 +287,90 @@ PODS:
- React - React
- React-callinvoker - React-callinvoker
- React-Core - React-Core
- React-perflogger (0.71.2) - React-perflogger (0.71.3)
- React-RCTActionSheet (0.71.2): - React-RCTActionSheet (0.71.3):
- React-Core/RCTActionSheetHeaders (= 0.71.2) - React-Core/RCTActionSheetHeaders (= 0.71.3)
- React-RCTAnimation (0.71.2): - React-RCTAnimation (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTAnimationHeaders (= 0.71.2) - React-Core/RCTAnimationHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTAppDelegate (0.71.2): - React-RCTAppDelegate (0.71.3):
- RCT-Folly - RCT-Folly
- RCTRequired - RCTRequired
- RCTTypeSafety - RCTTypeSafety
- React-Core - React-Core
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- React-RCTBlob (0.71.2): - React-RCTBlob (0.71.3):
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTBlobHeaders (= 0.71.2) - React-Core/RCTBlobHeaders (= 0.71.3)
- React-Core/RCTWebSocket (= 0.71.2) - React-Core/RCTWebSocket (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-RCTNetwork (= 0.71.2) - React-RCTNetwork (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTImage (0.71.2): - React-RCTImage (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTImageHeaders (= 0.71.2) - React-Core/RCTImageHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-RCTNetwork (= 0.71.2) - React-RCTNetwork (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTLinking (0.71.2): - React-RCTLinking (0.71.3):
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTLinkingHeaders (= 0.71.2) - React-Core/RCTLinkingHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTNetwork (0.71.2): - React-RCTNetwork (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTNetworkHeaders (= 0.71.2) - React-Core/RCTNetworkHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTSettings (0.71.2): - React-RCTSettings (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.2) - RCTTypeSafety (= 0.71.3)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTSettingsHeaders (= 0.71.2) - React-Core/RCTSettingsHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-RCTText (0.71.2): - React-RCTText (0.71.3):
- React-Core/RCTTextHeaders (= 0.71.2) - React-Core/RCTTextHeaders (= 0.71.3)
- React-RCTVibration (0.71.2): - React-RCTVibration (0.71.3):
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.71.2) - React-Codegen (= 0.71.3)
- React-Core/RCTVibrationHeaders (= 0.71.2) - React-Core/RCTVibrationHeaders (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/core (= 0.71.2) - ReactCommon/turbomodule/core (= 0.71.3)
- React-runtimeexecutor (0.71.2): - React-runtimeexecutor (0.71.3):
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- ReactCommon/turbomodule/bridging (0.71.2): - ReactCommon/turbomodule/bridging (0.71.3):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.2) - React-callinvoker (= 0.71.3)
- React-Core (= 0.71.2) - React-Core (= 0.71.3)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-logger (= 0.71.2) - React-logger (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- ReactCommon/turbomodule/core (0.71.2): - ReactCommon/turbomodule/core (0.71.3):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2021.07.22.00) - RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.2) - React-callinvoker (= 0.71.3)
- React-Core (= 0.71.2) - React-Core (= 0.71.3)
- React-cxxreact (= 0.71.2) - React-cxxreact (= 0.71.3)
- React-jsi (= 0.71.2) - React-jsi (= 0.71.3)
- React-logger (= 0.71.2) - React-logger (= 0.71.3)
- React-perflogger (= 0.71.2) - React-perflogger (= 0.71.3)
- RNGestureHandler (2.9.0): - RNGestureHandler (2.9.0):
- React-Core - React-Core
- RNReanimated (3.0.0-rc.10): - RNReanimated (3.0.0-rc.10):
@ -400,7 +400,7 @@ PODS:
- React-RCTText - React-RCTText
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- RNScreens (3.19.0): - RNScreens (3.20.0):
- React-Core - React-Core
- React-RCTImage - React-RCTImage
- RNStaticSafeAreaInsets (2.2.0): - RNStaticSafeAreaInsets (2.2.0):
@ -566,52 +566,52 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558 boost: 57d2868c099736d80fcd648bf211b4431e51a558
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: d58428b28fe1f5070fe993495b0e2eaf701d3820 FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
FBReactNativeSpec: 225fb0f0ab00493ce0731f954da3658638d9b191 FBReactNativeSpec: 9c191fb58d06dc05ab5559a5505fc32139e9e4a2
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: 6351580c827b3b03e5f25aadcf989f582d0b0a86 hermes-engine: 38bfe887e456b33b697187570a08de33969f5db7
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: c154ebcfbf41d6fef86c52674fc1aa08837ff538 RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a
RCTTypeSafety: 3063e5a1e5b1dc2cbeda5c8f8926c0ad1a6b0871 RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a
React: 0a1a36e8e81cfaac244ed88b97f23ab56e5434f0 React: d7433ccb6a8c36e4cbed59a73c0700fc83c3e98a
React-callinvoker: 679a09fbfe1a8bbf0c8588b588bf3ef85e7e4922 React-callinvoker: 15f165009bd22ae829b2b600e50bcc98076ce4b8
React-Codegen: 78f8966839f22b54d3303a6aca2679bce5723c3f React-Codegen: b5910000eaf1e0c2f47d29be6f82f5f1264420d7
React-Core: 679e5ff1eb0e3122463976d0b2049bebcb7b33d6 React-Core: b6f2f78d580a90b83fd7b0d1c6911c799f6eac82
React-CoreModules: 06cbf15185e6daf9fb3aec02c963f4807bd794b3 React-CoreModules: e0cbc1a4f4f3f60e23c476fef7ab37be363ea8c1
React-cxxreact: 645dc75c9deba4c15698b1b5902236d6a766461f React-cxxreact: c87f3f124b2117d00d410b35f16c2257e25e50fa
React-hermes: bc7bcfeaaa7cb98dc9f9252f2f3eca66f06f01e2 React-hermes: c64ca6bdf16a7069773103c9bedaf30ec90ab38f
React-jsi: 82625f9f1f8d7abf716d897612a9ea06ecf6db6e React-jsi: 39729361645568e238081b3b3180fbad803f25a4
React-jsiexecutor: c7e028406112db456ac3cf5720d266bc7bc20938 React-jsiexecutor: 515b703d23ffadeac7687bc2d12fb08b90f0aaa1
React-jsinspector: ea8101acf525ec08b2d87ddf0637d45f8e3b4148 React-jsinspector: 9f7c9137605e72ca0343db4cea88006cb94856dd
React-logger: 97987f46779d8dd24656474ad0c43a5b459f31d6 React-logger: 957e5dc96d9dbffc6e0f15e0ee4d2b42829ff207
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3 react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
react-native-cameraroll: 5b25d0be40185d02e522bf2abf8a1ba4e8faa107 react-native-cameraroll: 5b25d0be40185d02e522bf2abf8a1ba4e8faa107
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253 react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
react-native-worklets: c7576ad4ad0f030ff41e8d74ad0077c96054a6c1 react-native-worklets: c7576ad4ad0f030ff41e8d74ad0077c96054a6c1
React-perflogger: c7ccda3d1d1da837f7ff4e54e816022a6803ee87 React-perflogger: af8a3d31546077f42d729b949925cc4549f14def
React-RCTActionSheet: 01c125aebbad462a24228f68c584c7a921d6c28e React-RCTActionSheet: 57cc5adfefbaaf0aae2cf7e10bccd746f2903673
React-RCTAnimation: 5277a9440acffc4a5b7baa6ae3880fe467277ae6 React-RCTAnimation: 11c61e94da700c4dc915cf134513764d87fc5e2b
React-RCTAppDelegate: 3977201606125157aa94872b4171ca316478939b React-RCTAppDelegate: c3980adeaadcfd6cb495532e928b36ac6db3c14a
React-RCTBlob: 8e15fc9091d8947f406ba706f11505b38b1b5e40 React-RCTBlob: ccc5049d742b41971141415ca86b83b201495695
React-RCTImage: 65319acfe82b85219b2d410725a593abe19ac795 React-RCTImage: 7a9226b0944f1e76e8e01e35a9245c2477cdbabb
React-RCTLinking: a5fc2b9d7a346d6e7d34de8093bb5d1064042508 React-RCTLinking: bbe8cc582046a9c04f79c235b73c93700263e8b4
React-RCTNetwork: 5d1efcd01ca7f08ebf286d68be544f747a5d315a React-RCTNetwork: fc2ca322159dc54e06508d4f5c3e934da63dc013
React-RCTSettings: fa760b0add819ac3ad73b06715f9547316acdf20 React-RCTSettings: f1e9db2cdf946426d3f2b210e4ff4ce0f0d842ef
React-RCTText: 05c244b135d75d4395eb35c012949a5326f8ab70 React-RCTText: 1c41dd57e5d742b1396b4eeb251851ce7ff0fca1
React-RCTVibration: 0af3babdeee1b2d052811a2f86977d1e1c81ebd1 React-RCTVibration: 5199a180d04873366a83855de55ac33ce60fe4d5
React-runtimeexecutor: 4bf9a9086d27f74065fce1dddac274aa95216952 React-runtimeexecutor: 7bf0dafc7b727d93c8cb94eb00a9d3753c446c3e
ReactCommon: f697c0ac52e999aa818e43e2b6f277787c735e2d ReactCommon: 6f65ea5b7d84deb9e386f670dd11ce499ded7b40
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
RNReanimated: fbc356493970e3acddc15586b1bccb5eab3ff1ec RNReanimated: fbc356493970e3acddc15586b1bccb5eab3ff1ec
RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8 RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8 RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
VisionCamera: 312151eb95370d1d764720de3b7dad33d8c7fb40 VisionCamera: 312151eb95370d1d764720de3b7dad33d8c7fb40
Yoga: 5b0304b3dbef2b52e078052138e23a19c7dacaef Yoga: 5ed1699acbba8863755998a4245daa200ff3817b
PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636 PODFILE CHECKSUM: d53724fe402c2547f1dd1cc571bbe77d9820e636

View File

@ -20,16 +20,16 @@
"@react-navigation/native": "^6.1.3", "@react-navigation/native": "^6.1.3",
"@react-navigation/native-stack": "^6.9.9", "@react-navigation/native-stack": "^6.9.9",
"react": "^18.2.0", "react": "^18.2.0",
"react-native": "^0.71.2", "react-native": "^0.71.3",
"react-native-gesture-handler": "^2.9.0", "react-native-gesture-handler": "^2.9.0",
"react-native-pressable-opacity": "^1.0.10", "react-native-pressable-opacity": "^1.0.10",
"react-native-reanimated": "^3.0.0-rc.10", "react-native-reanimated": "https://github.com/software-mansion/react-native-reanimated#6cf9713a44ec61318bbb12c311f79c08eda95d10",
"react-native-safe-area-context": "^4.5.0", "react-native-safe-area-context": "^4.5.0",
"react-native-screens": "^3.19.0", "react-native-screens": "^3.19.0",
"react-native-static-safe-area-insets": "^2.2.0", "react-native-static-safe-area-insets": "^2.2.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-native-video": "^5.2.1", "react-native-video": "^5.2.1",
"react-native-worklets": "https://github.com/chrfalch/react-native-worklets#50950aa" "react-native-worklets": "https://github.com/chrfalch/react-native-worklets#15d52dd"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.12", "@babel/core": "^7.20.12",

View File

@ -5,7 +5,6 @@ import { PinchGestureHandler, PinchGestureHandlerGestureEvent, TapGestureHandler
import { import {
CameraDeviceFormat, CameraDeviceFormat,
CameraRuntimeError, CameraRuntimeError,
FrameProcessorPerformanceSuggestion,
PhotoFile, PhotoFile,
sortFormats, sortFormats,
useCameraDevices, useCameraDevices,
@ -203,10 +202,6 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
console.log(`Return Values: ${JSON.stringify(values)}`); console.log(`Return Values: ${JSON.stringify(values)}`);
}, []); }, []);
const onFrameProcessorSuggestionAvailable = useCallback((suggestion: FrameProcessorPerformanceSuggestion) => {
console.log(`Suggestion available! ${suggestion.type}: Can do ${suggestion.suggestedFrameProcessorFps} FPS`);
}, []);
return ( return (
<View style={styles.container}> <View style={styles.container}>
{device != null && ( {device != null && (
@ -231,8 +226,6 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
audio={hasMicrophonePermission} audio={hasMicrophonePermission}
frameProcessor={device.supportsParallelVideoProcessing ? frameProcessor : undefined} frameProcessor={device.supportsParallelVideoProcessing ? frameProcessor : undefined}
orientation="portrait" orientation="portrait"
frameProcessorFps={1}
onFrameProcessorPerformanceSuggestionAvailable={onFrameProcessorSuggestionAvailable}
/> />
</TapGestureHandler> </TapGestureHandler>
</Reanimated.View> </Reanimated.View>

View File

@ -781,38 +781,38 @@
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@jest/create-cache-key-function@^29.2.1": "@jest/create-cache-key-function@^29.2.1":
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.4.2.tgz#cc8e966c28fd3bed309b0487de2e0bd12cbf519f" resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.4.3.tgz#ea37769f69523019d81ee089a25a62550f209eb7"
integrity sha512-o2weIg3h8/7+jXF6EjcOiz9TAlNMtcmeH5IARTVgHs3IoQGbh5E/ZLLskfEsHVIYoCKgx+v093Vf8hOgMWggvg== integrity sha512-AJVFQTTy6jnZAQiAZrdOaTAPzJUrvAE/4IMe+Foav6WPhypFszqg7a4lOTyuzYQEEiT5RSrGYg9IY+/ivxiyXw==
dependencies: dependencies:
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@jest/environment@^29.4.2": "@jest/environment@^29.4.3":
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.2.tgz#ee92c316ee2fbdf0bcd9d2db0ef42d64fea26b56" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.3.tgz#9fe2f3169c3b33815dc4bd3960a064a83eba6548"
integrity sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ== integrity sha512-dq5S6408IxIa+lr54zeqce+QgI+CJT4nmmA+1yzFgtcsGK8c/EyiUb9XQOgz3BMKrRDfKseeOaxj2eO8LlD3lA==
dependencies: dependencies:
"@jest/fake-timers" "^29.4.2" "@jest/fake-timers" "^29.4.3"
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@types/node" "*" "@types/node" "*"
jest-mock "^29.4.2" jest-mock "^29.4.3"
"@jest/fake-timers@^29.4.2": "@jest/fake-timers@^29.4.3":
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.2.tgz#af43ee1a5720b987d0348f80df98f2cb17d45cd0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.3.tgz#31e982638c60fa657d310d4b9d24e023064027b0"
integrity sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ== integrity sha512-4Hote2MGcCTWSD2gwl0dwbCpBRHhE6olYEuTj8FMowdg3oQWNKr2YuxenPQYZ7+PfqPY1k98wKDU4Z+Hvd4Tiw==
dependencies: dependencies:
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@sinonjs/fake-timers" "^10.0.2" "@sinonjs/fake-timers" "^10.0.2"
"@types/node" "*" "@types/node" "*"
jest-message-util "^29.4.2" jest-message-util "^29.4.3"
jest-mock "^29.4.2" jest-mock "^29.4.3"
jest-util "^29.4.2" jest-util "^29.4.3"
"@jest/schemas@^29.4.2": "@jest/schemas@^29.4.3":
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788"
integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==
dependencies: dependencies:
"@sinclair/typebox" "^0.25.16" "@sinclair/typebox" "^0.25.16"
@ -838,12 +838,12 @@
"@types/yargs" "^16.0.0" "@types/yargs" "^16.0.0"
chalk "^4.0.0" chalk "^4.0.0"
"@jest/types@^29.4.2": "@jest/types@^29.4.3":
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.3.tgz#9069145f4ef09adf10cec1b2901b2d390031431f"
integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==
dependencies: dependencies:
"@jest/schemas" "^29.4.2" "@jest/schemas" "^29.4.3"
"@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0" "@types/istanbul-reports" "^3.0.0"
"@types/node" "*" "@types/node" "*"
@ -1153,23 +1153,23 @@
react-is "^16.13.0" react-is "^16.13.0"
use-latest-callback "^0.1.5" use-latest-callback "^0.1.5"
"@react-navigation/elements@^1.3.14": "@react-navigation/elements@^1.3.15":
version "1.3.14" version "1.3.15"
resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.14.tgz#ee9ef7f57e0877700ebb359947728b255bbb9c65" resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.15.tgz#b8aebf101080057508b98cb5da4173c72c095db0"
integrity sha512-RBbPhYq+KNFPAkWPaHB9gypq0jTGp/0fkMwRLToJ8jkLtWG4LV+JoQ/erFQnVARkR3Q807n0VnES15EYP4ITMQ== integrity sha512-CR4CEYJVY0OLyeLQi9N3Z2o4K47gXctvFxfZizDuW1xFtCJbA0eGvpjSLXEWHoY0hFjrlC6KinpdepGHVxhYIg==
"@react-navigation/native-stack@^6.9.9": "@react-navigation/native-stack@^6.9.9":
version "6.9.9" version "6.9.10"
resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.9.tgz#346c393e48d681c15e5831d7749fb807f76dcdce" resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.10.tgz#5a5d5de9f49b72e603d0cdca9771e0324f65c1c7"
integrity sha512-FIbTCEjqAt6guQ90lKIDvOfTo5vtKGG+aQTtHMdTV9JLGnS6gFsBgXmv5hvWLkyd426Nc04mpZXPTK7d80v/LQ== integrity sha512-dSazcWNxHg4qkid/AxFRvbhRtNXy/RqE00h/Qp+d7aBN0TwrOJn8mH/Inkkf4pHAntMbj0+mVAlKfxKmyLEGlA==
dependencies: dependencies:
"@react-navigation/elements" "^1.3.14" "@react-navigation/elements" "^1.3.15"
warn-once "^0.1.0" warn-once "^0.1.0"
"@react-navigation/native@^6.1.3": "@react-navigation/native@^6.1.3":
version "6.1.3" version "6.1.4"
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.3.tgz#06064c5e49c417a3dbe210b3f00841beefd326c5" resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.4.tgz#49666596c9df16e22284f35f482d05d6b5d47d60"
integrity sha512-DB5FyG6aqGfcjjVozljF5NEkjWaSymIbQHfWwsjL0YrvC1gfc7E53QXDOjxZ/wfbCo8qZs8RIC/LAgclP2YK/w== integrity sha512-8IGpMFvD21XINpSf9gyU19yv4O+NyF9FQAxEzwbJSef19W5XEJKXPN/0RINc43Tt+YnQyFGQ2+qJM1uoB9pKcA==
dependencies: dependencies:
"@react-navigation/core" "^6.4.6" "@react-navigation/core" "^6.4.6"
escape-string-regexp "^4.0.0" escape-string-regexp "^4.0.0"
@ -1201,9 +1201,9 @@
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
"@sinclair/typebox@^0.25.16": "@sinclair/typebox@^0.25.16":
version "0.25.21" version "0.25.22"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.22.tgz#2808d895e9c2722b20a622a9c8cb332f6720eb4a"
integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== integrity sha512-6U6r2L7rnM7EG8G1tWzIjdB3QlsHF4slgcqXNN/SF0xJOAr0nDmT2GedlkyO3mrv8mDTJ24UuOMWR3diBrCvQQ==
"@sinonjs/commons@^2.0.0": "@sinonjs/commons@^2.0.0":
version "2.0.0" version "2.0.0"
@ -1275,23 +1275,23 @@
"@types/react-native" "*" "@types/react-native" "*"
"@types/react-native@*", "@types/react-native@^0.71.2": "@types/react-native@*", "@types/react-native@^0.71.2":
version "0.71.2" version "0.71.3"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.71.2.tgz#b8ba52c2fd07d3d64fa87ae5d9b4fbbd3b9cbffd" resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.71.3.tgz#537f669ed6b38b5ae47444bd9d253c4cff23bed7"
integrity sha512-RJ0slxB/aVh6gDBZcGusDLIMW+ydsigzOVva9nODPfZApvIY4UgacV5PAlyk/CScJaN7Frh4sZaD9bD73uc9HQ== integrity sha512-0Uqw1YZ0qbVla0MMWFTANFm6W8KYWNvGQmYfucdecbXivLMcQ2v4PovuYFKr7bE6Bc5nDCUEaga962Y8gcDF7A==
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-native@^0.70": "@types/react-native@^0.70":
version "0.70.10" version "0.70.11"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.70.10.tgz#494576e0dc20aa319003f0cdd99192124d64038d" resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.70.11.tgz#50c3d81e1351aac0562c3341c9bd57885651a2fe"
integrity sha512-m9B9hJk1w/c04zj5PCYTjJNXt+1UvKtzJj4nO/BjiS4s/zmUdkLdnbLqRQCQiTA0wuvvMbrffuPdheRtYu/M1Q== integrity sha512-FobPtzoNPNHugBKMfzs4Li0Q9ei4tgU8SI1M5Ayg7+t5/+noCm2sknI8uwij22wMkcHcefv8RFx4q28nNVJtCQ==
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react@*", "@types/react@^18.0.27": "@types/react@*", "@types/react@^18.0.27":
version "18.0.27" version "18.0.28"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065"
integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA== integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==
dependencies: dependencies:
"@types/prop-types" "*" "@types/prop-types" "*"
"@types/scheduler" "*" "@types/scheduler" "*"
@ -1339,13 +1339,13 @@
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^5.30.5": "@typescript-eslint/eslint-plugin@^5.30.5":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz#da3f2819633061ced84bb82c53bba45a6fe9963a" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz#5fb0d43574c2411f16ea80f5fc335b8eaa7b28a8"
integrity sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ== integrity sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.51.0" "@typescript-eslint/scope-manager" "5.52.0"
"@typescript-eslint/type-utils" "5.51.0" "@typescript-eslint/type-utils" "5.52.0"
"@typescript-eslint/utils" "5.51.0" "@typescript-eslint/utils" "5.52.0"
debug "^4.3.4" debug "^4.3.4"
grapheme-splitter "^1.0.4" grapheme-splitter "^1.0.4"
ignore "^5.2.0" ignore "^5.2.0"
@ -1355,71 +1355,71 @@
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/parser@^5.30.5": "@typescript-eslint/parser@^5.30.5":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.51.0.tgz#2d74626652096d966ef107f44b9479f02f51f271" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.52.0.tgz#73c136df6c0133f1d7870de7131ccf356f5be5a4"
integrity sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA== integrity sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.51.0" "@typescript-eslint/scope-manager" "5.52.0"
"@typescript-eslint/types" "5.51.0" "@typescript-eslint/types" "5.52.0"
"@typescript-eslint/typescript-estree" "5.51.0" "@typescript-eslint/typescript-estree" "5.52.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@5.51.0": "@typescript-eslint/scope-manager@5.52.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz#ad3e3c2ecf762d9a4196c0fbfe19b142ac498990" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz#a993d89a0556ea16811db48eabd7c5b72dcb83d1"
integrity sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ== integrity sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==
dependencies: dependencies:
"@typescript-eslint/types" "5.51.0" "@typescript-eslint/types" "5.52.0"
"@typescript-eslint/visitor-keys" "5.51.0" "@typescript-eslint/visitor-keys" "5.52.0"
"@typescript-eslint/type-utils@5.51.0": "@typescript-eslint/type-utils@5.52.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz#7af48005531700b62a20963501d47dfb27095988" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz#9fd28cd02e6f21f5109e35496df41893f33167aa"
integrity sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ== integrity sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "5.51.0" "@typescript-eslint/typescript-estree" "5.52.0"
"@typescript-eslint/utils" "5.51.0" "@typescript-eslint/utils" "5.52.0"
debug "^4.3.4" debug "^4.3.4"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/types@5.51.0": "@typescript-eslint/types@5.52.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.51.0.tgz#e7c1622f46c7eea7e12bbf1edfb496d4dec37c90" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.52.0.tgz#19e9abc6afb5bd37a1a9bea877a1a836c0b3241b"
integrity sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw== integrity sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==
"@typescript-eslint/typescript-estree@5.51.0": "@typescript-eslint/typescript-estree@5.52.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz#0ec8170d7247a892c2b21845b06c11eb0718f8de" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz#6408cb3c2ccc01c03c278cb201cf07e73347dfca"
integrity sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA== integrity sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==
dependencies: dependencies:
"@typescript-eslint/types" "5.51.0" "@typescript-eslint/types" "5.52.0"
"@typescript-eslint/visitor-keys" "5.51.0" "@typescript-eslint/visitor-keys" "5.52.0"
debug "^4.3.4" debug "^4.3.4"
globby "^11.1.0" globby "^11.1.0"
is-glob "^4.0.3" is-glob "^4.0.3"
semver "^7.3.7" semver "^7.3.7"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/utils@5.51.0", "@typescript-eslint/utils@^5.10.0": "@typescript-eslint/utils@5.52.0", "@typescript-eslint/utils@^5.10.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.51.0.tgz#074f4fabd5b12afe9c8aa6fdee881c050f8b4d47" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.52.0.tgz#b260bb5a8f6b00a0ed51db66bdba4ed5e4845a72"
integrity sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw== integrity sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==
dependencies: dependencies:
"@types/json-schema" "^7.0.9" "@types/json-schema" "^7.0.9"
"@types/semver" "^7.3.12" "@types/semver" "^7.3.12"
"@typescript-eslint/scope-manager" "5.51.0" "@typescript-eslint/scope-manager" "5.52.0"
"@typescript-eslint/types" "5.51.0" "@typescript-eslint/types" "5.52.0"
"@typescript-eslint/typescript-estree" "5.51.0" "@typescript-eslint/typescript-estree" "5.52.0"
eslint-scope "^5.1.1" eslint-scope "^5.1.1"
eslint-utils "^3.0.0" eslint-utils "^3.0.0"
semver "^7.3.7" semver "^7.3.7"
"@typescript-eslint/visitor-keys@5.51.0": "@typescript-eslint/visitor-keys@5.52.0":
version "5.51.0" version "5.52.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz#c0147dd9a36c0de758aaebd5b48cae1ec59eba87" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz#e38c971259f44f80cfe49d97dbffa38e3e75030f"
integrity sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ== integrity sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==
dependencies: dependencies:
"@typescript-eslint/types" "5.51.0" "@typescript-eslint/types" "5.52.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
abort-controller@^3.0.0: abort-controller@^3.0.0:
@ -1777,7 +1777,7 @@ braces@^3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
browserslist@^4.21.3, browserslist@^4.21.4: browserslist@^4.21.3, browserslist@^4.21.5:
version "4.21.5" version "4.21.5"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7"
integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==
@ -1870,9 +1870,9 @@ camelcase@^6.0.0:
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001449: caniuse-lite@^1.0.30001449:
version "1.0.30001451" version "1.0.30001452"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz#dff7b8bb834b3a91808f0a9ff0453abb1fbba02a"
integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== integrity sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==
chalk@^2.0.0: chalk@^2.0.0:
version "2.4.2" version "2.4.2"
@ -1897,9 +1897,9 @@ ci-info@^2.0.0:
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
ci-info@^3.2.0: ci-info@^3.2.0:
version "3.7.1" version "3.8.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91"
integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==
class-utils@^0.3.5: class-utils@^0.3.5:
version "0.3.6" version "0.3.6"
@ -2077,11 +2077,11 @@ copy-descriptor@^0.1.0:
integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==
core-js-compat@^3.25.1: core-js-compat@^3.25.1:
version "3.27.2" version "3.28.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.2.tgz#607c50ad6db8fd8326af0b2883ebb987be3786da" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.28.0.tgz#c08456d854608a7264530a2afa281fadf20ecee6"
integrity sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg== integrity sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==
dependencies: dependencies:
browserslist "^4.21.4" browserslist "^4.21.5"
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
@ -2170,9 +2170,9 @@ defaults@^1.0.3:
clone "^1.0.2" clone "^1.0.2"
define-properties@^1.1.3, define-properties@^1.1.4: define-properties@^1.1.3, define-properties@^1.1.4:
version "1.1.4" version "1.2.0"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5"
integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==
dependencies: dependencies:
has-property-descriptors "^1.0.0" has-property-descriptors "^1.0.0"
object-keys "^1.1.1" object-keys "^1.1.1"
@ -2259,9 +2259,9 @@ ee-first@1.1.1:
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.4.284: electron-to-chromium@^1.4.284:
version "1.4.294" version "1.4.296"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.294.tgz#ad80317b85f0859a9454680fbc1c726fefa7e6fd" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.296.tgz#dbc84a25c25a432a12fbf62903cae4a87461eb8c"
integrity sha512-PuHZB3jEN7D8WPPjLmBQAsqQz8tWHlkkB4n0E2OYw8RwVdmBYV0Wn+rUFH8JqYyIRb4HQhhedgxlZL163wqLrQ== integrity sha512-i/6Q+Y9bluDa2a0NbMvdtG5TuS/1Fr3TKK8L+7UUL9QjRS5iFJzCC3r70xjyOnLiYG8qGV4/mMpe6HuAbdJW4w==
eme-encryption-scheme-polyfill@^2.0.1: eme-encryption-scheme-polyfill@^2.0.1:
version "2.1.1" version "2.1.1"
@ -2509,9 +2509,9 @@ eslint-visitor-keys@^3.3.0:
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@^8.33.0: eslint@^8.33.0:
version "8.33.0" version "8.34.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.33.0.tgz#02f110f32998cb598c6461f24f4d306e41ca33d7" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.34.0.tgz#fe0ab0ef478104c1f9ebc5537e303d25a8fb22d6"
integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== integrity sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==
dependencies: dependencies:
"@eslint/eslintrc" "^1.4.1" "@eslint/eslintrc" "^1.4.1"
"@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/config-array" "^0.11.8"
@ -3451,45 +3451,45 @@ isobject@^3.0.0, isobject@^3.0.1:
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
jest-environment-node@^29.2.1: jest-environment-node@^29.2.1:
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.2.tgz#0eab835b41e25fd0c1a72f62665fc8db08762ad2" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.3.tgz#579c4132af478befc1889ddc43c2413a9cdbe014"
integrity sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w== integrity sha512-gAiEnSKF104fsGDXNkwk49jD/0N0Bqu2K9+aMQXA6avzsA9H3Fiv1PW2D+gzbOSR705bWd2wJZRFEFpV0tXISg==
dependencies: dependencies:
"@jest/environment" "^29.4.2" "@jest/environment" "^29.4.3"
"@jest/fake-timers" "^29.4.2" "@jest/fake-timers" "^29.4.3"
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@types/node" "*" "@types/node" "*"
jest-mock "^29.4.2" jest-mock "^29.4.3"
jest-util "^29.4.2" jest-util "^29.4.3"
jest-get-type@^26.3.0: jest-get-type@^26.3.0:
version "26.3.0" version "26.3.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==
jest-message-util@^29.4.2: jest-message-util@^29.4.3:
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.3.tgz#65b5280c0fdc9419503b49d4f48d4999d481cb5b"
integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==
dependencies: dependencies:
"@babel/code-frame" "^7.12.13" "@babel/code-frame" "^7.12.13"
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@types/stack-utils" "^2.0.0" "@types/stack-utils" "^2.0.0"
chalk "^4.0.0" chalk "^4.0.0"
graceful-fs "^4.2.9" graceful-fs "^4.2.9"
micromatch "^4.0.4" micromatch "^4.0.4"
pretty-format "^29.4.2" pretty-format "^29.4.3"
slash "^3.0.0" slash "^3.0.0"
stack-utils "^2.0.3" stack-utils "^2.0.3"
jest-mock@^29.4.2: jest-mock@^29.4.3:
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.3.tgz#23d84a20a74cdfff0510fdbeefb841ed57b0fe7e"
integrity sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g== integrity sha512-LjFgMg+xed9BdkPMyIJh+r3KeHt1klXPJYBULXVVAkbTaaKjPX1o1uVCAZADMEp/kOxGTwy/Ot8XbvgItOrHEg==
dependencies: dependencies:
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@types/node" "*" "@types/node" "*"
jest-util "^29.4.2" jest-util "^29.4.3"
jest-regex-util@^27.0.6: jest-regex-util@^27.0.6:
version "27.5.1" version "27.5.1"
@ -3516,12 +3516,12 @@ jest-util@^27.2.0:
graceful-fs "^4.2.9" graceful-fs "^4.2.9"
picomatch "^2.2.3" picomatch "^2.2.3"
jest-util@^29.4.2: jest-util@^29.4.3:
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.3.tgz#851a148e23fc2b633c55f6dad2e45d7f4579f496"
integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==
dependencies: dependencies:
"@jest/types" "^29.4.2" "@jest/types" "^29.4.3"
"@types/node" "*" "@types/node" "*"
chalk "^4.0.0" chalk "^4.0.0"
ci-info "^3.2.0" ci-info "^3.2.0"
@ -3585,10 +3585,10 @@ js-yaml@^4.1.0:
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
jsc-android@^250230.2.1: jsc-android@^250231.0.0:
version "250230.2.1" version "250231.0.0"
resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250230.2.1.tgz#3790313a970586a03ab0ad47defbc84df54f1b83" resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262"
integrity sha512-KmxeBlRjwoqCnBBKGsihFtvsBHyUFlBxJPK4FzeYcIuBfdjv6jFys44JITAgSTbQD+vIdwMEfyZklsuQX0yI1Q== integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==
jscodeshift@^0.13.1: jscodeshift@^0.13.1:
version "0.13.1" version "0.13.1"
@ -4912,12 +4912,12 @@ pretty-format@^26.5.2, pretty-format@^26.6.2:
ansi-styles "^4.0.0" ansi-styles "^4.0.0"
react-is "^17.0.1" react-is "^17.0.1"
pretty-format@^29.4.2: pretty-format@^29.4.3:
version "29.4.2" version "29.4.3"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c"
integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==
dependencies: dependencies:
"@jest/schemas" "^29.4.2" "@jest/schemas" "^29.4.3"
ansi-styles "^5.0.0" ansi-styles "^5.0.0"
react-is "^18.0.0" react-is "^18.0.0"
@ -5011,10 +5011,10 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-native-codegen@^0.71.3: react-native-codegen@^0.71.5:
version "0.71.3" version "0.71.5"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.3.tgz#75fbc591819050791319ebdb9fe341ee4df5c288" resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14"
integrity sha512-5AvdHVU1sAaXg05i0dG664ZTaCaIFaY1znV5vNsj+wUu6MGxNEUNbDKk9dxKUkkxOyk2KZOK5uhzWL0p5H5yZQ== integrity sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg==
dependencies: dependencies:
"@babel/parser" "^7.14.0" "@babel/parser" "^7.14.0"
flow-parser "^0.185.0" flow-parser "^0.185.0"
@ -5032,20 +5032,19 @@ react-native-gesture-handler@^2.9.0:
lodash "^4.17.21" lodash "^4.17.21"
prop-types "^15.7.2" prop-types "^15.7.2"
react-native-gradle-plugin@^0.71.14: react-native-gradle-plugin@^0.71.15:
version "0.71.14" version "0.71.15"
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.14.tgz#cc399662f04fbfcc0e352d03eae1d3efbd5f635a" resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.15.tgz#9e6b506f30729fe8eb086981702f4e3c891d2b13"
integrity sha512-nnLawTZEPPRAKq92UqDkzoGgCBl9aa9zAihFHMwmwzn4WRVdK4O6Cd4XYiyoNOiQzx3Hh9k5WOckHE80C92ivQ== integrity sha512-7S3pAuPaQJlhax6EZ4JMsDNpj05TfuzX9gPgWLrFfAIWIFLuJ6aDQYAZy2TEI9QJALPoWrj8LWaqP/DGYh14pw==
react-native-pressable-opacity@^1.0.10: react-native-pressable-opacity@^1.0.10:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d" resolved "https://registry.yarnpkg.com/react-native-pressable-opacity/-/react-native-pressable-opacity-1.0.10.tgz#799df1a913d3b28f42ada765465fe7723eb7166d"
integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w== integrity sha512-Py9YH9TlS3Lv1so5JCj6bgiqkeYYGupF4ZImlpoyhhId/t/RiSqR68LlASOHgdctqQuqVJObQiFfzX8oZI9+6w==
react-native-reanimated@^3.0.0-rc.10: "react-native-reanimated@https://github.com/software-mansion/react-native-reanimated#6cf9713a44ec61318bbb12c311f79c08eda95d10":
version "3.0.0-rc.10" version "3.0.0-rc.10"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.0.0-rc.10.tgz#40e5b628759aa81f94317fd0301231292b4eacf5" resolved "https://github.com/software-mansion/react-native-reanimated#6cf9713a44ec61318bbb12c311f79c08eda95d10"
integrity sha512-0P2jSO+dXHRxSzqSxNp08VaUy89nqeUIvqBS0wlI8lsli8CJcqulL3pjNqTGzBkxXjt13mGdIzJv4u9lSjHPzg==
dependencies: dependencies:
"@babel/plugin-transform-object-assign" "^7.16.7" "@babel/plugin-transform-object-assign" "^7.16.7"
"@babel/preset-typescript" "^7.16.7" "@babel/preset-typescript" "^7.16.7"
@ -5061,9 +5060,9 @@ react-native-safe-area-context@^4.5.0:
integrity sha512-0WORnk9SkREGUg2V7jHZbuN5x4vcxj/1B0QOcXJjdYWrzZHgLcUzYWWIUecUPJh747Mwjt/42RZDOaFn3L8kPQ== integrity sha512-0WORnk9SkREGUg2V7jHZbuN5x4vcxj/1B0QOcXJjdYWrzZHgLcUzYWWIUecUPJh747Mwjt/42RZDOaFn3L8kPQ==
react-native-screens@^3.19.0: react-native-screens@^3.19.0:
version "3.19.0" version "3.20.0"
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.19.0.tgz#ec68685e04b074ebce4641b3a0ae7e2571629b75" resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.20.0.tgz#4d154177395e5541387d9a05bc2e12e54d2fb5b1"
integrity sha512-Ehsmy7jr3H3j5pmN+/FqsAaIAD+k+xkcdePfLcg4rYRbN5X7fJPgaqhcmiCcZ0YxsU8ttsstP9IvRLNQuIkRRA== integrity sha512-joWUKWAVHxymP3mL9gYApFHAsbd9L6ZcmpoZa6Sl3W/82bvvNVMqcfP7MeNqVCg73qZ8yL4fW+J/syusHleUgg==
dependencies: dependencies:
react-freeze "^1.0.0" react-freeze "^1.0.0"
warn-once "^0.1.0" warn-once "^0.1.0"
@ -5091,14 +5090,14 @@ react-native-video@^5.2.1:
prop-types "^15.7.2" prop-types "^15.7.2"
shaka-player "^2.5.9" shaka-player "^2.5.9"
"react-native-worklets@https://github.com/chrfalch/react-native-worklets#50950aa": "react-native-worklets@https://github.com/chrfalch/react-native-worklets#15d52dd":
version "0.1.0" version "0.1.0"
resolved "https://github.com/chrfalch/react-native-worklets#50950aa1b671a0d8a9e79878e63a3445991e7192" resolved "https://github.com/chrfalch/react-native-worklets#15d52dd1289831cecc7906823f613172e0c6cd2e"
react-native@^0.71.2: react-native@^0.71.3:
version "0.71.2" version "0.71.3"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.3.tgz#0faab799c49e61ba12df9e6525c3ac7d595d673c"
integrity sha512-ZSianM+j+09LoEdVIhrAP/uP8sQhT7dH6olCqM2xlpxmfCgA5NubsK6NABIuZiBlmmqjigyijm5Y/GhBIHDvEg== integrity sha512-RYJXCcQGa4NTfKiPgl92eRDUuQ6JGDnHqFEzRwJSqEx9lWvlvRRIebstJfurzPDKLQWQrvITR7aI7e09E25mLw==
dependencies: dependencies:
"@jest/create-cache-key-function" "^29.2.1" "@jest/create-cache-key-function" "^29.2.1"
"@react-native-community/cli" "10.1.3" "@react-native-community/cli" "10.1.3"
@ -5114,7 +5113,7 @@ react-native@^0.71.2:
event-target-shim "^5.0.1" event-target-shim "^5.0.1"
invariant "^2.2.4" invariant "^2.2.4"
jest-environment-node "^29.2.1" jest-environment-node "^29.2.1"
jsc-android "^250230.2.1" jsc-android "^250231.0.0"
memoize-one "^5.0.0" memoize-one "^5.0.0"
metro-react-native-babel-transformer "0.73.7" metro-react-native-babel-transformer "0.73.7"
metro-runtime "0.73.7" metro-runtime "0.73.7"
@ -5124,8 +5123,8 @@ react-native@^0.71.2:
pretty-format "^26.5.2" pretty-format "^26.5.2"
promise "^8.3.0" promise "^8.3.0"
react-devtools-core "^4.26.1" react-devtools-core "^4.26.1"
react-native-codegen "^0.71.3" react-native-codegen "^0.71.5"
react-native-gradle-plugin "^0.71.14" react-native-gradle-plugin "^0.71.15"
react-refresh "^0.4.0" react-refresh "^0.4.0"
react-shallow-renderer "^16.15.0" react-shallow-renderer "^16.15.0"
regenerator-runtime "^0.13.2" regenerator-runtime "^0.13.2"

View File

@ -17,20 +17,13 @@ public class CameraQueues: NSObject {
autoreleaseFrequency: .inherit, autoreleaseFrequency: .inherit,
target: nil) target: nil)
/// The serial execution queue for output processing of videos for recording. /// The serial execution queue for output processing of videos for recording or synchronous frame processing.
@objc public static let videoQueue = DispatchQueue(label: "mrousavy/VisionCamera.video", @objc public static let videoQueue = DispatchQueue(label: "mrousavy/VisionCamera.video",
qos: .userInteractive, qos: .userInteractive,
attributes: [], attributes: [],
autoreleaseFrequency: .inherit, autoreleaseFrequency: .inherit,
target: nil) target: nil)
/// The serial execution queue for output processing of videos for frame processing.
@objc public static let frameProcessorQueue = DispatchQueue(label: "mrousavy/VisionCamera.frame-processor",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
/// The serial execution queue for output processing of audio buffers. /// The serial execution queue for output processing of audio buffers.
@objc public static let audioQueue = DispatchQueue(label: "mrousavy/VisionCamera.audio", @objc public static let audioQueue = DispatchQueue(label: "mrousavy/VisionCamera.audio",
qos: .userInteractive, qos: .userInteractive,

View File

@ -190,8 +190,8 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud
} }
public final func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from _: AVCaptureConnection) { public final func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from _: AVCaptureConnection) {
// Video Recording runs in the same queue
if isRecording { if isRecording {
// Write Video / Audio frame to file
guard let recordingSession = recordingSession else { guard let recordingSession = recordingSession else {
invokeOnError(.capture(.unknown(message: "isRecording was true but the RecordingSession was null!"))) invokeOnError(.capture(.unknown(message: "isRecording was true but the RecordingSession was null!")))
return return
@ -211,54 +211,9 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud
} }
if let frameProcessor = frameProcessorCallback, captureOutput is AVCaptureVideoDataOutput { if let frameProcessor = frameProcessorCallback, captureOutput is AVCaptureVideoDataOutput {
// check if last frame was x nanoseconds ago, effectively throttling FPS // Call the JavaScript Frame Processor func (worklet)
let frameTime = UInt64(CMSampleBufferGetPresentationTimeStamp(sampleBuffer).seconds * 1_000_000_000.0)
let lastFrameProcessorCallElapsedTime = frameTime - lastFrameProcessorCall
let secondsPerFrame = 1.0 / actualFrameProcessorFps
let nanosecondsPerFrame = secondsPerFrame * 1_000_000_000.0
if lastFrameProcessorCallElapsedTime >= UInt64(nanosecondsPerFrame) {
if !isRunningFrameProcessor {
// we're not in the middle of executing the Frame Processor, so prepare for next call.
CameraQueues.frameProcessorQueue.async {
self.isRunningFrameProcessor = true
let perfSample = self.frameProcessorPerformanceDataCollector.beginPerformanceSampleCollection()
let frame = Frame(buffer: sampleBuffer, orientation: self.bufferOrientation) let frame = Frame(buffer: sampleBuffer, orientation: self.bufferOrientation)
frameProcessor(frame) frameProcessor(frame)
perfSample.endPerformanceSampleCollection()
self.isRunningFrameProcessor = false
}
lastFrameProcessorCall = frameTime
} else {
// we're still in the middle of executing a Frame Processor for a previous frame, so a frame was dropped.
ReactLogger.log(level: .warning, message: "The Frame Processor took so long to execute that a frame was dropped.")
}
}
if isReadyForNewEvaluation {
// last evaluation was more than 1sec ago, evaluate again
evaluateNewPerformanceSamples()
}
}
}
private func evaluateNewPerformanceSamples() {
lastFrameProcessorPerformanceEvaluation = DispatchTime.now()
guard let videoDevice = videoDeviceInput?.device else { return }
guard frameProcessorPerformanceDataCollector.hasEnoughData else { return }
let maxFrameProcessorFps = Double(videoDevice.activeVideoMinFrameDuration.timescale) * Double(videoDevice.activeVideoMinFrameDuration.value)
let averageFps = 1.0 / frameProcessorPerformanceDataCollector.averageExecutionTimeSeconds
let suggestedFrameProcessorFps = max(floor(min(averageFps, maxFrameProcessorFps)), 1)
if frameProcessorFps.intValue == -1 {
// frameProcessorFps="auto"
actualFrameProcessorFps = suggestedFrameProcessorFps
} else {
// frameProcessorFps={someCustomFpsValue}
invokeOnFrameProcessorPerformanceSuggestionAvailable(currentFps: frameProcessorFps.doubleValue,
suggestedFps: suggestedFrameProcessorFps)
} }
} }
@ -270,11 +225,6 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud
} }
} }
private var isReadyForNewEvaluation: Bool {
let lastPerformanceEvaluationElapsedTime = DispatchTime.now().uptimeNanoseconds - lastFrameProcessorPerformanceEvaluation.uptimeNanoseconds
return lastPerformanceEvaluationElapsedTime > 1_000_000_000
}
/** /**
Gets the orientation of the CameraView's images (CMSampleBuffers). Gets the orientation of the CameraView's images (CMSampleBuffers).
*/ */

View File

@ -52,7 +52,6 @@ public final class CameraView: UIView {
// props that require format reconfiguring // props that require format reconfiguring
@objc var format: NSDictionary? @objc var format: NSDictionary?
@objc var fps: NSNumber? @objc var fps: NSNumber?
@objc var frameProcessorFps: NSNumber = -1.0 // "auto"
@objc var hdr: NSNumber? // nullable bool @objc var hdr: NSNumber? // nullable bool
@objc var lowLightBoost: NSNumber? // nullable bool @objc var lowLightBoost: NSNumber? // nullable bool
@objc var colorSpace: NSString? @objc var colorSpace: NSString?
@ -65,7 +64,6 @@ public final class CameraView: UIView {
// events // events
@objc var onInitialized: RCTDirectEventBlock? @objc var onInitialized: RCTDirectEventBlock?
@objc var onError: RCTDirectEventBlock? @objc var onError: RCTDirectEventBlock?
@objc var onFrameProcessorPerformanceSuggestionAvailable: RCTDirectEventBlock?
@objc var onViewReady: RCTDirectEventBlock? @objc var onViewReady: RCTDirectEventBlock?
// zoom // zoom
@objc var enableZoomGesture = false { @objc var enableZoomGesture = false {
@ -105,13 +103,6 @@ public final class CameraView: UIView {
internal let videoQueue = CameraQueues.videoQueue internal let videoQueue = CameraQueues.videoQueue
internal let audioQueue = CameraQueues.audioQueue internal let audioQueue = CameraQueues.audioQueue
/// Specifies whether the frameProcessor() function is currently executing. used to drop late frames.
internal var isRunningFrameProcessor = false
internal let frameProcessorPerformanceDataCollector = FrameProcessorPerformanceDataCollector()
internal var actualFrameProcessorFps = 30.0
internal var lastSuggestedFrameProcessorFps = 0.0
internal var lastFrameProcessorPerformanceEvaluation = DispatchTime.now()
/// Returns whether the AVCaptureSession is currently running (reflected by isActive) /// Returns whether the AVCaptureSession is currently running (reflected by isActive)
var isRunning: Bool { var isRunning: Bool {
return captureSession.isRunning return captureSession.isRunning
@ -259,18 +250,6 @@ public final class CameraView: UIView {
} }
} }
} }
// Frame Processor FPS Configuration
if changedProps.contains("frameProcessorFps") {
if frameProcessorFps.doubleValue == -1 {
// "auto"
actualFrameProcessorFps = 30.0
} else {
actualFrameProcessorFps = frameProcessorFps.doubleValue
}
lastFrameProcessorPerformanceEvaluation = DispatchTime.now()
frameProcessorPerformanceDataCollector.clear()
}
} }
internal final func setTorchMode(_ torchMode: String) { internal final func setTorchMode(_ torchMode: String) {
@ -343,18 +322,4 @@ public final class CameraView: UIView {
guard let onInitialized = onInitialized else { return } guard let onInitialized = onInitialized else { return }
onInitialized([String: Any]()) onInitialized([String: Any]())
} }
internal final func invokeOnFrameProcessorPerformanceSuggestionAvailable(currentFps: Double, suggestedFps: Double) {
ReactLogger.log(level: .info, message: "Frame Processor Performance Suggestion available!")
guard let onFrameProcessorPerformanceSuggestionAvailable = onFrameProcessorPerformanceSuggestionAvailable else { return }
if lastSuggestedFrameProcessorFps == suggestedFps { return }
if suggestedFps == currentFps { return }
onFrameProcessorPerformanceSuggestionAvailable([
"type": suggestedFps > currentFps ? "can-use-higher-fps" : "should-use-lower-fps",
"suggestedFrameProcessorFps": suggestedFps,
])
lastSuggestedFrameProcessorFps = suggestedFps
}
} }

View File

@ -35,7 +35,6 @@ RCT_EXPORT_VIEW_PROPERTY(enableFrameProcessor, BOOL);
// device format // device format
RCT_EXPORT_VIEW_PROPERTY(format, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(format, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(fps, NSNumber); RCT_EXPORT_VIEW_PROPERTY(fps, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(frameProcessorFps, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(hdr, NSNumber); // nullable bool RCT_EXPORT_VIEW_PROPERTY(hdr, NSNumber); // nullable bool
RCT_EXPORT_VIEW_PROPERTY(lowLightBoost, NSNumber); // nullable bool RCT_EXPORT_VIEW_PROPERTY(lowLightBoost, NSNumber); // nullable bool
RCT_EXPORT_VIEW_PROPERTY(colorSpace, NSString); RCT_EXPORT_VIEW_PROPERTY(colorSpace, NSString);
@ -49,7 +48,6 @@ RCT_EXPORT_VIEW_PROPERTY(orientation, NSString);
// Camera View Events // Camera View Events
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onInitialized, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onInitialized, RCTDirectEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onFrameProcessorPerformanceSuggestionAvailable, RCTDirectEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onViewReady, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onViewReady, RCTDirectEventBlock);
// Camera View Functions // Camera View Functions

View File

@ -21,11 +21,7 @@ public:
public: public:
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override; std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
void close();
public: public:
Frame* frame; Frame* frame;
private:
void assertIsFrameStrong(jsi::Runtime& runtime, const std::string& accessedPropName);
}; };

View File

@ -9,16 +9,21 @@
#import "FrameHostObject.h" #import "FrameHostObject.h"
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <jsi/jsi.h> #import <jsi/jsi.h>
#import "JsiHostObject.h"
#import "JsiSharedValue.h"
std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) { std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) {
std::vector<jsi::PropNameID> result; std::vector<jsi::PropNameID> result;
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("width"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("width")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("height"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("height")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("bytesPerRow"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("bytesPerRow")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("planesCount"))); result.push_back(jsi::PropNameID::forUtf8(rt, std::string("planesCount")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("close"))); // Debugging
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
// Ref Management
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("isValid")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("incrementRefCount")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("decrementRefCount")));
return result; return result;
} }
@ -26,7 +31,7 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
auto name = propName.utf8(runtime); auto name = propName.utf8(runtime);
if (name == "toString") { if (name == "toString") {
auto toString = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value { auto toString = JSI_HOST_FUNCTION_LAMBDA {
if (this->frame == nil) { if (this->frame == nil) {
return jsi::String::createFromUtf8(runtime, "[closed frame]"); return jsi::String::createFromUtf8(runtime, "[closed frame]");
} }
@ -39,60 +44,55 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
}; };
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString); return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
} }
if (name == "close") { if (name == "incrementRefCount") {
auto close = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value { auto incrementRefCount = JSI_HOST_FUNCTION_LAMBDA {
if (this->frame == nil) { // Increment retain count by one so ARC doesn't destroy the Frame Buffer.
throw jsi::JSError(runtime, "Trying to close an already closed frame! Did you call frame.close() twice?"); CFRetain(frame.buffer);
}
this->close();
return jsi::Value::undefined(); return jsi::Value::undefined();
}; };
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "close"), 0, close); return jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forUtf8(runtime, "incrementRefCount"),
0,
incrementRefCount);
}
if (name == "decrementRefCount") {
auto decrementRefCount = JSI_HOST_FUNCTION_LAMBDA {
// Decrement retain count by one. If the retain count is zero, ARC will destroy the Frame Buffer.
CFRelease(frame.buffer);
return jsi::Value::undefined();
};
return jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forUtf8(runtime, "decrementRefCount"),
0,
decrementRefCount);
} }
if (name == "isValid") { if (name == "isValid") {
auto isValid = frame != nil && CMSampleBufferIsValid(frame.buffer); auto isValid = frame != nil && frame.buffer != nil && CFGetRetainCount(frame.buffer) > 0 && CMSampleBufferIsValid(frame.buffer);
return jsi::Value(isValid); return jsi::Value(isValid);
} }
if (name == "width") { if (name == "width") {
this->assertIsFrameStrong(runtime, name);
auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer); auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
auto width = CVPixelBufferGetWidth(imageBuffer); auto width = CVPixelBufferGetWidth(imageBuffer);
return jsi::Value((double) width); return jsi::Value((double) width);
} }
if (name == "height") { if (name == "height") {
this->assertIsFrameStrong(runtime, name);
auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer); auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
auto height = CVPixelBufferGetHeight(imageBuffer); auto height = CVPixelBufferGetHeight(imageBuffer);
return jsi::Value((double) height); return jsi::Value((double) height);
} }
if (name == "bytesPerRow") { if (name == "bytesPerRow") {
this->assertIsFrameStrong(runtime, name);
auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer); auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
auto bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); auto bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
return jsi::Value((double) bytesPerRow); return jsi::Value((double) bytesPerRow);
} }
if (name == "planesCount") { if (name == "planesCount") {
this->assertIsFrameStrong(runtime, name);
auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer); auto imageBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
auto planesCount = CVPixelBufferGetPlaneCount(imageBuffer); auto planesCount = CVPixelBufferGetPlaneCount(imageBuffer);
return jsi::Value((double) planesCount); return jsi::Value((double) planesCount);
} }
return jsi::Value::undefined(); // fallback to base implementation
} return HostObject::get(runtime, propName);
void FrameHostObject::assertIsFrameStrong(jsi::Runtime &runtime, const std::string &accessedPropName) {
if (frame == nil) {
auto message = "Cannot get `" + accessedPropName + "`, frame is already closed!";
throw jsi::JSError(runtime, message.c_str());
}
}
void FrameHostObject::close() {
if (frame != nil) {
CMSampleBufferInvalidate(frame.buffer);
// ARC will hopefully delete it lol
this->frame = nil;
}
} }

View File

@ -1,67 +0,0 @@
//
// FrameProcessorPerformanceDataCollector.swift
// VisionCamera
//
// Created by Marc Rousavy on 30.08.21.
// Copyright © 2021 mrousavy. All rights reserved.
//
import Foundation
// keep a maximum of `maxSampleSize` historical performance data samples cached.
private let maxSampleSize = 15
// MARK: - PerformanceSampleCollection
struct PerformanceSampleCollection {
var endPerformanceSampleCollection: () -> Void
init(end: @escaping () -> Void) {
endPerformanceSampleCollection = end
}
}
// MARK: - FrameProcessorPerformanceDataCollector
class FrameProcessorPerformanceDataCollector {
private var performanceSamples: [Double] = []
private var counter = 0
private var lastEvaluation = -1
var hasEnoughData: Bool {
return !performanceSamples.isEmpty
}
var averageExecutionTimeSeconds: Double {
let sum = performanceSamples.reduce(0, +)
let average = sum / Double(performanceSamples.count)
lastEvaluation = counter
return average
}
func beginPerformanceSampleCollection() -> PerformanceSampleCollection {
let begin = DispatchTime.now()
return PerformanceSampleCollection {
let end = DispatchTime.now()
let seconds = Double(end.uptimeNanoseconds - begin.uptimeNanoseconds) / 1_000_000_000.0
let index = self.counter % maxSampleSize
if self.performanceSamples.count > index {
self.performanceSamples[index] = seconds
} else {
self.performanceSamples.append(seconds)
}
self.counter += 1
}
}
func clear() {
counter = 0
performanceSamples.removeAll()
}
}

View File

@ -22,6 +22,7 @@
#import "JsiWorkletContext.h" #import "JsiWorkletContext.h"
#import "JsiWorkletApi.h" #import "JsiWorkletApi.h"
#import "JsiWorklet.h" #import "JsiWorklet.h"
#import "JsiHostObject.h"
#import "FrameProcessorUtils.h" #import "FrameProcessorUtils.h"
#import "FrameProcessorCallback.h" #import "FrameProcessorCallback.h"
@ -30,7 +31,7 @@
// Forward declarations for the Swift classes // Forward declarations for the Swift classes
__attribute__((objc_runtime_name("_TtC12VisionCamera12CameraQueues"))) __attribute__((objc_runtime_name("_TtC12VisionCamera12CameraQueues")))
@interface CameraQueues : NSObject @interface CameraQueues : NSObject
@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull frameProcessorQueue; @property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull videoQueue;
@end @end
__attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView"))) __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
@interface CameraView : UIView @interface CameraView : UIView
@ -38,6 +39,7 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
@end @end
@implementation FrameProcessorRuntimeManager { @implementation FrameProcessorRuntimeManager {
// Running Frame Processors on camera's video thread (synchronously)
std::shared_ptr<RNWorklet::JsiWorkletContext> workletContext; std::shared_ptr<RNWorklet::JsiWorkletContext> workletContext;
} }
@ -59,13 +61,12 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
}; };
auto runOnWorklet = [](std::function<void()>&& f) { auto runOnWorklet = [](std::function<void()>&& f) {
// Run on Frame Processor Worklet Runtime // Run on Frame Processor Worklet Runtime
dispatch_async(CameraQueues.frameProcessorQueue, [f = std::move(f)](){ dispatch_async(CameraQueues.videoQueue, [f = std::move(f)](){
f(); f();
}); });
}; };
workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera"); workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("VisionCamera",
workletContext->initialize("VisionCamera",
&runtime, &runtime,
runOnJS, runOnJS,
runOnWorklet); runOnWorklet);
@ -136,28 +137,17 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
NSLog(@"FrameProcessorBindings: Installing global functions..."); NSLog(@"FrameProcessorBindings: Installing global functions...");
// setFrameProcessor(viewTag: number, frameProcessor: (frame: Frame) => void) // setFrameProcessor(viewTag: number, frameProcessor: (frame: Frame) => void)
auto setFrameProcessor = [self](jsi::Runtime& runtime, auto setFrameProcessor = JSI_HOST_FUNCTION_LAMBDA {
const jsi::Value& thisValue,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
NSLog(@"FrameProcessorBindings: Setting new frame processor..."); NSLog(@"FrameProcessorBindings: Setting new frame processor...");
if (!arguments[0].isNumber()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: First argument ('viewTag') must be a number!");
if (!arguments[1].isObject()) throw jsi::JSError(runtime, "Camera::setFrameProcessor: Second argument ('frameProcessor') must be a function!");
auto viewTag = arguments[0].asNumber(); auto viewTag = arguments[0].asNumber();
NSLog(@"FrameProcessorBindings: Converting JSI Function to Worklet...");
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, arguments[1]); auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, arguments[1]);
RCTExecuteOnMainQueue([=]() { RCTExecuteOnMainQueue(^{
auto currentBridge = [RCTBridge currentBridge]; auto currentBridge = [RCTBridge currentBridge];
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]]; auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
auto view = static_cast<CameraView*>(anonymousView); auto view = static_cast<CameraView*>(anonymousView);
auto callback = convertWorkletToFrameProcessorCallback(self->workletContext->getWorkletRuntime(), worklet);
NSLog(@"FrameProcessorBindings: Converting worklet to Objective-C callback..."); view.frameProcessorCallback = callback;
view.frameProcessorCallback = convertWorkletToFrameProcessorCallback(workletContext->getWorkletRuntime(), worklet);
NSLog(@"FrameProcessorBindings: Frame processor set!");
}); });
return jsi::Value::undefined(); return jsi::Value::undefined();
@ -168,12 +158,8 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
setFrameProcessor)); setFrameProcessor));
// unsetFrameProcessor(viewTag: number) // unsetFrameProcessor(viewTag: number)
auto unsetFrameProcessor = [](jsi::Runtime& runtime, auto unsetFrameProcessor = JSI_HOST_FUNCTION_LAMBDA {
const jsi::Value& thisValue,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
NSLog(@"FrameProcessorBindings: Removing frame processor..."); NSLog(@"FrameProcessorBindings: Removing frame processor...");
if (!arguments[0].isNumber()) throw jsi::JSError(runtime, "Camera::unsetFrameProcessor: First argument ('viewTag') must be a number!");
auto viewTag = arguments[0].asNumber(); auto viewTag = arguments[0].asNumber();
RCTExecuteOnMainQueue(^{ RCTExecuteOnMainQueue(^{
@ -185,7 +171,6 @@ __attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
auto view = static_cast<CameraView*>(anonymousView); auto view = static_cast<CameraView*>(anonymousView);
view.frameProcessorCallback = nil; view.frameProcessorCallback = nil;
NSLog(@"FrameProcessorBindings: Frame processor removed!");
}); });
return jsi::Value::undefined(); return jsi::Value::undefined();

View File

@ -28,13 +28,15 @@ FrameProcessorCallback convertWorkletToFrameProcessorCallback(jsi::Runtime& runt
// Converts a Worklet to a callable Objective-C block function // Converts a Worklet to a callable Objective-C block function
return ^(Frame* frame) { return ^(Frame* frame) {
auto frameHostObject = std::make_shared<FrameHostObject>(frame);
try { try {
// Call JS Frame Processor function with boxed Frame Host Object // Box the Frame to a JS Host Object
auto frameHostObject = std::make_shared<FrameHostObject>(frame);
auto argument = jsi::Object::createFromHostObject(runtime, frameHostObject); auto argument = jsi::Object::createFromHostObject(runtime, frameHostObject);
jsi::Value jsValue(std::move(argument)); jsi::Value jsValue(std::move(argument));
// Call the Worklet with the Frame JS Host Object as an argument
workletInvoker->call(runtime, jsi::Value::undefined(), &jsValue, 1); workletInvoker->call(runtime, jsi::Value::undefined(), &jsValue, 1);
} catch (jsi::JSError& jsError) { } catch (jsi::JSError& jsError) {
// JS Error occured, print it to console.
auto stack = std::regex_replace(jsError.getStack(), std::regex("\n"), "\n "); auto stack = std::regex_replace(jsError.getStack(), std::regex("\n"), "\n ");
auto message = [NSString stringWithFormat:@"Frame Processor threw an error: %s\nIn: %s", jsError.getMessage().c_str(), stack.c_str()]; auto message = [NSString stringWithFormat:@"Frame Processor threw an error: %s\nIn: %s", jsError.getMessage().c_str(), stack.c_str()];
@ -48,11 +50,5 @@ FrameProcessorCallback convertWorkletToFrameProcessorCallback(jsi::Runtime& runt
NSLog(@"%@", message); NSLog(@"%@", message);
} }
} }
// Manually free the buffer because:
// 1. we are sure we don't need it anymore, the frame processor worklet has finished executing.
// 2. we don't know when the JS runtime garbage collects this object, it might be holding it for a few more frames
// which then blocks the camera queue from pushing new frames (memory limit)
frameHostObject->close();
}; };
} }

View File

@ -138,7 +138,6 @@
B887518425E0102000DB86D6 /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; }; B887518425E0102000DB86D6 /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; };
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPlugin.h; sourceTree = "<group>"; }; B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPlugin.h; sourceTree = "<group>"; };
B88B47462667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureSession+setVideoStabilizationMode.swift"; sourceTree = "<group>"; }; B88B47462667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureSession+setVideoStabilizationMode.swift"; sourceTree = "<group>"; };
B8948BDF26DCEE2B00B430E2 /* FrameProcessorPerformanceDataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameProcessorPerformanceDataCollector.swift; sourceTree = "<group>"; };
B8994E6B263F03E100069589 /* JSIUtils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSIUtils.mm; sourceTree = "<group>"; }; B8994E6B263F03E100069589 /* JSIUtils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSIUtils.mm; sourceTree = "<group>"; };
B8A751D62609E4980011C623 /* FrameProcessorRuntimeManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorRuntimeManager.h; sourceTree = "<group>"; }; B8A751D62609E4980011C623 /* FrameProcessorRuntimeManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorRuntimeManager.h; sourceTree = "<group>"; };
B8A751D72609E4B30011C623 /* FrameProcessorRuntimeManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameProcessorRuntimeManager.mm; sourceTree = "<group>"; }; B8A751D72609E4B30011C623 /* FrameProcessorRuntimeManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameProcessorRuntimeManager.mm; sourceTree = "<group>"; };
@ -273,7 +272,6 @@
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */, B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */,
B80416F026AB16E8000DEB6A /* VisionCameraScheduler.mm */, B80416F026AB16E8000DEB6A /* VisionCameraScheduler.mm */,
B80416F126AB16F3000DEB6A /* VisionCameraScheduler.h */, B80416F126AB16F3000DEB6A /* VisionCameraScheduler.h */,
B8948BDF26DCEE2B00B430E2 /* FrameProcessorPerformanceDataCollector.swift */,
); );
path = "Frame Processor"; path = "Frame Processor";
sourceTree = "<group>"; sourceTree = "<group>";

View File

@ -80,8 +80,9 @@
"pod-install": "^0.1.38", "pod-install": "^0.1.38",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"react": "^18.2.0", "react": "^18.2.0",
"react-native": "^0.71.2", "react-native": "^0.71.3",
"react-native-builder-bob": "^0.20.3", "react-native-builder-bob": "^0.20.3",
"react-native-worklets": "https://github.com/chrfalch/react-native-worklets#15d52dd",
"release-it": "^15.6.0", "release-it": "^15.6.0",
"typescript": "^4.9.5" "typescript": "^4.9.5"
}, },

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { requireNativeComponent, NativeModules, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native'; import { requireNativeComponent, NativeModules, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native';
import type { FrameProcessorPerformanceSuggestion, VideoFileType } from '.'; import type { VideoFileType } from '.';
import type { CameraDevice } from './CameraDevice'; import type { CameraDevice } from './CameraDevice';
import type { ErrorWithCause } from './CameraError'; import type { ErrorWithCause } from './CameraError';
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError'; import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
@ -20,16 +20,11 @@ interface OnErrorEvent {
message: string; message: string;
cause?: ErrorWithCause; cause?: ErrorWithCause;
} }
type NativeCameraViewProps = Omit< type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onError' | 'frameProcessor'> & {
CameraProps,
'device' | 'onInitialized' | 'onError' | 'onFrameProcessorPerformanceSuggestionAvailable' | 'frameProcessor' | 'frameProcessorFps'
> & {
cameraId: string; cameraId: string;
frameProcessorFps?: number; // native cannot use number | string, so we use '-1' for 'auto'
enableFrameProcessor: boolean; enableFrameProcessor: boolean;
onInitialized?: (event: NativeSyntheticEvent<void>) => void; onInitialized?: (event: NativeSyntheticEvent<void>) => void;
onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void; onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void;
onFrameProcessorPerformanceSuggestionAvailable?: (event: NativeSyntheticEvent<FrameProcessorPerformanceSuggestion>) => void;
onViewReady: () => void; onViewReady: () => void;
}; };
type RefType = React.Component<NativeCameraViewProps> & Readonly<NativeMethods>; type RefType = React.Component<NativeCameraViewProps> & Readonly<NativeMethods>;
@ -86,7 +81,6 @@ export class Camera extends React.PureComponent<CameraProps> {
this.onViewReady = this.onViewReady.bind(this); this.onViewReady = this.onViewReady.bind(this);
this.onInitialized = this.onInitialized.bind(this); this.onInitialized = this.onInitialized.bind(this);
this.onError = this.onError.bind(this); this.onError = this.onError.bind(this);
this.onFrameProcessorPerformanceSuggestionAvailable = this.onFrameProcessorPerformanceSuggestionAvailable.bind(this);
this.ref = React.createRef<RefType>(); this.ref = React.createRef<RefType>();
this.lastFrameProcessor = undefined; this.lastFrameProcessor = undefined;
} }
@ -422,11 +416,6 @@ export class Camera extends React.PureComponent<CameraProps> {
private onInitialized(): void { private onInitialized(): void {
this.props.onInitialized?.(); this.props.onInitialized?.();
} }
private onFrameProcessorPerformanceSuggestionAvailable(event: NativeSyntheticEvent<FrameProcessorPerformanceSuggestion>): void {
if (this.props.onFrameProcessorPerformanceSuggestionAvailable != null)
this.props.onFrameProcessorPerformanceSuggestionAvailable(event.nativeEvent);
}
//#endregion //#endregion
//#region Lifecycle //#region Lifecycle
@ -479,17 +468,15 @@ export class Camera extends React.PureComponent<CameraProps> {
/** @internal */ /** @internal */
public render(): React.ReactNode { public render(): React.ReactNode {
// We remove the big `device` object from the props because we only need to pass `cameraId` to native. // We remove the big `device` object from the props because we only need to pass `cameraId` to native.
const { device, frameProcessor, frameProcessorFps, ...props } = this.props; const { device, frameProcessor, ...props } = this.props;
return ( return (
<NativeCameraView <NativeCameraView
{...props} {...props}
frameProcessorFps={frameProcessorFps === 'auto' ? -1 : frameProcessorFps}
cameraId={device.id} cameraId={device.id}
ref={this.ref} ref={this.ref}
onViewReady={this.onViewReady} onViewReady={this.onViewReady}
onInitialized={this.onInitialized} onInitialized={this.onInitialized}
onError={this.onError} onError={this.onError}
onFrameProcessorPerformanceSuggestionAvailable={this.onFrameProcessorPerformanceSuggestionAvailable}
enableFrameProcessor={frameProcessor != null} enableFrameProcessor={frameProcessor != null}
/> />
); );

View File

@ -4,11 +4,6 @@ import type { CameraRuntimeError } from './CameraError';
import type { CameraPreset } from './CameraPreset'; import type { CameraPreset } from './CameraPreset';
import type { Frame } from './Frame'; import type { Frame } from './Frame';
export interface FrameProcessorPerformanceSuggestion {
type: 'can-use-higher-fps' | 'should-use-lower-fps';
suggestedFrameProcessorFps: number;
}
export interface CameraProps extends ViewProps { export interface CameraProps extends ViewProps {
/** /**
* The Camera Device to use. * The Camera Device to use.
@ -171,11 +166,7 @@ export interface CameraProps extends ViewProps {
*/ */
onInitialized?: () => void; onInitialized?: () => void;
/** /**
* Called when a new performance suggestion for a Frame Processor is available - either if your Frame Processor is running too fast and frames are being dropped, or because it is able to run faster. Optionally, you can adjust your `frameProcessorFps` accordingly. * A worklet which will be called for every frame the Camera "sees".
*/
onFrameProcessorPerformanceSuggestionAvailable?: (suggestion: FrameProcessorPerformanceSuggestion) => void;
/**
* A worklet which will be called for every frame the Camera "sees". Throttle the Frame Processor's frame rate with {@linkcode frameProcessorFps}.
* *
* > See [the Frame Processors documentation](https://mrousavy.github.io/react-native-vision-camera/docs/guides/frame-processors) for more information * > See [the Frame Processors documentation](https://mrousavy.github.io/react-native-vision-camera/docs/guides/frame-processors) for more information
* *
@ -193,20 +184,5 @@ export interface CameraProps extends ViewProps {
* ``` * ```
*/ */
frameProcessor?: (frame: Frame) => void; frameProcessor?: (frame: Frame) => void;
/**
* Specifies the maximum frame rate the frame processor can use, independent of the Camera's frame rate (`fps` property).
*
* * A value of `'auto'` (default) indicates that the frame processor should execute as fast as it can, without dropping frames. This is achieved by collecting historical data for previous frame processor calls and adjusting frame rate accordingly.
* * A value of `1` indicates that the frame processor gets executed once per second, perfect for code scanning.
* * A value of `10` indicates that the frame processor gets executed 10 times per second, perfect for more realtime use-cases.
* * A value of `25` indicates that the frame processor gets executed 25 times per second, perfect for high-speed realtime use-cases.
* * ...and so on
*
* If you're using higher values, always check your Xcode/Android Studio Logs to make sure your frame processors are executing fast enough
* without blocking the video recording queue.
*
* @default 'auto'
*/
frameProcessorFps?: number | 'auto';
//#endregion //#endregion
} }

View File

@ -31,19 +31,19 @@ export interface Frame {
* ``` * ```
*/ */
toString(): string; toString(): string;
/** }
* Closes and disposes the Frame.
* Only close frames that you have created yourself, e.g. by copying the frame you receive in a frame processor. export interface FrameInternal extends Frame {
* /**
* @example * Increment the Frame Buffer ref-count by one.
* ```ts *
* const frameProcessor = useFrameProcessor((frame) => { * This is a private API, do not use this.
* const smallerCopy = resize(frame, 480, 270) */
* // run AI ... incrementRefCount(): void;
* smallerCopy.close() /**
* // don't close `frame`! * Increment the Frame Buffer ref-count by one.
* }) *
* ``` * This is a private API, do not use this.
*/ */
close(): void; decrementRefCount(): void;
} }

View File

@ -1,13 +1,101 @@
import type { Frame } from './Frame'; import type { Frame, FrameInternal } from './Frame';
import { Camera } from './Camera'; import { Camera } from './Camera';
import { Worklets } from 'react-native-worklets/src';
// Install VisionCamera Frame Processor JSI Bindings and Plugins // Install VisionCamera Frame Processor JSI Bindings and Plugins
Camera.installFrameProcessorBindings(); Camera.installFrameProcessorBindings();
type FrameProcessor = (frame: Frame, ...args: unknown[]) => unknown; type BasicParameterType = string | number | boolean | undefined;
type ParameterType = BasicParameterType | BasicParameterType[] | Record<string, BasicParameterType | undefined>;
type FrameProcessor = (frame: Frame, parameters?: Record<string, ParameterType | undefined>) => unknown;
type TFrameProcessorPlugins = Record<string, FrameProcessor>; type TFrameProcessorPlugins = Record<string, FrameProcessor>;
/** /**
* All natively installed Frame Processor Plugins. * All natively installed Frame Processor Plugins.
*/ */
export const FrameProcessorPlugins = global.FrameProcessorPlugins as TFrameProcessorPlugins; export const FrameProcessorPlugins = global.FrameProcessorPlugins as TFrameProcessorPlugins;
const lastFrameProcessorCall = Worklets.createSharedValue(performance.now());
/**
* Runs the given function at the given target FPS rate.
*
* For example, if you want to run a heavy face detection algorithm
* only once per second, you can use `runAtTargetFps(1, ...)` to
* throttle it to 1 FPS.
*
* @param fps The target FPS rate at which the given function should be executed
* @param func The function to execute.
* @returns The result of the function if it was executed, or `undefined` otherwise.
* @example
*
* ```ts
* const frameProcessor = useFrameProcessor((frame) => {
* 'worklet'
* console.log('New Frame')
* const face = runAtTargetFps(5, () => {
* 'worklet'
* const faces = detectFaces(frame)
* return faces[0]
* })
* if (face != null) console.log(`Detected a new face: ${face}`)
* })
* ```
*/
export function runAtTargetFps<T>(fps: number, func: () => T): T | undefined {
'worklet';
const targetIntervalMs = 1000 / fps; // <-- 60 FPS => 16,6667ms interval
const now = performance.now();
const diffToLastCall = now - lastFrameProcessorCall.value;
if (diffToLastCall >= targetIntervalMs) {
lastFrameProcessorCall.value = now;
// Last Frame Processor call is already so long ago that we want to make a new call
return func();
}
return undefined;
}
const asyncContext = Worklets.createContext('VisionCamera.async');
const runOnAsyncContext = Worklets.createRunInContextFn((frame: Frame, func: () => void) => {
'worklet';
try {
// Call long-running function
func();
} finally {
// Potentially delete Frame if we were the last ref
(frame as FrameInternal).decrementRefCount();
}
}, asyncContext);
/**
* Runs the given function asynchronously, while keeping a strong reference to the Frame.
*
* For example, if you want to run a heavy face detection algorithm
* while still drawing to the screen at 60 FPS, you can use `runAsync(...)`
* to offload the face detection algorithm to a separate thread.
*
* @param frame The current Frame of the Frame Processor.
* @param func The function to execute.
* @example
*
* ```ts
* const frameProcessor = useFrameProcessor((frame) => {
* 'worklet'
* console.log('New Frame')
* runAsync(frame, () => {
* 'worklet'
* const faces = detectFaces(frame)
* const face = [faces0]
* console.log(`Detected a new face: ${face}`)
* })
* })
* ```
*/
export function runAsync(frame: Frame, func: () => void): void {
'worklet';
// Increment ref count by one
(frame as FrameInternal).incrementRefCount();
// Call in separate background context
runOnAsyncContext(frame, func);
}

View File

@ -1,5 +1,5 @@
import { DependencyList, useCallback } from 'react'; import { DependencyList, useCallback } from 'react';
import type { Frame } from '../Frame'; import type { Frame, FrameInternal } from '../Frame';
// Install RN Worklets by importing it // Install RN Worklets by importing it
import 'react-native-worklets/src'; import 'react-native-worklets/src';
@ -23,6 +23,17 @@ type FrameProcessor = (frame: Frame) => void;
* ``` * ```
*/ */
export function useFrameProcessor(frameProcessor: FrameProcessor, dependencies: DependencyList): FrameProcessor { export function useFrameProcessor(frameProcessor: FrameProcessor, dependencies: DependencyList): FrameProcessor {
// eslint-disable-next-line react-hooks/exhaustive-deps return useCallback((frame: Frame) => {
return useCallback(frameProcessor, dependencies); 'worklet';
// Increment ref-count by one
(frame as FrameInternal).incrementRefCount();
try {
// Call sync frame processor
frameProcessor(frame);
} finally {
// Potentially delete Frame if we were the last ref (no runAsync)
(frame as FrameInternal).decrementRefCount();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies);
} }

View File

@ -4,7 +4,7 @@ export * from './CameraError';
export * from './CameraPosition'; export * from './CameraPosition';
export * from './CameraPreset'; export * from './CameraPreset';
export * from './CameraProps'; export * from './CameraProps';
export * from './Frame'; export { Frame } from './Frame';
export * from './FrameProcessorPlugins'; export * from './FrameProcessorPlugins';
export * from './CameraProps'; export * from './CameraProps';
export * from './PhotoFile'; export * from './PhotoFile';

View File

@ -5217,10 +5217,10 @@ js-yaml@^4.1.0:
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
jsc-android@^250230.2.1: jsc-android@^250231.0.0:
version "250230.2.1" version "250231.0.0"
resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250230.2.1.tgz#3790313a970586a03ab0ad47defbc84df54f1b83" resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262"
integrity sha512-KmxeBlRjwoqCnBBKGsihFtvsBHyUFlBxJPK4FzeYcIuBfdjv6jFys44JITAgSTbQD+vIdwMEfyZklsuQX0yI1Q== integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==
jscodeshift@^0.13.1: jscodeshift@^0.13.1:
version "0.13.1" version "0.13.1"
@ -6867,25 +6867,29 @@ react-native-builder-bob@^0.20.3:
optionalDependencies: optionalDependencies:
jetifier "^2.0.0" jetifier "^2.0.0"
react-native-codegen@^0.71.3: react-native-codegen@^0.71.5:
version "0.71.3" version "0.71.5"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.3.tgz#75fbc591819050791319ebdb9fe341ee4df5c288" resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14"
integrity sha512-5AvdHVU1sAaXg05i0dG664ZTaCaIFaY1znV5vNsj+wUu6MGxNEUNbDKk9dxKUkkxOyk2KZOK5uhzWL0p5H5yZQ== integrity sha512-rfsuc0zkuUuMjFnrT55I1mDZ+pBRp2zAiRwxck3m6qeGJBGK5OV5JH66eDQ4aa+3m0of316CqrJDRzVlYufzIg==
dependencies: dependencies:
"@babel/parser" "^7.14.0" "@babel/parser" "^7.14.0"
flow-parser "^0.185.0" flow-parser "^0.185.0"
jscodeshift "^0.13.1" jscodeshift "^0.13.1"
nullthrows "^1.1.1" nullthrows "^1.1.1"
react-native-gradle-plugin@^0.71.14: react-native-gradle-plugin@^0.71.15:
version "0.71.14" version "0.71.15"
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.14.tgz#cc399662f04fbfcc0e352d03eae1d3efbd5f635a" resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.15.tgz#9e6b506f30729fe8eb086981702f4e3c891d2b13"
integrity sha512-nnLawTZEPPRAKq92UqDkzoGgCBl9aa9zAihFHMwmwzn4WRVdK4O6Cd4XYiyoNOiQzx3Hh9k5WOckHE80C92ivQ== integrity sha512-7S3pAuPaQJlhax6EZ4JMsDNpj05TfuzX9gPgWLrFfAIWIFLuJ6aDQYAZy2TEI9QJALPoWrj8LWaqP/DGYh14pw==
react-native@^0.71.2: "react-native-worklets@https://github.com/chrfalch/react-native-worklets#15d52dd":
version "0.71.2" version "0.1.0"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.2.tgz#b6977eda2a6dc10baa006bf4ab1ee08318607ce9" resolved "https://github.com/chrfalch/react-native-worklets#15d52dd1289831cecc7906823f613172e0c6cd2e"
integrity sha512-ZSianM+j+09LoEdVIhrAP/uP8sQhT7dH6olCqM2xlpxmfCgA5NubsK6NABIuZiBlmmqjigyijm5Y/GhBIHDvEg==
react-native@^0.71.3:
version "0.71.3"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.71.3.tgz#0faab799c49e61ba12df9e6525c3ac7d595d673c"
integrity sha512-RYJXCcQGa4NTfKiPgl92eRDUuQ6JGDnHqFEzRwJSqEx9lWvlvRRIebstJfurzPDKLQWQrvITR7aI7e09E25mLw==
dependencies: dependencies:
"@jest/create-cache-key-function" "^29.2.1" "@jest/create-cache-key-function" "^29.2.1"
"@react-native-community/cli" "10.1.3" "@react-native-community/cli" "10.1.3"
@ -6901,7 +6905,7 @@ react-native@^0.71.2:
event-target-shim "^5.0.1" event-target-shim "^5.0.1"
invariant "^2.2.4" invariant "^2.2.4"
jest-environment-node "^29.2.1" jest-environment-node "^29.2.1"
jsc-android "^250230.2.1" jsc-android "^250231.0.0"
memoize-one "^5.0.0" memoize-one "^5.0.0"
metro-react-native-babel-transformer "0.73.7" metro-react-native-babel-transformer "0.73.7"
metro-runtime "0.73.7" metro-runtime "0.73.7"
@ -6911,8 +6915,8 @@ react-native@^0.71.2:
pretty-format "^26.5.2" pretty-format "^26.5.2"
promise "^8.3.0" promise "^8.3.0"
react-devtools-core "^4.26.1" react-devtools-core "^4.26.1"
react-native-codegen "^0.71.3" react-native-codegen "^0.71.5"
react-native-gradle-plugin "^0.71.14" react-native-gradle-plugin "^0.71.15"
react-refresh "^0.4.0" react-refresh "^0.4.0"
react-shallow-renderer "^16.15.0" react-shallow-renderer "^16.15.0"
regenerator-runtime "^0.13.2" regenerator-runtime "^0.13.2"