diff --git a/Video.js b/Video.js index 1e77052b67..184b00ba59 100644 --- a/Video.js +++ b/Video.js @@ -220,6 +220,16 @@ Video.propTypes = { src: PropTypes.object, seek: PropTypes.number, fullscreen: PropTypes.bool, + onVideoLoadStart: PropTypes.func, + onVideoLoad: PropTypes.func, + onVideoError: PropTypes.func, + onVideoProgress: PropTypes.func, + onVideoSeek: PropTypes.func, + onVideoEnd: PropTypes.func, + onVideoFullscreenPlayerWillPresent: PropTypes.func, + onVideoFullscreenPlayerDidPresent: PropTypes.func, + onVideoFullscreenPlayerWillDismiss: PropTypes.func, + onVideoFullscreenPlayerDidDismiss: PropTypes.func, /* Wrapper component */ source: PropTypes.oneOfType([ diff --git a/ios/RCTVideo.h b/ios/RCTVideo.h index 6f8aeab2fa..b1df0a4398 100644 --- a/ios/RCTVideo.h +++ b/ios/RCTVideo.h @@ -9,6 +9,21 @@ @interface RCTVideo : UIView +@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoError; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillPresent; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidPresent; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillDismiss; +@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidDismiss; +@property (nonatomic, copy) RCTBubblingEventBlock onReadyForDisplay; +@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackStalled; +@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume; +@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange; + - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem; diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 0cb588ca2f..bda6c19b97 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -185,15 +185,14 @@ - (void)sendProgressUpdate const Float64 duration = CMTimeGetSeconds(playerDuration); const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime); if( currentTimeSecs >= 0) { - [_eventDispatcher sendInputEventWithName:@"onVideoProgress" - body:@{ - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], - @"playableDuration": [self calculatePlayableDuration], - @"atValue": [NSNumber numberWithLongLong:currentTime.value], - @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], - @"target": self.reactTag, - @"seekableDuration": [NSNumber numberWithFloat:CMTimeGetSeconds([self playerItemSeekableTimeRange].duration)], - }]; + self.onVideoProgress(@{ + @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], + @"playableDuration": [self calculatePlayableDuration], + @"atValue": [NSNumber numberWithLongLong:currentTime.value], + @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], + @"target": self.reactTag, + @"seekableDuration": [NSNumber numberWithFloat:CMTimeGetSeconds([self playerItemSeekableTimeRange].duration)], + }); } } @@ -275,12 +274,16 @@ - (void)setSrc:(NSDictionary *)source queue:NULL usingBlock:^(CMTime time) { [weakSelf sendProgressUpdate]; } ]; - [_eventDispatcher sendInputEventWithName:@"onVideoLoadStart" - body:@{@"src": @{ - @"uri": [source objectForKey:@"uri"], - @"type": [source objectForKey:@"type"], - @"isNetwork":[NSNumber numberWithBool:(bool)[source objectForKey:@"isNetwork"]]}, - @"target": self.reactTag}]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + //Perform on next run loop, otherwise onVideoLoadStart is nil + self.onVideoLoadStart(@{@"src": @{ + @"uri": [source objectForKey:@"uri"], + @"type": [source objectForKey:@"type"], + @"isNetwork": [NSNumber numberWithBool:(bool)[source objectForKey:@"isNetwork"]]}, + @"target": self.reactTag + }); + }); } - (AVPlayerItem*)playerItemForSource:(NSDictionary *)source @@ -339,30 +342,27 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N orientation = @"portrait"; } - [_eventDispatcher sendInputEventWithName:@"onVideoLoad" - body:@{@"duration": [NSNumber numberWithFloat:duration], - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(_playerItem.currentTime)], - @"canPlayReverse": [NSNumber numberWithBool:_playerItem.canPlayReverse], - @"canPlayFastForward": [NSNumber numberWithBool:_playerItem.canPlayFastForward], - @"canPlaySlowForward": [NSNumber numberWithBool:_playerItem.canPlaySlowForward], - @"canPlaySlowReverse": [NSNumber numberWithBool:_playerItem.canPlaySlowReverse], - @"canStepBackward": [NSNumber numberWithBool:_playerItem.canStepBackward], - @"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward], - @"naturalSize": @{ - @"width": width, - @"height": height, - @"orientation": orientation - }, - @"target": self.reactTag}]; + self.onVideoLoad(@{@"duration": [NSNumber numberWithFloat:duration], + @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(_playerItem.currentTime)], + @"canPlayReverse": [NSNumber numberWithBool:_playerItem.canPlayReverse], + @"canPlayFastForward": [NSNumber numberWithBool:_playerItem.canPlayFastForward], + @"canPlaySlowForward": [NSNumber numberWithBool:_playerItem.canPlaySlowForward], + @"canPlaySlowReverse": [NSNumber numberWithBool:_playerItem.canPlaySlowReverse], + @"canStepBackward": [NSNumber numberWithBool:_playerItem.canStepBackward], + @"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward], + @"naturalSize": @{ + @"width": width, + @"height": height, + @"orientation": orientation + }, + @"target": self.reactTag}); [self attachListeners]; [self applyModifiers]; } else if(_playerItem.status == AVPlayerItemStatusFailed) { - [_eventDispatcher sendInputEventWithName:@"onVideoError" - body:@{@"error": @{ - @"code": [NSNumber numberWithInteger: _playerItem.error.code], - @"domain": _playerItem.error.domain}, - @"target": self.reactTag}]; + self.onVideoError(@{@"error": @{@"code": [NSNumber numberWithInteger: _playerItem.error.code], + @"domain": _playerItem.error.domain}, + @"target": self.reactTag}); } } else if ([keyPath isEqualToString:playbackBufferEmptyKeyPath]) { _playerBufferEmpty = YES; @@ -376,19 +376,16 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } else if (object == _playerLayer) { if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) { if([change objectForKey:NSKeyValueChangeNewKey]) { - [_eventDispatcher sendInputEventWithName:@"onReadyForDisplay" - body:@{@"target": self.reactTag}]; + self.onReadyForDisplay(@{@"target": self.reactTag}); } } } else if (object == _player) { if([keyPath isEqualToString:playbackRate]) { - [_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange" - body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}]; + self.onPlaybackRateChange(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], + @"target": self.reactTag}); if(_playbackStalled && _player.rate > 0) { - [_eventDispatcher sendInputEventWithName:@"onPlaybackResume" - body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], - @"target": self.reactTag}]; + self.onPlaybackResume(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], + @"target": self.reactTag}); _playbackStalled = NO; } } @@ -412,13 +409,13 @@ - (void)attachListeners - (void)playbackStalled:(NSNotification *)notification { - [_eventDispatcher sendInputEventWithName:@"onPlaybackStalled" body:@{@"target": self.reactTag}]; + self.onPlaybackStalled(@{@"target": self.reactTag}); _playbackStalled = YES; } - (void)playerItemDidReachEnd:(NSNotification *)notification { - [_eventDispatcher sendInputEventWithName:@"onVideoEnd" body:@{@"target": self.reactTag}]; + self.onVideoEnd(@{@"target": self.reactTag}); if (_repeat) { AVPlayerItem *item = [notification object]; @@ -490,10 +487,9 @@ - (void)setSeek:(float)seekTime if (CMTimeCompare(current, cmSeekTime) != 0) { [_player seekToTime:cmSeekTime toleranceBefore:tolerance toleranceAfter:tolerance completionHandler:^(BOOL finished) { - [_eventDispatcher sendInputEventWithName:@"onVideoSeek" - body:@{@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], - @"seekTime": [NSNumber numberWithFloat:seekTime], - @"target": self.reactTag}]; + self.onVideoSeek(@{@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], + @"seekTime": [NSNumber numberWithFloat:seekTime], + @"target": self.reactTag}); }]; _pendingSeek = false; @@ -575,11 +571,11 @@ - (void)setFullscreen:(BOOL)fullscreen if( viewController ) { _presentingViewController = viewController; - [_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerWillPresent" body:@{@"target": self.reactTag}]; + self.onVideoFullscreenPlayerWillPresent(@{@"target": self.reactTag}); [viewController presentViewController:_playerViewController animated:true completion:^{ _playerViewController.showsPlaybackControls = YES; _fullscreenPlayerPresented = fullscreen; - [_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerDidPresent" body:@{@"target": self.reactTag}]; + self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); }]; } } @@ -659,7 +655,7 @@ - (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerVie { if (_playerViewController == playerViewController && _fullscreenPlayerPresented) { - [_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerWillDismiss" body:@{@"target": self.reactTag}]; + self.onVideoFullscreenPlayerWillDismiss(@{@"target": self.reactTag}); } } @@ -670,7 +666,7 @@ - (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerView _fullscreenPlayerPresented = false; _presentingViewController = nil; [self applyModifiers]; - [_eventDispatcher sendInputEventWithName:@"onVideoFullscreenPlayerDidDismiss" body:@{@"target": self.reactTag}]; + self.onVideoFullscreenPlayerDidDismiss(@{@"target": self.reactTag}); } } diff --git a/ios/RCTVideoManager.m b/ios/RCTVideoManager.m index 959e9359f1..c4bfa4d6e4 100644 --- a/ios/RCTVideoManager.m +++ b/ios/RCTVideoManager.m @@ -14,28 +14,6 @@ - (UIView *)view return [[RCTVideo alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; } -/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ - -- (NSArray *)customDirectEventTypes -{ - return @[ - @"onVideoLoadStart", - @"onVideoLoad", - @"onVideoError", - @"onVideoProgress", - @"onVideoSeek", - @"onVideoEnd", - @"onVideoFullscreenPlayerWillPresent", - @"onVideoFullscreenPlayerDidPresent", - @"onVideoFullscreenPlayerWillDismiss", - @"onVideoFullscreenPlayerDidDismiss", - @"onReadyForDisplay", - @"onPlaybackStalled", - @"onPlaybackResume", - @"onPlaybackRateChange" - ]; -} - - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); @@ -55,6 +33,21 @@ - (dispatch_queue_t)methodQueue RCT_EXPORT_VIEW_PROPERTY(currentTime, float); RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL); RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); +/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ +RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillPresent, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidPresent, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillDismiss, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidDismiss, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onReadyForDisplay, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTBubblingEventBlock); - (NSDictionary *)constantsToExport {