Implement picture in picture for iOS
Test Plan: - Run on ipad - test out onIsPictureInPictureSupported, onIsPictureInPictureActive, restoreUserInterfaceForPictureInPictureStop, startPictureInPicture, stopPictureInPicture
This commit is contained in:
committed by
Abdulrahman Alzenki
parent
35e26427ea
commit
617b046789
@@ -16,7 +16,7 @@
|
||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate>
|
||||
#else
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate>
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate>
|
||||
#endif
|
||||
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
||||
@@ -38,6 +38,8 @@
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume;
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange;
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoExternalPlaybackChange;
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onIsPictureInPictureSupported;
|
||||
@property (nonatomic, copy) RCTBubblingEventBlock onIsPictureInPictureActive;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
@@ -27,6 +27,8 @@ static int const RCTVideoUnset = -1;
|
||||
AVPlayer *_player;
|
||||
AVPlayerItem *_playerItem;
|
||||
NSDictionary *_source;
|
||||
AVPictureInPictureController *_pipController;
|
||||
void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)(BOOL);
|
||||
BOOL _playerItemObserversSet;
|
||||
BOOL _playerBufferEmpty;
|
||||
AVPlayerLayer *_playerLayer;
|
||||
@@ -101,6 +103,7 @@ static int const RCTVideoUnset = -1;
|
||||
_allowsExternalPlayback = YES;
|
||||
_playWhenInactive = false;
|
||||
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler = NULL;
|
||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||
_videoCache = [RCTVideoCache sharedInstance];
|
||||
#endif
|
||||
@@ -379,6 +382,12 @@ static int const RCTVideoUnset = -1;
|
||||
@"target": self.reactTag
|
||||
});
|
||||
}
|
||||
|
||||
if (@available(iOS 9, *)) {
|
||||
if (self.onIsPictureInPictureSupported) {
|
||||
self.onIsPictureInPictureSupported(@{@"supported": [NSNumber numberWithBool:(bool)[AVPictureInPictureController isPictureInPictureSupported]]});
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
_videoLoadStarted = YES;
|
||||
@@ -780,6 +789,37 @@ static int const RCTVideoUnset = -1;
|
||||
_playWhenInactive = playWhenInactive;
|
||||
}
|
||||
|
||||
- (void)setPictureInPicture:(BOOL)pictureInPicture
|
||||
{
|
||||
if (_pipController && pictureInPicture && ![_pipController isPictureInPictureActive]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_pipController startPictureInPicture];
|
||||
});
|
||||
} else if (_pipController && !pictureInPicture && [_pipController isPictureInPictureActive]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_pipController stopPictureInPicture];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore
|
||||
{
|
||||
if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) {
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler(restore);
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupPipController {
|
||||
if (@available(iOS 9, *)) {
|
||||
if (!_pipController && _playerLayer && [AVPictureInPictureController isPictureInPictureSupported]) {
|
||||
// Create new controller passing reference to the AVPlayerLayer
|
||||
_pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:_playerLayer];
|
||||
_pipController.delegate = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch
|
||||
{
|
||||
_ignoreSilentSwitch = ignoreSilentSwitch;
|
||||
@@ -1234,6 +1274,8 @@ static int const RCTVideoUnset = -1;
|
||||
|
||||
[self.layer addSublayer:_playerLayer];
|
||||
self.layer.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
[self setupPipController];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1490,4 +1532,35 @@ static int const RCTVideoUnset = -1;
|
||||
return array[0];
|
||||
}
|
||||
|
||||
#pragma mark - Picture in Picture
|
||||
|
||||
- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||
if (self.onIsPictureInPictureActive && _pipController) {
|
||||
self.onIsPictureInPictureActive(@{@"active": [NSNumber numberWithBool:false]});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||
if (self.onIsPictureInPictureActive && _pipController) {
|
||||
self.onIsPictureInPictureActive(@{@"active": [NSNumber numberWithBool:true]});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||
|
||||
}
|
||||
|
||||
- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
|
||||
|
||||
}
|
||||
|
||||
- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error {
|
||||
|
||||
}
|
||||
|
||||
- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL))completionHandler {
|
||||
NSAssert(_restoreUserInterfaceForPIPStopCompletionHandler == NULL, @"restoreUserInterfaceForPIPStopCompletionHandler was not called after picture in picture was exited.");
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler = completionHandler;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@@ -42,6 +42,8 @@ RCT_EXPORT_VIEW_PROPERTY(fullscreenOrientation, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(filter, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL);
|
||||
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(pictureInPicture, BOOL);
|
||||
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
||||
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
|
||||
@@ -77,6 +79,8 @@ RCT_REMAP_METHOD(save,
|
||||
}
|
||||
}];
|
||||
}
|
||||
RCT_EXPORT_VIEW_PROPERTY(onIsPictureInPictureSupported, RCTBubblingEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onIsPictureInPictureActive, RCTBubblingEventBlock);
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
|
Reference in New Issue
Block a user