@rejourneyco/react-native 1.0.0 → 1.0.2
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.
- package/README.md +29 -0
- package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +47 -30
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +25 -1
- package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +70 -32
- package/android/src/main/java/com/rejourney/core/Constants.kt +4 -4
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +14 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
- package/ios/Capture/RJCaptureEngine.m +72 -34
- package/ios/Capture/RJCaptureHeuristics.h +7 -5
- package/ios/Capture/RJCaptureHeuristics.m +138 -112
- package/ios/Capture/RJVideoEncoder.m +0 -26
- package/ios/Core/Rejourney.mm +64 -102
- package/ios/Utils/RJPerfTiming.m +0 -5
- package/ios/Utils/RJWindowUtils.m +0 -1
- package/lib/commonjs/components/Mask.js +1 -6
- package/lib/commonjs/index.js +12 -101
- package/lib/commonjs/sdk/autoTracking.js +55 -353
- package/lib/commonjs/sdk/constants.js +2 -13
- package/lib/commonjs/sdk/errorTracking.js +1 -29
- package/lib/commonjs/sdk/metricsTracking.js +3 -24
- package/lib/commonjs/sdk/navigation.js +3 -42
- package/lib/commonjs/sdk/networkInterceptor.js +7 -49
- package/lib/commonjs/sdk/utils.js +0 -5
- package/lib/module/components/Mask.js +1 -6
- package/lib/module/index.js +11 -105
- package/lib/module/sdk/autoTracking.js +55 -354
- package/lib/module/sdk/constants.js +2 -13
- package/lib/module/sdk/errorTracking.js +1 -29
- package/lib/module/sdk/index.js +0 -2
- package/lib/module/sdk/metricsTracking.js +3 -24
- package/lib/module/sdk/navigation.js +3 -42
- package/lib/module/sdk/networkInterceptor.js +7 -49
- package/lib/module/sdk/utils.js +0 -5
- package/lib/typescript/NativeRejourney.d.ts +2 -0
- package/lib/typescript/sdk/autoTracking.d.ts +5 -6
- package/lib/typescript/types/index.d.ts +0 -1
- package/package.json +11 -3
- package/src/NativeRejourney.ts +4 -0
- package/src/components/Mask.tsx +0 -3
- package/src/index.ts +11 -88
- package/src/sdk/autoTracking.ts +72 -331
- package/src/sdk/constants.ts +13 -13
- package/src/sdk/errorTracking.ts +1 -17
- package/src/sdk/index.ts +0 -2
- package/src/sdk/metricsTracking.ts +5 -33
- package/src/sdk/navigation.ts +8 -29
- package/src/sdk/networkInterceptor.ts +9 -33
- package/src/sdk/utils.ts +0 -5
- package/src/types/index.ts +0 -29
|
@@ -20,7 +20,7 @@ static const NSTimeInterval kRJQuietScrollSeconds = 0.2;
|
|
|
20
20
|
static const NSTimeInterval kRJQuietBounceSeconds = 0.2;
|
|
21
21
|
static const NSTimeInterval kRJQuietRefreshSeconds = 0.22;
|
|
22
22
|
static const NSTimeInterval kRJQuietMapSeconds = 0.55;
|
|
23
|
-
static const NSTimeInterval kRJQuietTransitionSeconds = 0.
|
|
23
|
+
static const NSTimeInterval kRJQuietTransitionSeconds = 0.1;
|
|
24
24
|
static const NSTimeInterval kRJQuietKeyboardSeconds = 0.25;
|
|
25
25
|
static const NSTimeInterval kRJQuietAnimationSeconds = 0.25;
|
|
26
26
|
|
|
@@ -204,7 +204,8 @@ typedef struct {
|
|
|
204
204
|
|
|
205
205
|
- (void)recordTouchEventAtTime:(NSTimeInterval)time {
|
|
206
206
|
self.lastTouchTime = time;
|
|
207
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusInteractionDelaySeconds
|
|
207
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusInteractionDelaySeconds
|
|
208
|
+
now:time];
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
- (void)recordInteractionEventAtTime:(NSTimeInterval)time {
|
|
@@ -221,12 +222,11 @@ typedef struct {
|
|
|
221
222
|
|
|
222
223
|
- (void)recordNavigationEventAtTime:(NSTimeInterval)time {
|
|
223
224
|
self.lastTransitionTime = time;
|
|
224
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
225
|
-
now:time];
|
|
225
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds now:time];
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
- (void)recordRenderedSignature:(nullable NSString *)signature
|
|
229
|
-
|
|
229
|
+
atTime:(NSTimeInterval)time {
|
|
230
230
|
self.lastRenderedSignature = signature.length > 0 ? signature : nil;
|
|
231
231
|
self.lastRenderedTime = time;
|
|
232
232
|
if (self.pendingKeyframes > 0) {
|
|
@@ -262,8 +262,8 @@ typedef struct {
|
|
|
262
262
|
}
|
|
263
263
|
self.lastObservedSignatureTime = now;
|
|
264
264
|
self.lastSignatureChurnTime = now;
|
|
265
|
-
self.lastObservedSignature =
|
|
266
|
-
|
|
265
|
+
self.lastObservedSignature =
|
|
266
|
+
currentSignature.length > 0 ? currentSignature : nil;
|
|
267
267
|
} else if (self.lastSignatureChurnTime > 0 &&
|
|
268
268
|
(now - self.lastSignatureChurnTime) >
|
|
269
269
|
kRJSignatureChurnWindowSeconds) {
|
|
@@ -296,15 +296,15 @@ typedef struct {
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
[self updateScrollActiveState:scanResult.scrollActive
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
refreshActive:scanResult.refreshActive
|
|
300
|
+
mapActive:scanResult.mapActive
|
|
301
|
+
now:now];
|
|
302
302
|
|
|
303
303
|
BOOL blockingAnimation = NO;
|
|
304
304
|
if (scanResult.hasAnyAnimations) {
|
|
305
305
|
self.lastAnimationAreaRatio = scanResult.animationAreaRatio;
|
|
306
|
-
blockingAnimation =
|
|
307
|
-
|
|
306
|
+
blockingAnimation =
|
|
307
|
+
(scanResult.animationAreaRatio >= kRJAnimationSmallAreaAllowed);
|
|
308
308
|
} else {
|
|
309
309
|
self.lastAnimationAreaRatio = 0.0;
|
|
310
310
|
}
|
|
@@ -335,88 +335,113 @@ typedef struct {
|
|
|
335
335
|
[self probeAnimatedViewsAtTime:now];
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
- (RJCaptureHeuristicsDecision *)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
- (RJCaptureHeuristicsDecision *)
|
|
339
|
+
decisionForSignature:(nullable NSString *)signature
|
|
340
|
+
now:(NSTimeInterval)now
|
|
341
|
+
hasLastFrame:(BOOL)hasLastFrame
|
|
342
|
+
importance:(RJCaptureImportance)importance {
|
|
342
343
|
RJCaptureHeuristicsDecision *decision =
|
|
343
344
|
[[RJCaptureHeuristicsDecision alloc] init];
|
|
344
345
|
|
|
345
346
|
NSTimeInterval earliestSafeTime = now;
|
|
346
347
|
RJCaptureHeuristicsReason blockerReason = RJCaptureHeuristicsReasonRenderNow;
|
|
347
348
|
|
|
349
|
+
// Check importance to potentially bypass heuristics
|
|
350
|
+
BOOL isUrgent = (importance == RJCaptureImportanceHigh ||
|
|
351
|
+
importance == RJCaptureImportanceCritical);
|
|
352
|
+
|
|
348
353
|
// Active touch/gesture input should be avoided to keep input latency smooth.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
// CRITICAL updates bypass this.
|
|
355
|
+
if (!isUrgent) {
|
|
356
|
+
[self considerBlockerSince:self.lastTouchTime
|
|
357
|
+
quietInterval:kRJQuietTouchSeconds
|
|
358
|
+
now:now
|
|
359
|
+
reason:RJCaptureHeuristicsReasonDeferTouch
|
|
360
|
+
earliestTime:&earliestSafeTime
|
|
361
|
+
chosenReason:&blockerReason];
|
|
362
|
+
}
|
|
355
363
|
|
|
356
364
|
// Scroll motion (dragging or deceleration) is a high-jank period.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
// Even urgent captures should respect scroll to avoid visible hitching,
|
|
366
|
+
// unless CRITICAL
|
|
367
|
+
if (importance != RJCaptureImportanceCritical) {
|
|
368
|
+
[self considerBlockerSince:self.lastScrollTime
|
|
369
|
+
quietInterval:kRJQuietScrollSeconds
|
|
370
|
+
now:now
|
|
371
|
+
reason:RJCaptureHeuristicsReasonDeferScroll
|
|
372
|
+
earliestTime:&earliestSafeTime
|
|
373
|
+
chosenReason:&blockerReason];
|
|
374
|
+
}
|
|
363
375
|
|
|
364
376
|
// Rubber-band bounce and inset animations are visually sensitive.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
377
|
+
if (!isUrgent) {
|
|
378
|
+
[self considerBlockerSince:self.lastBounceTime
|
|
379
|
+
quietInterval:kRJQuietBounceSeconds
|
|
380
|
+
now:now
|
|
381
|
+
reason:RJCaptureHeuristicsReasonDeferBounce
|
|
382
|
+
earliestTime:&earliestSafeTime
|
|
383
|
+
chosenReason:&blockerReason];
|
|
384
|
+
}
|
|
371
385
|
|
|
372
386
|
// Pull-to-refresh animations should finish before rendering.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
387
|
+
if (!isUrgent) {
|
|
388
|
+
[self considerBlockerSince:self.lastRefreshTime
|
|
389
|
+
quietInterval:kRJQuietRefreshSeconds
|
|
390
|
+
now:now
|
|
391
|
+
reason:RJCaptureHeuristicsReasonDeferRefresh
|
|
392
|
+
earliestTime:&earliestSafeTime
|
|
393
|
+
chosenReason:&blockerReason];
|
|
394
|
+
}
|
|
379
395
|
|
|
380
396
|
// Interactive transitions (swipe back, drag-to-dismiss) are hitch-sensitive.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
397
|
+
// KEY FIX: Urgent captures (NAVIGATION) must bypass this!
|
|
398
|
+
if (!isUrgent) {
|
|
399
|
+
[self considerBlockerSince:self.lastTransitionTime
|
|
400
|
+
quietInterval:kRJQuietTransitionSeconds
|
|
401
|
+
now:now
|
|
402
|
+
reason:RJCaptureHeuristicsReasonDeferTransition
|
|
403
|
+
earliestTime:&earliestSafeTime
|
|
404
|
+
chosenReason:&blockerReason];
|
|
405
|
+
}
|
|
387
406
|
|
|
388
407
|
// Keyboard frame animations can stutter; wait for settle.
|
|
389
408
|
if (self.keyboardAnimating) {
|
|
390
409
|
self.lastKeyboardTime = now;
|
|
391
410
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
411
|
+
if (!isUrgent) {
|
|
412
|
+
[self considerBlockerSince:self.lastKeyboardTime
|
|
413
|
+
quietInterval:kRJQuietKeyboardSeconds
|
|
414
|
+
now:now
|
|
415
|
+
reason:RJCaptureHeuristicsReasonDeferKeyboard
|
|
416
|
+
earliestTime:&earliestSafeTime
|
|
417
|
+
chosenReason:&blockerReason];
|
|
418
|
+
}
|
|
398
419
|
|
|
399
420
|
// Map camera or tile motion is visually obvious; avoid rendering mid-flight.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
421
|
+
// Maps are special; even CRITICAL captures might want to wait for map settle
|
|
422
|
+
// if possible.
|
|
423
|
+
if (importance != RJCaptureImportanceCritical) {
|
|
424
|
+
[self considerBlockerSince:self.lastMapTime
|
|
425
|
+
quietInterval:kRJQuietMapSeconds
|
|
426
|
+
now:now
|
|
427
|
+
reason:RJCaptureHeuristicsReasonDeferMap
|
|
428
|
+
earliestTime:&earliestSafeTime
|
|
429
|
+
chosenReason:&blockerReason];
|
|
406
430
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
431
|
+
if (self.mapSettleUntil > now && self.mapSettleUntil > earliestSafeTime) {
|
|
432
|
+
earliestSafeTime = self.mapSettleUntil;
|
|
433
|
+
blockerReason = RJCaptureHeuristicsReasonDeferMap;
|
|
434
|
+
}
|
|
410
435
|
}
|
|
411
436
|
|
|
412
437
|
// Large-area animations (Lottie, shimmer, etc.) are very noticeable.
|
|
413
|
-
if (self.animationBlocking) {
|
|
438
|
+
if (self.animationBlocking && !isUrgent) {
|
|
414
439
|
[self considerBlockerSince:self.lastAnimationTime
|
|
415
440
|
quietInterval:kRJQuietAnimationSeconds
|
|
416
441
|
now:now
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
442
|
+
reason:RJCaptureHeuristicsReasonDeferBigAnimation
|
|
443
|
+
earliestTime:&earliestSafeTime
|
|
444
|
+
chosenReason:&blockerReason];
|
|
420
445
|
}
|
|
421
446
|
|
|
422
447
|
if (earliestSafeTime > now) {
|
|
@@ -426,26 +451,27 @@ typedef struct {
|
|
|
426
451
|
return decision;
|
|
427
452
|
}
|
|
428
453
|
|
|
429
|
-
BOOL signatureChanged =
|
|
430
|
-
|
|
454
|
+
BOOL signatureChanged =
|
|
455
|
+
(signature.length == 0 ||
|
|
456
|
+
![signature isEqualToString:self.lastRenderedSignature]);
|
|
431
457
|
BOOL stale = (self.lastRenderedTime <= 0 ||
|
|
432
458
|
(now - self.lastRenderedTime) > self.maxStaleSeconds);
|
|
433
459
|
BOOL bonusDue = (self.bonusCaptureTime > 0 && now >= self.bonusCaptureTime);
|
|
434
|
-
BOOL keyframeDue =
|
|
435
|
-
|
|
436
|
-
|
|
460
|
+
BOOL keyframeDue =
|
|
461
|
+
bonusDue && self.pendingKeyframes > 0 &&
|
|
462
|
+
(now - self.lastKeyframeRenderTime) >= kRJKeyframeSpacingSeconds;
|
|
437
463
|
BOOL staleOnly = stale && hasLastFrame && !signatureChanged && !keyframeDue;
|
|
438
464
|
BOOL suppressStaleRender =
|
|
439
465
|
staleOnly &&
|
|
440
466
|
(self.hasVideoSurface || self.hasWebSurface || self.hasCameraSurface);
|
|
441
467
|
|
|
442
|
-
if (suppressStaleRender) {
|
|
468
|
+
if (suppressStaleRender && !isUrgent) {
|
|
443
469
|
decision.action = RJCaptureHeuristicsActionReuseLast;
|
|
444
470
|
decision.reason = RJCaptureHeuristicsReasonReuseSignatureUnchanged;
|
|
445
471
|
return decision;
|
|
446
472
|
}
|
|
447
473
|
|
|
448
|
-
if (!hasLastFrame || signatureChanged || stale || keyframeDue) {
|
|
474
|
+
if (!hasLastFrame || signatureChanged || stale || keyframeDue || isUrgent) {
|
|
449
475
|
decision.action = RJCaptureHeuristicsActionRenderNow;
|
|
450
476
|
decision.reason = RJCaptureHeuristicsReasonRenderNow;
|
|
451
477
|
return decision;
|
|
@@ -516,7 +542,8 @@ typedef struct {
|
|
|
516
542
|
UIViewController *topVC = [self topViewControllerForWindow:window];
|
|
517
543
|
if (topVC != self.lastTopVC) {
|
|
518
544
|
self.lastTransitionTime = now;
|
|
519
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
545
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusTransitionDelaySeconds
|
|
546
|
+
now:now];
|
|
520
547
|
self.lastTopVC = topVC;
|
|
521
548
|
}
|
|
522
549
|
id<UIViewControllerTransitionCoordinator> coordinator =
|
|
@@ -552,7 +579,7 @@ typedef struct {
|
|
|
552
579
|
#pragma mark - Scroll Tracking
|
|
553
580
|
|
|
554
581
|
- (void)updateTrackedScrollViews:(NSArray<NSValue *> *)scrollViewPointers
|
|
555
|
-
|
|
582
|
+
now:(NSTimeInterval)now {
|
|
556
583
|
if (!scrollViewPointers) {
|
|
557
584
|
return;
|
|
558
585
|
}
|
|
@@ -563,18 +590,18 @@ typedef struct {
|
|
|
563
590
|
continue;
|
|
564
591
|
}
|
|
565
592
|
[self evaluateScrollView:scrollView
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
593
|
+
now:now
|
|
594
|
+
scrollActive:NULL
|
|
595
|
+
refreshActive:NULL];
|
|
569
596
|
}
|
|
570
|
-
|
|
571
597
|
}
|
|
572
598
|
|
|
573
599
|
- (void)probeScrollViewsAtTime:(NSTimeInterval)now {
|
|
574
600
|
BOOL anyScrollActive = NO;
|
|
575
601
|
BOOL anyRefreshActive = NO;
|
|
576
602
|
|
|
577
|
-
NSArray<UIScrollView *> *scrollViews =
|
|
603
|
+
NSArray<UIScrollView *> *scrollViews =
|
|
604
|
+
[[self.scrollSamples keyEnumerator] allObjects];
|
|
578
605
|
if (!scrollViews) {
|
|
579
606
|
scrollViews = @[];
|
|
580
607
|
}
|
|
@@ -583,21 +610,21 @@ typedef struct {
|
|
|
583
610
|
continue;
|
|
584
611
|
}
|
|
585
612
|
[self evaluateScrollView:scrollView
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
613
|
+
now:now
|
|
614
|
+
scrollActive:&anyScrollActive
|
|
615
|
+
refreshActive:&anyRefreshActive];
|
|
589
616
|
}
|
|
590
617
|
|
|
591
618
|
[self updateScrollActiveState:anyScrollActive
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
619
|
+
refreshActive:anyRefreshActive
|
|
620
|
+
mapActive:self.mapActive
|
|
621
|
+
now:now];
|
|
595
622
|
}
|
|
596
623
|
|
|
597
624
|
- (void)evaluateScrollView:(UIScrollView *)scrollView
|
|
598
625
|
now:(NSTimeInterval)now
|
|
599
|
-
|
|
600
|
-
|
|
626
|
+
scrollActive:(BOOL *)scrollActive
|
|
627
|
+
refreshActive:(BOOL *)refreshActive {
|
|
601
628
|
RJScrollViewSample *sample = [self.scrollSamples objectForKey:scrollView];
|
|
602
629
|
if (!sample) {
|
|
603
630
|
sample = [[RJScrollViewSample alloc] init];
|
|
@@ -609,8 +636,9 @@ typedef struct {
|
|
|
609
636
|
|
|
610
637
|
BOOL tracking = scrollView.isTracking || scrollView.isDragging ||
|
|
611
638
|
scrollView.isDecelerating;
|
|
612
|
-
BOOL offsetMoved =
|
|
613
|
-
|
|
639
|
+
BOOL offsetMoved =
|
|
640
|
+
(fabs(offset.x - sample.contentOffset.x) > kRJScrollEpsilon ||
|
|
641
|
+
fabs(offset.y - sample.contentOffset.y) > kRJScrollEpsilon);
|
|
614
642
|
BOOL zoomMoved = fabs(zoomScale - sample.zoomScale) > kRJZoomEpsilon;
|
|
615
643
|
BOOL isScrolling = tracking || offsetMoved || zoomMoved;
|
|
616
644
|
|
|
@@ -635,8 +663,8 @@ typedef struct {
|
|
|
635
663
|
}
|
|
636
664
|
|
|
637
665
|
BOOL refreshVisible = [self isRefreshActiveForScrollView:scrollView
|
|
638
|
-
|
|
639
|
-
|
|
666
|
+
offset:offset
|
|
667
|
+
inset:inset];
|
|
640
668
|
if (refreshVisible) {
|
|
641
669
|
// Pull-to-refresh is active or settling.
|
|
642
670
|
self.lastRefreshTime = now;
|
|
@@ -660,8 +688,8 @@ typedef struct {
|
|
|
660
688
|
}
|
|
661
689
|
CGFloat topLimit = -inset.top - kRJScrollEpsilon;
|
|
662
690
|
CGFloat bottomLimit = scrollView.contentSize.height -
|
|
663
|
-
scrollView.bounds.size.height +
|
|
664
|
-
|
|
691
|
+
scrollView.bounds.size.height + inset.bottom +
|
|
692
|
+
kRJScrollEpsilon;
|
|
665
693
|
|
|
666
694
|
if (offset.y < topLimit || offset.y > bottomLimit) {
|
|
667
695
|
return YES;
|
|
@@ -669,8 +697,8 @@ typedef struct {
|
|
|
669
697
|
|
|
670
698
|
CGFloat leftLimit = -inset.left - kRJScrollEpsilon;
|
|
671
699
|
CGFloat rightLimit = scrollView.contentSize.width -
|
|
672
|
-
scrollView.bounds.size.width +
|
|
673
|
-
|
|
700
|
+
scrollView.bounds.size.width + inset.right +
|
|
701
|
+
kRJScrollEpsilon;
|
|
674
702
|
if (offset.x < leftLimit || offset.x > rightLimit) {
|
|
675
703
|
return YES;
|
|
676
704
|
}
|
|
@@ -690,8 +718,8 @@ typedef struct {
|
|
|
690
718
|
return YES;
|
|
691
719
|
}
|
|
692
720
|
|
|
693
|
-
CGFloat triggerOffset =
|
|
694
|
-
|
|
721
|
+
CGFloat triggerOffset =
|
|
722
|
+
-scrollView.adjustedContentInset.top - kRJScrollEpsilon;
|
|
695
723
|
if (offset.y < triggerOffset) {
|
|
696
724
|
return YES;
|
|
697
725
|
}
|
|
@@ -709,9 +737,9 @@ typedef struct {
|
|
|
709
737
|
}
|
|
710
738
|
|
|
711
739
|
- (void)updateScrollActiveState:(BOOL)scrollActive
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
740
|
+
refreshActive:(BOOL)refreshActive
|
|
741
|
+
mapActive:(BOOL)mapActive
|
|
742
|
+
now:(NSTimeInterval)now {
|
|
715
743
|
if (self.scrollActive && !scrollActive) {
|
|
716
744
|
[self scheduleBonusCaptureAfterDelay:kRJBonusScrollDelaySeconds now:now];
|
|
717
745
|
}
|
|
@@ -734,7 +762,7 @@ typedef struct {
|
|
|
734
762
|
#pragma mark - Map Tracking
|
|
735
763
|
|
|
736
764
|
- (void)updateTrackedMapViews:(NSArray<NSValue *> *)mapViewPointers
|
|
737
|
-
|
|
765
|
+
now:(NSTimeInterval)now {
|
|
738
766
|
if (!mapViewPointers) {
|
|
739
767
|
return;
|
|
740
768
|
}
|
|
@@ -763,9 +791,9 @@ typedef struct {
|
|
|
763
791
|
self.lastMapTime = now;
|
|
764
792
|
}
|
|
765
793
|
[self updateScrollActiveState:self.scrollActive
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
794
|
+
refreshActive:self.refreshActive
|
|
795
|
+
mapActive:anyMapActive || self.mapActive
|
|
796
|
+
now:now];
|
|
769
797
|
}
|
|
770
798
|
|
|
771
799
|
- (BOOL)updateMapStateForView:(UIView *)view atTime:(NSTimeInterval)now {
|
|
@@ -836,8 +864,7 @@ typedef struct {
|
|
|
836
864
|
|
|
837
865
|
- (void)probeAnimatedViewsAtTime:(NSTimeInterval)now {
|
|
838
866
|
if (self.churnBlocking) {
|
|
839
|
-
if ((now - self.lastSignatureChurnTime) <
|
|
840
|
-
kRJSignatureChurnWindowSeconds) {
|
|
867
|
+
if ((now - self.lastSignatureChurnTime) < kRJSignatureChurnWindowSeconds) {
|
|
841
868
|
self.lastAnimationTime = now;
|
|
842
869
|
return;
|
|
843
870
|
}
|
|
@@ -863,15 +890,14 @@ typedef struct {
|
|
|
863
890
|
self.lastAnimationTime = now;
|
|
864
891
|
} else {
|
|
865
892
|
self.animationBlocking = NO;
|
|
866
|
-
[self scheduleBonusCaptureAfterDelay:kRJBonusAnimationDelaySeconds
|
|
867
|
-
now:now];
|
|
893
|
+
[self scheduleBonusCaptureAfterDelay:kRJBonusAnimationDelaySeconds now:now];
|
|
868
894
|
}
|
|
869
895
|
}
|
|
870
896
|
|
|
871
897
|
#pragma mark - Bonus Capture
|
|
872
898
|
|
|
873
899
|
- (void)scheduleBonusCaptureAfterDelay:(NSTimeInterval)delay
|
|
874
|
-
|
|
900
|
+
now:(NSTimeInterval)now {
|
|
875
901
|
if (self.pendingKeyframes < kRJMaxPendingKeyframes) {
|
|
876
902
|
self.pendingKeyframes += 1;
|
|
877
903
|
}
|
|
@@ -886,9 +912,9 @@ typedef struct {
|
|
|
886
912
|
- (void)considerBlockerSince:(NSTimeInterval)timestamp
|
|
887
913
|
quietInterval:(NSTimeInterval)quietInterval
|
|
888
914
|
now:(NSTimeInterval)now
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
915
|
+
reason:(RJCaptureHeuristicsReason)reason
|
|
916
|
+
earliestTime:(NSTimeInterval *)earliestTime
|
|
917
|
+
chosenReason:(RJCaptureHeuristicsReason *)chosenReason {
|
|
892
918
|
if (timestamp <= 0) {
|
|
893
919
|
return;
|
|
894
920
|
}
|
|
@@ -520,16 +520,7 @@
|
|
|
520
520
|
}
|
|
521
521
|
|
|
522
522
|
- (void)cleanup {
|
|
523
|
-
// Only cancel the current in-progress segment, don't delete the entire temp
|
|
524
|
-
// directory. Completed segments may still be uploading and must not be
|
|
525
|
-
// deleted here. The RJSegmentUploader handles file cleanup after successful
|
|
526
|
-
// upload.
|
|
527
523
|
[self cancelSegment];
|
|
528
|
-
|
|
529
|
-
// NOTE: Do NOT delete rj_segments directory here!
|
|
530
|
-
// Other segments may be in the middle of uploading.
|
|
531
|
-
// Old orphaned segments are cleaned up by
|
|
532
|
-
// RJSegmentUploader.cleanupOrphanedSegments()
|
|
533
524
|
}
|
|
534
525
|
|
|
535
526
|
#pragma mark - Private Methods
|
|
@@ -561,14 +552,9 @@
|
|
|
561
552
|
size_t width = (size_t)self.currentFrameSize.width;
|
|
562
553
|
size_t height = (size_t)self.currentFrameSize.height;
|
|
563
554
|
|
|
564
|
-
// CRITICAL FIX: Validate incoming image dimensions match expected size
|
|
565
|
-
// During keyboard/rotation transitions, image size may temporarily differ
|
|
566
|
-
// from currentFrameSize, causing CGBitmapContextCreate bytesPerRow mismatch
|
|
567
555
|
size_t imageWidth = CGImageGetWidth(cgImage);
|
|
568
556
|
size_t imageHeight = CGImageGetHeight(cgImage);
|
|
569
557
|
|
|
570
|
-
// Allow small variance (1-2 pixels) due to rounding, but reject major
|
|
571
|
-
// mismatches
|
|
572
558
|
if (labs((long)imageWidth - (long)width) > 2 ||
|
|
573
559
|
labs((long)imageHeight - (long)height) > 2) {
|
|
574
560
|
RJLogDebug(@"Video encoder: Skipping frame - size mismatch (got %zux%zu, "
|
|
@@ -639,9 +625,6 @@
|
|
|
639
625
|
colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
640
626
|
}
|
|
641
627
|
|
|
642
|
-
// CRITICAL: Validate bytesPerRow is sufficient for the target width
|
|
643
|
-
// Error "CGBitmapContextCreate: invalid data bytes/row" occurs when
|
|
644
|
-
// bytesPerRow < width * 4 (4 bytes per pixel for BGRA)
|
|
645
628
|
size_t requiredBytesPerRow = width * 4;
|
|
646
629
|
if (bytesPerRow < requiredBytesPerRow) {
|
|
647
630
|
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
|
@@ -663,7 +646,6 @@
|
|
|
663
646
|
return NULL;
|
|
664
647
|
}
|
|
665
648
|
|
|
666
|
-
// Use fastest interpolation for pixel buffer drawing
|
|
667
649
|
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
|
668
650
|
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
|
|
669
651
|
CGContextRelease(context);
|
|
@@ -681,14 +663,10 @@
|
|
|
681
663
|
}
|
|
682
664
|
|
|
683
665
|
- (void)prewarmPixelBufferPool {
|
|
684
|
-
// Pre-warm the VideoToolbox H.264 encoder by creating a minimal AVAssetWriter
|
|
685
|
-
// and encoding a single dummy frame. This eliminates the ~1.5s spike on first
|
|
686
|
-
// real frame encode by front-loading the hardware encoder initialization.
|
|
687
666
|
dispatch_async(self.encodingQueue, ^{
|
|
688
667
|
@autoreleasepool {
|
|
689
668
|
NSTimeInterval startTime = CACurrentMediaTime();
|
|
690
669
|
|
|
691
|
-
// Use a small size for fast prewarm (H.264 requires even dimensions)
|
|
692
670
|
CGSize warmupSize = CGSizeMake(100, 100);
|
|
693
671
|
|
|
694
672
|
// Create temp file for dummy segment
|
|
@@ -808,15 +786,12 @@ static dispatch_once_t sPrewarmOnceToken;
|
|
|
808
786
|
return;
|
|
809
787
|
sEncoderPrewarmed = YES;
|
|
810
788
|
|
|
811
|
-
// Run prewarm on a low-priority background queue
|
|
812
789
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
|
813
790
|
@autoreleasepool {
|
|
814
791
|
NSTimeInterval startTime = CACurrentMediaTime();
|
|
815
792
|
|
|
816
|
-
// Use a small size for fast prewarm (H.264 requires even dimensions)
|
|
817
793
|
CGSize warmupSize = CGSizeMake(100, 100);
|
|
818
794
|
|
|
819
|
-
// Create temp file for dummy segment
|
|
820
795
|
NSURL *tempDir = [NSURL fileURLWithPath:NSTemporaryDirectory()];
|
|
821
796
|
NSURL *warmupURL =
|
|
822
797
|
[tempDir URLByAppendingPathComponent:@"rj_encoder_prewarm.mp4"];
|
|
@@ -873,7 +848,6 @@ static dispatch_once_t sPrewarmOnceToken;
|
|
|
873
848
|
|
|
874
849
|
[warmupWriter startSessionAtSourceTime:kCMTimeZero];
|
|
875
850
|
|
|
876
|
-
// Create and encode a single dummy frame to trigger H.264 encoder init
|
|
877
851
|
CVPixelBufferRef dummyBuffer = NULL;
|
|
878
852
|
NSDictionary *pixelBufferOpts = @{
|
|
879
853
|
(id)kCVPixelBufferCGImageCompatibilityKey : @YES,
|