From eb3fc2373a5861e0b430139dcdbaa55e23f45f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:25:45 +0200 Subject: [PATCH 1/9] Add readyForDisplay callback --- RCTVideo.m | 19 +++++++++++++------ RCTVideoManager.m | 3 ++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/RCTVideo.m b/RCTVideo.m index a613ded7..69db2336 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -7,6 +7,7 @@ static NSString *const statusKeyPath = @"status"; static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; +static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; @implementation RCTVideo { @@ -213,8 +214,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; [self addPlayerItemObservers]; [_player pause]; - [_playerLayer removeFromSuperlayer]; - _playerLayer = nil; + [self removePlayerLayer]; [_playerViewController.view removeFromSuperview]; _playerViewController = nil; @@ -504,6 +504,8 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; _playerLayer.frame = self.bounds; _playerLayer.needsDisplayOnBoundsChange = YES; + + [_playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; [self.layer addSublayer:_playerLayer]; self.layer.needsDisplayOnBoundsChange = YES; @@ -517,8 +519,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; _controls = controls; if( _controls ) { - [_playerLayer removeFromSuperlayer]; - _playerLayer = nil; + [self removePlayerLayer]; [self usePlayerViewController]; } else @@ -530,6 +531,13 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; } } +- (void)removePlayerLayer +{ + [_playerLayer removeFromSuperlayer]; + [_playerLayer removeObserver:self forKeyPath:readyForDisplayKeyPath]; + _playerLayer = nil; +} + #pragma mark - RCTVideoPlayerViewControllerDelegate - (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController @@ -615,8 +623,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; [_player pause]; _player = nil; - [_playerLayer removeFromSuperlayer]; - _playerLayer = nil; + [self removePlayerLayer]; [_playerViewController.view removeFromSuperview]; _playerViewController = nil; diff --git a/RCTVideoManager.m b/RCTVideoManager.m index b75e379c..e6d93670 100644 --- a/RCTVideoManager.m +++ b/RCTVideoManager.m @@ -28,7 +28,8 @@ RCT_EXPORT_MODULE(); @"onVideoFullscreenPlayerWillPresent", @"onVideoFullscreenPlayerDidPresent", @"onVideoFullscreenPlayerWillDismiss", - @"onVideoFullscreenPlayerDidDismiss" + @"onVideoFullscreenPlayerDidDismiss", + @"onReadyForDisplay" ]; } From 505b94f9a6a71bf2db1cc913124bacdd8ccbc64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:28:29 +0200 Subject: [PATCH 2/9] Add sending onReadyForDisplay callback --- RCTVideo.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RCTVideo.m b/RCTVideo.m index 69db2336..9514c18a 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -308,6 +308,13 @@ static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; } _playerBufferEmpty = NO; } + } else if (object == _playerLayer) { + if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) { + if([change objectForKey:NSKeyValueChangeNewKey]) { + [_eventDispatcher sendInputEventWithName:@"onReadyForDisplay" + body:@{@"target": self.reactTag}]; + } + } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } From f244f662b6cee6ca66c411105fed3eb12b8e1ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:29:09 +0200 Subject: [PATCH 3/9] Add observer for _player rate --- RCTVideo.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RCTVideo.m b/RCTVideo.m index 9514c18a..14dbf5bc 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -8,6 +8,7 @@ static NSString *const statusKeyPath = @"status"; static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; +static NSString *const playbackRate = @"rate"; @implementation RCTVideo { @@ -220,6 +221,7 @@ static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; _player = [AVPlayer playerWithPlayerItem:_playerItem]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; + [_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; // @see endScrubbing in AVPlayerDemoPlaybackViewController.m of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html @@ -628,6 +630,7 @@ static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; - (void)removeFromSuperview { [_player pause]; + [_player removeObserver:self forKeyPath:playbackRate]; _player = nil; [self removePlayerLayer]; From b55f71a29d92543230d8ea948c1def1ab5478286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:37:45 +0200 Subject: [PATCH 4/9] Add onPlaybackRateChange callback --- RCTVideo.m | 6 ++++++ RCTVideoManager.m | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/RCTVideo.m b/RCTVideo.m index 14dbf5bc..446eef04 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -317,6 +317,12 @@ static NSString *const playbackRate = @"rate"; body:@{@"target": self.reactTag}]; } } + } else if (object == _player) { + if([keyPath isEqualToString:playbackRate] && [change objectForKey:NSKeyValueChangeNewKey]) { + [_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange" + body:@{@"playbackRate": _player.rate, + @"target": self.reactTag}]; + } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } diff --git a/RCTVideoManager.m b/RCTVideoManager.m index e6d93670..2e69ea0c 100644 --- a/RCTVideoManager.m +++ b/RCTVideoManager.m @@ -29,7 +29,8 @@ RCT_EXPORT_MODULE(); @"onVideoFullscreenPlayerDidPresent", @"onVideoFullscreenPlayerWillDismiss", @"onVideoFullscreenPlayerDidDismiss", - @"onReadyForDisplay" + @"onReadyForDisplay", + @"onPlaybackRateChange" ]; } From b37243fca250bb7ccc257508a1404264a0bb33f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:38:21 +0200 Subject: [PATCH 5/9] Add playbackStalled and Resume events --- RCTVideo.m | 17 +++++++++++++++++ RCTVideoManager.m | 2 ++ 2 files changed, 19 insertions(+) diff --git a/RCTVideo.m b/RCTVideo.m index 446eef04..b1144025 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -38,6 +38,7 @@ static NSString *const playbackRate = @"rate"; BOOL _muted; BOOL _paused; BOOL _repeat; + BOOL _playbackStalled; NSString * _resizeMode; BOOL _fullscreenPlayerPresented; UIViewController * _presentingViewController; @@ -48,6 +49,7 @@ static NSString *const playbackRate = @"rate"; if ((self = [super init])) { _eventDispatcher = eventDispatcher; + _playbackStalled = NO; _rate = 1.0; _volume = 1.0; _resizeMode = @"AVLayerVideoGravityResizeAspectFill"; @@ -322,6 +324,11 @@ static NSString *const playbackRate = @"rate"; [_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange" body:@{@"playbackRate": _player.rate, @"target": self.reactTag}]; + if(_playbackStalled && _player.rate > 0) { + [_eventDispatcher sendInputEventWithName:@"onPlaybackResume" + body:@{@"playbackRate": _player.rate, + @"target": self.reactTag}]; + } } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; @@ -335,6 +342,16 @@ static NSString *const playbackRate = @"rate"; selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:[_player currentItem]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(playbackStalled:) + name:AVPlayerItemPlaybackStalledNotification + object:nil]; +} + +- (void)playbackStalled:(NSNotification *)notification +{ + [_eventDispatcher sendInputEventWithName:@"onPlaybackStalled" body:@{@"target": self.reactTag}]; + _playbackStalled = YES; } - (void)playerItemDidReachEnd:(NSNotification *)notification diff --git a/RCTVideoManager.m b/RCTVideoManager.m index 2e69ea0c..1eb56369 100644 --- a/RCTVideoManager.m +++ b/RCTVideoManager.m @@ -30,6 +30,8 @@ RCT_EXPORT_MODULE(); @"onVideoFullscreenPlayerWillDismiss", @"onVideoFullscreenPlayerDidDismiss", @"onReadyForDisplay", + @"onPlaybackStalled", + @"onPlaybackResume", @"onPlaybackRateChange" ]; } From c3552fbf1ea78889109516597cc3cf6bfedd4d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:42:44 +0200 Subject: [PATCH 6/9] Add JS handlers --- Video.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Video.js b/Video.js index 60859b17..93f7522e 100644 --- a/Video.js +++ b/Video.js @@ -34,6 +34,10 @@ export default class Video extends Component { this._onFullscreenPlayerDidPresent = this._onFullscreenPlayerDidPresent.bind(this); this._onFullscreenPlayerWillDismiss = this._onFullscreenPlayerWillDismiss.bind(this); this._onFullscreenPlayerDidDismiss = this._onFullscreenPlayerDidDismiss.bind(this); + this._onReadyForDisplay = this._onReadyForDisplay.bind(this); + this._onPlaybackStalled = this._onPlaybackStalled.bind(this); + this._onPlaybackResume = this._onPlaybackResume.bind(this); + this._onPlaybackRateChange = this._onPlaybackRateChange.bind(this); } setNativeProps(nativeProps) { @@ -116,6 +120,30 @@ export default class Video extends Component { } } + _onReadyForDisplay(event) { + if (this.props.onReadyForDisplay) { + this.props.onReadyForDisplay(event.nativeEvent); + } + } + + _onPlaybackStalled(event) { + if (this.props.onPlaybackStalled) { + this.props.onPlaybackStalled(event.nativeEvent); + } + } + + _onPlaybackResume(event) { + if (this.props.onPlaybackResume) { + this.props.onPlaybackResume(event.nativeEvent); + } + } + + _onPlaybackRateChange(event) { + if (this.props.onPlaybackRateChange) { + this.props.onPlaybackRateChange(event.nativeEvent); + } + } + render() { const { source, @@ -161,6 +189,10 @@ export default class Video extends Component { onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent, onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss, onVideoFullscreenPlayerDidDismiss: this._onFullscreenPlayerDidDismiss, + onReadyForDisplay: this._onReadyForDisplay, + onPlaybackStalled: this._onPlaybackStalled, + onPlaybackResume: this._onPlaybackResume, + onPlaybackRateChange: this._onPlaybackRateChange, }); return ( @@ -198,6 +230,10 @@ Video.propTypes = { onFullscreenPlayerDidPresent: PropTypes.func, onFullscreenPlayerWillDismiss: PropTypes.func, onFullscreenPlayerDidDismiss: PropTypes.func, + onReadyForDisplay: PropTypes.func, + onPlaybackStalled: PropTypes.func, + onPlaybackResume: PropTypes.func, + onPlaybackRateChange: PropTypes.func, /* Required by react-native */ scaleX: React.PropTypes.number, From b03c02b4f2a5c8391855f5e189c51ffaf0da3866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:45:14 +0200 Subject: [PATCH 7/9] Fix Obj-C problem --- RCTVideo.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RCTVideo.m b/RCTVideo.m index b1144025..f129f4ad 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -322,11 +322,11 @@ static NSString *const playbackRate = @"rate"; } else if (object == _player) { if([keyPath isEqualToString:playbackRate] && [change objectForKey:NSKeyValueChangeNewKey]) { [_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange" - body:@{@"playbackRate": _player.rate, + body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], @"target": self.reactTag}]; if(_playbackStalled && _player.rate > 0) { [_eventDispatcher sendInputEventWithName:@"onPlaybackResume" - body:@{@"playbackRate": _player.rate, + body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], @"target": self.reactTag}]; } } From d1eae07cfa77b3468348ae95996040d5eeb9fb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:56:50 +0200 Subject: [PATCH 8/9] Unstall video in model --- RCTVideo.m | 1 + 1 file changed, 1 insertion(+) diff --git a/RCTVideo.m b/RCTVideo.m index f129f4ad..08a679cf 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -328,6 +328,7 @@ static NSString *const playbackRate = @"rate"; [_eventDispatcher sendInputEventWithName:@"onPlaybackResume" body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], @"target": self.reactTag}]; + _playbackStalled = NO; } } } else { From e35eaf645fa59b54c52098fe1ee688e114bc4645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 28 Apr 2016 14:59:49 +0200 Subject: [PATCH 9/9] Don't require more details than we set to receive --- RCTVideo.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RCTVideo.m b/RCTVideo.m index 08a679cf..cbc665e0 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -320,7 +320,7 @@ static NSString *const playbackRate = @"rate"; } } } else if (object == _player) { - if([keyPath isEqualToString:playbackRate] && [change objectForKey:NSKeyValueChangeNewKey]) { + if([keyPath isEqualToString:playbackRate]) { [_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange" body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], @"target": self.reactTag}];