Added fullscreen options for iOS Player

This commit is contained in:
Ash Mishra 2018-08-09 09:58:03 -07:00
parent 3ba26eb45a
commit f45d6a2c3e
5 changed files with 159 additions and 97 deletions

View File

@ -289,6 +289,11 @@ Video.propTypes = {
PropTypes.object PropTypes.object
]), ]),
fullscreen: PropTypes.bool, fullscreen: PropTypes.bool,
fullscreenOptions: PropTypes.shape({
enabled: PropTypes.bool,
preferredOrientation: PropTypes.string,
autorotate: PropTypes.bool
}),
onVideoLoadStart: PropTypes.func, onVideoLoadStart: PropTypes.func,
onVideoLoad: PropTypes.func, onVideoLoad: PropTypes.func,
onVideoBuffer: PropTypes.func, onVideoBuffer: PropTypes.func,

View File

@ -62,6 +62,7 @@ static int const RCTVideoUnset = -1;
NSString * _ignoreSilentSwitch; NSString * _ignoreSilentSwitch;
NSString * _resizeMode; NSString * _resizeMode;
BOOL _fullscreen; BOOL _fullscreen;
NSDictionary* _fullscreenOptions;
BOOL _fullscreenPlayerPresented; BOOL _fullscreenPlayerPresented;
UIViewController * _presentingViewController; UIViewController * _presentingViewController;
#if __has_include(<react-native-video/RCTVideoCache.h>) #if __has_include(<react-native-video/RCTVideoCache.h>)
@ -117,13 +118,20 @@ static int const RCTVideoUnset = -1;
} }
- (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem { - (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem {
RCTVideoPlayerViewController* playerLayer= [[RCTVideoPlayerViewController alloc] init];
playerLayer.showsPlaybackControls = YES; RCTVideoPlayerViewController* playerLayer= [[RCTVideoPlayerViewController alloc] init];
playerLayer.rctDelegate = self; playerLayer.showsPlaybackControls = YES;
playerLayer.view.frame = self.bounds; playerLayer.rctDelegate = self;
playerLayer.player = player;
playerLayer.view.frame = self.bounds; if (_fullscreenOptions) {
return playerLayer; playerLayer.preferredOrientation = [RCTConvert NSString:[_fullscreenOptions objectForKey:@"preferredOrientation"]];
playerLayer.autorotate = [RCTConvert BOOL:[_fullscreenOptions objectForKey:@"autorotate"]];
}
playerLayer.view.frame = self.bounds;
playerLayer.player = player;
playerLayer.view.frame = self.bounds;
return playerLayer;
} }
/* --------------------------------------------------------- /* ---------------------------------------------------------
@ -214,11 +222,11 @@ static int const RCTVideoUnset = -1;
- (void)audioRouteChanged:(NSNotification *)notification - (void)audioRouteChanged:(NSNotification *)notification
{ {
NSNumber *reason = [[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey]; NSNumber *reason = [[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey];
NSNumber *previousRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey]; NSNumber *previousRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
if (reason.unsignedIntValue == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { if (reason.unsignedIntValue == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
self.onVideoAudioBecomingNoisy(@{@"target": self.reactTag}); self.onVideoAudioBecomingNoisy(@{@"target": self.reactTag});
} }
} }
#pragma mark - Progress #pragma mark - Progress
@ -343,7 +351,7 @@ static int const RCTVideoUnset = -1;
[self addPlayerTimeObserver]; [self addPlayerTimeObserver];
[self setFullscreen:_fullscreen]; [self setFullscreenOptions:_fullscreenOptions];
//Perform on next run loop, otherwise onVideoLoadStart is nil //Perform on next run loop, otherwise onVideoLoadStart is nil
if (self.onVideoLoadStart) { if (self.onVideoLoadStart) {
@ -422,9 +430,9 @@ static int const RCTVideoUnset = -1;
addMutableTrackWithMediaType:AVMediaTypeText addMutableTrackWithMediaType:AVMediaTypeText
preferredTrackID:kCMPersistentTrackID_Invalid]; preferredTrackID:kCMPersistentTrackID_Invalid];
[textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) [textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration)
ofTrack:textTrackAsset ofTrack:textTrackAsset
atTime:kCMTimeZero atTime:kCMTimeZero
error:nil]; error:nil];
} }
if (validTextTracks.count != _textTracks.count) { if (validTextTracks.count != _textTracks.count) {
[self setTextTracks:validTextTracks]; [self setTextTracks:validTextTracks];
@ -719,8 +727,8 @@ static int const RCTVideoUnset = -1;
- (void)setAllowsExternalPlayback:(BOOL)allowsExternalPlayback - (void)setAllowsExternalPlayback:(BOOL)allowsExternalPlayback
{ {
_allowsExternalPlayback = allowsExternalPlayback; _allowsExternalPlayback = allowsExternalPlayback;
_player.allowsExternalPlayback = _allowsExternalPlayback; _player.allowsExternalPlayback = _allowsExternalPlayback;
} }
- (void)setPlayWhenInactive:(BOOL)playWhenInactive - (void)setPlayWhenInactive:(BOOL)playWhenInactive
@ -853,52 +861,52 @@ static int const RCTVideoUnset = -1;
- (void)setMediaSelectionTrackForCharacteristic:(AVMediaCharacteristic)characteristic - (void)setMediaSelectionTrackForCharacteristic:(AVMediaCharacteristic)characteristic
withCriteria:(NSDictionary *)criteria withCriteria:(NSDictionary *)criteria
{ {
NSString *type = criteria[@"type"]; NSString *type = criteria[@"type"];
AVMediaSelectionGroup *group = [_player.currentItem.asset AVMediaSelectionGroup *group = [_player.currentItem.asset
mediaSelectionGroupForMediaCharacteristic:characteristic]; mediaSelectionGroupForMediaCharacteristic:characteristic];
AVMediaSelectionOption *mediaOption; AVMediaSelectionOption *mediaOption;
if ([type isEqualToString:@"disabled"]) { if ([type isEqualToString:@"disabled"]) {
// Do nothing. We want to ensure option is nil // Do nothing. We want to ensure option is nil
} else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) {
NSString *value = criteria[@"value"]; NSString *value = criteria[@"value"];
for (int i = 0; i < group.options.count; ++i) { for (int i = 0; i < group.options.count; ++i) {
AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i];
NSString *optionValue; NSString *optionValue;
if ([type isEqualToString:@"language"]) { if ([type isEqualToString:@"language"]) {
optionValue = [currentOption extendedLanguageTag]; optionValue = [currentOption extendedLanguageTag];
} else { } else {
optionValue = [[[currentOption commonMetadata] optionValue = [[[currentOption commonMetadata]
valueForKey:@"value"] valueForKey:@"value"]
objectAtIndex:0]; objectAtIndex:0];
} }
if ([value isEqualToString:optionValue]) { if ([value isEqualToString:optionValue]) {
mediaOption = currentOption; mediaOption = currentOption;
break; break;
} }
}
//} else if ([type isEqualToString:@"default"]) {
// option = group.defaultOption; */
} else if ([type isEqualToString:@"index"]) {
if ([criteria[@"value"] isKindOfClass:[NSNumber class]]) {
int index = [criteria[@"value"] intValue];
if (group.options.count > index) {
mediaOption = [group.options objectAtIndex:index];
}
}
} else { // default. invalid type or "system"
[_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group];
return;
} }
//} else if ([type isEqualToString:@"default"]) {
// option = group.defaultOption; */
} else if ([type isEqualToString:@"index"]) {
if ([criteria[@"value"] isKindOfClass:[NSNumber class]]) {
int index = [criteria[@"value"] intValue];
if (group.options.count > index) {
mediaOption = [group.options objectAtIndex:index];
}
}
} else { // default. invalid type or "system"
[_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group];
return;
}
// If a match isn't found, option will be nil and text tracks will be disabled // If a match isn't found, option will be nil and text tracks will be disabled
[_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group];
} }
- (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack { - (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack {
_selectedAudioTrack = selectedAudioTrack; _selectedAudioTrack = selectedAudioTrack;
[self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicAudible [self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicAudible
withCriteria:_selectedAudioTrack]; withCriteria:_selectedAudioTrack];
} }
- (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { - (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack {
@ -1033,25 +1041,25 @@ static int const RCTVideoUnset = -1;
- (NSArray *)getAudioTrackInfo - (NSArray *)getAudioTrackInfo
{ {
NSMutableArray *audioTracks = [[NSMutableArray alloc] init]; NSMutableArray *audioTracks = [[NSMutableArray alloc] init];
AVMediaSelectionGroup *group = [_player.currentItem.asset AVMediaSelectionGroup *group = [_player.currentItem.asset
mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
for (int i = 0; i < group.options.count; ++i) { for (int i = 0; i < group.options.count; ++i) {
AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i]; AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i];
NSString *title = @""; NSString *title = @"";
NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"]; NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"];
if (values.count > 0) { if (values.count > 0) {
title = [values objectAtIndex:0]; title = [values objectAtIndex:0];
}
NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @"";
NSDictionary *audioTrack = @{
@"index": [NSNumber numberWithInt:i],
@"title": title,
@"language": language
};
[audioTracks addObject:audioTrack];
} }
return audioTracks; NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @"";
NSDictionary *audioTrack = @{
@"index": [NSNumber numberWithInt:i],
@"title": title,
@"language": language
};
[audioTracks addObject:audioTrack];
}
return audioTracks;
} }
- (NSArray *)getTextTrackInfo - (NSArray *)getTextTrackInfo
@ -1086,10 +1094,37 @@ static int const RCTVideoUnset = -1;
return _fullscreenPlayerPresented; return _fullscreenPlayerPresented;
} }
- (void)setFullscreen:(BOOL)fullscreen - (void)setFullscreen:(BOOL) fullscreen {
{
_fullscreen = fullscreen; _fullscreen = fullscreen;
if( fullscreen && !_fullscreenPlayerPresented && _player )
NSString* enabled = _fullscreen ? @"1" : @"0";
if (!_fullscreenOptions) {
[self setFullscreenOptions:
@{
@"enabled": enabled,
@"autorotate": @"0",
@"preferredOrientation": @"default"
}];
}
else {
[_fullscreenOptions setValue:enabled forKey:@"enabled"];
[self setFullscreenOptions:_fullscreenOptions];
}
}
- (void)setFullscreenOptions:(NSDictionary*) fullscreenOptions
{
_fullscreenOptions = fullscreenOptions;
if (!_fullscreenOptions) return;
BOOL fullscreenEnabled = [RCTConvert BOOL:[_fullscreenOptions objectForKey:@"enabled"]];
if( fullscreenEnabled && !_fullscreenPlayerPresented && _player )
{
// Ensure player view controller is not null
if( !_playerViewController )
{ {
[self usePlayerViewController]; [self usePlayerViewController];
} }
@ -1115,14 +1150,14 @@ static int const RCTVideoUnset = -1;
} }
[viewController presentViewController:_playerViewController animated:true completion:^{ [viewController presentViewController:_playerViewController animated:true completion:^{
_playerViewController.showsPlaybackControls = YES; _playerViewController.showsPlaybackControls = YES;
_fullscreenPlayerPresented = fullscreen; _fullscreenPlayerPresented = fullscreenEnabled;
if(self.onVideoFullscreenPlayerDidPresent) { if(self.onVideoFullscreenPlayerDidPresent) {
self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag});
} }
}]; }];
} }
} }
else if ( !fullscreen && _fullscreenPlayerPresented ) else if ( !fullscreenEnabled && _fullscreenPlayerPresented )
{ {
[self videoPlayerViewControllerWillDismiss:_playerViewController]; [self videoPlayerViewControllerWillDismiss:_playerViewController];
[_presentingViewController dismissViewControllerAnimated:true completion:^{ [_presentingViewController dismissViewControllerAnimated:true completion:^{

View File

@ -36,7 +36,8 @@ RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString);
RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(rate, float);
RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(currentTime, float); RCT_EXPORT_VIEW_PROPERTY(currentTime, float);
RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL); RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL); // deprecated property
RCT_EXPORT_VIEW_PROPERTY(fullscreenOptions, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);

View File

@ -12,4 +12,9 @@
@interface RCTVideoPlayerViewController : AVPlayerViewController @interface RCTVideoPlayerViewController : AVPlayerViewController
@property (nonatomic, weak) id<RCTVideoPlayerViewControllerDelegate> rctDelegate; @property (nonatomic, weak) id<RCTVideoPlayerViewControllerDelegate> rctDelegate;
// Optional paramters
@property (nonatomic, weak) NSString* preferredOrientation;
@property (nonatomic) BOOL autorotate;
@end @end

View File

@ -6,6 +6,14 @@
@implementation RCTVideoPlayerViewController @implementation RCTVideoPlayerViewController
- (id)init {
self = [super init];
if (self) {
self.autorotate = false;
}
return self;
}
- (void)viewDidDisappear:(BOOL)animated - (void)viewDidDisappear:(BOOL)animated
{ {
[super viewDidDisappear:animated]; [super viewDidDisappear:animated];
@ -14,17 +22,25 @@
} }
- (BOOL)shouldAutorotate { - (BOOL)shouldAutorotate {
return YES; return self.autorotate;
} }
- (UIInterfaceOrientationMask)supportedInterfaceOrientations { - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape; return UIInterfaceOrientationMaskAll;
} }
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
if ([self.preferredOrientation.lowercaseString isEqualToString:@"landscape"]) {
return UIInterfaceOrientationLandscapeLeft; return UIInterfaceOrientationLandscapeLeft;
}
else if ([self.preferredOrientation.lowercaseString isEqualToString:@"portrait"]) {
return UIInterfaceOrientationPortrait;
}
else { // default case
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
return orientation;
}
} }
@end @end