From f7a1025f40db3ef9dab98afccd3149372e9ccd1e Mon Sep 17 00:00:00 2001 From: jedagen Date: Fri, 27 Feb 2026 12:19:40 -0800 Subject: [PATCH] feat(ios): add blurIntensity support for PiP video call view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `blurIntensity` option (0.0–1.0) to the `iosPIP` prop that overlays a variable-intensity UIVisualEffectView on the SampleBufferVideoCallView. This allows apps to blur the PiP camera feed for privacy without disabling the camera entirely. Uses UIViewPropertyAnimator with fractionComplete to control the blur amount, supporting any value between 0 (no blur) and 1 (full blur). Usage: iosPIP={{ blurIntensity: 0.8, startAutomatically: true, ... }} Co-Authored-By: Claude Opus 4.6 --- ios/RCTWebRTC/PIPController.h | 1 + ios/RCTWebRTC/PIPController.m | 5 +++++ ios/RCTWebRTC/RTCVideoViewManager.m | 6 +++++ ios/RCTWebRTC/SampleBufferVideoCallView.h | 1 + ios/RCTWebRTC/SampleBufferVideoCallView.m | 27 +++++++++++++++++++++++ 5 files changed, 40 insertions(+) diff --git a/ios/RCTWebRTC/PIPController.h b/ios/RCTWebRTC/PIPController.h index 6e81776c5..7ebb89208 100644 --- a/ios/RCTWebRTC/PIPController.h +++ b/ios/RCTWebRTC/PIPController.h @@ -13,6 +13,7 @@ API_AVAILABLE(ios(15.0)) @property(nonatomic, assign) BOOL startAutomatically; @property(nonatomic, assign) BOOL stopAutomatically; @property(nonatomic, assign) CGSize preferredSize; +@property(nonatomic, assign) CGFloat blurIntensity; - (instancetype)initWithSourceView:(UIView *)sourceView; - (void)togglePIP; diff --git a/ios/RCTWebRTC/PIPController.m b/ios/RCTWebRTC/PIPController.m index 5137c71d5..8bade307d 100644 --- a/ios/RCTWebRTC/PIPController.m +++ b/ios/RCTWebRTC/PIPController.m @@ -127,6 +127,11 @@ - (void)setPreferredSize:(CGSize)size { } } +- (void)setBlurIntensity:(CGFloat)blurIntensity { + _blurIntensity = blurIntensity; + _sampleView.blurIntensity = blurIntensity; +} + - (BOOL)startAutomatically { return _pipController.canStartPictureInPictureAutomaticallyFromInline; } diff --git a/ios/RCTWebRTC/RTCVideoViewManager.m b/ios/RCTWebRTC/RTCVideoViewManager.m index e64005daf..1e0ad1cbd 100644 --- a/ios/RCTWebRTC/RTCVideoViewManager.m +++ b/ios/RCTWebRTC/RTCVideoViewManager.m @@ -197,10 +197,16 @@ - (void)API_AVAILABLE(ios(15.0))setPIPOptions:(NSDictionary *)pipOptions { _pipController.videoTrack = _videoTrack; } + CGFloat blurIntensity = 0; + if ([pipOptions objectForKey:@"blurIntensity"]) { + blurIntensity = [pipOptions[@"blurIntensity"] doubleValue]; + } + _pipController.startAutomatically = startAutomatically; _pipController.stopAutomatically = stopAutomatically; _pipController.objectFit = _objectFit; _pipController.preferredSize = preferredSize; + _pipController.blurIntensity = blurIntensity; } - (void)API_AVAILABLE(ios(15.0))startPIP { diff --git a/ios/RCTWebRTC/SampleBufferVideoCallView.h b/ios/RCTWebRTC/SampleBufferVideoCallView.h index 6f7e5a2d8..64f20d378 100644 --- a/ios/RCTWebRTC/SampleBufferVideoCallView.h +++ b/ios/RCTWebRTC/SampleBufferVideoCallView.h @@ -7,6 +7,7 @@ @property(nonnull, nonatomic, readonly) AVSampleBufferDisplayLayer *sampleBufferLayer; @property(nonatomic, assign) BOOL shouldRender; +@property(nonatomic, assign) CGFloat blurIntensity; - (void)requestScaleRecalculation; @end diff --git a/ios/RCTWebRTC/SampleBufferVideoCallView.m b/ios/RCTWebRTC/SampleBufferVideoCallView.m index b1a0b960b..c6af6d513 100644 --- a/ios/RCTWebRTC/SampleBufferVideoCallView.m +++ b/ios/RCTWebRTC/SampleBufferVideoCallView.m @@ -20,6 +20,7 @@ @interface SampleBufferVideoCallView () @property(nonatomic, retain) I420Converter *i420Converter; @property(nonatomic, strong) id renderer; @property(nonatomic, assign) NSInteger currentRotation; +@property(nonatomic, strong) UIVisualEffectView *blurEffectView; @end @@ -171,6 +172,32 @@ - (CVPixelBufferRef)pixelBufferFromI420:(RTCI420Buffer *)i420Buffer { return convertedPixelBuffer; } +- (void)setBlurIntensity:(CGFloat)blurIntensity { + blurIntensity = MAX(0.0, MIN(1.0, blurIntensity)); + if (_blurIntensity == blurIntensity) { + return; + } + _blurIntensity = blurIntensity; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (blurIntensity > 0) { + if (!self.blurEffectView) { + UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + self.blurEffectView = [[UIVisualEffectView alloc] initWithEffect:effect]; + self.blurEffectView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + } + self.blurEffectView.frame = self.bounds; + self.blurEffectView.alpha = blurIntensity; + if (!self.blurEffectView.superview) { + [self addSubview:self.blurEffectView]; + } + } else { + [self.blurEffectView removeFromSuperview]; + } + }); +} + - (void)dealloc { [_i420Converter unprepareForAccelerateConversion]; }