feature: Frame Processors (iOS) (#2)

* Clean up Frame Processor

* Create FrameProcessorHolder

* Create FrameProcessorDelegate in ObjC++

* Move frame processor to FrameProcessorDelegate

* Decorate runtime, check for null

* Update FrameProcessorDelegate.mm

* Cleanup FrameProcessorBindings.mm

* Fix RuntimeDecorator.h import

* Update FrameProcessorDelegate.mm

* "React" -> "React Helper" to avoid confusion

* Rename folders again

* Fix podspec flattening a lot of headers, causing REA nameclash

* Fix header imports to avoid REA naming collision

* Lazily initialize jsi::Runtime on DispatchQueue

* Install frame processor bindings from Swift

* First try to call jsi::Function (frame processor) 👀

* Call viewForReactTag on RCT main thread

* Fix bridge accessing

* Add more logs

* Update CameraViewManager.swift

* Add more TODOs

* Re-indent .cpp files

* Fix RCTTurboModule import podspec

* Remove unnecessary include check for swift umbrella header

* Merge branch 'main' into frame-processors

* Docs: use static width for images (283)

* Create validate-cpp.yml

* Update a lot of packages to latest

* Set SWIFT_VERSION to 5.2 in podspec

* Create clean.sh

* Delete unused C++ files

* podspec: Remove CLANG_CXX_LANGUAGE_STANDARD and OTHER_CFLAGS

* Update pod lockfiles

* Regenerate lockfiles

* Remove IOSLogger

* Use NSLog

* Create FrameProcessorManager (inherits from REA RuntimeManager)

* Create reanimated::RuntimeManager shared_ptr

* Re-integrate pods

* Add react-native-reanimated >=2 peerDependency

* Add metro-config

* blacklist -> exclusionList

* Try to call worklet

* Fix jsi::Value* initializer

* Call ShareableValue::adapt (makeShareable) with React/JS Runtime

* Add null-checks

* Lift runtime manager creation out of delegate, into bindings

* Remove debug statement

* Make RuntimeManager unique_ptr

* Set _FRAME_PROCESSOR

* Extract convertJSIFunctionToFrameProcessorCallback

* Print frame

* Merge branch 'main' into frame-processors

* Reformat Swift code

* Install reanimated from npm again

* Re-integrate Pods

* Dependabot: Also scan example/ and docs/

* Update validate-cpp.yml

* Create FrameProcessorUtils

* Create Frame.h

* Abstract HostObject creation away

* Fix types

* Fix frame processor call

* Add todo

* Update lockfiles

* Add C++ contributing instructions

* Update CONTRIBUTING.md

* Add android/src/main/cpp to cpplint

* Update cpplint.sh

* Fix a few cpplint errors

* Fix globals

* Fix a few more cpplint errors

* Update App.tsx

* Update AndroidLogger.cpp

* Format

* Fix cpplint script (check-cpp)

* Try to simplify frame processor

* y

* Update FrameProcessorUtils.mm

* Update FrameProcessorBindings.mm

* Update CameraView.swift

* Update CameraViewManager.m

* Restructure everything

* fix

* Fix `@objc` export (make public)

* Refactor installFrameProcessorBindings into FrameProcessorRuntimeManager

* Add swift RCTBridge.runOnJS helper

* Fix run(onJS)

* Add pragma once

* Add `&self` to lambda

* Update FrameProcessorRuntimeManager.mm

* reorder imports

* Fix imports

* forward declare

* Rename extension

* Destroy buffer after execution

* Add FrameProcessorPluginRegistry base

* Merge branch 'main' into frame-processors

* Add frameProcessor to types

* Update Camera.tsx

* Fix rebase merge

* Remove movieOutput

* Use `useFrameProcessor`

* Fix bad merge

* Add additional ESLint rules

* Update lockfiles

* Update CameraViewManager.m

* Add support for V8 runtime

* Add frame processor plugins API

* Print plugin invoke

* Fix React Utils in podspec

* Fix runOnJS swift name

* Remove invalid redecl of `captureSession`

* Use REA 2.1.0 which includes all my big PRs 🎉

* Update validate-cpp.yml

* Update Podfile.lock

* Remove Flipper

* Fix dereferencing

* Capture `self` by value. Fucking hell, what a dumb mistake.

* Override a few HostObject functions

* Expose isReady, width, height, bytesPerRow and planesCount

* use hook again

* Expose property names

* FrameProcessor -> Frame

* Update CameraView+RecordVideo.swift

* Add Swift support for Frame Processors Plugins

* Add macros for plugin installation

* Add ObjC frame processor plugin

* Correctly install frame processor plugins

* Don't require custom name for macro

* Check if plugin already exists

* Implement QR Code Frame Processor Plugin in Swift

* Adjust ObjC style frame processor macro

* optimize

* Add `frameProcessorFrameDropRate`

* Fix types

* Only log once

* Log if it executes slowly

* Implement `frameProcessorFps`

* Implement manual encoded video recordings

* Use recommended video settings

* Add fileType types

* Ignore if input is not ready for media data

* Add completion handler

* Add audio buffer sampling

* Init only for video frame

* use AVAssetWriterInputPixelBufferAdaptor

* Remove AVAssetWriterInputPixelBufferAdaptor

* Rotate VideoWriter

* Always assume portrait orientation

* Update RecordingSession.swift

* Use a separate Queue for Audio

* Format Swift

* Update CameraView+RecordVideo.swift

* Use `videoQueue` instead of `cameraQueue`

* Move example plugins to example app

* Fix hardcoded name in plugin macro

* QRFrame... -> QRCodeFrame...

* Update FrameProcessorPlugin.h

* Add example frame processors to JS base

* Update QRCodeFrameProcessorPluginSwift.m

* Add docs to create FP Plugins

* Update FRAME_PROCESSORS_CREATE.mdx

* Update FRAME_PROCESSORS_CREATE.mdx

* Use `AVAssetWriterInputPixelBufferAdaptor` for efficient pixel buffer recycling

* Add customizable `pixelFormat`

* Use native format if available

* Update project.pbxproj

* Set video width and height as source-pixel-buffer attributes

* Catch

* Update App.tsx

* Don't explicitly set video dimensions, let CVPixelBufferPool handle it

* Add a few logs

* Cleanup

* Update CameraView+RecordVideo.swift

* Eagerly initialize asset writer to fix stutter at first frame

* Use `cameraQueue` DispatchQueue to not block CaptureDataOutputDelegate

* Fix duration calculation

* cleanup

* Cleanup

* Swiftformat

* Return available video codecs

* Only show frame drop notification for video output

* Remove photo and video codec functionality

It was too much complexity and probably never used anyways.

* Revert all android related changes for now

* Cleanup

* Remove unused header

* Update AVAssetWriter.Status+descriptor.swift

* Only call Frame Processor for Video Frames

* Fix `if`

* Add support for Frame Processor plugin parameters/arguments

* Fix arg support

* Move to JSIUtils.mm

* Update JSIUtils.h

* Update FRAME_PROCESSORS_CREATE.mdx

* Update FRAME_PROCESSORS_CREATE.mdx

* Upgrade packages for docs/

* fix docs

* Rename

* highlight lines

* docs

* community plugins

* Update FRAME_PROCESSOR_CREATE_FINAL.mdx

* Update FRAME_PROCESSOR_PLUGIN_LIST.mdx

* Update FRAME_PROCESSOR_PLUGIN_LIST.mdx

* Update dependencies (1/2)

* Update dependencies (2/2)

* Update Gemfile.lock

* add FP docs

* Update README.md

* Make `lastFrameProcessor` private

* add `frameProcessor` docs

* fix docs

* adjust docs

* Update DEVICES.mdx

* fix

* s

* Add logs demo

* add metro restart note

* Update FRAME_PROCESSOR_CREATE_PLUGIN_IOS.mdx

* Mirror video device

* Update AVCaptureVideoDataOutput+mirror.swift

* Create .swift-version

* Enable whole module optimization

* Fix recording mirrored video

* Swift format

* Clean dictionary on `markInvalid`

* Fix cleanup

* Add docs for disabling frame processors

* Update project.pbxproj

* Revert "Update project.pbxproj"

This reverts commit e67861e51b88b4888a6940e2d20388f3044211d0.

* Log frame drop reason

* Format

* add more samples

* Add clang-format

* also check .mm

* Revert "also check .mm"

This reverts commit 8b9d5e2c29866b05909530d104f6633d6c49eadd.

* Revert "Add clang-format"

This reverts commit 7643ac808e0fc34567ea1f814e73d84955381636.

* Use `kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange` as default

* Read matching video attributes from videoSettings

* Add TODO

* Swiftformat

* Conditionally disable frame processors

* Assert if trying to use frame processors when disabled

* Add frame-processors demo gif

* Allow disabling frame processors via `VISION_CAMERA_DISABLE_FRAME_PROCESSORS`

* Update FrameProcessorRuntimeManager.mm

* Update FRAME_PROCESSORS.mdx

* Update project.pbxproj

* Update FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
This commit is contained in:
Marc Rousavy 2021-05-06 14:11:55 +02:00 committed by GitHub
parent 77b3d78566
commit b6a67d5ced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 4703 additions and 3664 deletions

View File

@ -92,4 +92,7 @@ module.exports = {
env: {
node: true,
},
globals: {
_log: 'readonly',
},
};

31
.github/workflows/validate-cpp.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Validate C++
on:
push:
branches:
- main
paths:
- '.github/workflows/validate-cpp.yml'
- 'cpp/**'
pull_request:
paths:
- '.github/workflows/validate-cpp.yml'
- 'cpp/**'
jobs:
lint:
name: cpplint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: reviewdog/action-cpplint@master
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
flags: --linelength=230
targets: --recursive cpp android/src/main/cpp
filter: "-legal/copyright\
,-readability/todo\
,-build/namespaces\
,-whitespace/comments\
"

4
.gitignore vendored
View File

@ -64,3 +64,7 @@ package-lock.json
# TypeDoc/Docusaurus stuff
docs/docs/api
docs/typedoc-sidebar.js
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

View File

@ -13,6 +13,8 @@
yarn bootstrap
```
Read the READMEs in [`android/`](android/README.md), [`ios/`](ios/README.md), and [`cpp/`](cpp/README.md) for a quick overview of the development workflow.
> You can also open VisionCamera in [a quick online editor (github1s)](https://github1s.com/cuvent/react-native-vision-camera)
### iOS
@ -22,12 +24,22 @@
3. Select your device in the devices drop-down
4. Hit run
> Run `yarn check-ios` to validate codestyle
### Android
1. Open the `example/android/` folder with Android Studio
2. Select your device in the devices drop-down
3. Hit run
> Run `yarn check-android` to validate codestyle
### C++
The C++ codebase is shared between Android and iOS. This means you can make changes to those files in either the Android example or the iOS example, but make sure to test changes on both platforms.
> Run `yarn check-cpp` to validate codestyle
## Committing
We love to keep our codebases clean. To achieve that, we use linters and formatters which output errors when something isn't formatted the way we like it to be.

View File

@ -41,7 +41,7 @@
* Photo, Video and Snapshot capture
* Customizable devices and multi-cameras (smoothly zoom out to "fish-eye" camera)
* Customizable FPS
* Frame Processors (JS worklets to run QR-Code scanning, facial recognition, AI object detection, realtime video chats and more) (**Work in progress: [#2](https://github.com/cuvent/react-native-vision-camera/pull/2)**)
* Frame Processors (JS worklets to run QR-Code scanning, facial recognition, AI object detection, realtime video chats and more)
* Smooth zooming (Reanimated)
* Fast pause and resume
* HDR & Night modes

View File

@ -13,7 +13,34 @@ Pod::Spec.new do |s|
s.platforms = { :ios => "11.0" }
s.source = { :git => "https://github.com/cuvent/react-native-vision-camera.git", :tag => "#{s.version}" }
s.source_files = "ios/**/*.{h,m,mm,swift}"
s.pod_target_xcconfig = {
"DEFINES_MODULE" => "YES",
"USE_HEADERMAP" => "YES",
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/Headers/Private/React-Core\" "
}
s.requires_arc = true
# All source files that should be publicly visible
# Note how this does not include headers, since those can nameclash.
s.source_files = [
"ios/**/*.{m,mm,swift}",
"ios/CameraBridge.h",
"ios/Frame Processor/FrameProcessorCallback.h",
"ios/Frame Processor/FrameProcessorRuntimeManager.h",
"ios/Frame Processor/FrameProcessorPluginRegistry.h",
"ios/Frame Processor/FrameProcessorPlugin.h",
"ios/React Utils/RCTBridge+runOnJS.h",
"cpp/**/*.{cpp}",
]
# Any private headers that are not globally unique should be mentioned here.
# Otherwise there will be a nameclash, since CocoaPods flattens out any header directories
# See https://github.com/firebase/firebase-ios-sdk/issues/4035 for more details.
s.preserve_paths = [
"cpp/**/*.h",
"ios/**/*.h"
]
s.dependency "React-callinvoker"
s.dependency "React"
s.dependency "React-Core"
end

View File

@ -336,16 +336,6 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
}
}
fun getAvailablePhotoCodecs(): WritableArray {
// TODO
return Arguments.createArray()
}
fun getAvailableVideoCodecs(): WritableArray {
// TODO
return Arguments.createArray()
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
Log.i(TAG, "onLayout($changed, $left, $top, $right, $bottom) was called! (Width: $width, Height: $height)")

View File

@ -91,22 +91,6 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
}
}
@ReactMethod
fun getAvailableVideoCodecs(viewTag: Int, promise: Promise) {
withPromise(promise) {
val view = findCameraView(viewTag)
view.getAvailableVideoCodecs()
}
}
@ReactMethod
fun getAvailablePhotoCodecs(viewTag: Int, promise: Promise) {
withPromise(promise) {
val view = findCameraView(viewTag)
view.getAvailablePhotoCodecs()
}
}
// TODO: This uses the Camera2 API to list all characteristics of a camera device and therefore doesn't work with Camera1. Find a way to use CameraX for this
// https://issuetracker.google.com/issues/179925896
@ReactMethod

31
cpp/MakeJSIRuntime.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <jsi/jsi.h>
#include <memory>
#if __has_include(<hermes/hermes.h>)
// Hermes (https://hermesengine.dev)
#include <hermes/hermes.h>
#elif __has_include(<v8runtime/V8RuntimeFactory.h>)
// V8 (https://github.com/Kudo/react-native-v8)
#include <v8runtime/V8RuntimeFactory.h>
#else
// JSC
#include <jsi/JSCRuntime.h>
#endif
using namespace facebook;
namespace vision {
static std::unique_ptr<jsi::Runtime> makeJSIRuntime() {
#if __has_include(<hermes/hermes.h>)
return facebook::hermes::makeHermesRuntime();
#elif __has_include(<v8runtime/V8RuntimeFactory.h>)
return facebook::createV8Runtime("");
#else
return facebook::jsc::makeJSCRuntime();
#endif
}
} // namespace vision

26
cpp/README.md Normal file
View File

@ -0,0 +1,26 @@
# cpp
This folder contains the Shared C++ code for react-native-vision-camera.
## Prerequesites
1. For Android, download the [NDK and build tools](https://developer.android.com/studio/projects/add-native-code#download-ndk)
2. For iOS, Xcode will be enough.
3. Install cpplint
```sh
brew install cpplint
```
## Getting Started
It is recommended that you work on the code using the Example project (`example/android/` or `example/ios/VisionCameraExample.xcworkspace`), since that always includes the React Native header files, plus you can easily test changes that way.
You can however still edit the library project here by opening this folder with any C++ editor.
## Committing
Before committing, make sure that you're not violating the cpplint codestyles. To do that, run the following command:
```bash
yarn check-cpp
```

20
cpp/SpeedChecker.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "Logger.h"
#include <string>
namespace vision {
class SpeedChecker {
public:
static void checkSpeed(std::string tag, std::function<void()> fun) {
auto start = std::chrono::system_clock::now();
fun();
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
tag += " " + std::to_string(elapsed_seconds.count()) + "s";
Logger::log(tag.c_str());
}
};
} // namespace vision

View File

@ -17,14 +17,16 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
Often you'd want to animate specific props in the Camera. For example, if you'd want to create a custom zoom gesture, you can smoothly animate the Camera's `zoom` property.
Note: The `<Camera>` component does provide a natively implemented zoom gesture which you can enable with the `enableZoomGesture={true}` prop. This does not require any additional work, but if you want to setup a custom gesture, such as the one in Snapchat or Instagram where you move up your finger while recording, continue reading.
The `<Camera>` component already provides a natively implemented zoom gesture which you can enable with the [`enableZoomGesture`](/docs/api/interfaces/cameraprops.cameraprops-1#enablezoomgesture) prop. This does not require any additional work, but if you want to setup a custom gesture, such as the one in Snapchat or Instagram where you move up your finger while recording, continue reading.
### Installing reanimated
### Animation libraries
The following example uses [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) (v2) to animate the `zoom` property. Head over to their [Installation guide](https://docs.swmansion.com/react-native-reanimated/docs/installation) to install Reanimated if you haven't already.
While you can use any animation library to animate the `zoom` property (or use no animation library at all) it is recommended to use [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) (v2) to achieve best performance. Head over to their [Installation guide](https://docs.swmansion.com/react-native-reanimated/docs/installation) to install Reanimated if you haven't already.
### Implementation
The following example implements a button which smoothly zooms to a random value using [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated):
```tsx
import Reanimated, {
useAnimatedProps,
@ -72,13 +74,16 @@ export function App() {
### Explanation
1. The `Camera` is converted to a reanimated Camera using `Reanimated.createAnimatedComponent`
1. The `Camera` was made animatable using `Reanimated.createAnimatedComponent`
2. The `zoom` property is added to the whitelisted native props to make it animatable.
> Note that this might not be needed in the future, see: [reanimated#1409](https://github.com/software-mansion/react-native-reanimated/pull/1409)
3. Using [`useSharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/api/useSharedValue), we're creating a shared value that holds the `zoom` property.
4. Using the [`useAnimatedProps`](https://docs.swmansion.com/react-native-reanimated/docs/api/useAnimatedProps) hook, we apply the shared value to the animated props.
3. Using [`useSharedValue`](https://docs.swmansion.com/react-native-reanimated/docs/api/useSharedValue), we're creating a shared value that holds the value for the `zoom` property.
4. Using the [`useAnimatedProps`](https://docs.swmansion.com/react-native-reanimated/docs/api/useAnimatedProps) hook, we apply the shared value to Camera's `zoom` property.
5. We apply the animated props to the `ReanimatedCamera` component's `animatedProps` property.
### Logarithmic scale
A Camera's `zoom` property is represented in a **logarithmic scale**. That means, increasing from `0` to `0.1` will appear to be a much larger offset than increasing from `0.9` to `1`. If you want to implement a zoom gesture (`<PinchGestureHandler>`, `<PanGestureHandler>`), try to flatten the `zoom` property to a **linear scale** by raising it **exponentially**. (`zoom.value ** 2`)
<br />

View File

@ -126,7 +126,7 @@ function App() {
```
:::info
Note: If you don't care about fast resume times you can also fully unmount the `<Camera>` view instead, which will use a lot less memory (RAM).
If you don't care about fast resume times you can also fully unmount the `<Camera>` view instead, which will use less memory (RAM).
:::
<br />

View File

@ -98,3 +98,8 @@ function App() {
return <Camera ref={camera} {...cameraProps} />
}
```
<br />
#### 🚀 Next section: [Troubleshooting](troubleshooting)

View File

@ -6,14 +6,10 @@ sidebar_label: Frame Processors
import useBaseUrl from '@docusaurus/useBaseUrl';
:::warning
FRAME PROCESSORS ARE STILL WORK IN PROGRESS - SEE [#2](https://github.com/cuvent/react-native-vision-camera/pull/2)
:::
<!-- TODO: Demo of QR code scanning or smth -->
<div>
<svg xmlns="http://www.w3.org/2000/svg" width="283" height="535" style={{ float: 'right' }}>
<image href={useBaseUrl("img/demo.gif")} x="18" y="33" width="247" height="469" />
<image href={useBaseUrl("img/frame-processors.gif")} x="18" y="33" width="247" height="469" />
<image href={useBaseUrl("img/frame.png")} width="283" height="535" />
</svg>
</div>
@ -21,48 +17,147 @@ FRAME PROCESSORS ARE STILL WORK IN PROGRESS - SEE [#2](https://github.com/cuvent
### What are frame processors?
Frame processors are functions that are written in JavaScript (or TypeScript) which can be used to **process frames the camera "sees"**.
Inside those functions you can call **Frame Processor Plugins**, which are high performance native functions specifically designed for certain use-cases.
For example, you might want to create a QR code scanner _without ever writing native code while still achieving almost-native performance_. Since you can write the scanning part yourself, you can implement a custom QR code system like the one Snapchat uses for Snap-codes.
For example, you might want to create a QR code scanner **without ever writing native code**, while still **achieving almost-native performance**:
<div align="center">
<img src={useBaseUrl("img/snap-code.png")} width="15%" />
</div>
<br />
```jsx
function App() {
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const qrCodes = scanQRCodes(frame)
console.log(`Detected QR Codes: ${qrCodes}`)
}, [])
return (
<Camera
{...cameraProps}
frameProcessor={frameProcessor}
/>
)
}
```
Frame processors are by far not limited to QR code detection, other examples include:
* **AI** for **facial recognition**
* **AI** for **object detection**
* Using **Tensorflow**, **MLKit Vision** or other libraries (if they provide React Native JSI bindings in the form of "react-native-vision-camera plugins")
* Creating **realtime video-chats** since you can directly send the camera frames over the network
* Creating **snapchat-like filters**, e.g. draw a dog-mask filter over the user's face (WIP)
* Using **Tensorflow**, **MLKit Vision** or other libraries
* Creating **realtime video-chats** using **WebRTC** to directly send the camera frames over the network
* Creating **snapchat-like filters**, e.g. draw a dog-mask filter over the user's face
* Creating **color filters** with depth-detection
* Using **custom C++ processors** exposed to JS for maximum performance
Because of the Frame Processor API's extensibility, you can even create your **custom code-scanner plugins** - for example you might want to support a custom code design such as **Snapchat's SnapCodes** or **Apple's AppClips**:
<div align="center">
<img src={useBaseUrl("img/snap-code.png")} height={150} />
<img src={useBaseUrl("img/appclip.png")} height={150} style={{ marginLeft: 50 }} />
</div>
### Interacting with Frame Processors
Since Frame Processors run in Worklets, you can also easily read from, and assign to [**Shared Values**](https://docs.swmansion.com/react-native-reanimated/docs/shared-values):
```tsx {6}
// represents position of the cat on the screen 🐈
const catBounds = useSharedValue({ top: 0, left: 0, right: 0, bottom: 0 })
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
catBounds.value = scanFrameForCat(frame)
}, [])
const boxOverlayStyle = useAnimatedStyle(() => ({
borderWidth: 1,
borderColor: 'red',
...catBounds.value
}), [catBounds])
return (
<View>
<Camera {...cameraProps} frameProcessor={frameProcessor} />
// draws a red rectangle on the screen which surrounds the cat
<Reanimated.View style={boxOverlayStyle} />
</View>
)
```
And you can also call back to the React-JS thread using [`runOnJS`](https://docs.swmansion.com/react-native-reanimated/docs/api/runOnJS):
```tsx {9}
const onQRCodeDetected = useCallback((qrCode: string) => {
navigation.push("ProductPage", { productId: qrCode })
}, [])
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const qrCodes = scanQRCodes(frame)
if (qrCodes.length > 0) {
runOnJS(onQRCodeDetected)(qrCodes[0])
}
}, [onQRCodeDetected])
```
### Technical
Frame processors are JS functions that will be **workletized** using [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated). They are created on a **separate thread** using a separate Hermes/JSC Runtime and are **invoked synchronously** (using JSI) without ever going over the bridge.
**Frame Processors** are JS functions that will be **workletized** using [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated). They are created on a **custom camera thread** using a separate JavaScript Runtime (_"VisionCamera JS-Runtime"_) and are **invoked synchronously** (using JSI) without ever going over the bridge.
### Example
**Frame Processor Plugins** are native functions (written in Objective-C, Swift, C++, Java or Kotlin) that are injected into the VisionCamera JS-Runtime. They can be **synchronously called** from your JS Frame Processors (using JSI) without ever going over the bridge.
```tsx
function App() {
const frameProcessor = useFrameProcessor((frame) => {
const qrCodes = scanQrCodes(frame)
console.log(qrCodes)
}, [])
> Learn how to [**create Frame Processor Plugins**](frame-processors-plugins-overview)
return (
<Camera frameProcessor={frameProcessor} {...cameraProps} />
)
}
### Using Frame Processor Plugins
Frame Processor Plugins are distributed through npm. To install the [**vision-camera-qrcode-scanner**](https://github.com/mrousavy/vision-camera-qrcode-scanner) plugin, run:
```terminal
npm i vision-camera-qrcode-scanner
cd ios && pod install
```
### Plugins
Then add it to your `babel.config.js`. For the QR Code Scanner, this will be `__scanQRCodes`:
> TODO
```js {6}
module.exports = {
plugins: [
[
'react-native-reanimated/plugin',
{
globals: ['__scanQRCodes'],
},
],
```
:::note
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
:::
That's it! 🎉 Now you can use it:
```ts
const frameProcessor = useFrameProcessor((frame: Frame) => {
'worklet'
const codes = scanQRCodes(frame)
// ...
}, [])
```
Check out [**Frame Processor community plugins**](frame-processor-plugin-list) to discover plugins!
### Disabling Frame Processors
The Frame Processor API spawns a secondary JavaScript Runtime which consumes a small amount of extra CPU and RAM. If you're not using Frame Processors at all, you can disable them by setting the `VISION_CAMERA_DISABLE_FRAME_PROCESSORS` flag. Inside your `project.pbxproj`, find the `GCC_PREPROCESSOR_DEFINITIONS` parameter and add the flag:
```txt {3}
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"VISION_CAMERA_DISABLE_FRAME_PROCESSORS=1",
"$(inherited)",
);
```
Make sure to add this to your Debug-, as well as your Release-configuration.
<br />
#### 🚀 Next section: [Zooming with Reanimated](animated)
#### 🚀 Next section: [Zooming with Reanimated](animated) (or [creating a Frame Processor Plugin](frame-processors-plugins-overview))

View File

@ -0,0 +1,149 @@
---
id: frame-processors-plugins-overview
title: Creating Frame Processor Plugins
sidebar_label: Overview
---
import useBaseUrl from '@docusaurus/useBaseUrl';
## Overview
Frame Processor Plugins are **native functions** which can be directly called from a JS Frame Processor. (See [Frame Processors](frame-processors))
They **receive a frame from the Camera** as an input and can return any kind of output. For example, a `scanQRCodes` function returns an array of detected QR code strings in the frame:
```tsx {4-5}
function App() {
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const qrCodes = scanQRCodes(frame)
_log(`QR Codes in Frame: ${qrCodes}`)
}, [])
return (
<Camera frameProcessor={frameProcessor} {...cameraProps} />
)
}
```
To achieve **maximum performance**, the `scanQRCodes` function is written in a native language (e.g. Objective-C), but it will be directly called from the VisionCamera Frame Processor JavaScript-Runtime.
### Execution
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`](../api/interfaces/cameraprops.cameraprops-1#frameprocessorfps) to throttle the frame processor's FPS. For a QR Code Scanner, **5 FPS** might suffice.
### Return Types
Frame Processors can return any primitive value that is representable in JS. So for Objective-C that maps to:
| Objective-C Type | JS Type |
|--------------------------|----------------------|
| `NSNumber` | `number` |
| `NSNumber` (boolean) | `boolean` |
| `NSString` | `string` |
| `NSArray` | `[]` |
| `NSDictionary` | `{}` |
| `nil` / `NSNull` | `undefined` |
| `RCTResponseSenderBlock` | `(any, any) => void` |
The values will automatically be converted to JS values, so the following Objective-C frame processor:
```objc
static inline id detectObject(CMSampleBufferRef buffer, NSArray args) {
return @"cat";
}
```
Returns a `string` in JS:
```js
export function detectObject(frame: Frame): string {
'worklet';
const result = __detectObject(frame);
_log(result) // <-- "cat"
}
```
### Parameters
Frame Processors can also accept parameters, following the same type convention as [return values](#return-types):
```ts
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const codes = scanCodes(frame, ['qr', 'barcode'])
}, [])
```
Or with multiple parameters:
```ts
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const codes = scanCodes(frame, true, 'hello-world', 42)
}, [])
```
### Long-running Frame Processors
If your Frame Processor takes longer than a single frame interval to execute, or runs asynchronously, you can create a **copy of the frame** and dispatch the actual frame processing to a **separate thread**.
For example, a realtime video chat application might use WebRTC to send the frames to the server. I/O operations (networking) are asynchronous, and we don't _need_ to wait for the upload to succeed before pushing the next frame, so we copy the frame and perform the upload on another Thread.
```objc
static dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
static inline id sendFrameToWebRTC(CMSampleBufferRef buffer, NSArray args) {
CMSampleBufferRef bufferCopy;
CMSampleBufferCreateCopy(kCFAllocatorDefault, &buffer, &bufferCopy);
dispatch_async(queue, ^{
NSString* serverURL = (NSString*)args[0];
[WebRTC uploadFrame:bufferCopy toServer:serverURL];
});
return nil;
}
```
### Async Frame Processors with Event Emitters
You might also run some very complex AI algorithms which are not fast enough to smoothly run at **30 FPS** (**33ms**). To not drop any frames you can create a custom "frame queue" which processes the copied frames and calls back into JS via a React event emitter. For this you'll have to create a Native Module that handles the asynchronous native -> JS communication, see ["Sending events to JavaScript" (Android)](https://reactnative.dev/docs/native-modules-android#sending-events-to-javascript) and ["Sending events to JavaScript" (iOS)](https://reactnative.dev/docs/native-modules-ios#sending-events-to-javascript).
This might look like this for the user:
```tsx
function App() {
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
SomeAI.process(frame) // does not block frame processor, runs async
}, [])
useEffect(() => {
SomeAI.addListener((results) => {
console.log(`AI results: ${results}`)
})
}, [])
return (
<Camera frameProcessor={frameProcessor} {...cameraProps} />
)
}
```
This way you can handle queueing up the frames yourself and asynchronously call back into JS at some later point in time using event emitters.
### Benchmarking Frame Processor Plugins
Your Frame Processor Plugins have to be fast. VisionCamera automatically detects slow Frame Processors and outputs relevant information in the native console (Xcode: **Debug Area**, Android Studio: **Logcat**):
<div align="center">
<img src={useBaseUrl("img/slow-log.png")} width="80%" />
</div>
<div align="center">
<img src={useBaseUrl("img/slow-log-2.png")} width="80%" />
</div>
<br />
#### 🚀 Create your first Frame Processor Plugin for [iOS](frame-processors-plugins-ios) or [Android](frame-processors-plugins-android)!

View File

@ -0,0 +1,74 @@
---
id: frame-processors-plugins-final
title: Finish creating your Frame Processor Plugin
sidebar_label: Finish creating your Frame Processor Plugin
---
## Make your Frame Processor Plugin available to JS
To make the Frame Processor Plugin available to the Frame Processor Worklet Runtime, create the following wrapper function in JS/TS:
```ts
import type { Frame } from 'react-native-vision-camera';
/**
* Scans QR codes.
*/
export function scanQRCodes(frame: Frame): string[] {
'worklet';
return __scanQRCodes(frame);
}
```
Users will then have to add the Frame Processor Plugin's name to their `babel.config.js`.
For the QR Code Scanner, this will be `__scanQRCodes`:
```js {6}
module.exports = {
plugins: [
[
'react-native-reanimated/plugin',
{
globals: ['__scanQRCodes'],
},
],
```
:::note
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
:::
### Test it!
Simply call the wrapper Worklet in your Frame Processor:
```tsx {4}
function App() {
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
const qrCodes = scanQRCodes(frame)
_log(`QR Codes in Frame: ${qrCodes}`)
}, [])
return (
<Camera frameProcessor={frameProcessor} {...cameraProps} />
)
}
```
### Next Steps
If you want to distribute your Frame Processor Plugin, simply use npm.
1. Create a blank Native Module using [bob](https://github.com/callstack/react-native-builder-bob) or [create-react-native-module](https://github.com/brodybits/create-react-native-module)
2. Name it `vision-camera-plugin-xxxxx` where `xxxxx` is the name of your plugin
3. Remove all the source files for the Example Native Module
4. Implement the Frame Processor Plugin in the iOS, Android and JS/TS Codebase using the guides above
5. Add installation instructions to let users know they have to add your frame processor in the `babel.config.js` configuration.
6. Publish the plugin to npm. Users will only have to install the plugin using `npm i vision-camera-plugin-xxxxx` and add it to their `babel.config.js` file.
7. [Add the plugin to the **official VisionCamera plugin list**](https://github.com/cuvent/react-native-vision-camera/edit/main/docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx) for more visibility
<br />
#### 🚀 Next section: [Browse Community Plugins](frame-processor-plugin-list)

View File

@ -0,0 +1,17 @@
---
id: frame-processors-plugins-android
title: Creating Frame Processor Plugins for Android
sidebar_label: Creating Frame Processor Plugins (Android)
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
:::warning
FRAME PROCESSORS ARE NOT YET AVAILABLE FOR ANDROId. SEE #?? FOR PROGRESS UPDATES
:::
<br />
#### 🚀 Next section: [Finish creating your Frame Processor Plugin](frame-processors-plugins-final) (or [add iOS support to your Frame Processor Plugin](frame-processors-plugins-ios))

View File

@ -0,0 +1,102 @@
---
id: frame-processors-plugins-ios
title: Creating Frame Processor Plugins for iOS
sidebar_label: Creating Frame Processor Plugins (iOS)
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
## Creating a Frame Processor
The Frame Processor Plugin API is built to be as extensible as possible, which allows you to create custom Frame Processor Plugins.
In this guide we will create a custom QR Code Scanner Plugin which can be used from JS.
iOS Frame Processor Plugins can be written in either **Objective-C** or **Swift**.
<Tabs
defaultValue="objc"
values={[
{label: 'Objective-C', value: 'objc'},
{label: 'Swift', value: 'swift'}
]}>
<TabItem value="objc">
1. Open your Project in Xcode
2. Create an Objective-C source file, for the QR Code Plugin this will be called `QRCodeFrameProcessorPlugin.m`.
3. Add the following code:
```objc {9}
#import <VisionCamera/FrameProcessorPlugin.h>
@interface QRCodeFrameProcessorPlugin : NSObject
@end
@implementation QRCodeFrameProcessorPlugin
static inline id scanQRCodes(CMSampleBufferRef buffer, NSArray args) {
// code goes here
return @[];
}
VISION_EXPORT_FRAME_PROCESSOR(scanQRCodes)
@end
```
4. **Implement your Frame Processing.** See the [QR Code Plugin (Objective-C)](https://github.com/cuvent/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/QR%20Code%20Plugin%20%28Objective%2DC%29) for reference.
:::note
The JS function name will be equal to the Objective-C function name you choose (with a `__` prefix). Make sure it is unique across other Frame Processor Plugins, and doesn't shadow a JS runtime variable.
:::
</TabItem>
<TabItem value="swift">
1. Create a Swift file, for the QR Code Plugin this will be `QRCodeFrameProcessorPlugin.swift`. If Xcode asks you to create a Bridging Header, press **create**.
![Xcode "Create Bridging Header" alert](https://docs-assets.developer.apple.com/published/7ebca7212c/2a065d1a-7e53-4907-a889-b7fa4f2206c9.png)
2. Inside the newly created Bridging Header, add the following code:
```objc
#import <VisionCamera/FrameProcessorPlugin.h>
```
3. Create an Objective-C source file with the same name as the Swift file, for the QR Code Plugin this will be `QRCodeFrameProcessorPlugin.m`. Add the following code:
```objc
#import <VisionCamera/FrameProcessorPlugin.h>
@interface VISION_EXPORT_SWIFT_FRAME_PROCESSOR(scanQRCodes, QRCodeFrameProcessorPlugin)
@end
```
:::note
The first parameter in the Macro specifies the JS function name. Make sure it is unique across other Frame Processors, and doesn't shadow a JS runtime variable (such as `Map`, `Number`, ...)
:::
4. In the Swift file, add the following code:
```swift {6}
@objc(QRCodeFrameProcessorPlugin)
public class QRCodeFrameProcessorPlugin: NSObject, FrameProcessorPluginBase {
@objc
public static func callback(_: CMSampleBuffer!, withArgs _: [Any]!) -> Any! {
// code goes here
return []
}
}
```
5. **Implement your frame processing.** See [QR Code Plugin (Swift)](https://github.com/cuvent/react-native-vision-camera/blob/main/example/ios/Frame%20Processor%20Plugins/QR%20Code%20Plugin%20%28Swift%29) for reference.
</TabItem>
</Tabs>
<br />
#### 🚀 Next section: [Finish creating your Frame Processor Plugin](frame-processors-plugins-final) (or [add Android support to your Frame Processor Plugin](frame-processors-plugins-android))

View File

@ -0,0 +1,51 @@
---
id: frame-processor-plugin-list
title: Community Plugins
sidebar_label: Community Plugins
---
These are VisionCamera Frame Processor Plugins created by the community.
## Installing a Plugin
1. Install using npm:
```
npm i vision-camera-xxxxx
```
2. Add the native function's name to your `babel.config.js`. (See their README for instructions)
:::note
You have to restart metro-bundler for changes in the `babel.config.js` file to take effect.
:::
## Plugin List
* [mrousavy/**vision-camera-qrcode-scanner**](https://github.com/mrousavy/vision-camera-qrcode-scanner): A fast QR code scanner using the Apple Vision API.
<!-- Add your Frame Processor Plugin here! -->
<br />
<br />
<br />
<p align="center">
<b>
<a href="https://github.com/cuvent/react-native-vision-camera/edit/main/docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx">Click here</a> to add your Frame Processor Plugin to this list!
</b>
</p>
<br />
<br />
<br />
#### 🚀 Next section: [Zooming with Reanimated](animated)

View File

@ -13,13 +13,15 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
## Installing the library
Install react-native-vision-camera through npm:
Install [**react-native-vision-camera**](https://www.npmjs.com/package/react-native-vision-camera) through npm:
```sh
```terminal
npm i react-native-vision-camera
npx pod-install
```
VisionCamera requires **iOS 11 or higher**, and **Android-SDK version 21 or higher**. See [Troubleshooting](/docs/guides/troubleshooting) if you're having installation issues.
## Updating manifests
To use a Camera or Microphone you must first specify that your app requires camera and microphone permissions.
@ -35,24 +37,6 @@ Open your project's `Info.plist` and add the following lines inside the outermos
<string>$(PRODUCT_NAME) needs access to your Microphone to record videos with audio.</string>
```
#### Compatibility
VisionCamera is written in Swift. If your project is written in Objective-C, you have to create a Bridging Header first:
1. Open your project (`.xcworkspace`) in Xcode
2. Press **File** > **New** > **File** (<kbd>⌘</kbd>+<kbd>N</kbd>)
3. Select **Swift File** and press **Next**
4. Choose whatever name you want, e.g. `File.swift` and press **Create**
5. Press **Create Bridging Header** when promted.
Also, make sure you're using Swift 5.2 or above:
1. Open `project.pbxproj` in a Text Editor
2. If the `LIBRARY_SEARCH_PATH` value is set, make sure there is no explicit reference to Swift-5.0. If there is, remove it. See [this StackOverflow answer](https://stackoverflow.com/a/66281846/1123156).
3. If the `SWIFT_VERSION` value is set, make sure it is set to `5.2` or higher.
> See [Troubleshooting](/docs/guides/troubleshooting) if you're having problems
### Android
Open your project's `AndroidManifest.xml` and add the following lines inside the `<manifest>` tag:
@ -62,22 +46,7 @@ Open your project's `AndroidManifest.xml` and add the following lines inside the
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```
#### Compatibility
VisionCamera requires a minimum Android SDK version of **21 or higher**, and a target SDK version of **30 or higher**. See [the example app's `build.gradle`](https://github.com/cuvent/react-native-vision-camera/blob/main/example/android/build.gradle#L6-L9) for reference.
Open your project's `build.gradle`, and set the following values:
```groovy
buildToolsVersion = "30.0.0"
minSdkVersion = 21
compileSdkVersion = 30
targetSdkVersion = 30
```
> See [Troubleshooting](/docs/guides/troubleshooting) if you're having problems
## Permissions
## Getting/Requesting Permissions
VisionCamera also provides functions to easily get and request Microphone and Camera permissions.
@ -92,7 +61,7 @@ const microphonePermission = await Camera.getMicrophonePermissionStatus()
A permission status can have the following values:
* `authorized`: Your app is authorized to use said permission. Continue with mounting the `<Camera>` view.
* `authorized`: Your app is authorized to use said permission. Continue with [**using the `<Camera>` view**](#use-the-camera-view).
* `not-determined`: Your app has not yet requested permission from the user. [Continue by calling the **request** functions.](#requesting-permissions)
* `denied`: Your app has already requested permissions from the user, but was explicitly denied. You cannot use the **request** functions again, but you can use the [`Linking` API](https://reactnative.dev/docs/linking#opensettings) to redirect the user to the Settings App where he can manually grant the permission.
* `restricted`: (iOS only) Your app cannot use the Camera or Microphone because that functionality has been restricted, possibly due to active restrictions such as parental controls being in place.
@ -101,8 +70,8 @@ A permission status can have the following values:
Use the **request** functions to prompt the user to give your app permission to use the Camera or Microphone.
:::caution
Note: You can only use **request** functions if the current permission status is `not-determined`.
:::note
The **request** functions only have effect if the current permission status is `not-determined`.
:::
```ts
@ -112,8 +81,29 @@ const newMicrophonePermission = await Camera.requestMicrophonePermission()
The permission request status can have the following values:
* `authorized`: Your app is authorized to use said permission. Continue with mounting the `<Camera>` view.
* `authorized`: Your app is authorized to use said permission. Continue with [**using the `<Camera>` view**](#use-the-camera-view).
* `denied`: The user explicitly denied the permission request alert. You cannot use the **request** functions again, but you can use the [`Linking` API](https://reactnative.dev/docs/linking#opensettings) to redirect the user to the Settings App where he can manually grant the permission.
* `restricted`: (iOS only) Your app cannot use the Camera or Microphone because that functionality has been restricted, possibly due to active restrictions such as parental controls being in place.
## Use the `<Camera>` view
If your app has permission to use the Camera and Microphone, simply use the [`useCameraDevices(...)`](/docs/api/modules/hooks_usecameradevices) hook to get a Camera device (see [Camera Devices](/docs/guides/devices)) and mount the `<Camera>` view:
```tsx
function App() {
const devices = useCameraDevices()
const device = devices.back
if (device == null) return <LoadingView />
return (
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
/>
)
}
```
<br />

View File

@ -59,7 +59,7 @@ Before opening an issue, make sure you try the following:
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
```
5. If you're having runtime issues, check the logs in Android Studio/Logcat to find out more. In Android Studio, go to **View** > **Tool Windows** > **Logcat** (<kbd>⌘</kbd>+<kbd>6</kbd>) or run `adb logcat` in Terminal.
6. If a camera device is not being returned by [`Camera.getAvailableCameraDevices()`](/docs/api/classes/camera.camera-1#getavailablecameradevices), make sure it meets the minimum requirements - that is minum supported harwdware level of `LIMITED` and above. See [this section in the Android docs](https://developer.android.com/reference/android/hardware/camera2/CameraDevice#reprocessing) for more information.
6. If a camera device is not being returned by [`Camera.getAvailableCameraDevices()`](/docs/api/classes/camera.camera-1#getavailablecameradevices), make sure it meets the minimum requirements - that is a minum supported harwdware level of `LIMITED` and above. See [this section in the Android docs](https://developer.android.com/reference/android/hardware/camera2/CameraDevice#reprocessing) for more information.
## Issues

View File

@ -13,6 +13,9 @@ module.exports = {
apiKey: 'ab7f44570bb62d0e07c0f7d92312ed1a',
indexName: 'react-native-vision-camera',
},
prism: {
additionalLanguages: ['swift'],
},
navbar: {
title: 'VisionCamera',
logo: {

View File

@ -12,8 +12,8 @@
"clear": "docusaurus clear"
},
"dependencies": {
"@docusaurus/core": "2.0.0-alpha.72",
"@docusaurus/preset-classic": "2.0.0-alpha.72",
"@docusaurus/core": "2.0.0-alpha.75",
"@docusaurus/preset-classic": "2.0.0-alpha.75",
"@mdx-js/react": "^1.6.21",
"clsx": "^1.1.1",
"react": "^17.0.1",
@ -32,9 +32,9 @@
]
},
"devDependencies": {
"docusaurus-plugin-typedoc": "^0.12.0",
"typedoc": "^0.20.34",
"typedoc-plugin-markdown": "^3.6.0",
"docusaurus-plugin-typedoc": "^0.13.0",
"typedoc": "^0.20.36",
"typedoc-plugin-markdown": "^3.7.2",
"typescript": "^4.2.3"
}
}

View File

@ -1,6 +1,26 @@
module.exports = {
visionSidebar: {
Guides: ['guides/setup', 'guides/devices', 'guides/formats', 'guides/capturing', 'guides/frame-processors', 'guides/animated', 'guides/errors', 'guides/troubleshooting'],
Guides: [
'guides/setup',
'guides/devices',
'guides/formats',
'guides/capturing',
'guides/frame-processors',
{
type: 'category',
label: 'Creating Frame Processor Plugins',
items: [
'guides/frame-processors-plugins-overview',
'guides/frame-processors-plugins-ios',
'guides/frame-processors-plugins-android',
'guides/frame-processors-plugins-final',
'guides/frame-processor-plugin-list'
]
},
'guides/animated',
'guides/errors',
'guides/troubleshooting',
],
API: require('./typedoc-sidebar.js'),
},
};

BIN
docs/static/img/appclip.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/static/img/frame-processors.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 MiB

BIN
docs/static/img/slow-log-2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/static/img/slow-log.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

View File

@ -174,8 +174,7 @@ android {
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
output.versionCodeOverride = defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
@ -189,16 +188,6 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";

View File

@ -1,69 +0,0 @@
/*
Copyright (c) Facebook, Inc. and its affiliates.
<p>This source code is licensed under the MIT license found in the LICENSE file in the root
directory of this source tree.
*/
package com.mrousavy.camera.example;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}

View File

@ -52,35 +52,5 @@ public class MainApplication extends NavigationApplication {
@Override
public void onCreate() {
super.onCreate();
initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); // Remove this line if you don't want Flipper enabled
}
/**
* Loads Flipper in React Native templates.
*
* @param context
*/
private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.mrousavy.camera.example.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}

View File

@ -19,4 +19,3 @@
android.useAndroidX=true
android.enableJetifier=true
FLIPPER_VERSION=0.75.1

View File

@ -5,7 +5,12 @@ const pak = require('../package.json');
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'react-native-reanimated/plugin',
[
'react-native-reanimated/plugin',
{
globals: ['__exampleSwift___scanQRCodes', '__exampleObjC___scanQRCodes'],
},
],
[
'module-resolver',
{

24
example/clean.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
echo "rm -rf node_modules"
rm -rf node_modules
echo "rm -rf yarn.lock"
rm -rf yarn.lock
rm -rf package-lock.json
echo "rm -rf ~/Library/Caches/CocoaPods"
rm -rf ~/Library/Caches/CocoaPods
echo "rm -rf ios/Pods"
rm -rf ios/Pods
echo "rm -rf ~/Library/Developer/Xcode/DerivedData/*"
rm -rf ~/Library/Developer/Xcode/DerivedData/*
cd ios
echo "pod deintegrate"
pod deintegrate
echo "pod setup"
pod setup
echo "yarn"
yarn
echo "pod install"
pod install

View File

@ -0,0 +1 @@
5.2

View File

@ -0,0 +1,26 @@
//
// QRCodeFrameProcessorPluginObjC.m
// VisionCameraExample
//
// Created by Marc Rousavy on 01.05.21.
//
#import <Foundation/Foundation.h>
#import <VisionCamera/FrameProcessorPlugin.h>
#import <Vision/VNDetectBarcodesRequest.h>
// Example for an Objective-C Frame Processor plugin
@interface QRCodeFrameProcessorPluginObjC : NSObject
@end
@implementation QRCodeFrameProcessorPluginObjC
static inline id exampleObjC___scanQRCodes(CMSampleBufferRef buffer, NSArray* arguments) {
// TODO: Use some AI to detect QR codes in the CMSampleBufferRef
return @[];
}
VISION_EXPORT_FRAME_PROCESSOR(exampleObjC___scanQRCodes)
@end

View File

@ -0,0 +1,13 @@
//
// QRCodeFrameProcessorPluginSwift.m
// VisionCamera
//
// Created by Marc Rousavy on 01.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <VisionCamera/FrameProcessorPlugin.h>
@interface VISION_EXPORT_SWIFT_FRAME_PROCESSOR(exampleSwift___scanQRCodes, QRCodeFrameProcessorPluginSwift)
@end

View File

@ -0,0 +1,19 @@
//
// QRCodeFrameProcessorPluginSwift.swift
// VisionCamera
//
// Created by Marc Rousavy on 30.04.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVKit
import Vision
@objc(QRCodeFrameProcessorPluginSwift)
public class QRCodeFrameProcessorPluginSwift: NSObject, FrameProcessorPluginBase {
@objc
public static func callback(_: CMSampleBuffer!, withArgs _: [Any]!) -> Any! {
// TODO: Use some AI to detect QR codes in the CMSampleBufferRef
[]
}
}

View File

@ -2,7 +2,7 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.3)
activesupport (5.2.4.5)
activesupport (5.2.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@ -56,14 +56,14 @@ GEM
colored2 (3.1.2)
concurrent-ruby (1.1.8)
escape (0.0.4)
ethon (0.12.0)
ffi (>= 1.3.0)
ethon (0.14.0)
ffi (>= 1.15.0)
ffi (1.15.0)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (1.8.9)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
json (2.5.1)
minitest (5.14.4)

View File

@ -13,11 +13,6 @@ target 'VisionCameraExample' do
pod 'VisionCamera', :path => '../..'
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!()
post_install do |installer|
react_native_post_install(installer)
end

View File

@ -1,6 +1,5 @@
PODS:
- boost-for-react-native (1.63.0)
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- FBLazyVector (0.64.0)
- FBReactNativeSpec (0.64.0):
@ -10,56 +9,7 @@ PODS:
- React-Core (= 0.64.0)
- React-jsi (= 0.64.0)
- ReactCommon/turbomodule/core (= 0.64.0)
- Flipper (0.75.1):
- Flipper-Folly (~> 2.5)
- Flipper-RSocket (~> 1.3)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.5.1):
- boost-for-react-native
- Flipper-DoubleConversion
- Flipper-Glog
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.180)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.3.0):
- Flipper-Folly (~> 2.5)
- FlipperKit (0.75.1):
- FlipperKit/Core (= 0.75.1)
- FlipperKit/Core (0.75.1):
- Flipper (~> 0.75.1)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- FlipperKit/CppBridge (0.75.1):
- Flipper (~> 0.75.1)
- FlipperKit/FBCxxFollyDynamicConvert (0.75.1):
- Flipper-Folly (~> 2.5)
- FlipperKit/FBDefines (0.75.1)
- FlipperKit/FKPortForwarding (0.75.1):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- FlipperKit/FlipperKitHighlightOverlay (0.75.1)
- FlipperKit/FlipperKitLayoutPlugin (0.75.1):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- YogaKit (~> 1.18)
- FlipperKit/FlipperKitLayoutTextSearchable (0.75.1)
- FlipperKit/FlipperKitNetworkPlugin (0.75.1):
- FlipperKit/Core
- FlipperKit/FlipperKitReactPlugin (0.75.1):
- FlipperKit/Core
- FlipperKit/FlipperKitUserDefaultsPlugin (0.75.1):
- FlipperKit/Core
- FlipperKit/SKIOSNetworkPlugin (0.75.1):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- glog (0.3.5)
- hermes-engine (0.7.2)
- libevent (2.1.12)
- OpenSSL-Universal (1.1.180)
- RCT-Folly (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
@ -69,11 +19,6 @@ PODS:
- boost-for-react-native
- DoubleConversion
- glog
- RCT-Folly/Futures (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- glog
- libevent
- RCTRequired (0.64.0)
- RCTTypeSafety (0.64.0):
- FBLazyVector (= 0.64.0)
@ -131,16 +76,6 @@ PODS:
- React-jsinspector (= 0.64.0)
- React-perflogger (= 0.64.0)
- Yoga
- React-Core/Hermes (0.64.0):
- glog
- hermes-engine
- RCT-Folly (= 2020.01.13.00)
- RCT-Folly/Futures
- React-cxxreact (= 0.64.0)
- React-jsi (= 0.64.0)
- React-jsiexecutor (= 0.64.0)
- React-perflogger (= 0.64.0)
- Yoga
- React-Core/RCTActionSheetHeaders (0.64.0):
- glog
- RCT-Folly (= 2020.01.13.00)
@ -270,7 +205,7 @@ PODS:
- React-jsinspector (0.64.0)
- react-native-blur (0.8.0):
- React
- react-native-cameraroll (4.0.2):
- react-native-cameraroll (4.0.4):
- React-Core
- react-native-slider (3.0.3):
- React
@ -354,7 +289,7 @@ PODS:
- React-RCTText
- RNGestureHandler (1.10.3):
- React-Core
- RNReanimated (2.0.0):
- RNReanimated (2.1.0):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
@ -387,38 +322,17 @@ PODS:
- React
- RNVectorIcons (8.1.0):
- React-Core
- VisionCamera (1.0.4):
- VisionCamera (1.0.10):
- React
- React-callinvoker
- React-Core
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- Flipper (~> 0.75.1)
- Flipper-DoubleConversion (= 1.1.7)
- Flipper-Folly (~> 2.5)
- Flipper-Glog (= 0.3.6)
- Flipper-PeerTalk (~> 0.0.4)
- Flipper-RSocket (~> 1.3)
- FlipperKit (~> 0.75.1)
- FlipperKit/Core (~> 0.75.1)
- FlipperKit/CppBridge (~> 0.75.1)
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.75.1)
- FlipperKit/FBDefines (~> 0.75.1)
- FlipperKit/FKPortForwarding (~> 0.75.1)
- FlipperKit/FlipperKitHighlightOverlay (~> 0.75.1)
- FlipperKit/FlipperKitLayoutPlugin (~> 0.75.1)
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.75.1)
- FlipperKit/FlipperKitNetworkPlugin (~> 0.75.1)
- FlipperKit/FlipperKitReactPlugin (~> 0.75.1)
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.75.1)
- FlipperKit/SKIOSNetworkPlugin (~> 0.75.1)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (~> 0.7.2)
- libevent (~> 2.1.12)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
@ -426,7 +340,6 @@ DEPENDENCIES:
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/Hermes (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
@ -460,18 +373,6 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- boost-for-react-native
- CocoaAsyncSocket
- Flipper
- Flipper-DoubleConversion
- Flipper-Folly
- Flipper-Glog
- Flipper-PeerTalk
- Flipper-RSocket
- FlipperKit
- hermes-engine
- libevent
- OpenSSL-Universal
- YogaKit
EXTERNAL SOURCES:
DoubleConversion:
@ -553,21 +454,10 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: 49cbe4b43e445b06bf29199b6ad2057649e4c8f5
FBReactNativeSpec: 06c29ba6920affcab9cda6154497386d21f43410
Flipper: d3da1aa199aad94455ae725e9f3aa43f3ec17021
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: f7a3caafbd74bda4827954fd7a6e000e36355489
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: 602921fee03edacf18f5d6f3d3594ba477f456e5
FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00
FBReactNativeSpec: e800dc469340da7e8e47f45145f69d75a7b06874
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
hermes-engine: 7d97ba46a1e29bacf3e3c61ecb2804a5ddd02d4f
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
RCTRequired: 2f8cb5b7533219bf4218a045f92768129cf7050a
RCTTypeSafety: 512728b73549e72ad7330b92f3d42936f2a4de5b
@ -580,7 +470,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 06a9c77b56902ae7ffcdd7a4905f664adc5d237b
React-jsinspector: 0ae35a37b20d5e031eb020a69cc5afdbd6406301
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
react-native-cameraroll: 1965db75c851b15e77a22ca0ac78e32af6b571ae
react-native-cameraroll: 88f4e62d9ecd0e1f253abe4f685474f2ea14bfa2
react-native-slider: e99fc201cefe81270fc9d81714a7a0f5e566b168
react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e
React-perflogger: 9c547d8f06b9bf00cb447f2b75e8d7f19b7e02af
@ -597,13 +487,12 @@ SPEC CHECKSUMS:
ReactCommon: cfe2b7fd20e0dbd2d1185cd7d8f99633fbc5ff05
ReactNativeNavigation: 63321d37e8172bdcc1fbb93e77cffc74a0ba6d20
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
RNReanimated: 64f6c5789f82818c07ba3c71864b73619cb23c76
RNReanimated: b8c8004b43446e3c2709fe64b2b41072f87428ad
RNStaticSafeAreaInsets: 6103cf09647fa427186d30f67b0f5163c1ae8252
RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4
VisionCamera: df21c7ec84e2fb254ee0e085bafcb95b0217376a
VisionCamera: 65836f8bb8dda4e718c350e32c35a0b9f166b731
Yoga: 8c8436d4171c87504c648ae23b1d81242bdf3bbf
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 703892d4cb39598dd9a51bd3a58fe2502c68dfbd
PODFILE CHECKSUM: 4b093c1d474775c2eac3268011e4b0b80929d3a2
COCOAPODS: 1.10.1

View File

@ -1,3 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import <VisionCamera/FrameProcessorPlugin.h>

View File

@ -11,18 +11,29 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
B8DB3BD5263DE8B7004C18D7 /* BuildFile in Sources */ = {isa = PBXBuildFile; };
B8DB3BDC263DEA31004C18D7 /* QRCodeFrameProcessorPluginObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BD8263DEA31004C18D7 /* QRCodeFrameProcessorPluginObjC.m */; };
B8DB3BDD263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BDA263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.swift */; };
B8DB3BDE263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.m in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BDB263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.m */; };
B8F0E10825E0199F00586F16 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F0E10725E0199F00586F16 /* File.swift */; };
D27D5196997E7C9532F05776 /* libPods-VisionCameraExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 321EF902E53C449CC036AD27 /* libPods-VisionCameraExample.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
0547A6E7EED7421DBCBCF7E4 /* Pods-VisionCameraExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraExample.release.xcconfig"; path = "Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample.release.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* VisionCameraExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisionCameraExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = VisionCameraExample/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = VisionCameraExample/AppDelegate.m; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VisionCameraExample/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VisionCameraExample/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = VisionCameraExample/main.m; sourceTree = "<group>"; };
321EF902E53C449CC036AD27 /* libPods-VisionCameraExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VisionCameraExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = VisionCameraExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
9993ED8FD4B3171BB28C87C9 /* Pods-VisionCameraExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VisionCameraExample.debug.xcconfig"; path = "Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample.debug.xcconfig"; sourceTree = "<group>"; };
B8DB3BD8263DEA31004C18D7 /* QRCodeFrameProcessorPluginObjC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QRCodeFrameProcessorPluginObjC.m; sourceTree = "<group>"; };
B8DB3BDA263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeFrameProcessorPluginSwift.swift; sourceTree = "<group>"; };
B8DB3BDB263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QRCodeFrameProcessorPluginSwift.m; sourceTree = "<group>"; };
B8F0E10625E0199F00586F16 /* VisionCameraExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VisionCameraExample-Bridging-Header.h"; sourceTree = "<group>"; };
B8F0E10725E0199F00586F16 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@ -34,6 +45,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D27D5196997E7C9532F05776 /* libPods-VisionCameraExample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -43,6 +55,7 @@
13B07FAE1A68108700A75B9A /* VisionCameraExample */ = {
isa = PBXGroup;
children = (
B8DB3BD6263DEA31004C18D7 /* Frame Processor Plugins */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
@ -61,6 +74,7 @@
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
321EF902E53C449CC036AD27 /* libPods-VisionCameraExample.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -68,6 +82,8 @@
6B9684456A2045ADE5A6E47E /* Pods */ = {
isa = PBXGroup;
children = (
9993ED8FD4B3171BB28C87C9 /* Pods-VisionCameraExample.debug.xcconfig */,
0547A6E7EED7421DBCBCF7E4 /* Pods-VisionCameraExample.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -101,6 +117,32 @@
name = Products;
sourceTree = "<group>";
};
B8DB3BD6263DEA31004C18D7 /* Frame Processor Plugins */ = {
isa = PBXGroup;
children = (
B8DB3BD7263DEA31004C18D7 /* QR Code Plugin (Objective-C) */,
B8DB3BD9263DEA31004C18D7 /* QR Code Plugin (Swift) */,
);
path = "Frame Processor Plugins";
sourceTree = "<group>";
};
B8DB3BD7263DEA31004C18D7 /* QR Code Plugin (Objective-C) */ = {
isa = PBXGroup;
children = (
B8DB3BD8263DEA31004C18D7 /* QRCodeFrameProcessorPluginObjC.m */,
);
path = "QR Code Plugin (Objective-C)";
sourceTree = "<group>";
};
B8DB3BD9263DEA31004C18D7 /* QR Code Plugin (Swift) */ = {
isa = PBXGroup;
children = (
B8DB3BDA263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.swift */,
B8DB3BDB263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.m */,
);
path = "QR Code Plugin (Swift)";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -108,6 +150,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VisionCameraExample" */;
buildPhases = (
DECBF3FC22FA76EDF8EAAE3A /* [CP] Check Pods Manifest.lock */,
B81F12712604CF8800B08949 /* SwiftFormat */,
B81F12702604CF6600B08949 /* SwiftLint */,
FD10A7F022414F080027D42C /* Start Packager */,
@ -115,6 +158,7 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
A8A786DD4DE7AB6FD7D4C54C /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -131,7 +175,7 @@
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1240;
LastUpgradeCheck = 1250;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = CJW62Q77E7;
@ -184,6 +228,56 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
A8A786DD4DE7AB6FD7D4C54C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample-resources.sh",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VisionCameraExample/Pods-VisionCameraExample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B81F12702604CF6600B08949 /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -220,6 +314,28 @@
shellPath = /bin/sh;
shellScript = "if which swiftformat >/dev/null; then\n swiftformat .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
};
DECBF3FC22FA76EDF8EAAE3A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-VisionCameraExample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -247,8 +363,12 @@
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
B8DB3BDC263DEA31004C18D7 /* QRCodeFrameProcessorPluginObjC.m in Sources */,
B8DB3BD5263DE8B7004C18D7 /* BuildFile in Sources */,
B8DB3BDD263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.swift in Sources */,
B8F0E10825E0199F00586F16 /* File.swift in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
B8DB3BDE263DEA31004C18D7 /* QRCodeFrameProcessorPluginSwift.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -257,6 +377,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9993ED8FD4B3171BB28C87C9 /* Pods-VisionCameraExample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@ -281,6 +402,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0547A6E7EED7421DBCBCF7E4 /* Pods-VisionCameraExample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@ -334,7 +456,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -395,7 +517,7 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -412,6 +534,7 @@
);
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
};
name = Release;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1240"
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"

View File

@ -11,31 +11,10 @@
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
static void InitializeFlipper(UIApplication *application) {
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin:[FlipperKitReactPlugin new]];
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
}
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#ifdef FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];
return YES;
}

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const blacklist = require('metro-config/src/defaults/exclusionList');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const escape = require('escape-string-regexp');
const pak = require('../package.json');
@ -18,7 +18,7 @@ module.exports = {
// We need to make sure that only one version is loaded for peerDependencies
// So we blacklist them at the root, and alias them to the versions in example's node_modules
resolver: {
blacklistRE: blacklist(modules.map((m) => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`))),
blacklistRE: exclusionList(modules.map((m) => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`))),
extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);

View File

@ -15,34 +15,35 @@
"@react-native-community/cameraroll": "^4.0.2",
"@react-native-community/slider": "^3.0.3",
"pipestate": "^1.0.2",
"react": "17.0.1",
"react": "17.0.2",
"react-native": "0.64",
"react-native-gesture-handler": "^1.10.3",
"react-native-navigation": "7.8.4-snapshot.1439",
"react-native-reanimated": "^2.0.0",
"react-native-reanimated": "^2.1.0",
"react-native-static-safe-area-insets": "^2.1.1",
"react-native-vector-icons": "^8.0.0",
"react-native-video": "^5.1.1"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/runtime": "^7.13.10",
"@babel/core": "^7.14.0",
"@babel/runtime": "^7.14.0",
"@react-native-community/eslint-config": "^2.0.0",
"@react-native-community/eslint-plugin": "^1.1.0",
"@types/react": "^17.0.3",
"@types/react-native": "^0.63.52",
"@types/react": "^17.0.4",
"@types/react-native": "^0.64.4",
"@types/react-native-vector-icons": "^6.4.6",
"@types/react-native-video": "^5.0.4",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"babel-plugin-module-resolver": "^4.0.0",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react-native": "^3.10.0",
"metro-react-native-babel-preset": "^0.65.2",
"metro-config": "^0.66.0",
"metro-react-native-babel-preset": "^0.66.0",
"prettier": "^2.2.1",
"typescript": "^4.2.3"
"typescript": "^4.2.4"
}
}

View File

@ -25,9 +25,7 @@ import { useSelector } from 'pipestate';
import { FpsSelector } from './state/selectors';
import { useCameraDevice } from './hooks/useCameraDevice';
// TODO: Remove once https://github.com/software-mansion/react-native-reanimated/pull/1697 gets merged
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera) as any;
const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera);
Reanimated.addWhitelistedNativeProps({
zoom: true,
});
@ -213,6 +211,12 @@ export const App: NavigationFunctionComponent = ({ componentId }) => {
console.log('re-rendering camera page without active camera');
}
// const frameProcessor = useFrameProcessor((frame) => {
// 'worklet';
// const codes = scanQRCodesObjC(frame);
// _log(`Codes: ${JSON.stringify(codes)}`);
// }, []);
// TODO: Implement camera flipping (back <-> front) while recording and stich the videos together
// TODO: iOS: Use custom video data stream output to manually process the data and write the MOV/MP4 for more customizability.
return (
@ -234,6 +238,8 @@ export const App: NavigationFunctionComponent = ({ componentId }) => {
onError={onError}
enableZoomGesture={false}
animatedProps={cameraAnimatedProps}
// frameProcessor={frameProcessor}
// frameProcessorFps={1}
/>
</TapGestureHandler>
</Reanimated.View>

View File

@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useState } from 'react';
import { StyleSheet, View, Image, ActivityIndicator, PermissionsAndroid, Platform } from 'react-native';
import { Navigation, NavigationFunctionComponent, OptionsModalPresentationStyle } from 'react-native-navigation';
import Video, { OnLoadData } from 'react-native-video';
import Video, { LoadError, OnLoadData } from 'react-native-video';
import { SAFE_AREA_PADDING } from './Constants';
import { useIsForeground } from './hooks/useIsForeground';
import { useIsScreenFocused } from './hooks/useIsScreenFocused';
@ -58,6 +58,9 @@ export const Media: NavigationFunctionComponent<MediaProps> = ({ componentId, ty
console.log('media has loaded.');
setHasMediaLoaded(true);
}, []);
const onMediaLoadError = useCallback((error: LoadError) => {
console.log(`failed to load media: ${JSON.stringify(error)}`);
}, []);
const onSavePressed = useCallback(async () => {
try {
@ -104,6 +107,7 @@ export const Media: NavigationFunctionComponent<MediaProps> = ({ componentId, ty
ignoreSilentSwitch="ignore"
onReadyForDisplay={onMediaLoadEnd}
onLoad={onMediaLoad}
onError={onMediaLoadError}
/>
)}

View File

@ -0,0 +1,18 @@
/* global _WORKLET __exampleObjC___scanQRCodes __exampleSwift___scanQRCodes */
import type { Frame } from 'react-native-vision-camera';
export function scanQRCodesSwift(frame: Frame): string[] {
'worklet';
if (!_WORKLET) throw new Error('scanQRCodesSwift must be called from a frame processor!');
// @ts-expect-error because this function is dynamically injected by VisionCamera
return __exampleSwift___scanQRCodes(frame);
}
export function scanQRCodesObjC(frame: Frame): string[] {
'worklet';
if (!_WORKLET) throw new Error('scanQRCodesObjC must be called from a frame processor!');
// @ts-expect-error because this function is dynamically injected by VisionCamera
return __exampleObjC___scanQRCodes(frame, 'hello!', 'parameter2', true, 42);
}

View File

@ -21,7 +21,12 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1"
integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==
"@babel/core@^7.0.0", "@babel/core@^7.1.6", "@babel/core@^7.13.10":
"@babel/compat-data@^7.13.15":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919"
integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==
"@babel/core@^7.0.0", "@babel/core@^7.1.6":
version "7.13.14"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06"
integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==
@ -42,6 +47,27 @@
semver "^6.3.0"
source-map "^0.5.0"
"@babel/core@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88"
integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.14.0"
"@babel/helper-compilation-targets" "^7.13.16"
"@babel/helper-module-transforms" "^7.14.0"
"@babel/helpers" "^7.14.0"
"@babel/parser" "^7.14.0"
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.1.2"
semver "^6.3.0"
source-map "^0.5.0"
"@babel/generator@^7.13.9", "@babel/generator@^7.5.0":
version "7.13.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39"
@ -51,6 +77,15 @@
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/generator@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.0.tgz#0f35d663506c43e4f10898fbda0d752ec75494be"
integrity sha512-C6u00HbmsrNPug6A+CiNl8rEys7TsdcXwg12BHi2ca5rUfAs3+UwZsuDQSXnc+wCElCXMB8gMaJ3YXDdh8fAlg==
dependencies:
"@babel/types" "^7.14.0"
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab"
@ -76,6 +111,16 @@
browserslist "^4.14.5"
semver "^6.3.0"
"@babel/helper-compilation-targets@^7.13.16":
version "7.13.16"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c"
integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==
dependencies:
"@babel/compat-data" "^7.13.15"
"@babel/helper-validator-option" "^7.12.17"
browserslist "^4.14.5"
semver "^6.3.0"
"@babel/helper-create-class-features-plugin@^7.13.0":
version "7.13.11"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6"
@ -160,6 +205,20 @@
"@babel/traverse" "^7.13.13"
"@babel/types" "^7.13.14"
"@babel/helper-module-transforms@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad"
integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==
dependencies:
"@babel/helper-module-imports" "^7.13.12"
"@babel/helper-replace-supers" "^7.13.12"
"@babel/helper-simple-access" "^7.13.12"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/helper-validator-identifier" "^7.14.0"
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
"@babel/helper-optimise-call-expression@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea"
@ -217,6 +276,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
"@babel/helper-validator-identifier@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==
"@babel/helper-validator-option@^7.12.17":
version "7.12.17"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831"
@ -241,6 +305,15 @@
"@babel/traverse" "^7.13.0"
"@babel/types" "^7.13.0"
"@babel/helpers@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62"
integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==
dependencies:
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13":
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
@ -255,6 +328,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df"
integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==
"@babel/parser@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.0.tgz#2f0ebfed92bcddcc8395b91f1895191ce2760380"
integrity sha512-AHbfoxesfBALg33idaTBVUkLnfXtsgvJREf93p4p0Lwsz4ppfE7g1tpEXVm4vrxUcH4DVhAa9Z1m1zqf9WUC7Q==
"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.1.0":
version "7.13.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37"
@ -644,7 +722,14 @@
pirates "^4.0.0"
source-map-support "^0.5.16"
"@babel/runtime@^7.13.10", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.8.4":
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
@ -674,6 +759,20 @@
debug "^4.1.0"
globals "^11.1.0"
"@babel/traverse@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef"
integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.14.0"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/parser" "^7.14.0"
"@babel/types" "^7.14.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.7.0":
version "7.13.14"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d"
@ -683,6 +782,14 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@babel/types@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.0.tgz#3fc3fc74e0cdad878182e5f66cc6bcab1915a802"
integrity sha512-O2LVLdcnWplaGxiPBz12d0HcdN8QdxdsWYhz5LSeuukV/5mn2xUUc3gBeU4QBYPJ18g/UToe8F532XJ608prmg==
dependencies:
"@babel/helper-validator-identifier" "^7.14.0"
to-fast-properties "^2.0.0"
"@cnakazawa/watch@^1.0.3":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
@ -1035,14 +1142,14 @@
dependencies:
"@types/react" "*"
"@types/react-native@^0.63.52":
version "0.63.52"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.52.tgz#449beb4a413ec0f2c172cbf676a95f5b0952adf4"
integrity sha512-sBXvvtJaIUSXQLDh9NZitx1KHkKUdBLZy34lFKJaIXtpHIh5OEbBXeyUTFBtFwjk/RD0tneAtUqsdhheZRzAzw==
"@types/react-native@^0.64.4":
version "0.64.4"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.4.tgz#9f11bef7dd5520801884829c73b19d75aa42e73c"
integrity sha512-VqnlmadGkD5usREvnuyVpWDS1W8f6cCz6MP5fZdgONsaZ9/Ijfb9Iq9MZ5O3bnW1OyJixDX9HtSp3COsFSLD8Q==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.3":
"@types/react@*":
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
@ -1051,6 +1158,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^17.0.4":
version "17.0.4"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.4.tgz#a67c6f7a460d2660e950d9ccc1c2f18525c28220"
integrity sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.1"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
@ -1080,13 +1196,13 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/eslint-plugin@^4.18.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.20.0.tgz#9d8794bd99aad9153092ad13c96164e3082e9a92"
integrity sha512-sw+3HO5aehYqn5w177z2D82ZQlqHCwcKSMboueo7oE4KU9QiC0SAgfS/D4z9xXvpTc8Bt41Raa9fBR8T2tIhoQ==
"@typescript-eslint/eslint-plugin@^4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc"
integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==
dependencies:
"@typescript-eslint/experimental-utils" "4.20.0"
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/experimental-utils" "4.22.0"
"@typescript-eslint/scope-manager" "4.22.0"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
lodash "^4.17.15"
@ -1105,15 +1221,15 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/experimental-utils@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.20.0.tgz#a8ab2d7b61924f99042b7d77372996d5f41dc44b"
integrity sha512-sQNlf6rjLq2yB5lELl3gOE7OuoA/6IVXJUJ+Vs7emrQMva14CkOwyQwD7CW+TkmOJ4Q/YGmoDLmbfFrpGmbKng==
"@typescript-eslint/experimental-utils@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz#68765167cca531178e7b650a53456e6e0bef3b1f"
integrity sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/typescript-estree" "4.20.0"
"@typescript-eslint/scope-manager" "4.22.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/typescript-estree" "4.22.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
@ -1128,33 +1244,33 @@
"@typescript-eslint/typescript-estree" "3.10.1"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/parser@^4.18.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.20.0.tgz#8dd403c8b4258b99194972d9799e201b8d083bdd"
integrity sha512-m6vDtgL9EABdjMtKVw5rr6DdeMCH3OA1vFb0dAyuZSa3e5yw1YRzlwFnm9knma9Lz6b2GPvoNSa8vOXrqsaglA==
"@typescript-eslint/parser@^4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.0.tgz#e1637327fcf796c641fe55f73530e90b16ac8fe8"
integrity sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==
dependencies:
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/typescript-estree" "4.20.0"
"@typescript-eslint/scope-manager" "4.22.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/typescript-estree" "4.22.0"
debug "^4.1.1"
"@typescript-eslint/scope-manager@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.20.0.tgz#953ecbf3b00845ece7be66246608be9d126d05ca"
integrity sha512-/zm6WR6iclD5HhGpcwl/GOYDTzrTHmvf8LLLkwKqqPKG6+KZt/CfSgPCiybshmck66M2L5fWSF/MKNuCwtKQSQ==
"@typescript-eslint/scope-manager@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a"
integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/visitor-keys" "4.20.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/visitor-keys" "4.22.0"
"@typescript-eslint/types@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
"@typescript-eslint/types@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.20.0.tgz#c6cf5ef3c9b1c8f699a9bbdafb7a1da1ca781225"
integrity sha512-cYY+1PIjei1nk49JAPnH1VEnu7OYdWRdJhYI5wiKOUMhLTG1qsx5cQxCUTuwWCmQoyriadz3Ni8HZmGSofeC+w==
"@typescript-eslint/types@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6"
integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==
"@typescript-eslint/typescript-estree@3.10.1":
version "3.10.1"
@ -1170,13 +1286,13 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.20.0.tgz#8b3b08f85f18a8da5d88f65cb400f013e88ab7be"
integrity sha512-Knpp0reOd4ZsyoEJdW8i/sK3mtZ47Ls7ZHvD8WVABNx5Xnn7KhenMTRGegoyMTx6TiXlOVgMz9r0pDgXTEEIHA==
"@typescript-eslint/typescript-estree@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz#b5d95d6d366ff3b72f5168c75775a3e46250d05c"
integrity sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/visitor-keys" "4.20.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/visitor-keys" "4.22.0"
debug "^4.1.1"
globby "^11.0.1"
is-glob "^4.0.1"
@ -1190,12 +1306,12 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/visitor-keys@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.20.0.tgz#1e84db034da13f208325e6bfc995c3b75f7dbd62"
integrity sha512-NXKRM3oOVQL8yNFDNCZuieRIwZ5UtjNLYtmMx2PacEAGmbaEYtGgVHUHVyZvU/0rYZcizdrWjDo+WBtRPSgq+A==
"@typescript-eslint/visitor-keys@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz#169dae26d3c122935da7528c839f42a8a42f6e47"
integrity sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/types" "4.22.0"
eslint-visitor-keys "^2.0.0"
abort-controller@^3.0.0:
@ -2165,10 +2281,10 @@ eslint-config-prettier@^6.10.1:
dependencies:
get-stdin "^6.0.0"
eslint-config-prettier@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6"
integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==
eslint-config-prettier@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a"
integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==
eslint-plugin-eslint-comments@^3.1.2:
version "3.2.0"
@ -2197,10 +2313,10 @@ eslint-plugin-prettier@3.1.2:
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-prettier@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7"
integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==
eslint-plugin-prettier@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==
dependencies:
prettier-linter-helpers "^1.0.0"
@ -2265,10 +2381,10 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7.22.0:
version "7.23.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325"
integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==
eslint@^7.25.0:
version "7.25.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67"
integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==
dependencies:
"@babel/code-frame" "7.12.11"
"@eslint/eslintrc" "^0.4.0"
@ -3528,6 +3644,20 @@ metro-babel-register@0.64.0:
"@babel/register" "^7.0.0"
escape-string-regexp "^1.0.5"
metro-babel-register@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.66.0.tgz#4a2646a0197189d0e7f85b93f823bb57e5bcc28e"
integrity sha512-Al1fJquMISNsbjaHOYVFVYO5vxyw3gOvIf1ScdxNwzndCOA+5yersRO7JSUUoVfoQZAXEdtcj027BthPmHDjsw==
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0"
"@babel/plugin-proposal-optional-chaining" "^7.0.0"
"@babel/plugin-syntax-class-properties" "^7.0.0"
"@babel/plugin-transform-flow-strip-types" "^7.0.0"
"@babel/plugin-transform-modules-commonjs" "^7.0.0"
"@babel/register" "^7.0.0"
escape-string-regexp "^1.0.5"
metro-babel-transformer@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.64.0.tgz#a21f8a989a5ea60c1109456e21bd4d9374194ea0"
@ -3537,11 +3667,25 @@ metro-babel-transformer@0.64.0:
metro-source-map "0.64.0"
nullthrows "^1.1.1"
metro-babel-transformer@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.66.0.tgz#77b8f5fde576d35220caed5c17ba5e4e626304d2"
integrity sha512-IJCF4wILV4FT8xX4DHSntFKjYZFBi/3EJ+4TQlvcauZv89c7u4HEZ+Jjmg+M6L4pnv1m8GRNjRcgilnsY9N5nw==
dependencies:
"@babel/core" "^7.0.0"
metro-source-map "0.66.0"
nullthrows "^1.1.1"
metro-cache-key@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.64.0.tgz#98d0a94332453c4c52b74f72c07cc62a5c264c4f"
integrity sha512-O9B65G8L/fopck45ZhdRosyVZdMtUQuX5mBWEC1NRj02iWBIUPLmYMjrunqIe8vHipCMp3DtTCm/65IlBmO8jg==
metro-cache-key@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.66.0.tgz#e628445f2a17e6e3dec970a87a090b4ec876bd1d"
integrity sha512-qAHMC4Tpj3rUH8Pz5IEmT/fGgitCO86B6jndzKyXT+aDBr7BcNnRA8T25MoUMiBtsuRdxAeMHmBbhmvNK+tEEg==
metro-cache@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.64.0.tgz#a769503e12521d9e9d95ce5840ffb2efdb4e8703"
@ -3551,6 +3695,15 @@ metro-cache@0.64.0:
mkdirp "^0.5.1"
rimraf "^2.5.4"
metro-cache@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.66.0.tgz#65bfcf8a4bff559855c404220368866779216430"
integrity sha512-Yppsf28TLGZwQIpNkx/9c5xc9FWFglhaUH6Ovtbth8DmDauAwakvdoMklFvkYQGE9W8OceehQAvRW2D9YQn9Ew==
dependencies:
metro-core "0.66.0"
mkdirp "^0.5.1"
rimraf "^2.5.4"
metro-config@0.64.0, metro-config@^0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.64.0.tgz#b634fa05cffd06b1e50e4339c200f90a42924afb"
@ -3563,6 +3716,18 @@ metro-config@0.64.0, metro-config@^0.64.0:
metro-core "0.64.0"
metro-runtime "0.64.0"
metro-config@0.66.0, metro-config@^0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.66.0.tgz#7755aa0323549e351670d50fda9df9702ec60b00"
integrity sha512-mzJ8bc/sAYSgQp72+0ZWeNEGqAdetLIWde+i5AMDfjFobgzrITjz5SwDGToDEKUgQWIt4BlJzbWNyKEId7TsPw==
dependencies:
cosmiconfig "^5.0.5"
jest-validate "^26.5.2"
metro "0.66.0"
metro-cache "0.66.0"
metro-core "0.66.0"
metro-runtime "0.66.0"
metro-core@0.64.0, metro-core@^0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.64.0.tgz#7616b27acfe7baa476f6cd6bd9e70ae64fa62541"
@ -3572,11 +3737,25 @@ metro-core@0.64.0, metro-core@^0.64.0:
lodash.throttle "^4.1.1"
metro-resolver "0.64.0"
metro-core@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.66.0.tgz#e000cff338b072288146e166180095b272e0fbfa"
integrity sha512-F/U1qQl6LxHLn8P5pfyvW3S0BUiYdwgU23icmNgUx2GRFazOdtZUgR9EJAhISZAsoYsVpznftA6lS8Kok6pZOw==
dependencies:
jest-haste-map "^26.5.2"
lodash.throttle "^4.1.1"
metro-resolver "0.66.0"
metro-hermes-compiler@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.64.0.tgz#e6043d7aa924e5b2be99bd3f602e693685d15386"
integrity sha512-CLAjVDWGAoGhbi2ZyPHnH5YDdfrDIx6+tzFWfHGIMTZkYBXsYta9IfYXBV8lFb6BIbrXLjlXZAOoosknetMPOA==
metro-hermes-compiler@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.66.0.tgz#8fd9c19c5ac581649f86e678f11e6b615efc8b2e"
integrity sha512-fiMNxQ3WDEmFNpZgWgGGBYP8Q3rIXmIBDq2GPepvbH5KLDTKgAsjGMS4VYGe9M2eCBKKmElewkzDNKL+Wu5ivQ==
metro-inspector-proxy@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.64.0.tgz#9a481b3f49773d5418e028178efec68f861bec88"
@ -3587,6 +3766,16 @@ metro-inspector-proxy@0.64.0:
ws "^1.1.5"
yargs "^15.3.1"
metro-inspector-proxy@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.66.0.tgz#77425ece4d42e06ff0a2af23c30049070035580f"
integrity sha512-lYC6LWfTT9bUh+MmZhzbBgCS1ztGAq1sEYrQKbIMJNZuTgGfXXsGSti/Qq0pbH4VoxXv5OAaL5um346XHfkqZQ==
dependencies:
connect "^3.6.5"
debug "^2.2.0"
ws "^1.1.5"
yargs "^15.3.1"
metro-minify-uglify@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.64.0.tgz#da6ab4dda030e3211f5924e7f41ed308d466068f"
@ -3594,6 +3783,13 @@ metro-minify-uglify@0.64.0:
dependencies:
uglify-es "^3.1.9"
metro-minify-uglify@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.66.0.tgz#745887cf594ff8afad5521147d365f7660780dc3"
integrity sha512-7j47/YIUJjorDh4Sbz3toxxNBSG/dO7bFCvpF5gW1i5ORdLFjsC6Jdr2RN8mQU1bRkA6T2NaMO+q5t491tTMFw==
dependencies:
uglify-es "^3.1.9"
metro-react-native-babel-preset@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.64.0.tgz#76861408681dfda3c1d962eb31a8994918c976f8"
@ -3639,10 +3835,10 @@ metro-react-native-babel-preset@0.64.0:
"@babel/template" "^7.0.0"
react-refresh "^0.4.0"
metro-react-native-babel-preset@^0.65.2:
version "0.65.2"
resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.65.2.tgz#786cbb8b21daa614cbbebcc5c3ce72b6b0710892"
integrity sha512-jBpZwJwnGHXUnzoZl81LlUzvec2dh1llMJ2A7pbTMuCKhx4LjqOGEE1E+hkNqj/Uh7gi6tCPy5JYSCo9Ue/Vog==
metro-react-native-babel-preset@0.66.0, metro-react-native-babel-preset@^0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.66.0.tgz#a4495df4b24a2eb9f82705e0a53f4cbbd36d983e"
integrity sha512-rO3yayxplLNxFDc7HyMShN+psgEb2mbw15EMreNvgV8QnXNYHmgU6e15tLbtEvC8LuftOLuSufEdSmR/ykm+aA==
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
@ -3704,11 +3900,23 @@ metro-resolver@0.64.0, metro-resolver@^0.64.0:
dependencies:
absolute-path "^0.0.0"
metro-resolver@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.66.0.tgz#068c1bb98cd80c239f051e7b0f43f00e763b889d"
integrity sha512-JUbkmznwgMSb5ViFpvau5sPD7uuZekToVsitrDLTAeMPruvjxjae2+XSIai9NmcGSprfvqyGYURlz0qXu1YnJQ==
dependencies:
absolute-path "^0.0.0"
metro-runtime@0.64.0, metro-runtime@^0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.64.0.tgz#cdaa1121d91041bf6345f2a69eb7c2fb289eff7b"
integrity sha512-m7XbWOaIOeFX7YcxUhmnOi6Pg8EaeL89xyZ+quZyZVF1aNoTr4w8FfbKxvijpjsytKHIZtd+43m2Wt5JrqyQmQ==
metro-runtime@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.66.0.tgz#aff887fdcbcd202b18ae1c2a9d8572d0289fec00"
integrity sha512-oGkALjm248OGbPN0ivrI52gS6yEBnWH9Jr+rHZDSdldD/MZtpT77hBgwLj+fu0axkRgGF9xnBji0KZvozaDXKQ==
metro-source-map@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.64.0.tgz#4310e17c3d4539c6369688022494ad66fa4d39a1"
@ -3723,6 +3931,20 @@ metro-source-map@0.64.0:
source-map "^0.5.6"
vlq "^1.0.0"
metro-source-map@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.66.0.tgz#3a61cd8d671842f54cb2863595d026bb1011ce7f"
integrity sha512-uutlmYb92uo/diHsbxk9RTi7e49PAfwMkw2RFPfDTUrjOr+DZICv1ltkQxs/dCgHaNm/nQ1T9hJpM/vuJavREA==
dependencies:
"@babel/traverse" "^7.0.0"
"@babel/types" "^7.0.0"
invariant "^2.2.4"
metro-symbolicate "0.66.0"
nullthrows "^1.1.1"
ob1 "0.66.0"
source-map "^0.5.6"
vlq "^1.0.0"
metro-symbolicate@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.64.0.tgz#405c21438ab553c29f6841da52ca76ee87bb06ac"
@ -3735,6 +3957,18 @@ metro-symbolicate@0.64.0:
through2 "^2.0.1"
vlq "^1.0.0"
metro-symbolicate@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.66.0.tgz#cac5fd328bb63ae20f5c64b85d86a9c08097377a"
integrity sha512-OasmbRZQBJ36mYMF4T0ckXditMRrekKMG8qJezPxvizCpS3xILNTpkVyP6crCpDuMNdxbd9GcTwYKxM5YKOdLw==
dependencies:
invariant "^2.2.4"
metro-source-map "0.66.0"
nullthrows "^1.1.1"
source-map "^0.5.6"
through2 "^2.0.1"
vlq "^1.0.0"
metro-transform-plugins@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.64.0.tgz#41d3dce0f2966bbd79fea1ecff61bcc8a00e4665"
@ -3746,6 +3980,17 @@ metro-transform-plugins@0.64.0:
"@babel/traverse" "^7.0.0"
nullthrows "^1.1.1"
metro-transform-plugins@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.66.0.tgz#d40cb1a88110b0033b5a870c497ce56a38ec5b1f"
integrity sha512-0jF27jozp4IYuzliM2R7NaXqbPXleiHQWVczECkHg3UjDroCKneCkkgeSeirVZ1TkBqu7KSLMiWdL2OOT+vVxQ==
dependencies:
"@babel/core" "^7.0.0"
"@babel/generator" "^7.5.0"
"@babel/template" "^7.0.0"
"@babel/traverse" "^7.0.0"
nullthrows "^1.1.1"
metro-transform-worker@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.64.0.tgz#f94429b2c42b13cb1c93be4c2e25e97f2d27ca60"
@ -3765,6 +4010,25 @@ metro-transform-worker@0.64.0:
metro-transform-plugins "0.64.0"
nullthrows "^1.1.1"
metro-transform-worker@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.66.0.tgz#9a44545e3c1f91bb1cfca04d04c73920f61675d4"
integrity sha512-7M2ns0nI3f3vy6zrNE+gLMkWcvlMqSDnmuZShMjHHADbd1K/MmU1qkJEALnd7ns/Yzsoy81dwEPRy7fxq3cKTw==
dependencies:
"@babel/core" "^7.0.0"
"@babel/generator" "^7.5.0"
"@babel/parser" "^7.0.0"
"@babel/types" "^7.0.0"
babel-preset-fbjs "^3.3.0"
metro "0.66.0"
metro-babel-transformer "0.66.0"
metro-cache "0.66.0"
metro-cache-key "0.66.0"
metro-hermes-compiler "0.66.0"
metro-source-map "0.66.0"
metro-transform-plugins "0.66.0"
nullthrows "^1.1.1"
metro@0.64.0, metro@^0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/metro/-/metro-0.64.0.tgz#0091a856cfbcc94dd576da563eee466e96186195"
@ -3822,6 +4086,63 @@ metro@0.64.0, metro@^0.64.0:
ws "^1.1.5"
yargs "^15.3.1"
metro@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/metro/-/metro-0.66.0.tgz#1218d55f4016edd5e47c3d50f6889f667aff2103"
integrity sha512-PZIV8IWZ0m3ceAIHGz/MmqrDlHJE6d5yur1VZldrQIVuzGCjNeCw/M+YT5ozo/fW0yI9pLpxA0E1vH1YtfEjWQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/core" "^7.0.0"
"@babel/generator" "^7.5.0"
"@babel/parser" "^7.0.0"
"@babel/template" "^7.0.0"
"@babel/traverse" "^7.0.0"
"@babel/types" "^7.0.0"
absolute-path "^0.0.0"
accepts "^1.3.7"
async "^2.4.0"
chalk "^4.0.0"
ci-info "^2.0.0"
connect "^3.6.5"
debug "^2.2.0"
denodeify "^1.2.1"
error-stack-parser "^2.0.6"
fs-extra "^1.0.0"
graceful-fs "^4.1.3"
image-size "^0.6.0"
invariant "^2.2.4"
jest-haste-map "^26.5.2"
jest-worker "^26.0.0"
lodash.throttle "^4.1.1"
metro-babel-register "0.66.0"
metro-babel-transformer "0.66.0"
metro-cache "0.66.0"
metro-cache-key "0.66.0"
metro-config "0.66.0"
metro-core "0.66.0"
metro-hermes-compiler "0.66.0"
metro-inspector-proxy "0.66.0"
metro-minify-uglify "0.66.0"
metro-react-native-babel-preset "0.66.0"
metro-resolver "0.66.0"
metro-runtime "0.66.0"
metro-source-map "0.66.0"
metro-symbolicate "0.66.0"
metro-transform-plugins "0.66.0"
metro-transform-worker "0.66.0"
mime-types "^2.1.27"
mkdirp "^0.5.1"
node-fetch "^2.2.0"
nullthrows "^1.1.1"
rimraf "^2.5.4"
serialize-error "^2.1.0"
source-map "^0.5.6"
strip-ansi "^6.0.0"
temp "0.8.3"
throat "^5.0.0"
ws "^1.1.5"
yargs "^15.3.1"
micromatch@^3.1.10, micromatch@^3.1.4:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@ -4026,6 +4347,11 @@ ob1@0.64.0:
resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19"
integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ==
ob1@0.66.0:
version "0.66.0"
resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.66.0.tgz#e7d52b4c49ecae2e4831d127081131b7f050984b"
integrity sha512-7G2SpMAUKFTydtao80/5FD6uN5MTRXfVqly7NPbl7YpO0MBe3SNMLRQw0oDoefMjpt1f2pYfq/UG40Sdc59yqQ==
object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@ -4457,10 +4783,10 @@ react-native-navigation@7.8.4-snapshot.1439:
react-lifecycles-compat "2.0.0"
tslib "1.9.3"
react-native-reanimated@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.0.1.tgz#440bbba813df283e9f410ed763e4791d859c67e9"
integrity sha512-Wg/mEdI8xMRDQHYkgNGztJDjAcx1EFR5OMMtXrLSMmT0qzqcRWcVZgDHBN2MEAJqem/HkPAoOFutWzibwvinVg==
react-native-reanimated@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.1.0.tgz#b9ad04aee490e1e030d0a6cdaa43a14895d9a54d"
integrity sha512-tlPvvcdf+X7HGQ7g/7npBFhwMznfdk7MHUc9gUB/kp2abSscXNe/kOVKlrNEOO4DS11rNOXc+llFxVFMuNk0zA==
dependencies:
"@babel/plugin-transform-object-assign" "^7.10.4"
fbjs "^3.0.0"
@ -4538,10 +4864,10 @@ react-refresh@^0.4.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"
integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==
react@17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
react@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -5337,10 +5663,10 @@ type-fest@^0.8.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
typescript@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
ua-parser-js@^0.7.18:
version "0.7.26"

View File

@ -9,9 +9,20 @@
#pragma once
#import <Foundation/Foundation.h>
#import <React/RCTViewManager.h>
#import <React/RCTUIManager.h>
#import "FrameProcessorCallback.h"
#import "FrameProcessorRuntimeManager.h"
#import "RCTBridge+runOnJS.h"
#ifdef VISION_CAMERA_DISABLE_FRAME_PROCESSORS
static bool enableFrameProcessors = false;
#else
static bool enableFrameProcessors = true;
#endif
@interface CameraBridge: RCTViewManager
@end

View File

@ -179,6 +179,7 @@ enum CaptureError {
case noRecordingInProgress
case fileError
case createTempFileError
case createRecorderError(message: String? = nil)
case invalidPhotoCodec
case unknown(message: String? = nil)
@ -194,6 +195,8 @@ enum CaptureError {
return "file-io-error"
case .createTempFileError:
return "create-temp-file-error"
case .createRecorderError:
return "create-recorder-error"
case .invalidPhotoCodec:
return "invalid-photo-codec"
case .unknown:
@ -215,6 +218,8 @@ enum CaptureError {
return "An unexpected File IO error occured!"
case .createTempFileError:
return "Failed to create a temporary file!"
case let .createRecorderError(message: message):
return "Failed to create the AVAssetWriter (Recorder)! \(message ?? "(no additional message)")"
case let .unknown(message: message):
return message ?? "An unknown error occured while capturing a video/photo."
}

33
ios/CameraQueues.swift Normal file
View File

@ -0,0 +1,33 @@
//
// CameraQueues.swift
// VisionCamera
//
// Created by Marc Rousavy on 22.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import Foundation
@objc
public class CameraQueues: NSObject {
/// The serial execution queue for the camera preview layer (input stream) as well as output processing of photos.
@objc public static let cameraQueue = DispatchQueue(label: "com.mrousavy.vision.camera-queue",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
/// The serial execution queue for output processing of videos as well as frame processors.
@objc public static let videoQueue = DispatchQueue(label: "com.mrousavy.vision.video-queue",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
// TODO: Is it a good idea to use a separate queue for audio output processing?
/// The serial execution queue for output processing of audio buffers.
@objc public static let audioQueue = DispatchQueue(label: "com.mrousavy.vision.audio-queue",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
}

View File

@ -98,19 +98,36 @@ extension CameraView {
photoOutput!.mirror()
}
// Video Output
if let movieOutput = self.movieOutput {
captureSession.removeOutput(movieOutput)
// Video Output + Frame Processor
if let videoOutput = self.videoOutput {
captureSession.removeOutput(videoOutput)
self.videoOutput = nil
}
movieOutput = AVCaptureMovieFileOutput()
guard captureSession.canAddOutput(movieOutput!) else {
return invokeOnError(.parameter(.unsupportedOutput(outputDescriptor: "movie-output")))
ReactLogger.log(level: .info, message: "Adding Video Data output...")
videoOutput = AVCaptureVideoDataOutput()
guard captureSession.canAddOutput(videoOutput!) else {
return invokeOnError(.parameter(.unsupportedOutput(outputDescriptor: "video-output")))
}
captureSession.addOutput(movieOutput!)
videoOutput!.setSampleBufferDelegate(self, queue: videoQueue)
videoOutput!.alwaysDiscardsLateVideoFrames = true
captureSession.addOutput(videoOutput!)
if videoDeviceInput!.device.position == .front {
movieOutput!.mirror()
videoOutput!.mirror()
}
// Audio Output
if let audioOutput = self.audioOutput {
captureSession.removeOutput(audioOutput)
self.audioOutput = nil
}
ReactLogger.log(level: .info, message: "Adding Audio Data output...")
audioOutput = AVCaptureAudioDataOutput()
guard captureSession.canAddOutput(audioOutput!) else {
return invokeOnError(.parameter(.unsupportedOutput(outputDescriptor: "audio-output")))
}
audioOutput!.setSampleBufferDelegate(self, queue: audioQueue)
captureSession.addOutput(audioOutput!)
invokeOnInitialized()
isReady = true
ReactLogger.log(level: .info, message: "Session successfully configured!")

View File

@ -8,48 +8,160 @@
import AVFoundation
extension CameraView {
private var hasLoggedFrameDropWarning = false
// MARK: - CameraView + AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate
extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
func startRecording(options: NSDictionary, callback: @escaping RCTResponseSenderBlock) {
queue.async {
guard let movieOutput = self.movieOutput else {
return callback([NSNull(), makeReactError(.session(.cameraNotReady))])
}
if movieOutput.isRecording {
return callback([NSNull(), makeReactError(.capture(.recordingInProgress))])
}
cameraQueue.async {
ReactLogger.log(level: .info, message: "Starting Video recording...")
do {
let errorPointer = ErrorPointer(nilLiteral: ())
guard let tempFilePath = RCTTempFilePath("mov", errorPointer) else {
return callback([NSNull(), makeReactError(.capture(.createTempFileError), cause: errorPointer?.pointee)])
}
let errorPointer = ErrorPointer(nilLiteral: ())
guard let tempFilePath = RCTTempFilePath("mov", errorPointer) else {
return callback([NSNull(), makeReactError(.capture(.createTempFileError), cause: errorPointer?.pointee)])
}
let tempURL = URL(string: "file://\(tempFilePath)")!
if let flashMode = options["flash"] as? String {
// use the torch as the video's flash
self.setTorchMode(flashMode)
}
let tempURL = URL(string: "file://\(tempFilePath)")!
if let flashMode = options["flash"] as? String {
// use the torch as the video's flash
self.setTorchMode(flashMode)
}
movieOutput.startRecording(to: tempURL, recordingDelegate: RecordingDelegateWithCallback(callback: callback, resetTorchMode: {
// reset torch in case it was used as the video's "flash"
self.setTorchMode(self.torch)
}))
// TODO: The startRecording() func cannot be async because RN doesn't allow both a callback and a Promise in a single function. Wait for TurboModules?
// return ["path": tempFilePath]
var fileType = AVFileType.mov
if let fileTypeOption = options["fileType"] as? String {
fileType = AVFileType(withString: fileTypeOption)
}
// TODO: The startRecording() func cannot be async because RN doesn't allow
// both a callback and a Promise in a single function. Wait for TurboModules?
// This means that any errors that occur in this function have to be delegated through
// the callback, but I'd prefer for them to throw for the original function instead.
let onFinish = { (status: AVAssetWriter.Status, error: Error?) -> Void in
defer {
self.recordingSession = nil
}
ReactLogger.log(level: .info, message: "RecordingSession finished with status \(status.descriptor).")
if let error = error {
let description = (error as NSError).description
return callback([NSNull(), CameraError.capture(.unknown(message: "An unknown recording error occured! \(description)"))])
} else {
if status == .completed {
return callback([[
"path": self.recordingSession!.url.absoluteString,
"duration": self.recordingSession!.duration,
], NSNull()])
} else {
return callback([NSNull(), CameraError.unknown(message: "AVAssetWriter completed with status: \(status.descriptor)")])
}
}
}
let videoSettings = self.videoOutput!.recommendedVideoSettingsForAssetWriter(writingTo: fileType)
let audioSettings = self.audioOutput!.recommendedAudioSettingsForAssetWriter(writingTo: fileType) as? [String: Any]
self.recordingSession = try RecordingSession(url: tempURL,
fileType: fileType,
videoSettings: videoSettings ?? [:],
audioSettings: audioSettings ?? [:],
isVideoMirrored: self.videoOutput!.isMirrored,
completion: onFinish)
self.isRecording = true
} catch EnumParserError.invalidValue {
return callback([NSNull(), EnumParserError.invalidValue])
} catch let error as NSError {
return callback([NSNull(), makeReactError(.capture(.createTempFileError), cause: error)])
}
}
}
func stopRecording(promise: Promise) {
queue.async {
withPromise(promise) {
guard let movieOutput = self.movieOutput else {
throw CameraError.session(SessionError.cameraNotReady)
}
if !movieOutput.isRecording {
throw CameraError.capture(CaptureError.noRecordingInProgress)
}
isRecording = false
movieOutput.stopRecording()
cameraQueue.async {
withPromise(promise) {
guard let recordingSession = self.recordingSession else {
throw CameraError.capture(.noRecordingInProgress)
}
recordingSession.finish()
return nil
}
}
}
// TODO: Implement for JS
func pauseRecording(promise: Promise) {
cameraQueue.async {
withPromise(promise) {
if self.isRecording {
self.isRecording = false
return nil
} else {
throw CameraError.capture(.noRecordingInProgress)
}
}
}
}
// TODO: Implement for JS
func resumeRecording(promise: Promise) {
cameraQueue.async {
withPromise(promise) {
if !self.isRecording {
self.isRecording = true
return nil
} else {
throw CameraError.capture(.noRecordingInProgress)
}
}
}
}
public final func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from _: AVCaptureConnection) {
if isRecording {
guard let recordingSession = recordingSession else {
return invokeOnError(.capture(.unknown(message: "isRecording was true but the RecordingSession was null!")))
}
switch captureOutput {
case is AVCaptureVideoDataOutput:
recordingSession.appendBuffer(sampleBuffer, type: .video)
case is AVCaptureAudioDataOutput:
recordingSession.appendBuffer(sampleBuffer, type: .audio)
default:
break
}
}
if let frameProcessor = frameProcessorCallback, captureOutput is AVCaptureVideoDataOutput {
// check if last frame was x nanoseconds ago, effectively throttling FPS
let diff = DispatchTime.now().uptimeNanoseconds - lastFrameProcessorCall.uptimeNanoseconds
let secondsPerFrame = 1.0 / frameProcessorFps.doubleValue
let nanosecondsPerFrame = secondsPerFrame * 1_000_000_000.0
if diff > UInt64(nanosecondsPerFrame) {
frameProcessor(sampleBuffer)
lastFrameProcessorCall = DispatchTime.now()
}
}
}
public final func captureOutput(_ captureOutput: AVCaptureOutput, didDrop buffer: CMSampleBuffer, from _: AVCaptureConnection) {
if frameProcessorCallback != nil && !hasLoggedFrameDropWarning && captureOutput is AVCaptureVideoDataOutput {
let reason = findFrameDropReason(inBuffer: buffer)
// TODO: Show in React console?
ReactLogger.log(level: .warning, message: "Dropped a Frame. This might indicate that your Frame Processor is doing too much work. " +
"Either throttle the frame processor's frame rate, or optimize your frame processor's execution speed. Frame drop reason: \(reason)")
hasLoggedFrameDropWarning = true
}
}
private final func findFrameDropReason(inBuffer buffer: CMSampleBuffer) -> String {
var mode: CMAttachmentMode = 0
guard let reason = CMGetAttachment(buffer,
key: kCMSampleBufferAttachmentKey_DroppedFrameReason,
attachmentModeOut: &mode) else {
return "unknown"
}
return String(describing: reason)
}
}

View File

@ -24,7 +24,7 @@ struct TakePhotoOptions {
extension CameraView {
func takePhoto(options: NSDictionary, promise: Promise) {
queue.async {
cameraQueue.async {
guard let photoOutput = self.photoOutput, let videoDeviceInput = self.videoDeviceInput else {
return promise.reject(error: .session(.cameraNotReady))
}

View File

@ -35,6 +35,7 @@ private let propsThatRequireDeviceReconfiguration = ["fps",
public final class CameraView: UIView {
// pragma MARK: React Properties
// pragma MARK: Exported Properties
// props that require reconfiguring
@objc var cameraId: NSString?
@objc var enableDepthData = false
@ -44,6 +45,7 @@ public final class CameraView: UIView {
// props that require format reconfiguring
@objc var format: NSDictionary?
@objc var fps: NSNumber?
@objc var frameProcessorFps: NSNumber = 1.0
@objc var hdr: NSNumber? // nullable bool
@objc var lowLightBoost: NSNumber? // nullable bool
@objc var colorSpace: NSString?
@ -75,18 +77,24 @@ public final class CameraView: UIView {
// Inputs
internal var videoDeviceInput: AVCaptureDeviceInput?
internal var audioDeviceInput: AVCaptureDeviceInput?
// Outputs
internal var photoOutput: AVCapturePhotoOutput?
internal var movieOutput: AVCaptureMovieFileOutput?
internal var videoOutput: AVCaptureVideoDataOutput?
internal var audioOutput: AVCaptureAudioDataOutput?
// CameraView+RecordView (+ FrameProcessorDelegate.mm)
internal var isRecording = false
internal var recordingSession: RecordingSession?
@objc public var frameProcessorCallback: FrameProcessorCallback?
internal var lastFrameProcessorCall = DispatchTime.now()
// CameraView+TakePhoto
internal var photoCaptureDelegates: [PhotoCaptureDelegate] = []
// CameraView+RecordVideo
internal var recordingDelegateResolver: RCTPromiseResolveBlock?
internal var recordingDelegateRejecter: RCTPromiseRejectBlock?
// CameraView+Zoom
internal var pinchGestureRecognizer: UIPinchGestureRecognizer?
internal var pinchScaleOffset: CGFloat = 1.0
internal let cameraQueue = CameraQueues.cameraQueue
internal let videoQueue = CameraQueues.videoQueue
internal let audioQueue = CameraQueues.audioQueue
var isRunning: Bool {
return captureSession.isRunning
}
@ -126,6 +134,11 @@ public final class CameraView: UIView {
object: AVAudioSession.sharedInstance)
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) is not implemented.")
}
deinit {
NotificationCenter.default.removeObserver(self,
name: .AVCaptureSessionRuntimeError,
@ -141,11 +154,6 @@ public final class CameraView: UIView {
object: AVAudioSession.sharedInstance)
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) is not implemented.")
}
override public func removeFromSuperview() {
ReactLogger.log(level: .info, message: "Removing Camera View...")
captureSession.stopRunning()
@ -166,7 +174,7 @@ public final class CameraView: UIView {
let shouldUpdateZoom = willReconfigure || changedProps.contains("zoom") || shouldCheckActive
if shouldReconfigure || shouldCheckActive || shouldUpdateTorch || shouldUpdateZoom || shouldReconfigureFormat || shouldReconfigureDevice {
queue.async {
cameraQueue.async {
if shouldReconfigure {
self.configureCaptureSession()
}
@ -198,7 +206,7 @@ public final class CameraView: UIView {
}
// This is a wack workaround, but if I immediately set torch mode after `startRunning()`, the session isn't quite ready yet and will ignore torch.
self.queue.asyncAfter(deadline: .now() + 0.1) {
self.cameraQueue.asyncAfter(deadline: .now() + 0.1) {
if shouldUpdateTorch {
self.setTorchMode(self.torch)
}

View File

@ -7,7 +7,9 @@
//
#import <Foundation/Foundation.h>
#import <React/RCTViewManager.h>
#import <React/RCTUtils.h>
@interface RCT_EXTERN_REMAP_MODULE(CameraView, CameraViewManager, RCTViewManager)
@ -28,6 +30,7 @@ RCT_EXPORT_VIEW_PROPERTY(enablePortraitEffectsMatteDelivery, BOOL);
// device format
RCT_EXPORT_VIEW_PROPERTY(format, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(fps, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(frameProcessorFps, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(hdr, NSNumber); // nullable bool
RCT_EXPORT_VIEW_PROPERTY(lowLightBoost, NSNumber); // nullable bool
RCT_EXPORT_VIEW_PROPERTY(colorSpace, NSString);
@ -46,7 +49,4 @@ RCT_EXTERN_METHOD(stopRecording:(nonnull NSNumber *)node resolve:(RCTPromiseReso
RCT_EXTERN_METHOD(takePhoto:(nonnull NSNumber *)node options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(focus:(nonnull NSNumber *)node point:(NSDictionary *)point resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(getAvailableVideoCodecs:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(getAvailablePhotoCodecs:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
@end

View File

@ -11,6 +11,23 @@ import Foundation
@objc(CameraViewManager)
final class CameraViewManager: RCTViewManager {
// pragma MARK: Properties
private var runtimeManager: FrameProcessorRuntimeManager?
override var bridge: RCTBridge! {
didSet {
if !enableFrameProcessors { return }
CameraQueues.videoQueue.async {
self.runtimeManager = FrameProcessorRuntimeManager(bridge: self.bridge)
self.bridge.runOnJS {
self.runtimeManager!.installFrameProcessorBindings()
}
}
}
}
override var methodQueue: DispatchQueue! {
return DispatchQueue.main
}
@ -19,12 +36,12 @@ final class CameraViewManager: RCTViewManager {
return true
}
// pragma MARK: Setup
override final func view() -> UIView! {
return CameraView()
}
// pragma MARK: Exported Functions
// pragma MARK: React Functions
@objc
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
let component = getCameraView(withTag: node)
@ -53,29 +70,6 @@ final class CameraViewManager: RCTViewManager {
component.focus(point: CGPoint(x: x.doubleValue, y: y.doubleValue), promise: promise)
}
@objc
final func getAvailableVideoCodecs(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let component = getCameraView(withTag: node)
guard let movieOutput = component.movieOutput else {
throw CameraError.session(SessionError.cameraNotReady)
}
return movieOutput.availableVideoCodecTypes.map(\.descriptor)
}
}
@objc
final func getAvailablePhotoCodecs(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
let component = getCameraView(withTag: node)
guard let photoOutput = component.photoOutput else {
throw CameraError.session(SessionError.cameraNotReady)
}
return photoOutput.availablePhotoCodecTypes.map(\.descriptor)
}
}
// pragma MARK: View Manager funcs
@objc
final func getAvailableCameraDevices(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
withPromise(resolve: resolve, reject: reject) {
@ -103,7 +97,7 @@ final class CameraViewManager: RCTViewManager {
"supportsRawCapture": false, // TODO: supportsRawCapture
"supportsLowLightBoost": $0.isLowLightBoostSupported,
"supportsFocus": $0.isFocusPointOfInterestSupported,
"formats": $0.formats.map { (format) -> [String: Any] in
"formats": $0.formats.map { format -> [String: Any] in
format.toDictionary()
},
]

View File

@ -0,0 +1,30 @@
//
// AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift
// VisionCamera
//
// Created by Marc Rousavy on 05.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
import Foundation
extension AVAssetWriterInputPixelBufferAdaptor {
/**
Convenience initializer to extract correct attributes from the given videoSettings.
*/
convenience init(assetWriterInput: AVAssetWriterInput, withVideoSettings videoSettings: [String: Any]) {
var attributes: [String: Any] = [:]
if let width = videoSettings[AVVideoWidthKey] as? NSNumber,
let height = videoSettings[AVVideoHeightKey] as? NSNumber {
attributes[kCVPixelBufferWidthKey as String] = width as CFNumber
attributes[kCVPixelBufferHeightKey as String] = height as CFNumber
}
// TODO: Is "Bi-Planar Y'CbCr 8-bit 4:2:0 full-range" the best CVPixelFormatType? How can I find natively supported ones?
attributes[kCVPixelBufferPixelFormatTypeKey as String] = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
self.init(assetWriterInput: assetWriterInput, sourcePixelBufferAttributes: attributes)
}
}

View File

@ -70,8 +70,8 @@ extension AVCaptureDevice.Format {
}
}
if let frameRateRanges = filter.value(forKey: "frameRateRanges") as? [NSDictionary] {
let allFrameRateRangesIncluded = videoSupportedFrameRateRanges.allSatisfy { (range) -> Bool in
frameRateRanges.contains { (dict) -> Bool in
let allFrameRateRangesIncluded = videoSupportedFrameRateRanges.allSatisfy { range -> Bool in
frameRateRanges.contains { dict -> Bool in
guard let max = dict.value(forKey: "maxFrameRate") as? NSNumber,
let min = dict.value(forKey: "minFrameRate") as? NSNumber
else {

View File

@ -1,5 +1,5 @@
//
// AVCaptureMovieFileOutput+mirror.swift
// AVCaptureVideoDataOutput+mirror.swift
// Cuvent
//
// Created by Marc Rousavy on 18.01.21.
@ -8,7 +8,7 @@
import AVFoundation
extension AVCaptureMovieFileOutput {
extension AVCaptureVideoDataOutput {
func mirror() {
connections.forEach { connection in
if connection.isVideoMirroringSupported {
@ -16,4 +16,10 @@ extension AVCaptureMovieFileOutput {
}
}
}
var isMirrored: Bool {
return connections.contains { connection in
connection.isVideoMirrored
}
}
}

View File

@ -0,0 +1,20 @@
//
// Frame.h
// VisionCamera
//
// Created by Marc Rousavy on 15.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <CoreMedia/CMSampleBuffer.h>
// TODO: Make this Objective-C so it can be imported in Swift?
class Frame {
public:
explicit Frame(CMSampleBufferRef buffer): buffer(buffer) {}
public:
CMSampleBufferRef buffer;
};

View File

@ -0,0 +1,26 @@
//
// FrameHostObject.h
// VisionCamera
//
// Created by Marc Rousavy on 22.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import "Frame.h"
#import <jsi/jsi.h>
#import <CoreMedia/CMSampleBuffer.h>
using namespace facebook;
class JSI_EXPORT FrameHostObject: public Frame, public jsi::HostObject {
public:
explicit FrameHostObject(CMSampleBufferRef buffer): Frame(buffer) {}
~FrameHostObject();
public:
jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
void destroyBuffer();
};

View File

@ -0,0 +1,93 @@
//
// FrameHostObject.m
// VisionCamera
//
// Created by Marc Rousavy on 22.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import "FrameHostObject.h"
#import <Foundation/Foundation.h>
#import <jsi/jsi.h>
std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt) {
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("isReady")));
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("bytesPerRow")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("planesCount")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("buffer")));
return result;
}
jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
auto name = propName.utf8(runtime);
if (name == "Symbol.toPrimitive") {
// not implemented
return jsi::Value::undefined();
}
if (name == "valueOf") {
// not implemented
return jsi::Value::undefined();
}
if (name == "toString") {
auto toString = [this] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
auto imageBuffer = CMSampleBufferGetImageBuffer(buffer);
auto width = CVPixelBufferGetWidth(imageBuffer);
auto height = CVPixelBufferGetHeight(imageBuffer);
NSMutableString* string = [NSMutableString stringWithFormat:@"%lu x %lu Frame", width, height];
return jsi::String::createFromUtf8(runtime, string.UTF8String);
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
}
if (name == "isValid") {
auto isValid = buffer != nil && CMSampleBufferIsValid(buffer);
return jsi::Value(isValid);
}
if (name == "isReady") {
auto isReady = buffer != nil && CMSampleBufferDataIsReady(buffer);
return jsi::Value(isReady);
}
if (name == "width") {
auto imageBuffer = CMSampleBufferGetImageBuffer(buffer);
auto width = CVPixelBufferGetWidth(imageBuffer);
return jsi::Value((double) width);
}
if (name == "height") {
auto imageBuffer = CMSampleBufferGetImageBuffer(buffer);
auto height = CVPixelBufferGetHeight(imageBuffer);
return jsi::Value((double) height);
}
if (name == "bytesPerRow") {
auto imageBuffer = CMSampleBufferGetImageBuffer(buffer);
auto bytesPerRow = CVPixelBufferGetPlaneCount(imageBuffer);
return jsi::Value((double) bytesPerRow);
}
if (name == "planesCount") {
auto imageBuffer = CMSampleBufferGetImageBuffer(buffer);
auto planesCount = CVPixelBufferGetPlaneCount(imageBuffer);
return jsi::Value((double) planesCount);
}
if (name == "buffer") {
// TODO: Actually return the pixels of the buffer. Not sure if this will be a huge performance hit or not
return jsi::Array(runtime, 0);
}
return jsi::Value::undefined();
}
FrameHostObject::~FrameHostObject() {
destroyBuffer();
}
void FrameHostObject::destroyBuffer() {
// ARC will hopefully delete it lol
this->buffer = nil;
}

View File

@ -0,0 +1,14 @@
//
// FrameProcessorCallback.h
// VisionCamera
//
// Created by Marc Rousavy on 11.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <Foundation/Foundation.h>
#import <CoreMedia/CMSampleBuffer.h>
typedef void (^FrameProcessorCallback) (CMSampleBufferRef buffer);

View File

@ -0,0 +1,63 @@
//
// FrameProcessorPlugin.h
// VisionCamera
//
// Created by Marc Rousavy on 01.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#ifndef FrameProcessorPlugin_h
#define FrameProcessorPlugin_h
#import <Foundation/Foundation.h>
#import "FrameProcessorPluginRegistry.h"
#import <CoreMedia/CMSampleBuffer.h>
@protocol FrameProcessorPluginBase
+ (id) callback:(CMSampleBufferRef)buffer withArgs:(NSArray<id>*)args;
@end
#define VISION_CONCAT2(A, B) A##B
#define VISION_CONCAT(A, B) VISION_CONCAT2(A, B)
/**
* Use this Macro to register the given function as a Frame Processor.
* * Make sure the given function is a C-style function with the following signature: static inline id callback(CMSampleBufferRef buffer)
* * Make sure the given function's name is unique across other frame processor plugins
* * Make sure your frame processor returns a Value that can be converted to JS
* * Make sure to use this Macro in an @implementation, not @interface
*
* The JS function will have the same name as the given Objective-C function, but with a "__" prefix.
* Make sure to add that function to the babel.config.js under reanimated's "globals" option, and add TypeScript type declarations.
*/
#define VISION_EXPORT_FRAME_PROCESSOR(frame_processor) \
\
+(void)load \
{ \
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@"__" @ #frame_processor callback:^id(CMSampleBufferRef buffer, NSArray<id>* args) { \
return frame_processor(buffer, args); \
}]; \
}
/**
* Same as VISION_EXPORT_FRAME_PROCESSOR, but uses __attribute__((constructor)) for
* registration. Useful for registering swift classes that forbids use of +(void)load.
*/
#define VISION_EXPORT_SWIFT_FRAME_PROCESSOR(name, objc_name) \
objc_name : NSObject<FrameProcessorPluginBase> \
@end \
\
@interface objc_name (FrameProcessorPlugin) \
@end \
@implementation objc_name (FrameProcessorPlugin) \
\
__attribute__((constructor)) static void VISION_CONCAT(initialize_, objc_name)() \
{ \
[FrameProcessorPluginRegistry addFrameProcessorPlugin:@"__" @ #name callback:^id(CMSampleBufferRef buffer, NSArray<id>* args) { \
return [objc_name callback:buffer withArgs:args]; \
}]; \
}
#endif /* FrameProcessorPlugin_h */

View File

@ -0,0 +1,23 @@
//
// FrameProcessorPluginRegistry.h
// VisionCamera
//
// Created by Marc Rousavy on 24.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <Foundation/Foundation.h>
#import <CoreMedia/CMSampleBuffer.h>
typedef id (^FrameProcessorPlugin) (CMSampleBufferRef buffer, NSArray<id>* arguments);
@interface FrameProcessorPluginRegistry : NSObject
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins;
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback;
+ (void) markInvalid;
@end

View File

@ -0,0 +1,37 @@
//
// FrameProcessorPluginRegistry.mm
// VisionCamera
//
// Created by Marc Rousavy on 24.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import "FrameProcessorPluginRegistry.h"
#import <Foundation/Foundation.h>
@implementation FrameProcessorPluginRegistry
+ (NSMutableDictionary<NSString*, FrameProcessorPlugin>*)frameProcessorPlugins {
static NSMutableDictionary<NSString*, FrameProcessorPlugin>* plugins = nil;
if (plugins == nil) {
plugins = [[NSMutableDictionary alloc] init];
}
return plugins;
}
static BOOL _isValid = YES;
+ (void) markInvalid {
_isValid = NO;
[[FrameProcessorPluginRegistry frameProcessorPlugins] removeAllObjects];
}
+ (void) addFrameProcessorPlugin:(NSString*)name callback:(FrameProcessorPlugin)callback {
NSAssert(_isValid, @"Tried to add Frame Processor Plugin but Frame Processor Registry has already registered all plugins!");
BOOL alreadyExists = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:name] != nil;
NSAssert(!alreadyExists, @"Tried to two Frame Processor Plugins with the same name! Either choose unique names, or remove the unused plugin.");
[[FrameProcessorPluginRegistry frameProcessorPlugins] setValue:callback forKey:name];
}
@end

View File

@ -0,0 +1,26 @@
//
// FrameProcessorRuntimeManager.h
// VisionCamera
//
// Created by Marc Rousavy on 23.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
@interface FrameProcessorRuntimeManager : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
Initializes the Frame Processor Runtime Manager with the given bridge.
This init is not thread safe, so only init this on the Thread you want the runtime to run on.
*/
- (instancetype) initWithBridge:(RCTBridge*)bridge;
- (void) installFrameProcessorBindings;
@end

View File

@ -0,0 +1,184 @@
//
// FrameProcessorRuntimeManager.m
// VisionCamera
//
// Created by Marc Rousavy on 23.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "FrameProcessorRuntimeManager.h"
#import "FrameProcessorPluginRegistry.h"
#import "FrameHostObject.h"
#import <memory>
#import <React/RCTBridge.h>
#import <ReactCommon/RCTTurboModule.h>
#import <React/RCTBridge+Private.h>
#import <React/RCTUIManager.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#if __has_include(<RNReanimated/NativeReanimatedModule.h>)
#if __has_include(<RNReanimated/RuntimeManager.h>)
#ifndef VISION_CAMERA_DISABLE_FRAME_PROCESSORS
#import <RNReanimated/RuntimeManager.h>
#import <RNReanimated/RuntimeDecorator.h>
#import <RNReanimated/REAIOSScheduler.h>
#import <RNReanimated/REAIOSErrorHandler.h>
#define ENABLE_FRAME_PROCESSORS
#endif
#else
#warning Your react-native-reanimated version is not compatible with VisionCamera, Frame Processors are disabled. Make sure you're using reanimated 2.1.0 or above!
#endif
#else
#warning The NativeReanimatedModule.h header could not be found, Frame Processors are disabled. If you want to use Frame Processors, make sure you install react-native-reanimated!
#endif
#import "../../cpp/MakeJSIRuntime.h"
#import "FrameProcessorUtils.h"
#import "FrameProcessorCallback.h"
#import "../React Utils/JSIUtils.h"
// Forward declarations for the Swift classes
__attribute__((objc_runtime_name("_TtC12VisionCamera12CameraQueues")))
@interface CameraQueues : NSObject
@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull videoQueue;
@end
__attribute__((objc_runtime_name("_TtC12VisionCamera10CameraView")))
@interface CameraView : UIView
@property (nonatomic, copy) FrameProcessorCallback _Nullable frameProcessorCallback;
@end
@implementation FrameProcessorRuntimeManager {
#ifdef ENABLE_FRAME_PROCESSORS
std::unique_ptr<reanimated::RuntimeManager> runtimeManager;
#endif
__weak RCTBridge* weakBridge;
}
- (instancetype) initWithBridge:(RCTBridge*)bridge {
self = [super init];
if (self) {
#ifdef ENABLE_FRAME_PROCESSORS
NSLog(@"FrameProcessorBindings: Creating Runtime Manager...");
weakBridge = bridge;
auto runtime = vision::makeJSIRuntime();
reanimated::RuntimeDecorator::decorateRuntime(*runtime, "FRAME_PROCESSOR");
runtime->global().setProperty(*runtime, "_FRAME_PROCESSOR", jsi::Value(true));
auto callInvoker = bridge.jsCallInvoker;
auto scheduler = std::make_shared<reanimated::REAIOSScheduler>(callInvoker);
runtimeManager = std::make_unique<reanimated::RuntimeManager>(std::move(runtime),
std::make_shared<reanimated::REAIOSErrorHandler>(scheduler),
scheduler);
NSLog(@"FrameProcessorBindings: Runtime Manager created!");
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugins...");
auto& visionRuntime = *runtimeManager->runtime;
auto visionGlobal = visionRuntime.global();
for (NSString* pluginKey in [FrameProcessorPluginRegistry frameProcessorPlugins]) {
auto pluginName = [pluginKey UTF8String];
NSLog(@"FrameProcessorBindings: Installing Frame Processor plugin \"%s\"...", pluginName);
FrameProcessorPlugin callback = [[FrameProcessorPluginRegistry frameProcessorPlugins] valueForKey:pluginKey];
auto function = [callback, callInvoker](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
auto frameHostObject = arguments[0].asObject(runtime).asHostObject(runtime);
auto frame = static_cast<FrameHostObject*>(frameHostObject.get());
auto args = convertJSICStyleArrayToNSArray(runtime,
arguments + 1, // start at index 1 since first arg = Frame
count - 1, // use smaller count
callInvoker);
id result = callback(frame->buffer, args);
return convertObjCObjectToJSIValue(runtime, result);
};
visionGlobal.setProperty(visionRuntime, pluginName, jsi::Function::createFromHostFunction(visionRuntime,
jsi::PropNameID::forAscii(visionRuntime, pluginName),
1, // frame
function));
}
[FrameProcessorPluginRegistry markInvalid];
NSLog(@"FrameProcessorBindings: Frame Processor plugins installed!");
#else
NSLog(@"Reanimated not found, Frame Processors are disabled.");
#endif
}
return self;
}
- (void) installFrameProcessorBindings {
#ifdef ENABLE_FRAME_PROCESSORS
if (!weakBridge) {
NSLog(@"FrameProcessorBindings: Failed to install Frame Processor Bindings - bridge was null!");
return;
}
NSLog(@"FrameProcessorBindings: Installing Frame Processor Bindings for Bridge...");
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)weakBridge;
if (!cxxBridge.runtime) {
return;
}
jsi::Runtime& jsiRuntime = *(jsi::Runtime*)cxxBridge.runtime;
NSLog(@"FrameProcessorBindings: Installing global functions...");
// setFrameProcessor(viewTag: number, frameProcessor: (frame: Frame) => void)
auto setFrameProcessor = [self](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
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!");
if (!runtimeManager || !runtimeManager->runtime) throw jsi::JSError(runtime, "Camera::setFrameProcessor: The RuntimeManager is not yet initialized!");
auto viewTag = arguments[0].asNumber();
NSLog(@"FrameProcessorBindings: Adapting Shareable value from function (conversion to worklet)...");
auto worklet = reanimated::ShareableValue::adapt(runtime, arguments[1], runtimeManager.get());
NSLog(@"FrameProcessorBindings: Successfully created worklet!");
RCTExecuteOnMainQueue([worklet, viewTag, self]() -> void {
auto currentBridge = [RCTBridge currentBridge];
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
auto view = static_cast<CameraView*>(anonymousView);
dispatch_async(CameraQueues.videoQueue, [worklet, view, self]() -> void {
NSLog(@"FrameProcessorBindings: Converting worklet to Objective-C callback...");
auto& rt = *runtimeManager->runtime;
auto function = worklet->getValue(rt).asObject(rt).asFunction(rt);
view.frameProcessorCallback = convertJSIFunctionToFrameProcessorCallback(rt, function);
NSLog(@"FrameProcessorBindings: Frame processor set!");
});
});
return jsi::Value::undefined();
};
jsiRuntime.global().setProperty(jsiRuntime, "setFrameProcessor", jsi::Function::createFromHostFunction(jsiRuntime,
jsi::PropNameID::forAscii(jsiRuntime, "setFrameProcessor"),
2, // viewTag, frameProcessor
setFrameProcessor));
// unsetFrameProcessor(viewTag: number)
auto unsetFrameProcessor = [](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
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();
RCTExecuteOnMainQueue(^{
auto currentBridge = [RCTBridge currentBridge];
if (!currentBridge) return;
auto anonymousView = [currentBridge.uiManager viewForReactTag:[NSNumber numberWithDouble:viewTag]];
if (!anonymousView) return;
auto view = static_cast<CameraView*>(anonymousView);
view.frameProcessorCallback = nil;
NSLog(@"FrameProcessorBindings: Frame processor removed!");
});
return jsi::Value::undefined();
};
jsiRuntime.global().setProperty(jsiRuntime, "unsetFrameProcessor", jsi::Function::createFromHostFunction(jsiRuntime,
jsi::PropNameID::forAscii(jsiRuntime, "unsetFrameProcessor"),
1, // viewTag
unsetFrameProcessor));
NSLog(@"FrameProcessorBindings: Finished installing bindings.");
#endif
}
@end

View File

@ -0,0 +1,23 @@
//
// FrameProcessorUtils.h
// VisionCamera
//
// Created by Marc Rousavy on 15.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "FrameProcessorCallback.h"
#ifndef __cplusplus
#error FrameProcessorUtils.h has to be compiled with C++!
#endif
#import <jsi/jsi.h>
using namespace facebook;
FrameProcessorCallback convertJSIFunctionToFrameProcessorCallback(jsi::Runtime &runtime, const jsi::Function &value);

View File

@ -0,0 +1,44 @@
//
// FrameProcessorUtils.m
// VisionCamera
//
// Created by Marc Rousavy on 15.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import "FrameProcessorUtils.h"
#import <CoreMedia/CMSampleBuffer.h>
#import <chrono>
#import <memory>
#import "FrameHostObject.h"
FrameProcessorCallback convertJSIFunctionToFrameProcessorCallback(jsi::Runtime &runtime, const jsi::Function &value) {
__block auto cb = value.getFunction(runtime);
return ^(CMSampleBufferRef buffer) {
#if DEBUG
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
#endif
auto frame = std::make_shared<FrameHostObject>(buffer);
try {
cb.call(runtime, jsi::Object::createFromHostObject(runtime, frame));
} catch (jsi::JSError& jsError) {
NSLog(@"Frame Processor threw an error: %s", jsError.getMessage().c_str());
}
#if DEBUG
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
if (duration > 100) {
NSLog(@"Warning: Frame Processor function took %lld ms to execute. This blocks the video queue from recording, optimize your frame processor!", duration);
}
#endif
// 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)
frame->destroyBuffer();
};
}

View File

@ -0,0 +1,28 @@
//
// AVAssetWriter.Status+descriptor.swift
// VisionCamera
//
// Created by Marc Rousavy on 01.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
extension AVAssetWriter.Status {
var descriptor: String {
switch self {
case .cancelled:
return "cancelled"
case .completed:
return "completed"
case .failed:
return "failed"
case .unknown:
return "unknown"
case .writing:
return "writing"
@unknown default:
fatalError("Unknown AVAssetWriter.Status value! \(rawValue)")
}
}
}

View File

@ -0,0 +1,20 @@
//
// AVFileType+descriptor.swift
// VisionCamera
//
// Created by Marc Rousavy on 01.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
import Foundation
extension AVFileType {
init(withString string: String) {
self.init(rawValue: string)
}
var descriptor: String {
return rawValue
}
}

View File

@ -0,0 +1,55 @@
//
// JSIUtils.h
// VisionCamera
//
// Created by Marc Rousavy on 30.04.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <jsi/jsi.h>
#import <ReactCommon/CallInvoker.h>
#import <React/RCTBridgeModule.h>
using namespace facebook;
using namespace facebook::react;
// NSNumber -> boolean
jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime& runtime, NSNumber* value);
// NSNumber -> number
jsi::Value convertNSNumberToJSINumber(jsi::Runtime& runtime, NSNumber* value);
// NSNumber -> string
jsi::String convertNSStringToJSIString(jsi::Runtime& runtime, NSString* value);
// NSDictionary -> {}
jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime& runtime, NSDictionary* value);
// NSArray -> []
jsi::Array convertNSArrayToJSIArray(jsi::Runtime& runtime, NSArray* value);
// id -> ???
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime& runtime, id value);
// string -> NSString
NSString* convertJSIStringToNSString(jsi::Runtime& runtime, const jsi::String& value);
// any... -> NSArray
NSArray* convertJSICStyleArrayToNSArray(jsi::Runtime& runtime, const jsi::Value* array, size_t length, std::shared_ptr<CallInvoker> jsInvoker);
// NSArray -> any...
jsi::Value* convertNSArrayToJSICStyleArray(jsi::Runtime& runtime, NSArray* array);
// [] -> NSArray
NSArray* convertJSIArrayToNSArray(jsi::Runtime& runtime, const jsi::Array& value, std::shared_ptr<CallInvoker> jsInvoker);
// {} -> NSDictionary
NSDictionary* convertJSIObjectToNSDictionary(jsi::Runtime& runtime, const jsi::Object& value, std::shared_ptr<CallInvoker> jsInvoker);
// any -> id
id convertJSIValueToObjCObject(jsi::Runtime& runtime, const jsi::Value& value, std::shared_ptr<CallInvoker> jsInvoker);
// (any...) => any -> (void)(id, id)
RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime& runtime, const jsi::Function& value, std::shared_ptr<CallInvoker> jsInvoker);

187
ios/React Utils/JSIUtils.mm Normal file
View File

@ -0,0 +1,187 @@
//
// JSIUtils.mm
// VisionCamera
//
// Created by Marc Rousavy on 02.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import "JSIUtils.h"
#import <Foundation/Foundation.h>
#import <jsi/jsi.h>
#import <ReactCommon/CallInvoker.h>
#import <React/RCTBridge.h>
#import <ReactCommon/TurboModuleUtils.h>
using namespace facebook;
using namespace facebook::react;
jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime &runtime, NSNumber *value)
{
return jsi::Value((bool)[value boolValue]);
}
jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *value)
{
return jsi::Value([value doubleValue]);
}
jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value)
{
return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: "");
}
jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value)
{
jsi::Object result = jsi::Object(runtime);
for (NSString *k in value) {
result.setProperty(runtime, [k UTF8String], convertObjCObjectToJSIValue(runtime, value[k]));
}
return result;
}
jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value)
{
jsi::Array result = jsi::Array(runtime, value.count);
for (size_t i = 0; i < value.count; i++) {
result.setValueAtIndex(runtime, i, convertObjCObjectToJSIValue(runtime, value[i]));
}
return result;
}
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value)
{
if (value == nil) {
return jsi::Value::undefined();
} else if ([value isKindOfClass:[NSString class]]) {
return convertNSStringToJSIString(runtime, (NSString *)value);
} else if ([value isKindOfClass:[NSNumber class]]) {
if ([value isKindOfClass:[@YES class]]) {
return convertNSNumberToJSIBoolean(runtime, (NSNumber *)value);
}
return convertNSNumberToJSINumber(runtime, (NSNumber *)value);
} else if ([value isKindOfClass:[NSDictionary class]]) {
return convertNSDictionaryToJSIObject(runtime, (NSDictionary *)value);
} else if ([value isKindOfClass:[NSArray class]]) {
return convertNSArrayToJSIArray(runtime, (NSArray *)value);
} else if (value == (id)kCFNull) {
return jsi::Value::null();
}
return jsi::Value::undefined();
}
NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value)
{
return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
}
NSArray* convertJSICStyleArrayToNSArray(jsi::Runtime &runtime, const jsi::Value* array, size_t length, std::shared_ptr<CallInvoker> jsInvoker) {
if (length == 0) return @[];
NSMutableArray *result = [NSMutableArray new];
for (size_t i = 0; i < length; i++) {
// Insert kCFNull when it's `undefined` value to preserve the indices.
[result
addObject:convertJSIValueToObjCObject(runtime, array[i], jsInvoker) ?: (id)kCFNull];
}
return [result copy];
}
jsi::Value* convertNSArrayToJSICStyleArray(jsi::Runtime &runtime, NSArray* array) {
auto result = new jsi::Value[array.count];
for (size_t i = 0; i < array.count; i++) {
result[i] = convertObjCObjectToJSIValue(runtime, array[i]);
}
return result;
}
NSArray* convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker)
{
size_t size = value.size(runtime);
NSMutableArray *result = [NSMutableArray new];
for (size_t i = 0; i < size; i++) {
// Insert kCFNull when it's `undefined` value to preserve the indices.
[result
addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker) ?: (id)kCFNull];
}
return [result copy];
}
NSDictionary* convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker)
{
jsi::Array propertyNames = value.getPropertyNames(runtime);
size_t size = propertyNames.size(runtime);
NSMutableDictionary *result = [NSMutableDictionary new];
for (size_t i = 0; i < size; i++) {
jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
NSString *k = convertJSIStringToNSString(runtime, name);
id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker);
if (v) {
result[k] = v;
}
}
return [result copy];
}
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
{
if (value.isUndefined() || value.isNull()) {
return nil;
}
if (value.isBool()) {
return @(value.getBool());
}
if (value.isNumber()) {
return @(value.getNumber());
}
if (value.isString()) {
return convertJSIStringToNSString(runtime, value.getString(runtime));
}
if (value.isObject()) {
jsi::Object o = value.getObject(runtime);
if (o.isArray(runtime)) {
return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker);
}
if (o.isFunction(runtime)) {
return convertJSIFunctionToCallback(runtime, std::move(o.getFunction(runtime)), jsInvoker);
}
return convertJSIObjectToNSDictionary(runtime, o, jsInvoker);
}
throw std::runtime_error("Unsupported jsi::jsi::Value kind");
}
RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr<CallInvoker> jsInvoker)
{
auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker);
BOOL __block wrapperWasCalled = NO;
RCTResponseSenderBlock callback = ^(NSArray *responses) {
if (wrapperWasCalled) {
throw std::runtime_error("callback arg cannot be called more than once");
}
auto strongWrapper = weakWrapper.lock();
if (!strongWrapper) {
return;
}
strongWrapper->jsInvoker().invokeAsync([weakWrapper, responses]() {
auto strongWrapper2 = weakWrapper.lock();
if (!strongWrapper2) {
return;
}
const jsi::Value* args = convertNSArrayToJSICStyleArray(strongWrapper2->runtime(), responses);
strongWrapper2->callback().call(strongWrapper2->runtime(), args, static_cast<size_t>(responses.count));
strongWrapper2->destroy();
delete[] args;
});
wrapperWasCalled = YES;
};
if (RCTTurboModuleBlockCopyEnabled()) {
return [callback copy];
}
return callback;
}

View File

@ -0,0 +1,18 @@
//
// RCTBridge+runOnJS.h
// VisionCamera
//
// Created by Marc Rousavy on 23.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#pragma once
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
@interface RCTBridge (RunOnJS)
- (void) runOnJS:(void (^)(void))block NS_SWIFT_NAME( runOnJS(_:) );
@end

View File

@ -0,0 +1,23 @@
//
// RCTBridge+runOnJS.mm
// VisionCamera
//
// Created by Marc Rousavy on 23.03.21.
// Copyright © 2021 Facebook. All rights reserved.
//
#import "RCTBridge+runOnJS.h"
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
#import <ReactCommon/RCTTurboModule.h>
@implementation RCTBridge (RunOnJS)
- (void) runOnJS:(void (^)())block {
auto callInvoker = [self jsCallInvoker];
callInvoker->invokeAsync([block]() {
block();
});
}
@end

135
ios/RecordingSession.swift Normal file
View File

@ -0,0 +1,135 @@
//
// RecordingSession.swift
// VisionCamera
//
// Created by Marc Rousavy on 01.05.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
import Foundation
// MARK: - BufferType
enum BufferType {
case audio
case video
}
// MARK: - RecordingSession
class RecordingSession {
private let assetWriter: AVAssetWriter
private let audioWriter: AVAssetWriterInput
private let videoWriter: AVAssetWriterInput
private let bufferAdaptor: AVAssetWriterInputPixelBufferAdaptor
private let completionHandler: (AVAssetWriter.Status, Error?) -> Void
private let initialTimestamp: CMTime
private var latestTimestamp: CMTime?
private var hasWrittenFirstVideoFrame = false
var url: URL {
return assetWriter.outputURL
}
var duration: Double {
guard let latestTimestamp = latestTimestamp else {
return 0.0
}
return (latestTimestamp - initialTimestamp).seconds
}
init(url: URL,
fileType: AVFileType,
videoSettings: [String: Any],
audioSettings: [String: Any],
isVideoMirrored: Bool,
completion: @escaping (AVAssetWriter.Status, Error?) -> Void) throws {
do {
assetWriter = try AVAssetWriter(outputURL: url, fileType: fileType)
audioWriter = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
videoWriter = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
completionHandler = completion
} catch let error as NSError {
throw CameraError.capture(.createRecorderError(message: error.description))
}
audioWriter.expectsMediaDataInRealTime = true
videoWriter.expectsMediaDataInRealTime = true
if isVideoMirrored {
videoWriter.transform = CGAffineTransform(rotationAngle: -(.pi / 2))
} else {
videoWriter.transform = CGAffineTransform(rotationAngle: .pi / 2)
}
bufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriter, withVideoSettings: videoSettings)
assetWriter.add(videoWriter)
assetWriter.add(audioWriter)
assetWriter.startWriting()
initialTimestamp = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: 1_000_000_000)
assetWriter.startSession(atSourceTime: initialTimestamp)
ReactLogger.log(level: .info, message: "Initialized Video and Audio AssetWriter.")
}
deinit {
if assetWriter.status == .writing {
ReactLogger.log(level: .info, message: "Cancelling AssetWriter...")
assetWriter.cancelWriting()
}
}
func appendBuffer(_ buffer: CMSampleBuffer, type bufferType: BufferType) {
if !CMSampleBufferDataIsReady(buffer) {
return
}
let timestamp = CMSampleBufferGetPresentationTimeStamp(buffer)
latestTimestamp = timestamp
switch bufferType {
case .video:
if !videoWriter.isReadyForMoreMediaData {
ReactLogger.log(level: .warning, message: "The Video AVAssetWriterInput was not ready for more data! Is your frame rate too high?")
return
}
guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
ReactLogger.log(level: .error, message: "Failed to get the CVImageBuffer!")
return
}
bufferAdaptor.append(imageBuffer, withPresentationTime: timestamp)
if !hasWrittenFirstVideoFrame {
hasWrittenFirstVideoFrame = true
ReactLogger.log(level: .warning, message: "VideoWriter: First frame arrived \((timestamp - initialTimestamp).seconds) seconds late.")
}
case .audio:
if !audioWriter.isReadyForMoreMediaData {
return
}
if !hasWrittenFirstVideoFrame {
// first video frame has not been written yet, so skip this audio frame.
return
}
audioWriter.append(buffer)
}
if assetWriter.status == .failed {
// TODO: Should I call the completion handler or is this instance still valid?
ReactLogger.log(level: .error, message: "AssetWriter failed to write buffer! Error: \(assetWriter.error?.localizedDescription ?? "none")")
}
}
func finish() {
ReactLogger.log(level: .info, message: "Finishing Recording with AssetWriter status \"\(assetWriter.status.descriptor)\"...")
if assetWriter.status == .writing {
videoWriter.markAsFinished()
assetWriter.finishWriting {
self.completionHandler(self.assetWriter.status, self.assetWriter.error)
}
} else {
completionHandler(assetWriter.status, assetWriter.error)
}
}
}

View File

@ -1,44 +0,0 @@
//
// VideoCaptureDelegate.swift
// Cuvent
//
// Created by Marc Rousavy on 14.01.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
// Functions like `startRecording(delegate: ...)` only maintain a weak reference on the delegates to prevent memory leaks.
// In our use case, we exit from the function which will deinit our recording delegate since no other references are being held.
// That's why we're keeping a strong reference to the delegate by appending it to the `delegateReferences` list and removing it
// once the delegate has been triggered once.
private var delegateReferences: [NSObject] = []
// MARK: - RecordingDelegateWithCallback
class RecordingDelegateWithCallback: NSObject, AVCaptureFileOutputRecordingDelegate {
init(callback: @escaping RCTResponseSenderBlock, resetTorchMode: @escaping () -> Void) {
self.callback = callback
self.resetTorchMode = resetTorchMode
super.init()
delegateReferences.append(self)
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from _: [AVCaptureConnection], error: Error?) {
defer {
self.resetTorchMode()
delegateReferences.removeAll(where: { $0 == self })
}
if let error = error as NSError? {
return callback([NSNull(), makeReactError(.capture(.unknown(message: error.description)), cause: error)])
}
let seconds = CMTimeGetSeconds(output.recordedDuration)
return callback([["path": outputFileURL.absoluteString, "duration": seconds, "size": output.recordedFileSize], NSNull()])
}
// MARK: Private
private let callback: RCTResponseSenderBlock // (video?, error?) => void
private let resetTorchMode: () -> Void
}

View File

@ -7,13 +7,17 @@
objects = {
/* Begin PBXBuildFile section */
B80C0E00260BDDF7001699AB /* FrameProcessorPluginRegistry.mm in Sources */ = {isa = PBXBuildFile; fileRef = B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */; };
B8103E1C25FF553B007A1684 /* FrameProcessorUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8103E1B25FF553B007A1684 /* FrameProcessorUtils.mm */; };
B82FBA962614B69D00909718 /* RCTBridge+runOnJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = B82FBA952614B69D00909718 /* RCTBridge+runOnJS.mm */; };
B84760A62608EE7C004C3180 /* FrameHostObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = B84760A52608EE7C004C3180 /* FrameHostObject.mm */; };
B84760DF2608F57D004C3180 /* CameraQueues.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84760DE2608F57D004C3180 /* CameraQueues.swift */; };
B86DC971260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */; };
B86DC974260E310600FB17B2 /* CameraView+AVAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */; };
B86DC977260E315100FB17B2 /* CameraView+AVCaptureSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */; };
B887518525E0102000DB86D6 /* PhotoCaptureDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887515C25E0102000DB86D6 /* PhotoCaptureDelegate.swift */; };
B887518625E0102000DB86D6 /* CameraView+RecordVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887515D25E0102000DB86D6 /* CameraView+RecordVideo.swift */; };
B887518725E0102000DB86D6 /* CameraViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B887515F25E0102000DB86D6 /* CameraViewManager.m */; };
B887518825E0102000DB86D6 /* VideoCaptureDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516025E0102000DB86D6 /* VideoCaptureDelegate.swift */; };
B887518925E0102000DB86D6 /* Collection+safe.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516225E0102000DB86D6 /* Collection+safe.swift */; };
B887518A25E0102000DB86D6 /* AVCaptureDevice+neutralZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516325E0102000DB86D6 /* AVCaptureDevice+neutralZoom.swift */; };
B887518B25E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516425E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift */; };
@ -23,7 +27,7 @@
B887518F25E0102000DB86D6 /* AVCapturePhotoOutput+mirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516825E0102000DB86D6 /* AVCapturePhotoOutput+mirror.swift */; };
B887519025E0102000DB86D6 /* AVCaptureDevice.Format+matchesFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516925E0102000DB86D6 /* AVCaptureDevice.Format+matchesFilter.swift */; };
B887519125E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516A25E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift */; };
B887519225E0102000DB86D6 /* AVCaptureMovieFileOutput+mirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516B25E0102000DB86D6 /* AVCaptureMovieFileOutput+mirror.swift */; };
B887519225E0102000DB86D6 /* AVCaptureVideoDataOutput+mirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516B25E0102000DB86D6 /* AVCaptureVideoDataOutput+mirror.swift */; };
B887519425E0102000DB86D6 /* MakeReactError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516E25E0102000DB86D6 /* MakeReactError.swift */; };
B887519525E0102000DB86D6 /* ReactLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887516F25E0102000DB86D6 /* ReactLogger.swift */; };
B887519625E0102000DB86D6 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517025E0102000DB86D6 /* Promise.swift */; };
@ -45,6 +49,12 @@
B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518225E0102000DB86D6 /* CameraView+Zoom.swift */; };
B88751A825E0102000DB86D6 /* CameraError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518325E0102000DB86D6 /* CameraError.swift */; };
B88751A925E0102000DB86D6 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518425E0102000DB86D6 /* CameraView.swift */; };
B8994E6C263F03E100069589 /* JSIUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8994E6B263F03E100069589 /* JSIUtils.mm */; };
B8A751D82609E4B30011C623 /* FrameProcessorRuntimeManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8A751D72609E4B30011C623 /* FrameProcessorRuntimeManager.mm */; };
B8D22CDC2642DB4D00234472 /* AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D22CDB2642DB4D00234472 /* AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift */; };
B8DB3BC8263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BC7263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift */; };
B8DB3BCA263DC4D8004C18D7 /* RecordingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BC9263DC4D8004C18D7 /* RecordingSession.swift */; };
B8DB3BCC263DC97E004C18D7 /* AVFileType+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8DB3BCB263DC97E004C18D7 /* AVFileType+descriptor.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -61,6 +71,18 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libVisionCamera.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libVisionCamera.a; sourceTree = BUILT_PRODUCTS_DIR; };
B80C0DFE260BDD97001699AB /* FrameProcessorPluginRegistry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPluginRegistry.h; sourceTree = "<group>"; };
B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameProcessorPluginRegistry.mm; sourceTree = "<group>"; };
B80D67A825FA25380008FE8D /* FrameProcessorCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorCallback.h; sourceTree = "<group>"; };
B8103E1B25FF553B007A1684 /* FrameProcessorUtils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameProcessorUtils.mm; sourceTree = "<group>"; };
B8103E1E25FF5550007A1684 /* FrameProcessorUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorUtils.h; sourceTree = "<group>"; };
B8103E5725FF56F0007A1684 /* Frame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Frame.h; sourceTree = "<group>"; };
B81D41EF263C86F900B041FD /* JSIUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSIUtils.h; sourceTree = "<group>"; };
B82FBA942614B69D00909718 /* RCTBridge+runOnJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTBridge+runOnJS.h"; sourceTree = "<group>"; };
B82FBA952614B69D00909718 /* RCTBridge+runOnJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "RCTBridge+runOnJS.mm"; sourceTree = "<group>"; };
B84760A22608EE38004C3180 /* FrameHostObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameHostObject.h; sourceTree = "<group>"; };
B84760A52608EE7C004C3180 /* FrameHostObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameHostObject.mm; sourceTree = "<group>"; };
B84760DE2608F57D004C3180 /* CameraQueues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraQueues.swift; sourceTree = "<group>"; };
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAudioSession+trySetAllowHaptics.swift"; sourceTree = "<group>"; };
B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVAudioSession.swift"; sourceTree = "<group>"; };
B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVCaptureSession.swift"; sourceTree = "<group>"; };
@ -68,7 +90,6 @@
B887515D25E0102000DB86D6 /* CameraView+RecordVideo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CameraView+RecordVideo.swift"; sourceTree = "<group>"; };
B887515E25E0102000DB86D6 /* CameraBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraBridge.h; sourceTree = "<group>"; };
B887515F25E0102000DB86D6 /* CameraViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraViewManager.m; sourceTree = "<group>"; };
B887516025E0102000DB86D6 /* VideoCaptureDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCaptureDelegate.swift; sourceTree = "<group>"; };
B887516225E0102000DB86D6 /* Collection+safe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+safe.swift"; sourceTree = "<group>"; };
B887516325E0102000DB86D6 /* AVCaptureDevice+neutralZoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice+neutralZoom.swift"; sourceTree = "<group>"; };
B887516425E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice.Format+isBetterThan.swift"; sourceTree = "<group>"; };
@ -78,7 +99,7 @@
B887516825E0102000DB86D6 /* AVCapturePhotoOutput+mirror.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCapturePhotoOutput+mirror.swift"; sourceTree = "<group>"; };
B887516925E0102000DB86D6 /* AVCaptureDevice.Format+matchesFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice.Format+matchesFilter.swift"; sourceTree = "<group>"; };
B887516A25E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice.Format+toDictionary.swift"; sourceTree = "<group>"; };
B887516B25E0102000DB86D6 /* AVCaptureMovieFileOutput+mirror.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureMovieFileOutput+mirror.swift"; sourceTree = "<group>"; };
B887516B25E0102000DB86D6 /* AVCaptureVideoDataOutput+mirror.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoDataOutput+mirror.swift"; sourceTree = "<group>"; };
B887516E25E0102000DB86D6 /* MakeReactError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MakeReactError.swift; sourceTree = "<group>"; };
B887516F25E0102000DB86D6 /* ReactLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactLogger.swift; sourceTree = "<group>"; };
B887517025E0102000DB86D6 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = "<group>"; };
@ -100,6 +121,16 @@
B887518225E0102000DB86D6 /* CameraView+Zoom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CameraView+Zoom.swift"; sourceTree = "<group>"; };
B887518325E0102000DB86D6 /* CameraError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraError.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>"; };
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>"; };
B8A751D72609E4B30011C623 /* FrameProcessorRuntimeManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameProcessorRuntimeManager.mm; sourceTree = "<group>"; };
B8D22CDB2642DB4D00234472 /* AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift"; sourceTree = "<group>"; };
B8DB3BC7263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAssetWriter.Status+descriptor.swift"; sourceTree = "<group>"; };
B8DB3BC9263DC4D8004C18D7 /* RecordingSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingSession.swift; sourceTree = "<group>"; };
B8DB3BCB263DC97E004C18D7 /* AVFileType+descriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVFileType+descriptor.swift"; sourceTree = "<group>"; };
B8DCF09125EA7BEE00EA5C72 /* SpeedChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpeedChecker.h; sourceTree = "<group>"; };
B8DCF14425EA817D00EA5C72 /* MakeJSIRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakeJSIRuntime.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -124,7 +155,9 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
B8DCF2D725EA940700EA5C72 /* Frame Processor */,
B887515E25E0102000DB86D6 /* CameraBridge.h */,
B84760DE2608F57D004C3180 /* CameraQueues.swift */,
B887518325E0102000DB86D6 /* CameraError.swift */,
B887518425E0102000DB86D6 /* CameraView.swift */,
B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */,
@ -135,11 +168,12 @@
B887518225E0102000DB86D6 /* CameraView+Zoom.swift */,
B887515F25E0102000DB86D6 /* CameraViewManager.m */,
B887518125E0102000DB86D6 /* CameraViewManager.swift */,
B8DB3BC9263DC4D8004C18D7 /* RecordingSession.swift */,
B8DCF08F25EA7BEE00EA5C72 /* cpp */,
B887516125E0102000DB86D6 /* Extensions */,
B887517225E0102000DB86D6 /* Parsers */,
B887515C25E0102000DB86D6 /* PhotoCaptureDelegate.swift */,
B887516D25E0102000DB86D6 /* React Utils */,
B887516025E0102000DB86D6 /* VideoCaptureDelegate.swift */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
@ -147,6 +181,7 @@
B887516125E0102000DB86D6 /* Extensions */ = {
isa = PBXGroup;
children = (
B8D22CDB2642DB4D00234472 /* AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift */,
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */,
B887516325E0102000DB86D6 /* AVCaptureDevice+neutralZoom.swift */,
B887516425E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift */,
@ -156,7 +191,7 @@
B887516825E0102000DB86D6 /* AVCapturePhotoOutput+mirror.swift */,
B887516925E0102000DB86D6 /* AVCaptureDevice.Format+matchesFilter.swift */,
B887516A25E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift */,
B887516B25E0102000DB86D6 /* AVCaptureMovieFileOutput+mirror.swift */,
B887516B25E0102000DB86D6 /* AVCaptureVideoDataOutput+mirror.swift */,
B887516225E0102000DB86D6 /* Collection+safe.swift */,
);
path = Extensions;
@ -168,6 +203,10 @@
B887516E25E0102000DB86D6 /* MakeReactError.swift */,
B887516F25E0102000DB86D6 /* ReactLogger.swift */,
B887517025E0102000DB86D6 /* Promise.swift */,
B82FBA942614B69D00909718 /* RCTBridge+runOnJS.h */,
B82FBA952614B69D00909718 /* RCTBridge+runOnJS.mm */,
B81D41EF263C86F900B041FD /* JSIUtils.h */,
B8994E6B263F03E100069589 /* JSIUtils.mm */,
);
path = "React Utils";
sourceTree = "<group>";
@ -176,6 +215,7 @@
isa = PBXGroup;
children = (
B887517325E0102000DB86D6 /* EnumParserError.swift */,
B8DB3BC7263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift */,
B887517425E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift */,
B887517525E0102000DB86D6 /* AVVideoCodecType+descriptor.swift */,
B887517625E0102000DB86D6 /* AVCaptureSession.Preset+descriptor.swift */,
@ -187,10 +227,39 @@
B887517D25E0102000DB86D6 /* AVCaptureColorSpace+descriptor.swift */,
B887517E25E0102000DB86D6 /* AVCaptureDevice.FlashMode+descriptor.swift */,
B887517F25E0102000DB86D6 /* AVCaptureDevice.Format.AutoFocusSystem+descriptor.swift */,
B8DB3BCB263DC97E004C18D7 /* AVFileType+descriptor.swift */,
);
path = Parsers;
sourceTree = "<group>";
};
B8DCF08F25EA7BEE00EA5C72 /* cpp */ = {
isa = PBXGroup;
children = (
B8DCF14425EA817D00EA5C72 /* MakeJSIRuntime.h */,
B8DCF09125EA7BEE00EA5C72 /* SpeedChecker.h */,
);
name = cpp;
path = ../cpp;
sourceTree = "<group>";
};
B8DCF2D725EA940700EA5C72 /* Frame Processor */ = {
isa = PBXGroup;
children = (
B80D67A825FA25380008FE8D /* FrameProcessorCallback.h */,
B8103E1E25FF5550007A1684 /* FrameProcessorUtils.h */,
B8103E1B25FF553B007A1684 /* FrameProcessorUtils.mm */,
B8103E5725FF56F0007A1684 /* Frame.h */,
B84760A22608EE38004C3180 /* FrameHostObject.h */,
B84760A52608EE7C004C3180 /* FrameHostObject.mm */,
B8A751D62609E4980011C623 /* FrameProcessorRuntimeManager.h */,
B8A751D72609E4B30011C623 /* FrameProcessorRuntimeManager.mm */,
B80C0DFE260BDD97001699AB /* FrameProcessorPluginRegistry.h */,
B80C0DFF260BDDF7001699AB /* FrameProcessorPluginRegistry.mm */,
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */,
);
path = "Frame Processor";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -198,8 +267,8 @@
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "VisionCamera" */;
buildPhases = (
B81F6C7625E515810008974A /* Run SwiftLint */,
B80D6CAB25F770FE006F2CB7 /* Run SwiftFormat */,
B81F6C7625E515810008974A /* Run SwiftLint */,
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
@ -291,6 +360,7 @@
files = (
B86DC974260E310600FB17B2 /* CameraView+AVAudioSession.swift in Sources */,
B887518625E0102000DB86D6 /* CameraView+RecordVideo.swift in Sources */,
B8DB3BCA263DC4D8004C18D7 /* RecordingSession.swift in Sources */,
B88751A225E0102000DB86D6 /* AVCaptureColorSpace+descriptor.swift in Sources */,
B887518925E0102000DB86D6 /* Collection+safe.swift in Sources */,
B887519125E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift in Sources */,
@ -299,6 +369,7 @@
B887518C25E0102000DB86D6 /* AVCaptureDevice+isMultiCam.swift in Sources */,
B887518D25E0102000DB86D6 /* AVCaptureDevice+physicalDevices.swift in Sources */,
B887519625E0102000DB86D6 /* Promise.swift in Sources */,
B8DB3BC8263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift in Sources */,
B887518725E0102000DB86D6 /* CameraViewManager.m in Sources */,
B88751A925E0102000DB86D6 /* CameraView.swift in Sources */,
B887519925E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift in Sources */,
@ -308,25 +379,33 @@
B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */,
B887518525E0102000DB86D6 /* PhotoCaptureDelegate.swift in Sources */,
B887518B25E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift in Sources */,
B84760A62608EE7C004C3180 /* FrameHostObject.mm in Sources */,
B8103E1C25FF553B007A1684 /* FrameProcessorUtils.mm in Sources */,
B887518E25E0102000DB86D6 /* AVFrameRateRange+includes.swift in Sources */,
B88751A125E0102000DB86D6 /* AVCaptureDevice.Position+descriptor.swift in Sources */,
B86DC977260E315100FB17B2 /* CameraView+AVCaptureSession.swift in Sources */,
B887518A25E0102000DB86D6 /* AVCaptureDevice+neutralZoom.swift in Sources */,
B88751A325E0102000DB86D6 /* AVCaptureDevice.FlashMode+descriptor.swift in Sources */,
B8A751D82609E4B30011C623 /* FrameProcessorRuntimeManager.mm in Sources */,
B887519A25E0102000DB86D6 /* AVVideoCodecType+descriptor.swift in Sources */,
B88751A825E0102000DB86D6 /* CameraError.swift in Sources */,
B887519225E0102000DB86D6 /* AVCaptureMovieFileOutput+mirror.swift in Sources */,
B887519225E0102000DB86D6 /* AVCaptureVideoDataOutput+mirror.swift in Sources */,
B88751A625E0102000DB86D6 /* CameraViewManager.swift in Sources */,
B887519F25E0102000DB86D6 /* AVCaptureDevice.DeviceType+descriptor.swift in Sources */,
B8D22CDC2642DB4D00234472 /* AVAssetWriterInputPixelBufferAdaptor+initWithVideoSettings.swift in Sources */,
B82FBA962614B69D00909718 /* RCTBridge+runOnJS.mm in Sources */,
B84760DF2608F57D004C3180 /* CameraQueues.swift in Sources */,
B887519025E0102000DB86D6 /* AVCaptureDevice.Format+matchesFilter.swift in Sources */,
B887518F25E0102000DB86D6 /* AVCapturePhotoOutput+mirror.swift in Sources */,
B88751A425E0102000DB86D6 /* AVCaptureDevice.Format.AutoFocusSystem+descriptor.swift in Sources */,
B8DB3BCC263DC97E004C18D7 /* AVFileType+descriptor.swift in Sources */,
B88751A025E0102000DB86D6 /* AVAuthorizationStatus+descriptor.swift in Sources */,
B80C0E00260BDDF7001699AB /* FrameProcessorPluginRegistry.mm in Sources */,
B887519C25E0102000DB86D6 /* AVCaptureDevice.TorchMode+descriptor.swift in Sources */,
B8994E6C263F03E100069589 /* JSIUtils.mm in Sources */,
B88751A525E0102000DB86D6 /* CameraView+Focus.swift in Sources */,
B86DC971260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift in Sources */,
B887519E25E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift in Sources */,
B887518825E0102000DB86D6 /* VideoCaptureDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -436,6 +515,7 @@
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
@ -449,12 +529,14 @@
SWIFT_OBJC_BRIDGING_HEADER = CameraBridge.h;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.2;
USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../cpp\"/**";
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
@ -467,6 +549,7 @@
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = CameraBridge.h;
SWIFT_VERSION = 5.2;
USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../cpp\"/**";
};
name = Release;
};

View File

@ -21,6 +21,8 @@
"ios/**/*.cpp",
"ios/**/*.swift",
"ios/VisionCamera.xcodeproj/project.pbxproj",
"cpp/**/*.h",
"cpp/**/*.cpp",
"VisionCamera.podspec",
"README.md"
],
@ -36,6 +38,7 @@
"bootstrap": "yarn example && yarn && yarn setup && yarn pods",
"check-android": "scripts/ktlint.sh",
"check-ios": "scripts/swiftformat.sh && scripts/swiftlint.sh",
"check-cpp": "scripts/cpplint.sh",
"check-all": "scripts/check-all.sh",
"clean": "scripts/clean.sh",
"docs": "cd docs && yarn build"
@ -71,27 +74,29 @@
"@react-native-community/eslint-config": "^2.0.0",
"@react-native-community/eslint-plugin": "^1.1.0",
"@release-it/conventional-changelog": "^2.0.0",
"@types/react": "^17.0.2",
"@types/react-native": "0.63.52",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"eslint": "^7.20.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"@types/react": "^17.0.4",
"@types/react-native": "0.64.4",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react-native": "^3.10.0",
"jest": "^26.0.1",
"pod-install": "^0.1.0",
"prettier": "^2.2.1",
"react": "17.0.1",
"react": "17.0.2",
"react-native": "0.64.0",
"react-native-builder-bob": "^0.18.1",
"release-it": "^14.2.2",
"typescript": "^4.1.3"
"react-native-reanimated": "^2.1.0",
"release-it": "^14.6.1",
"typescript": "^4.2.4"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
"react-native": "*",
"react-native-reanimated": ">=2.1.0"
},
"jest": {
"preset": "react-native",

View File

@ -9,6 +9,9 @@ echo "Linting Swift code.."
echo "Linting Kotlin code.."
./scripts/ktlint.sh
echo "Linting C++ code.."
./scripts/cpplint.sh
echo "Linting JS/TS code.."
yarn lint --fix

7
scripts/cpplint.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if which cpplint >/dev/null; then
cpplint --linelength=230 --filter=-legal/copyright,-readability/todo,-build/namespaces,-whitespace/comments --quiet --recursive cpp android/src/main/cpp
else
echo "warning: cpplint not installed, download from https://github.com/cpplint/cpplint"
fi

View File

@ -1,10 +1,10 @@
import React from 'react';
import { requireNativeComponent, NativeModules, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native';
import type { CameraPhotoCodec, CameraVideoCodec } from './CameraCodec';
import type { CameraDevice } from './CameraDevice';
import type { ErrorWithCause } from './CameraError';
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
import type { CameraProps } from './CameraProps';
import type { Frame } from './Frame';
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
import type { Point } from './Point';
import type { TakeSnapshotOptions } from './Snapshot';
@ -19,7 +19,7 @@ interface OnErrorEvent {
message: string;
cause?: ErrorWithCause;
}
type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onError'> & {
type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onError' | 'frameProcessor'> & {
cameraId: string;
onInitialized?: (event: NativeSyntheticEvent<void>) => void;
onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void;
@ -78,6 +78,7 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
* @internal
*/
displayName = Camera.displayName;
private lastFrameProcessor: ((frame: Frame) => void) | undefined;
private readonly ref: React.RefObject<RefType>;
@ -90,6 +91,7 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
this.onInitialized = this.onInitialized.bind(this);
this.onError = this.onError.bind(this);
this.ref = React.createRef<RefType>();
this.lastFrameProcessor = undefined;
}
private get handle(): number | null {
@ -232,39 +234,6 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
throw tryParseNativeCameraError(e);
}
}
/**
* Get a list of video codecs the current camera supports. Returned values are ordered by efficiency (descending).
*
* This function can only be called after the camera has been initialized,
* so only use this after the {@linkcode onInitialized | onInitialized()} event has fired.
*
* @platform iOS
* @throws {@linkcode CameraRuntimeError} When any kind of error occured while getting available video codecs. Use the {@linkcode CameraRuntimeError.code | code} property to get the actual error
*/
public async getAvailableVideoCodecs(): Promise<CameraVideoCodec[]> {
try {
return await CameraModule.getAvailableVideoCodecs(this.handle);
} catch (e) {
throw tryParseNativeCameraError(e);
}
}
/**
* Get a list of photo codecs the current camera supports. Returned values are ordered by efficiency (descending).
*
* This function can only be called after the camera has been initialized,
* so only use this after the {@linkcode onInitialized | onInitialized()} event has fired.
*
* @platform iOS
* @throws {@linkcode CameraRuntimeError} When any kind of error occured while getting available photo codecs. Use the {@linkcode CameraRuntimeError.code | code} property to get the actual error
*/
public async getAvailablePhotoCodecs(): Promise<CameraPhotoCodec[]> {
try {
return await CameraModule.getAvailablePhotoCodecs(this.handle);
} catch (e) {
throw tryParseNativeCameraError(e);
}
}
//#endregion
//#region Static Functions (NativeModule)
@ -382,11 +351,53 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
/**
* @internal
*/
public render(): React.ReactNode {
if (this.state.cameraId == null) throw new Error('CameraId was null! Did you pass a valid `device`?');
private assertFrameProcessorsEnabled(): void {
// @ts-expect-error JSI functions aren't typed
if (global.setFrameProcessor == null || global.unsetFrameProcessor == null)
throw new Error('Frame Processors are not enabled. Make sure you install react-native-reanimated 2.1.0 or above!');
}
/**
* @internal
*/
componentWillUnmount(): void {
this.assertFrameProcessorsEnabled();
// @ts-expect-error JSI functions aren't typed
global.unsetFrameProcessor(this.handle);
}
/**
* @internal
*/
componentDidUpdate(): void {
if (this.props.frameProcessor !== this.lastFrameProcessor) {
this.assertFrameProcessorsEnabled();
// frameProcessor argument changed. Update native to reflect the change.
if (this.props.frameProcessor != null) {
// 1. Spawn threaded JSI Runtime (if not already done)
// 2. Add video data output to Camera stream (if not already done)
// 3. Workletize the frameProcessor and prepare it for being called with frames
// @ts-expect-error JSI functions aren't typed
global.setFrameProcessor(this.handle, this.props.frameProcessor);
} else {
// 1. Destroy the threaded runtime
// 2. remove the frame processor
// 3. Remove the video data output
// @ts-expect-error JSI functions aren't typed
global.unsetFrameProcessor(this.handle);
}
this.lastFrameProcessor = this.props.frameProcessor;
}
}
/**
* @internal
*/
public render(): React.ReactNode {
if (this.state.cameraId == null) throw new Error('CameraID is null! Did you pass a valid `device`?');
// We remove the big `device` object from the props because we only need to pass `cameraId` to native.
const { device: _, ...props } = this.props;
const { device: _, frameProcessor: __, ...props } = this.props;
return (
<NativeCameraView
{...props}

View File

@ -1,33 +0,0 @@
/**
* Available Video Codec types used for recording a video.
*
* * `"hevc"`: The HEVC video codec. _(iOS 11.0+)_
* * `"h264"`: The H.264 (`avc1`) video codec. _(iOS 11.0+)_
* * `"jpeg"`: The JPEG (`jpeg`) video codec. _(iOS 11.0+)_
* * `"pro-res-4444"`: The Apple ProRes 4444 (`ap4h`) video codec. _(iOS 11.0+)_
* * `"pro-res-422"`: The Apple ProRes 422 (`apcn`) video codec. _(iOS 11.0+)_
* * `"pro-res-422-hq"`: The Apple ProRes 422 HQ (`apch`) video codec. _(iOS 13.0+)_
* * `"pro-res-422-lt"`: The Apple ProRes 422 LT (`apcs`) video codec. _(iOS 13.0+)_
* * `"pro-res-422-proxy"`: The Apple ProRes 422 Proxy (`apco`) video codec. _(iOS 13.0+)_
* * `"hevc-alpha"`: The HEVC (`muxa`) video codec that supports an alpha channel. This constant is used to select the appropriate encoder, but is NOT used on the encoded content, which is backwards compatible and hence uses `"hvc1"` as its codec type. _(iOS 13.0+)_
*/
export type CameraVideoCodec =
| 'h264'
| 'hevc'
| 'hevc-alpha'
| 'jpeg'
| 'pro-res-4444'
| 'pro-res-422'
| 'pro-res-422-hq'
| 'pro-res-422-lt'
| 'pro-res-422-proxy';
// TODO: Support RAW photo codec
/**
* Available Photo Codec types used for taking a photo.
*
* * `"hevc"`: The HEVC video codec. _(iOS 11.0+)_
* * `"jpeg"`: The JPEG (`jpeg`) video codec. _(iOS 11.0+)_
* * `"hevc-alpha"`: The HEVC (`muxa`) video codec that supports an alpha channel. This constant is used to select the appropriate encoder, but is NOT used on the encoded content, which is backwards compatible and hence uses `"hvc1"` as its codec type. _(iOS 13.0+)_
*/
export type CameraPhotoCodec = 'hevc' | 'jpeg' | 'hevc-alpha';

View File

@ -29,6 +29,7 @@ export type CaptureError =
| 'capture/no-recording-in-progress'
| 'capture/file-io-error'
| 'capture/create-temp-file-error'
| 'capture/create-recorder-error'
| 'capture/invalid-photo-codec'
| 'capture/not-bound-error'
| 'capture/capture-type-not-supported'

View File

@ -2,6 +2,7 @@ import type { ViewProps } from 'react-native';
import type { CameraDevice, CameraDeviceFormat, ColorSpace } from './CameraDevice';
import type { CameraRuntimeError } from './CameraError';
import type { CameraPreset } from './CameraPreset';
import type { Frame } from './Frame';
export interface CameraProps extends ViewProps {
/**
@ -126,5 +127,35 @@ export interface CameraProps extends ViewProps {
* Called when the camera was successfully initialized.
*/
onInitialized?: () => 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://cuvent.github.io/react-native-vision-camera/docs/guides/frame-processors) for more information
*
* @example
* ```tsx
* const frameProcessor = useFrameProcessor((frame) => {
* 'worklet'
* const qrCodes = scanQRCodes(frame)
* console.log(`Detected QR Codes: ${qrCodes}`)
* }, [])
*
* return <Camera {...cameraProps} frameProcessor={frameProcessor} />
* ```
*/
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 `1` (default) 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 30 times per second, perfect for high-speed realtime use-cases.
*
* 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 1
*/
frameProcessorFps?: number;
//#endregion
}

42
src/Frame.ts Normal file
View File

@ -0,0 +1,42 @@
/**
* A single frame, as seen by the camera.
*/
export interface Frame {
/**
* The raw pixel buffer.
*/
buffer: unknown[];
/**
* Whether the underlying buffer is still valid or not. The buffer will be released after the frame processor returns.
*/
isValid: boolean;
/**
* Whether the underlying buffer is marked as "ready" or not.
*/
isReady: boolean;
/**
* Returns the width of the frame, in pixels.
*/
width: number;
/**
* Returns the height of the frame, in pixels.
*/
height: number;
/**
* Returns the amount of bytes per row.
*/
bytesPerRow: number;
/**
* Returns the number of planes this frame contains.
*/
planesCount: number;
/**
* Returns a string representation of the frame.
* @example
* ```ts
* console.log(frame.toString()) // -> "3840 x 2160 Frame"
* ```
*/
toString(): string;
}

View File

@ -1,14 +1,6 @@
import type { CameraPhotoCodec } from './CameraCodec';
import type { TemporaryFile } from './TemporaryFile';
export interface TakePhotoOptions {
/**
* Specify the photo codec to use. To get a list of available photo codecs use the {@linkcode Camera.getAvailablePhotoCodecs | getAvailablePhotoCodecs()} function.
*
* @platform iOS
* @default undefined
*/
photoCodec?: CameraPhotoCodec;
/**
* Indicates how photo quality should be prioritized against speed.
*

View File

@ -1,39 +1,18 @@
// /**
// * not yet implemented.
// */
// declare interface RecordVideoOptions<TCodec extends CameraVideoCodec> {
// /**
// * Specify the video codec to use. To get a list of available video codecs use the `getAvailableVideoCodecs()` function.
// *
// * @default undefined
// */
// videoCodec?: TCodec;
// /**
// * Specify the average video bitrate in bits per second. (H.264 only)
// */
// bitrate?: TCodec extends "h264" ? number : never;
// /**
// * Specify the video quality. (`0.0` - `1.0`, where `1.0` means 100% quality. JPEG, HEIC and Apple ProRAW only. With HEIC and Apple ProRAW, 1.0 indicates lossless compression)
// */
// quality?: TCodec extends "jpeg" | "hevc" | "hevc-alpha" ? number : never;
// /**
// * Maximum number of frames per interval, `1` specifies to only use key frames. (H.264 only)
// */
// maxKeyFrameInterval?: TCodec extends "h264" ? number : never;
// /**
// * Maximum duration of a key frame interval in seconds, where as `0.0` means no limit. (H.264 only)
// */
// maxKeyFrameIntervalDuration?: TCodec extends "h264" ? number : never;
// }
import type { CameraCaptureError } from './CameraError';
import type { TemporaryFile } from './TemporaryFile';
export type VideoFileType = 'mov' | 'avci' | 'm4v' | 'mp4';
export interface RecordVideoOptions {
/**
* Set the video flash mode. Natively, this just enables the torch while recording.
*/
flash?: 'on' | 'off' | 'auto';
/**
* Sets the file type to use for the Video Recording.
* @default "mov"
*/
fileType?: VideoFileType;
/**
* Called when there was an unexpected runtime error while recording the video.
*/
@ -58,12 +37,4 @@ export interface VideoFile extends TemporaryFile {
* @platform iOS
*/
duration?: number;
/**
* Represents the file size of the recorded Video File, in bytes.
*
* This is `undefined` on Android, see [issue #77](https://github.com/cuvent/react-native-vision-camera/issues/77)
*
* @platform iOS
*/
size?: number;
}

19
src/globals.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
/* eslint-disable no-var */
/**
* `true` if currently running in a Frame Processor runtime
*/
declare var _FRAME_PROCESSOR: true | undefined;
/**
* `true` if currently running in a reanimated UI runtime
*/
declare var _UI: true | undefined;
/**
* `true` if currently running in a Worklet runtime (frame processor, multithreading, reanimated)
*/
declare var _WORKLET: true | undefined;
/**
* A native logging function (outputs to Xcode console/Android Logcat)
*/
declare var _log: (message: string) => void | undefined;

View File

@ -22,6 +22,7 @@ export function useCameraFormat(device?: CameraDevice, cameraViewSize?: Size): C
const bestFormat = sorted[0];
if (bestFormat == null) return [];
const bestFormatResolution = bestFormat.photoHeight * bestFormat.photoWidth;
return sorted.filter((f) => {
// difference in resolution in percent (e.g. 100x100 is 0.5 of 200x200)
const resolutionDiff = (bestFormatResolution - f.photoHeight * f.photoWidth) / bestFormatResolution;

View File

@ -0,0 +1,27 @@
import { DependencyList, useCallback } from 'react';
import type { Frame } from 'src/Frame';
/**
* Returns a memoized Frame Processor function wich you can pass to the `<Camera>`. (See ["Frame Processors"](https://cuvent.github.io/react-native-vision-camera/docs/guides/frame-processors))
*
* > If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks), make sure to add `useFrameProcessor` to `additionalHooks` inside your ESLint config. (See ["advanced configuration"](https://www.npmjs.com/package/eslint-plugin-react-hooks#advanced-configuration))
*
* @param frameProcessor The Frame Processor
* @param dependencies The React dependencies which will be copied into the VisionCamera JS-Runtime.
* @returns The memoized Frame Processor.
* @example
* ```ts
* const frameProcessor = useFrameProcessor((frame) => {
* 'worklet'
* const qrCodes = scanQRCodes(frame)
* _log(`QR Codes: ${qrCodes}`)
* }, [])
* ```
*/
export function useFrameProcessor(frameProcessor: (frame: Frame) => void, dependencies: DependencyList): (frame: Frame) => void {
return useCallback((frame: Frame) => {
'worklet';
return frameProcessor(frame);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies);
}

View File

@ -1,15 +1,19 @@
export * from './Camera';
export * from './CameraCodec';
export * from './CameraDevice';
export * from './CameraError';
export * from './CameraPosition';
export * from './CameraPreset';
export * from './CameraProps';
export * from './Frame';
export * from './CameraProps';
export * from './PhotoFile';
export * from './Point';
export * from './Snapshot';
export * from './TemporaryFile';
export * from './VideoFile';
export * from './hooks/useCameraDevices';
export * from './hooks/useCameraFormat';
export * from './hooks/useFrameProcessor';
export * from './utils/FormatFilter';

304
yarn.lock
View File

@ -689,7 +689,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/plugin-transform-object-assign@^7.0.0":
"@babel/plugin-transform-object-assign@^7.0.0", "@babel/plugin-transform-object-assign@^7.10.4":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.13.tgz#d9b9200a69e03403a813e44a933ad9f4bddfd050"
integrity sha512-4QxDMc0lAOkIBSfCrnSGbAJ+4epDBF2XXwcLXuBcG1xl9u7LrktNVD4+LwhL47XuKVPQ7R25e/WdcV+h97HyZA==
@ -1327,6 +1327,11 @@
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d"
integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ==
"@octokit/openapi-types@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.0.0.tgz#0f6992db9854af15eca77d71ab0ec7fad2f20411"
integrity sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw==
"@octokit/plugin-paginate-rest@^2.6.2":
version "2.13.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a"
@ -1339,12 +1344,12 @@
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
"@octokit/plugin-rest-endpoint-methods@4.13.5":
version "4.13.5"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.5.tgz#ad76285b82fe05fbb4adf2774a9c887f3534a880"
integrity sha512-kYKcWkFm4Ldk8bZai2RVEP1z97k1C/Ay2FN9FNTBg7JIyKoiiJjks4OtT6cuKeZX39tqa+C3J9xeYc6G+6g8uQ==
"@octokit/plugin-rest-endpoint-methods@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98"
integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==
dependencies:
"@octokit/types" "^6.12.2"
"@octokit/types" "^6.13.0"
deprecation "^2.3.1"
"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5":
@ -1370,23 +1375,30 @@
once "^1.4.0"
universal-user-agent "^6.0.0"
"@octokit/rest@18.3.5":
version "18.3.5"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.3.5.tgz#a89903d46e0b4273bd3234674ec2777a651d68ab"
integrity sha512-ZPeRms3WhWxQBEvoIh0zzf8xdU2FX0Capa7+lTca8YHmRsO3QNJzf1H3PcuKKsfgp91/xVDRtX91sTe1kexlbw==
"@octokit/rest@18.5.2":
version "18.5.2"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b"
integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw==
dependencies:
"@octokit/core" "^3.2.3"
"@octokit/plugin-paginate-rest" "^2.6.2"
"@octokit/plugin-request-log" "^1.0.2"
"@octokit/plugin-rest-endpoint-methods" "4.13.5"
"@octokit/plugin-rest-endpoint-methods" "5.0.0"
"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.12.2", "@octokit/types@^6.7.1":
"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.7.1":
version "6.13.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0"
integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==
dependencies:
"@octokit/openapi-types" "^6.0.0"
"@octokit/types@^6.13.0":
version "6.14.2"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.14.2.tgz#64c9457f38fb8522bdbba3c8cc814590a2d61bf5"
integrity sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==
dependencies:
"@octokit/openapi-types" "^7.0.0"
"@react-native-community/cli-debugger-ui@^5.0.1-alpha.1":
version "5.0.1-alpha.1"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1-alpha.1.tgz#09a856ccd2954cf16eea59b14dd26ae66720e4e6"
@ -1736,14 +1748,14 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react-native@0.63.52":
version "0.63.52"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.52.tgz#449beb4a413ec0f2c172cbf676a95f5b0952adf4"
integrity sha512-sBXvvtJaIUSXQLDh9NZitx1KHkKUdBLZy34lFKJaIXtpHIh5OEbBXeyUTFBtFwjk/RD0tneAtUqsdhheZRzAzw==
"@types/react-native@0.64.4":
version "0.64.4"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.4.tgz#9f11bef7dd5520801884829c73b19d75aa42e73c"
integrity sha512-VqnlmadGkD5usREvnuyVpWDS1W8f6cCz6MP5fZdgONsaZ9/Ijfb9Iq9MZ5O3bnW1OyJixDX9HtSp3COsFSLD8Q==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.2":
"@types/react@*":
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
@ -1752,6 +1764,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^17.0.4":
version "17.0.4"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.4.tgz#a67c6f7a460d2660e950d9ccc1c2f18525c28220"
integrity sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
@ -1793,13 +1814,13 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/eslint-plugin@^4.18.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.20.0.tgz#9d8794bd99aad9153092ad13c96164e3082e9a92"
integrity sha512-sw+3HO5aehYqn5w177z2D82ZQlqHCwcKSMboueo7oE4KU9QiC0SAgfS/D4z9xXvpTc8Bt41Raa9fBR8T2tIhoQ==
"@typescript-eslint/eslint-plugin@^4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc"
integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==
dependencies:
"@typescript-eslint/experimental-utils" "4.20.0"
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/experimental-utils" "4.22.0"
"@typescript-eslint/scope-manager" "4.22.0"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
lodash "^4.17.15"
@ -1818,15 +1839,15 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/experimental-utils@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.20.0.tgz#a8ab2d7b61924f99042b7d77372996d5f41dc44b"
integrity sha512-sQNlf6rjLq2yB5lELl3gOE7OuoA/6IVXJUJ+Vs7emrQMva14CkOwyQwD7CW+TkmOJ4Q/YGmoDLmbfFrpGmbKng==
"@typescript-eslint/experimental-utils@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz#68765167cca531178e7b650a53456e6e0bef3b1f"
integrity sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/typescript-estree" "4.20.0"
"@typescript-eslint/scope-manager" "4.22.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/typescript-estree" "4.22.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
@ -1841,33 +1862,33 @@
"@typescript-eslint/typescript-estree" "3.10.1"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/parser@^4.18.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.20.0.tgz#8dd403c8b4258b99194972d9799e201b8d083bdd"
integrity sha512-m6vDtgL9EABdjMtKVw5rr6DdeMCH3OA1vFb0dAyuZSa3e5yw1YRzlwFnm9knma9Lz6b2GPvoNSa8vOXrqsaglA==
"@typescript-eslint/parser@^4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.0.tgz#e1637327fcf796c641fe55f73530e90b16ac8fe8"
integrity sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==
dependencies:
"@typescript-eslint/scope-manager" "4.20.0"
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/typescript-estree" "4.20.0"
"@typescript-eslint/scope-manager" "4.22.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/typescript-estree" "4.22.0"
debug "^4.1.1"
"@typescript-eslint/scope-manager@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.20.0.tgz#953ecbf3b00845ece7be66246608be9d126d05ca"
integrity sha512-/zm6WR6iclD5HhGpcwl/GOYDTzrTHmvf8LLLkwKqqPKG6+KZt/CfSgPCiybshmck66M2L5fWSF/MKNuCwtKQSQ==
"@typescript-eslint/scope-manager@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a"
integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/visitor-keys" "4.20.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/visitor-keys" "4.22.0"
"@typescript-eslint/types@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
"@typescript-eslint/types@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.20.0.tgz#c6cf5ef3c9b1c8f699a9bbdafb7a1da1ca781225"
integrity sha512-cYY+1PIjei1nk49JAPnH1VEnu7OYdWRdJhYI5wiKOUMhLTG1qsx5cQxCUTuwWCmQoyriadz3Ni8HZmGSofeC+w==
"@typescript-eslint/types@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6"
integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==
"@typescript-eslint/typescript-estree@3.10.1":
version "3.10.1"
@ -1883,13 +1904,13 @@
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.20.0.tgz#8b3b08f85f18a8da5d88f65cb400f013e88ab7be"
integrity sha512-Knpp0reOd4ZsyoEJdW8i/sK3mtZ47Ls7ZHvD8WVABNx5Xnn7KhenMTRGegoyMTx6TiXlOVgMz9r0pDgXTEEIHA==
"@typescript-eslint/typescript-estree@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz#b5d95d6d366ff3b72f5168c75775a3e46250d05c"
integrity sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/visitor-keys" "4.20.0"
"@typescript-eslint/types" "4.22.0"
"@typescript-eslint/visitor-keys" "4.22.0"
debug "^4.1.1"
globby "^11.0.1"
is-glob "^4.0.1"
@ -1903,12 +1924,12 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/visitor-keys@4.20.0":
version "4.20.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.20.0.tgz#1e84db034da13f208325e6bfc995c3b75f7dbd62"
integrity sha512-NXKRM3oOVQL8yNFDNCZuieRIwZ5UtjNLYtmMx2PacEAGmbaEYtGgVHUHVyZvU/0rYZcizdrWjDo+WBtRPSgq+A==
"@typescript-eslint/visitor-keys@4.22.0":
version "4.22.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz#169dae26d3c122935da7528c839f42a8a42f6e47"
integrity sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==
dependencies:
"@typescript-eslint/types" "4.20.0"
"@typescript-eslint/types" "4.22.0"
eslint-visitor-keys "^2.0.0"
JSONStream@^1.0.4:
@ -2166,7 +2187,7 @@ arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
asap@~2.0.6:
asap@~2.0.3, asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@ -3152,6 +3173,13 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
js-yaml "^3.13.1"
parse-json "^4.0.0"
cross-fetch@^3.0.4:
version "3.1.3"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.3.tgz#b8e7d5f19161c4a0ca916f707978848786043afb"
integrity sha512-2i6v88DTqVBNODyjD9U6Ycn/uSZNvyHe25cIbo2fFnAACAsaLTJsd23miRWiR5NuiGXR9wpJ9d40/9WAhjDIrw==
dependencies:
node-fetch "2.6.1"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@ -3610,10 +3638,10 @@ eslint-config-prettier@^6.10.1:
dependencies:
get-stdin "^6.0.0"
eslint-config-prettier@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6"
integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==
eslint-config-prettier@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a"
integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==
eslint-plugin-eslint-comments@^3.1.2:
version "3.2.0"
@ -3642,10 +3670,10 @@ eslint-plugin-prettier@3.1.2:
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-prettier@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7"
integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==
eslint-plugin-prettier@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==
dependencies:
prettier-linter-helpers "^1.0.0"
@ -3710,10 +3738,10 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7.20.0:
version "7.23.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325"
integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==
eslint@^7.25.0:
version "7.25.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67"
integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==
dependencies:
"@babel/code-frame" "7.12.11"
"@eslint/eslintrc" "^0.4.0"
@ -3983,6 +4011,24 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
fbjs-css-vars@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
fbjs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165"
integrity sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==
dependencies:
cross-fetch "^3.0.4"
fbjs-css-vars "^1.0.0"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
figures@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@ -4365,19 +4411,7 @@ globals@^13.6.0:
dependencies:
type-fest "^0.20.2"
globby@11.0.2:
version "11.0.2"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83"
integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.1.1"
ignore "^5.1.4"
merge2 "^1.3.0"
slash "^3.0.0"
globby@^11.0.1:
globby@11.0.3, globby@^11.0.1:
version "11.0.3"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb"
integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==
@ -6370,7 +6404,19 @@ mime-db@1.46.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee"
integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==
mime-types@2.1.29, mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24:
mime-db@1.47.0:
version "1.47.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
mime-types@2.1.30:
version "2.1.30"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
dependencies:
mime-db "1.47.0"
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.29"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2"
integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==
@ -6448,6 +6494,11 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.5"
mockdate@^3.0.2:
version "3.0.5"
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
modify-values@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
@ -6522,7 +6573,7 @@ node-dir@^0.1.17:
dependencies:
minimatch "^3.0.2"
node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@ -6635,7 +6686,7 @@ ob1@0.64.0:
resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.64.0.tgz#f254a55a53ca395c4f9090e28a85483eac5eba19"
integrity sha512-CO1N+5dhvy+MoAwxz8+fymEUcwsT4a+wHhrHFb02LppcJdHxgcBWviwEhUwKOD2kLMQ7ijrrzybOqpGcqEtvpQ==
object-assign@^4.0.1, object-assign@^4.1.1:
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@ -7180,6 +7231,13 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
promise@^8.0.3:
version "8.1.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
@ -7345,6 +7403,16 @@ react-native-codegen@^0.0.6:
jscodeshift "^0.11.0"
nullthrows "^1.1.1"
react-native-reanimated@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.1.0.tgz#b9ad04aee490e1e030d0a6cdaa43a14895d9a54d"
integrity sha512-tlPvvcdf+X7HGQ7g/7npBFhwMznfdk7MHUc9gUB/kp2abSscXNe/kOVKlrNEOO4DS11rNOXc+llFxVFMuNk0zA==
dependencies:
"@babel/plugin-transform-object-assign" "^7.10.4"
fbjs "^3.0.0"
mockdate "^3.0.2"
string-hash-64 "^1.0.3"
react-native@0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.0.tgz#c3bde5b638bf8bcf12bae6e094930d39cb942ab7"
@ -7388,10 +7456,10 @@ react-refresh@^0.4.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"
integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==
react@17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
react@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -7587,13 +7655,13 @@ regjsparser@^0.6.4:
dependencies:
jsesc "~0.5.0"
release-it@^14.2.2:
version "14.5.0"
resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.5.0.tgz#84ced16247b2ea92bc8e7b06c87daa60d5ce2b7e"
integrity sha512-3xwKx3B5i4TVMlCZsNXOCc/S3iviQmi64FJExsvMg5ZjlbBsEYF4qbPLVMq308i7MeDWhubiFHIb3XYuh8pt6Q==
release-it@^14.6.1:
version "14.6.1"
resolved "https://registry.yarnpkg.com/release-it/-/release-it-14.6.1.tgz#a04623312d67886b37b8a6d298844ee3e0c7150a"
integrity sha512-noBho2997G3yrm6YvdLJj4Ua2SCFOU7ajCqtvteI3DZtpM1IhiyXSgcn2Q5irq8lTNK0it4eiNq9TSrAWNYDkA==
dependencies:
"@iarna/toml" "2.2.5"
"@octokit/rest" "18.3.5"
"@octokit/rest" "18.5.2"
async-retry "1.3.1"
chalk "4.1.0"
cosmiconfig "7.0.0"
@ -7603,17 +7671,17 @@ release-it@^14.2.2:
find-up "5.0.0"
form-data "4.0.0"
git-url-parse "11.4.4"
globby "11.0.2"
globby "11.0.3"
got "11.8.2"
import-cwd "3.0.0"
inquirer "8.0.0"
is-ci "3.0.0"
lodash "4.17.21"
mime-types "2.1.29"
mime-types "2.1.30"
ora "5.4.0"
os-name "4.0.0"
parse-json "5.2.0"
semver "7.3.4"
semver "7.3.5"
shelljs "0.8.4"
update-notifier "5.1.0"
url-join "4.0.1"
@ -7917,10 +7985,10 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
semver@7.3.4:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
semver@7.3.5, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
@ -7929,13 +7997,6 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.2.1, semver@^7.3.2, semver@^7.3.4:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@ -7985,6 +8046,11 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
@ -8282,6 +8348,11 @@ strict-uri-encode@^2.0.0:
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
string-hash-64@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322"
integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==
string-length@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
@ -8734,10 +8805,15 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.1.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
typescript@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
ua-parser-js@^0.7.18:
version "0.7.26"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.26.tgz#b3731860e241419abd5b542b1a0881070d92e0ce"
integrity sha512-VwIvGlFNmpKbjzRt51jpbbFTrKIEgGHxIwA8Y69K1Bqc6bTIV7TaGGABOkghSFQWsLmcRB4drGvpfv9z2szqoQ==
uglify-es@^3.1.9:
version "3.3.9"