From 45d79b701e99c39ea1abc73d84e3d9a3a4fa4bcc Mon Sep 17 00:00:00 2001 From: Johannes Lumpe Date: Thu, 9 Apr 2015 01:15:57 +0300 Subject: [PATCH] Initial commit for seeking --- RCTVideo.h | 1 + RCTVideo.m | 63 +++++++++++++++++++++++++++++++++++++++++------ RCTVideoManager.m | 4 +++ Video.ios.js | 12 +++++++-- 4 files changed, 70 insertions(+), 10 deletions(-) diff --git a/RCTVideo.h b/RCTVideo.h index a2888ad0..e6329434 100644 --- a/RCTVideo.h +++ b/RCTVideo.h @@ -3,6 +3,7 @@ extern NSString *const RNVideoEventLoaded; extern NSString *const RNVideoEventLoading; extern NSString *const RNVideoEventProgress; +extern NSString *const RNVideoEventSeek; extern NSString *const RNVideoEventLoadingError; @class RCTEventDispatcher; diff --git a/RCTVideo.m b/RCTVideo.m index 18a658c8..0f286698 100644 --- a/RCTVideo.m +++ b/RCTVideo.m @@ -9,6 +9,7 @@ NSString *const RNVideoEventLoaded = @"videoLoaded"; NSString *const RNVideoEventLoading = @"videoLoading"; NSString *const RNVideoEventProgress = @"videoProgress"; +NSString *const RNVideoEventSeek = @"videoSeek"; NSString *const RNVideoEventLoadingError = @"videoLoadError"; static NSString *const statusKeyPath = @"status"; @@ -23,6 +24,10 @@ static NSString *const statusKeyPath = @"status"; /* Required to publish events */ RCTEventDispatcher *_eventDispatcher; + bool _pendingSeek; + float _pendingSeekTime; + float _lastSeekTime; + /* For sending videoProgress events */ id _progressUpdateTimer; int _progressUpdateInterval; @@ -39,6 +44,10 @@ static NSString *const statusKeyPath = @"status"; _eventDispatcher = eventDispatcher; _rate = 1.0; _volume = 1.0; + + _pendingSeek = false; + _pendingSeekTime = 0.0f; + _lastSeekTime = 0.0f; } return self; } @@ -169,8 +178,7 @@ static NSString *const statusKeyPath = @"status"; _playerLayer.videoGravity = mode; } -- (void)setPaused:(BOOL)paused -{ +- (void)setPaused:(BOOL)paused { if (paused) { [self stopProgressTimer]; [_player pause]; @@ -181,14 +189,54 @@ static NSString *const statusKeyPath = @"status"; } } -- (void)setRate:(float)rate -{ +- (void)setSeek:(NSDictionary*)seek { + NSNumber *seekValue = [seek objectForKey:@"time"]; + if (!seekValue) { + return; + } + + int timeScale = 10000; + float seekTime = [RCTConvert float:seekValue]; + bool force = [RCTConvert BOOL:[seek objectForKey:@"force"]]; + + if (seekTime == _lastSeekTime && !force) { + return; + } + + AVPlayerItem *item = _player.currentItem; + if (item && item.status == AVPlayerItemStatusReadyToPlay) { + // TODO check loadedTimeRanges + + CMTime cmSeekTime = CMTimeMakeWithSeconds(seekTime, timeScale); + CMTime current = item.currentTime; + // TODO figure out a good tolerance level + CMTime tolerance = CMTimeMake(1000, timeScale); + + if (CMTimeCompare(current, cmSeekTime) != 0) { + [_player seekToTime:cmSeekTime toleranceBefore:tolerance toleranceAfter:tolerance]; + _pendingSeek = false; + _lastSeekTime = seekTime; + [_eventDispatcher sendInputEventWithName:RNVideoEventSeek body:@{ + @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], + @"seekTime": seekValue, + @"target": self.reactTag + }]; + } + + } else { + // TODO see if this makes sense and if so, + // actually implement it + _pendingSeek = true; + _pendingSeekTime = seekTime; + } +} + +- (void)setRate:(float)rate { _rate = rate; [self applyModifiers]; } -- (void)setMuted:(BOOL)muted -{ +- (void)setMuted:(BOOL)muted { _muted = muted; [self applyModifiers]; } @@ -198,8 +246,7 @@ static NSString *const statusKeyPath = @"status"; [self applyModifiers]; } -- (void)applyModifiers -{ +- (void)applyModifiers { /* volume must be set to 0 if muted is YES, or the video freezes playback */ if (_muted) { [_player setVolume:0]; diff --git a/RCTVideoManager.m b/RCTVideoManager.m index 008d1f5f..f61620b8 100644 --- a/RCTVideoManager.m +++ b/RCTVideoManager.m @@ -29,6 +29,9 @@ RNVideoEventProgress: @{ @"registrationName": @"onProgress" }, + RNVideoEventSeek: @{ + @"registrationName": @"onSeek" + } }; } @@ -39,6 +42,7 @@ RCT_EXPORT_VIEW_PROPERTY(paused, BOOL); RCT_EXPORT_VIEW_PROPERTY(muted, BOOL); RCT_EXPORT_VIEW_PROPERTY(volume, float); RCT_EXPORT_VIEW_PROPERTY(rate, float); +RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary); - (NSDictionary *)constantsToExport { diff --git a/Video.ios.js b/Video.ios.js index 706096a7..98946d56 100644 --- a/Video.ios.js +++ b/Video.ios.js @@ -21,6 +21,7 @@ var Video = React.createClass({ resizeMode: PropTypes.string, repeat: PropTypes.bool, paused: PropTypes.bool, + seek: PropTypes.number, muted: PropTypes.bool, volume: PropTypes.number, rate: PropTypes.number, @@ -53,6 +54,10 @@ var Video = React.createClass({ this.props.onProgress && this.props.onProgress(event.nativeEvent); }, + _onSeek(event) { + this.props.onSeek && this.props.onSeek(event.nativeEvent); + }, + render() { var style = flattenStyle([styles.base, this.props.style]); var source = this.props.source; @@ -71,6 +76,10 @@ var Video = React.createClass({ } var nativeProps = merge(this.props, { + seek: { + time: this.props.seek, + force: this.props.forceSeek + }, style, resizeMode: resizeMode, src: { @@ -81,7 +90,6 @@ var Video = React.createClass({ }, onLoad: this._onLoad, onProgress: this._onProgress, - }); return @@ -91,7 +99,7 @@ var Video = React.createClass({ var RCTVideo = createReactIOSNativeComponentClass({ validAttributes: merge(ReactIOSViewAttributes.UIView, {src: {diff: deepDiffer}, resizeMode: true, repeat: true, - paused: true, muted: true, volume: true, rate: true}), + seek: {diff: () => true}, paused: true, muted: true, volume: true, rate: true}), uiViewClassName: 'RCTVideo', });