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:
parent
77b3d78566
commit
b6a67d5ced
@ -92,4 +92,7 @@ module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
globals: {
|
||||
_log: 'readonly',
|
||||
},
|
||||
};
|
||||
|
31
.github/workflows/validate-cpp.yml
vendored
Normal file
31
.github/workflows/validate-cpp.yml
vendored
Normal 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
4
.gitignore
vendored
@ -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/
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)")
|
||||
|
@ -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
31
cpp/MakeJSIRuntime.h
Normal 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
26
cpp/README.md
Normal 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
20
cpp/SpeedChecker.h
Normal 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
|
@ -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 />
|
||||
|
||||
|
@ -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 />
|
||||
|
@ -98,3 +98,8 @@ function App() {
|
||||
return <Camera ref={camera} {...cameraProps} />
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
#### 🚀 Next section: [Troubleshooting](troubleshooting)
|
||||
|
@ -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))
|
||||
|
149
docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
Normal file
149
docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
Normal 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)!
|
74
docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx
Normal file
74
docs/docs/guides/FRAME_PROCESSOR_CREATE_FINAL.mdx
Normal 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)
|
17
docs/docs/guides/FRAME_PROCESSOR_CREATE_PLUGIN_ANDROID.mdx
Normal file
17
docs/docs/guides/FRAME_PROCESSOR_CREATE_PLUGIN_ANDROID.mdx
Normal 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))
|
102
docs/docs/guides/FRAME_PROCESSOR_CREATE_PLUGIN_IOS.mdx
Normal file
102
docs/docs/guides/FRAME_PROCESSOR_CREATE_PLUGIN_IOS.mdx
Normal 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))
|
51
docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx
Normal file
51
docs/docs/guides/FRAME_PROCESSOR_PLUGIN_LIST.mdx
Normal 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)
|
@ -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 />
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -13,6 +13,9 @@ module.exports = {
|
||||
apiKey: 'ab7f44570bb62d0e07c0f7d92312ed1a',
|
||||
indexName: 'react-native-vision-camera',
|
||||
},
|
||||
prism: {
|
||||
additionalLanguages: ['swift'],
|
||||
},
|
||||
navbar: {
|
||||
title: 'VisionCamera',
|
||||
logo: {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
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
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
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
BIN
docs/static/img/slow-log.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
4269
docs/yarn.lock
4269
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -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/";
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,3 @@
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
FLIPPER_VERSION=0.75.1
|
||||
|
@ -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
24
example/clean.sh
Executable 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
|
1
example/ios/.swift-version
Normal file
1
example/ios/.swift-version
Normal file
@ -0,0 +1 @@
|
||||
5.2
|
@ -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
|
@ -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
|
@ -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
|
||||
[]
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1240"
|
||||
LastUpgradeVersion = "1250"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "NO"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
18
example/src/frame-processors/ScanQRCode.ts
Normal file
18
example/src/frame-processors/ScanQRCode.ts
Normal 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);
|
||||
}
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
33
ios/CameraQueues.swift
Normal 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)
|
||||
}
|
@ -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!")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
},
|
||||
]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
20
ios/Frame Processor/Frame.h
Normal file
20
ios/Frame Processor/Frame.h
Normal 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;
|
||||
};
|
26
ios/Frame Processor/FrameHostObject.h
Normal file
26
ios/Frame Processor/FrameHostObject.h
Normal 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();
|
||||
};
|
93
ios/Frame Processor/FrameHostObject.mm
Normal file
93
ios/Frame Processor/FrameHostObject.mm
Normal 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;
|
||||
}
|
14
ios/Frame Processor/FrameProcessorCallback.h
Normal file
14
ios/Frame Processor/FrameProcessorCallback.h
Normal 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);
|
63
ios/Frame Processor/FrameProcessorPlugin.h
Normal file
63
ios/Frame Processor/FrameProcessorPlugin.h
Normal 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 */
|
23
ios/Frame Processor/FrameProcessorPluginRegistry.h
Normal file
23
ios/Frame Processor/FrameProcessorPluginRegistry.h
Normal 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
|
37
ios/Frame Processor/FrameProcessorPluginRegistry.mm
Normal file
37
ios/Frame Processor/FrameProcessorPluginRegistry.mm
Normal 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
|
26
ios/Frame Processor/FrameProcessorRuntimeManager.h
Normal file
26
ios/Frame Processor/FrameProcessorRuntimeManager.h
Normal 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
|
184
ios/Frame Processor/FrameProcessorRuntimeManager.mm
Normal file
184
ios/Frame Processor/FrameProcessorRuntimeManager.mm
Normal 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
|
23
ios/Frame Processor/FrameProcessorUtils.h
Normal file
23
ios/Frame Processor/FrameProcessorUtils.h
Normal 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);
|
44
ios/Frame Processor/FrameProcessorUtils.mm
Normal file
44
ios/Frame Processor/FrameProcessorUtils.mm
Normal 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();
|
||||
};
|
||||
}
|
28
ios/Parsers/AVAssetWriter.Status+descriptor.swift
Normal file
28
ios/Parsers/AVAssetWriter.Status+descriptor.swift
Normal 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)")
|
||||
}
|
||||
}
|
||||
}
|
20
ios/Parsers/AVFileType+descriptor.swift
Normal file
20
ios/Parsers/AVFileType+descriptor.swift
Normal 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
|
||||
}
|
||||
}
|
55
ios/React Utils/JSIUtils.h
Normal file
55
ios/React Utils/JSIUtils.h
Normal 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
187
ios/React Utils/JSIUtils.mm
Normal 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;
|
||||
}
|
18
ios/React Utils/RCTBridge+runOnJS.h
Normal file
18
ios/React Utils/RCTBridge+runOnJS.h
Normal 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
|
23
ios/React Utils/RCTBridge+runOnJS.mm
Normal file
23
ios/React Utils/RCTBridge+runOnJS.mm
Normal 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
135
ios/RecordingSession.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
27
package.json
27
package.json
@ -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",
|
||||
|
@ -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
7
scripts/cpplint.sh
Executable 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
|
@ -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}
|
||||
|
@ -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';
|
@ -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'
|
||||
|
@ -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
42
src/Frame.ts
Normal 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;
|
||||
}
|
@ -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.
|
||||
*
|
||||
|
@ -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
19
src/globals.d.ts
vendored
Normal 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;
|
@ -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;
|
||||
|
27
src/hooks/useFrameProcessor.ts
Normal file
27
src/hooks/useFrameProcessor.ts
Normal 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);
|
||||
}
|
@ -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
304
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user