@momo-kits/camerakit 0.160.1-beta.8 → 0.161.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -84,10 +84,10 @@ static id CKConvertFollyDynamicToId(const folly::dynamic &dyn)
|
|
|
84
84
|
- (void)prepareView
|
|
85
85
|
{
|
|
86
86
|
_view = [[CKCameraView alloc] init];
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
// just need to pass something, it won't really be used on fabric, but it's used to create events (it won't impact sending them)
|
|
89
89
|
_view.reactTag = @-1;
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
__weak __typeof__(self) weakSelf = self;
|
|
92
92
|
|
|
93
93
|
[_view setOnReadCode:^(NSDictionary* event) {
|
|
@@ -137,7 +137,7 @@ static id CKConvertFollyDynamicToId(const folly::dynamic &dyn)
|
|
|
137
137
|
std::dynamic_pointer_cast<const facebook::react::CKCameraEventEmitter>(strongSelf->_eventEmitter)->onMRZ({.docMRZ = docMRZ});
|
|
138
138
|
}
|
|
139
139
|
}];
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
self.contentView = _view;
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -251,8 +251,8 @@ static id CKConvertFollyDynamicToId(const folly::dynamic &dyn)
|
|
|
251
251
|
_view.barcodeFrameSize = @{@"width": @(barcodeWidth), @"height": @(barcodeHeight)};
|
|
252
252
|
[changedProps addObject:@"barcodeFrameSize"];
|
|
253
253
|
}
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
|
|
255
|
+
|
|
256
256
|
[super updateProps:props oldProps:oldProps];
|
|
257
257
|
[_view didSetProps:changedProps];
|
|
258
258
|
}
|
|
@@ -260,6 +260,12 @@ static id CKConvertFollyDynamicToId(const folly::dynamic &dyn)
|
|
|
260
260
|
- (void)prepareForRecycle
|
|
261
261
|
{
|
|
262
262
|
[super prepareForRecycle];
|
|
263
|
+
|
|
264
|
+
if (_view != nil) {
|
|
265
|
+
[_view removeFromSuperview];
|
|
266
|
+
_view = nil;
|
|
267
|
+
}
|
|
268
|
+
|
|
263
269
|
[self prepareView];
|
|
264
270
|
}
|
|
265
271
|
|
|
@@ -71,22 +71,16 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
71
71
|
// MARK: - Lifecycle
|
|
72
72
|
|
|
73
73
|
func cameraRemovedFromSuperview() {
|
|
74
|
-
sessionQueue.async {
|
|
75
|
-
|
|
76
|
-
// Only stop if session is running
|
|
77
|
-
guard self.session.isRunning else {
|
|
78
|
-
self.removeObservers()
|
|
79
|
-
return
|
|
80
|
-
}
|
|
74
|
+
sessionQueue.async { [weak self] in
|
|
75
|
+
guard let self, self.setupResult == .success else { return }
|
|
81
76
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
self.pendingStop = true
|
|
86
|
-
return
|
|
87
|
-
}
|
|
77
|
+
// Stop now if idle/safe, or defer the stop until the open configuration
|
|
78
|
+
// transaction commits (stopSessionIfNeeded sets pendingStop in that case).
|
|
79
|
+
self.stopSessionIfNeeded()
|
|
88
80
|
|
|
89
|
-
|
|
81
|
+
// If the stop was deferred, handlePendingStopIfNeeded() will remove the
|
|
82
|
+
// observers once the transaction commits; otherwise remove them now.
|
|
83
|
+
if !self.pendingStop {
|
|
90
84
|
self.removeObservers()
|
|
91
85
|
}
|
|
92
86
|
}
|
|
@@ -102,96 +96,117 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
102
96
|
|
|
103
97
|
deinit {
|
|
104
98
|
removeObservers()
|
|
99
|
+
|
|
100
|
+
// Ensure the capture hardware is released even if cameraRemovedFromSuperview()
|
|
101
|
+
// was never delivered (e.g. a forced React surface cleanup during rapid
|
|
102
|
+
// mount/unmount). Capture only the session object so the block does not
|
|
103
|
+
// resurrect `self`.
|
|
104
|
+
let session = self.session
|
|
105
|
+
sessionQueue.async {
|
|
106
|
+
if session.isRunning {
|
|
107
|
+
session.stopRunning()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
// MARK: - Public
|
|
108
113
|
|
|
109
114
|
func setup(cameraType: CameraType, supportedBarcodeType: [CodeFormat]) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
sessionQueue.async { [weak self] in
|
|
116
|
+
guard let self else { return }
|
|
117
|
+
|
|
118
|
+
// Idempotency guard: a RealCamera configures its session exactly once.
|
|
119
|
+
// A fresh instance is created on every mount / Fabric recycle, so this
|
|
120
|
+
// never blocks a legitimate restart — resuming after stopCamera() is
|
|
121
|
+
// handled by startCamera() -> startSessionIfNeeded().
|
|
122
|
+
guard self.setupResult == .notStarted else { return }
|
|
123
|
+
|
|
124
|
+
// Configure the WHOLE session (inputs, preset, outputs) inside a single
|
|
125
|
+
// begin/commit transaction on the session queue. The preset is set here
|
|
126
|
+
// and never on the main queue, so the startRunning() below can no longer
|
|
127
|
+
// observe the session mid-configuration from another thread.
|
|
128
|
+
//
|
|
129
|
+
// This is the root-cause fix for:
|
|
130
|
+
// "[AVCaptureSession startRunning] startRunning may not be called between
|
|
131
|
+
// calls to beginConfiguration and commitConfiguration".
|
|
132
|
+
self.setupResult = self.configureSession(cameraType: cameraType,
|
|
133
|
+
supportedBarcodeType: supportedBarcodeType)
|
|
134
|
+
|
|
135
|
+
guard self.setupResult == .success else { return }
|
|
136
|
+
|
|
137
|
+
// Order matches the previous behaviour: start, then observe, then torch.
|
|
138
|
+
self.startSessionIfNeeded()
|
|
139
|
+
self.addObservers()
|
|
140
|
+
self.update(torchMode: self.torchMode)
|
|
124
141
|
|
|
125
|
-
|
|
142
|
+
// Only the preview LAYER is touched on the main queue. Connecting an
|
|
143
|
+
// AVCaptureVideoPreviewLayer to a session from the main thread is
|
|
144
|
+
// sanctioned by AVFoundation (see Apple's AVCam) and does NOT open a
|
|
145
|
+
// session configuration transaction, so it cannot race startRunning().
|
|
146
|
+
DispatchQueue.main.async { [weak self] in
|
|
147
|
+
guard let self else { return }
|
|
126
148
|
self.cameraPreview.session = self.session
|
|
127
149
|
self.cameraPreview.previewLayer.videoGravity = .resizeAspectFill
|
|
128
|
-
self.session.sessionPreset = .photo
|
|
129
150
|
self.setVideoOrientationToInterfaceOrientation()
|
|
130
151
|
}
|
|
131
152
|
}
|
|
132
|
-
|
|
133
|
-
DispatchQueue.global(qos: .utility).async {
|
|
134
|
-
self.initializeMotionManager()
|
|
135
|
-
}
|
|
136
153
|
|
|
154
|
+
DispatchQueue.global(qos: .utility).async { [weak self] in
|
|
155
|
+
self?.initializeMotionManager()
|
|
156
|
+
}
|
|
137
157
|
}
|
|
138
158
|
|
|
139
159
|
// MARK: - Private optimization methods
|
|
140
|
-
|
|
141
|
-
|
|
160
|
+
|
|
161
|
+
/// Configures the video input, session preset and every output in ONE atomic
|
|
162
|
+
/// transaction. Must be called on `sessionQueue`. Order is significant:
|
|
163
|
+
/// beginConfiguration -> addInput -> set preset -> addOutputs -> commitConfiguration.
|
|
164
|
+
private func configureSession(cameraType: CameraType, supportedBarcodeType: [CodeFormat]) -> SetupResult {
|
|
165
|
+
assertOnSessionQueue()
|
|
142
166
|
guard let videoDevice = self.getBestDevice(for: cameraType),
|
|
143
167
|
let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
|
|
144
168
|
return .sessionConfigurationFailed
|
|
145
169
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if session.canAddInput(videoDeviceInput) {
|
|
156
|
-
session.addInput(videoDeviceInput)
|
|
157
|
-
self.videoDeviceInput = videoDeviceInput
|
|
158
|
-
self.resetZoom(forDevice: videoDevice)
|
|
159
|
-
return .success
|
|
170
|
+
|
|
171
|
+
beginConfiguration()
|
|
172
|
+
defer { commitConfiguration() }
|
|
173
|
+
|
|
174
|
+
// 1. Video input
|
|
175
|
+
guard session.canAddInput(videoDeviceInput) else {
|
|
176
|
+
return .sessionConfigurationFailed
|
|
160
177
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
session.addInput(videoDeviceInput)
|
|
179
|
+
self.videoDeviceInput = videoDeviceInput
|
|
180
|
+
self.resetZoom(forDevice: videoDevice)
|
|
181
|
+
|
|
182
|
+
// 2. Preset — set inside the transaction with the device input already
|
|
183
|
+
// present, so `canSetSessionPreset` reflects the real device. `.photo` is
|
|
184
|
+
// supported by every camera device; if it somehow is not, we keep whatever
|
|
185
|
+
// preset the session defaults to rather than crashing.
|
|
186
|
+
if session.canSetSessionPreset(.photo) {
|
|
187
|
+
session.sessionPreset = .photo
|
|
171
188
|
}
|
|
172
|
-
|
|
173
|
-
//
|
|
189
|
+
|
|
190
|
+
// 3. Photo output
|
|
174
191
|
if #available(iOS 13.0, *) {
|
|
175
192
|
if let maxPhotoQualityPrioritization = maxPhotoQualityPrioritization {
|
|
176
193
|
photoOutput.maxPhotoQualityPrioritization = maxPhotoQualityPrioritization.avQualityPrioritization
|
|
177
194
|
}
|
|
178
195
|
}
|
|
179
|
-
|
|
180
196
|
if session.canAddOutput(photoOutput) {
|
|
181
197
|
session.addOutput(photoOutput)
|
|
182
|
-
|
|
183
|
-
if let photoOutputConnection = self.photoOutput.connection(with: .video)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
198
|
+
// Connection only exists after the output is added to the session.
|
|
199
|
+
if let photoOutputConnection = self.photoOutput.connection(with: .video),
|
|
200
|
+
photoOutputConnection.isVideoStabilizationSupported {
|
|
201
|
+
photoOutputConnection.preferredVideoStabilizationMode = .auto
|
|
187
202
|
}
|
|
188
203
|
}
|
|
189
|
-
|
|
190
|
-
//
|
|
204
|
+
|
|
205
|
+
// 4. Metadata output for barcode scanning
|
|
191
206
|
if self.session.canAddOutput(metadataOutput) {
|
|
192
207
|
self.session.addOutput(metadataOutput)
|
|
193
208
|
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
|
194
|
-
|
|
209
|
+
|
|
195
210
|
let availableTypes = self.metadataOutput.availableMetadataObjectTypes
|
|
196
211
|
let filteredTypes = supportedBarcodeType
|
|
197
212
|
.map { $0.toAVMetadataObjectType() }
|
|
@@ -199,29 +214,49 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
199
214
|
|
|
200
215
|
metadataOutput.metadataObjectTypes = filteredTypes
|
|
201
216
|
}
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
if
|
|
217
|
+
|
|
218
|
+
// 5. Video data output for text / MRZ detection
|
|
219
|
+
if textRequest != nil && self.session.canAddOutput(self.videoDataOutput) {
|
|
205
220
|
self.session.addOutput(self.videoDataOutput)
|
|
206
221
|
}
|
|
222
|
+
|
|
223
|
+
return .success
|
|
207
224
|
}
|
|
208
225
|
|
|
209
226
|
// MARK: - Pause / Resume non-essential outputs
|
|
227
|
+
|
|
228
|
+
/// Disables OCR + barcode work while a photo is captured.
|
|
229
|
+
/// MUST be called on `sessionQueue` (these are capture-output mutations).
|
|
210
230
|
private func pauseNonEssentialOutputs() {
|
|
231
|
+
assertOnSessionQueue()
|
|
211
232
|
videoDataOutput.setSampleBufferDelegate(nil, queue: nil)
|
|
212
233
|
metadataOutput.rectOfInterest = CGRect(x: 0, y: 0, width: 0, height: 0)
|
|
213
234
|
}
|
|
214
|
-
|
|
235
|
+
|
|
236
|
+
/// Re-enables OCR + barcode work after a capture. May be called from any
|
|
237
|
+
/// thread (photo-capture completion handlers run on an arbitrary queue), so it
|
|
238
|
+
/// hops to the main queue only to read preview-layer geometry and applies all
|
|
239
|
+
/// output mutations back on `sessionQueue`.
|
|
215
240
|
private func resumeNonEssentialOutputs() {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
241
|
+
sessionQueue.async { [weak self] in
|
|
242
|
+
guard let self else { return }
|
|
243
|
+
if self.textDetectionEnabled {
|
|
244
|
+
self.videoDataOutput.setSampleBufferDelegate(self, queue: globalOCRQueue)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
guard let scanner = self.scannerFrameSize, scanner != .zero else {
|
|
248
|
+
self.metadataOutput.rectOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// metadataOutputRectConverted must be read on the main thread.
|
|
253
|
+
DispatchQueue.main.async { [weak self] in
|
|
254
|
+
guard let self else { return }
|
|
255
|
+
let rect = self.cameraPreview.previewLayer.metadataOutputRectConverted(fromLayerRect: scanner)
|
|
256
|
+
self.sessionQueue.async { [weak self] in
|
|
257
|
+
self?.metadataOutput.rectOfInterest = rect
|
|
258
|
+
}
|
|
259
|
+
}
|
|
225
260
|
}
|
|
226
261
|
}
|
|
227
262
|
|
|
@@ -361,21 +396,18 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
361
396
|
func update(maxPhotoQualityPrioritization: MaxPhotoQualityPrioritization?) {
|
|
362
397
|
guard #available(iOS 13.0, *) else { return }
|
|
363
398
|
guard maxPhotoQualityPrioritization != self.maxPhotoQualityPrioritization else { return }
|
|
364
|
-
sessionQueue.async {
|
|
365
|
-
self
|
|
366
|
-
self.
|
|
367
|
-
defer {
|
|
368
|
-
self.session.commitConfiguration()
|
|
369
|
-
self.configurationDepth -= 1
|
|
370
|
-
self.handlePendingStopIfNeeded()
|
|
371
|
-
}
|
|
399
|
+
sessionQueue.async { [weak self] in
|
|
400
|
+
guard let self else { return }
|
|
401
|
+
self.beginConfiguration()
|
|
402
|
+
defer { self.commitConfiguration() }
|
|
372
403
|
self.maxPhotoQualityPrioritization = maxPhotoQualityPrioritization
|
|
373
404
|
self.photoOutput.maxPhotoQualityPrioritization = maxPhotoQualityPrioritization?.avQualityPrioritization ?? .balanced
|
|
374
405
|
}
|
|
375
406
|
}
|
|
376
407
|
|
|
377
408
|
func update(cameraType: CameraType) {
|
|
378
|
-
sessionQueue.async {
|
|
409
|
+
sessionQueue.async { [weak self] in
|
|
410
|
+
guard let self else { return }
|
|
379
411
|
if self.videoDeviceInput?.device.position == cameraType.avPosition {
|
|
380
412
|
return
|
|
381
413
|
}
|
|
@@ -387,15 +419,10 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
387
419
|
let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
|
|
388
420
|
return
|
|
389
421
|
}
|
|
390
|
-
|
|
422
|
+
|
|
391
423
|
self.removeObservers()
|
|
392
|
-
self.
|
|
393
|
-
self.
|
|
394
|
-
defer {
|
|
395
|
-
self.session.commitConfiguration()
|
|
396
|
-
self.configurationDepth -= 1
|
|
397
|
-
self.handlePendingStopIfNeeded()
|
|
398
|
-
}
|
|
424
|
+
self.beginConfiguration()
|
|
425
|
+
defer { self.commitConfiguration() }
|
|
399
426
|
|
|
400
427
|
// Remove the existing device input first, since using the front and back camera simultaneously is not supported.
|
|
401
428
|
self.session.removeInput(currentViewDeviceInput)
|
|
@@ -436,9 +463,6 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
436
463
|
the main thread and session configuration is done on the session queue.
|
|
437
464
|
*/
|
|
438
465
|
|
|
439
|
-
// Pause OCR + barcode before capturing
|
|
440
|
-
self.pauseNonEssentialOutputs()
|
|
441
|
-
|
|
442
466
|
DispatchQueue.main.async { [weak self] in
|
|
443
467
|
guard let self = self else {
|
|
444
468
|
onError("Camera was deallocated")
|
|
@@ -480,6 +504,9 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
480
504
|
photoOutputConnection.videoOrientation = videoPreviewLayerOrientation
|
|
481
505
|
}
|
|
482
506
|
|
|
507
|
+
// Pause OCR + barcode work on the session queue, right before capture.
|
|
508
|
+
self.pauseNonEssentialOutputs()
|
|
509
|
+
|
|
483
510
|
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
|
|
484
511
|
if #available(iOS 13.0, *) {
|
|
485
512
|
settings.photoQualityPrioritization = self.photoOutput.maxPhotoQualityPrioritization
|
|
@@ -829,39 +856,99 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
|
|
|
829
856
|
|
|
830
857
|
print("Capture session runtime error: \(error)")
|
|
831
858
|
|
|
832
|
-
// Automatically try to restart the session
|
|
859
|
+
// Automatically try to restart the session if media services were reset.
|
|
860
|
+
// (After a reset the session is NOT running, so the restart is routed
|
|
861
|
+
// through the guarded helper, which only starts when it is safe to.)
|
|
833
862
|
if error.code == .mediaServicesWereReset {
|
|
834
|
-
sessionQueue.async {
|
|
835
|
-
|
|
836
|
-
self.session.startRunning()
|
|
837
|
-
}
|
|
863
|
+
sessionQueue.async { [weak self] in
|
|
864
|
+
self?.startSessionIfNeeded()
|
|
838
865
|
}
|
|
839
866
|
}
|
|
840
867
|
}
|
|
841
868
|
|
|
842
869
|
func startCamera() {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
self.session.startRunning()
|
|
846
|
-
}
|
|
870
|
+
sessionQueue.async { [weak self] in
|
|
871
|
+
self?.startSessionIfNeeded()
|
|
847
872
|
}
|
|
848
873
|
}
|
|
849
874
|
|
|
850
875
|
func stopCamera() {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
876
|
+
sessionQueue.async { [weak self] in
|
|
877
|
+
self?.stopSessionIfNeeded()
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// MARK: - Session configuration transaction helpers
|
|
882
|
+
|
|
883
|
+
/// Debug-only tripwire enforcing the core invariant of this class: EVERY
|
|
884
|
+
/// AVCaptureSession mutation and start/stop happens on `sessionQueue`. The whole
|
|
885
|
+
/// "startRunning may not be called between beginConfiguration and
|
|
886
|
+
/// commitConfiguration" crash class is prevented precisely because nothing
|
|
887
|
+
/// touches the session off this serial queue. If a future change — or an
|
|
888
|
+
/// external caller such as a mini-app native bridge — ever reaches one of these
|
|
889
|
+
/// methods on the wrong queue, this fails loudly in DEBUG/QA instead of
|
|
890
|
+
/// crashing randomly in production. Compiled out of release builds, so it can
|
|
891
|
+
/// never add a production crash.
|
|
892
|
+
private func assertOnSessionQueue() {
|
|
893
|
+
#if DEBUG
|
|
894
|
+
dispatchPrecondition(condition: .onQueue(sessionQueue))
|
|
895
|
+
#endif
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/// Opens a session configuration transaction and tracks nesting depth.
|
|
899
|
+
/// MUST be balanced by `commitConfiguration()` (use `defer`) and MUST run on
|
|
900
|
+
/// `sessionQueue`.
|
|
901
|
+
private func beginConfiguration() {
|
|
902
|
+
assertOnSessionQueue()
|
|
903
|
+
configurationDepth += 1
|
|
904
|
+
session.beginConfiguration()
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/// Closes a session configuration transaction and flushes a stop that was
|
|
908
|
+
/// requested while the transaction was open. MUST run on `sessionQueue`.
|
|
909
|
+
private func commitConfiguration() {
|
|
910
|
+
assertOnSessionQueue()
|
|
911
|
+
session.commitConfiguration()
|
|
912
|
+
configurationDepth -= 1
|
|
913
|
+
handlePendingStopIfNeeded()
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/// Starts the session only when it is safe to do so. MUST run on `sessionQueue`.
|
|
917
|
+
/// Because every begin/commit pair is synchronous on the serial session queue,
|
|
918
|
+
/// `configurationDepth` is always 0 at the start of a fresh queue block — the
|
|
919
|
+
/// depth check is therefore defence-in-depth against ever calling this from
|
|
920
|
+
/// inside an open transaction.
|
|
921
|
+
private func startSessionIfNeeded() {
|
|
922
|
+
assertOnSessionQueue()
|
|
923
|
+
guard setupResult == .success,
|
|
924
|
+
configurationDepth == 0,
|
|
925
|
+
!pendingStop,
|
|
926
|
+
!session.isRunning else {
|
|
927
|
+
return
|
|
928
|
+
}
|
|
929
|
+
session.startRunning()
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/// Stops the session, deferring until the current transaction commits if one
|
|
933
|
+
/// is open. MUST run on `sessionQueue`.
|
|
934
|
+
private func stopSessionIfNeeded() {
|
|
935
|
+
assertOnSessionQueue()
|
|
936
|
+
guard session.isRunning else { return }
|
|
937
|
+
if configurationDepth > 0 {
|
|
938
|
+
pendingStop = true
|
|
939
|
+
return
|
|
855
940
|
}
|
|
941
|
+
session.stopRunning()
|
|
856
942
|
}
|
|
857
943
|
|
|
858
944
|
// MARK: - Private helper for safe session stop
|
|
859
|
-
|
|
945
|
+
|
|
860
946
|
private func handlePendingStopIfNeeded() {
|
|
861
947
|
// Must be called on sessionQueue after configuration completes
|
|
948
|
+
assertOnSessionQueue()
|
|
862
949
|
// Only execute if all configurations are done (depth == 0)
|
|
863
950
|
guard configurationDepth == 0 && pendingStop else { return }
|
|
864
|
-
|
|
951
|
+
|
|
865
952
|
pendingStop = false
|
|
866
953
|
if session.isRunning {
|
|
867
954
|
session.stopRunning()
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
2
|
+
"name": "@momo-kits/camerakit",
|
|
3
|
+
"version": "0.161.1-beta.1",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/teslamotors/react-native-camera-kit.git"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"registry": "https://registry.npmjs.org/"
|
|
10
|
+
},
|
|
11
|
+
"description": "A high performance, fully featured, rock solid camera library for React Native applications",
|
|
12
|
+
"nativePackage": true,
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "echo",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"lint": "yarn eslint -c .eslintrc.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "./src/index.ts",
|
|
19
|
+
"dependencies": {},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"react": "19.0.0",
|
|
23
|
+
"react-native": "0.80.1"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "*",
|
|
27
|
+
"react-native": "*"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"codegenConfig": {
|
|
33
|
+
"name": "rncamerakit_specs",
|
|
34
|
+
"type": "all",
|
|
35
|
+
"jsSrcsDir": "src/specs",
|
|
36
|
+
"android": {
|
|
37
|
+
"javaPackageName": "com.rncamerakit"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"packageManager": "yarn@1.22.22"
|
|
41
|
+
}
|