Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,17 +293,19 @@ playbackAfterRebufferMs | number | The default duration of media that must be bu

This prop should only be set when you are setting the source, changing it after the media is loaded will cause it to be reloaded.

On iOS, only `bufferForPlaybackMs` and `bufferForPlaybackAfterRebufferMs` is supported. If these values are not specified, or no `bufferConfig` is supplied, then the default AVPlayer buffering is used. This behaviour tries to determine whether the video item is likely to play through given the current buffer rate, and if so, the video starts to play.

Example with default values:
```
bufferConfig={{
minBufferMs: 15000,
maxBufferMs: 50000,
minBufferMs: 15000, // not supported on iOS
maxBufferMs: 50000, // not supported on iOS
bufferForPlaybackMs: 2500,
bufferForPlaybackAfterRebufferMs: 5000
}}
```

Platforms: Android ExoPlayer
Platforms: Android ExoPlayer, iOS

#### ignoreSilentSwitch
Controls the iOS silent switch behavior
Expand Down
66 changes: 54 additions & 12 deletions ios/RCTVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp";
static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
static NSString *const readyForDisplayKeyPath = @"readyForDisplay";
static NSString *const loadedTimeRangesKeyPath = @"loadedTimeRanges";
static NSString *const playbackRate = @"rate";
static NSString *const timedMetadata = @"timedMetadata";

Expand Down Expand Up @@ -45,6 +46,11 @@ @implementation RCTVideo
Float64 _progressUpdateInterval;
BOOL _controls;
id _timeObserver;

/* For keeping track of buffer states */
BOOL _playbackStarted;
BOOL _seeked;
Float64 _previousTime;

/* Keep track of any modifiers, need to be applied after each play */
float _volume;
Expand All @@ -53,6 +59,7 @@ @implementation RCTVideo
BOOL _paused;
BOOL _repeat;
BOOL _allowsExternalPlayback;
NSDictionary * _bufferConfig;
NSArray * _textTracks;
NSDictionary * _selectedTextTrack;
NSDictionary * _selectedAudioTrack;
Expand Down Expand Up @@ -86,6 +93,9 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
_allowsExternalPlayback = YES;
_playWhenInactive = false;
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
_playbackStarted = NO;
_seeked = NO;
_previousTime = 0.0;
_videoCache = [RCTVideoCache sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive:)
Expand Down Expand Up @@ -236,16 +246,24 @@ - (void)sendProgressUpdate

[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}];

if( currentTimeSecs >= 0 && self.onVideoProgress) {
self.onVideoProgress(@{
@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)],
@"playableDuration": [self calculatePlayableDuration],
@"atValue": [NSNumber numberWithLongLong:currentTime.value],
@"atTimescale": [NSNumber numberWithInt:currentTime.timescale],
@"target": self.reactTag,
@"seekableDuration": [self calculateSeekableDuration],
});
if( currentTimeSecs >= 0) {
_playbackStarted = YES;
if (self.onVideoProgress) {
self.onVideoProgress(@{
@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)],
@"playableDuration": [self calculatePlayableDuration],
@"atValue": [NSNumber numberWithLongLong:currentTime.value],
@"atTimescale": [NSNumber numberWithInt:currentTime.timescale],
@"target": self.reactTag,
@"seekableDuration": [self calculateSeekableDuration],
});
}
}
if (_previousTime != currentTimeSecs) {
// video has progressed
_seeked = NO; // seeked has completed and video has enough data in buffer to play again
}
_previousTime = currentTimeSecs;
}

/*!
Expand Down Expand Up @@ -288,6 +306,7 @@ - (void)addPlayerItemObservers
[_playerItem addObserver:self forKeyPath:statusKeyPath options:0 context:nil];
[_playerItem addObserver:self forKeyPath:playbackBufferEmptyKeyPath options:0 context:nil];
[_playerItem addObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath options:0 context:nil];
[_playerItem addObserver:self forKeyPath:loadedTimeRangesKeyPath options:0 context:nil];
[_playerItem addObserver:self forKeyPath:timedMetadata options:NSKeyValueObservingOptionNew context:nil];
_playerItemObserversSet = YES;
}
Expand All @@ -301,6 +320,7 @@ - (void)removePlayerItemObservers
[_playerItem removeObserver:self forKeyPath:statusKeyPath];
[_playerItem removeObserver:self forKeyPath:playbackBufferEmptyKeyPath];
[_playerItem removeObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath];
[_playerItem removeObserver:self forKeyPath:loadedTimeRangesKeyPath];
[_playerItem removeObserver:self forKeyPath:timedMetadata];
_playerItemObserversSet = NO;
}
Expand Down Expand Up @@ -329,13 +349,13 @@ - (void)setSrc:(NSDictionary *)source
[_player removeObserver:self forKeyPath:playbackRate context:nil];
_playbackRateObserverRegistered = NO;
}

_player = [AVPlayer playerWithPlayerItem:_playerItem];
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;

[_player addObserver:self forKeyPath:playbackRate options:0 context:nil];
_playbackRateObserverRegistered = YES;

[self addPlayerTimeObserver];

//Perform on next run loop, otherwise onVideoLoadStart is nil
Expand Down Expand Up @@ -621,6 +641,23 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
}
_playerBufferEmpty = NO;
self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag});
} else if ([keyPath isEqualToString:loadedTimeRangesKeyPath]) {
if (_bufferConfig) {
double buffered = [[self calculatePlayableDuration] doubleValue] - [[NSNumber numberWithFloat:CMTimeGetSeconds(_player.currentTime)] doubleValue];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not so readable

double threshold = 0.0;
if (_bufferConfig[@"bufferForPlaybackAfterRebufferMs"]) {
double playbackAfterRebufferMs = [_bufferConfig[@"bufferForPlaybackAfterRebufferMs"] doubleValue];
threshold = playbackAfterRebufferMs / 1000; // default to playbackAfterRebufferMs
}
if ((!_playbackStarted || _seeked) && _bufferConfig[@"bufferForPlaybackMs"]) {
// video is yet to start playback, or user has interrupted video with a seek event
double bufferForPlaybackMs = [_bufferConfig[@"bufferForPlaybackMs"] doubleValue];
threshold = bufferForPlaybackMs / 1000; // bufferForPlaybackMs
}
if (threshold > 0.0 && buffered >= threshold && !_paused) {
[_player playImmediatelyAtRate:1];
}
}
}
} else if (object == _playerLayer) {
if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) {
Expand Down Expand Up @@ -791,7 +828,7 @@ - (void)setSeek:(NSDictionary *)info
@"target": self.reactTag});
}
}];

_seeked = YES;
_pendingSeek = false;
}

Expand Down Expand Up @@ -830,6 +867,7 @@ - (void)applyModifiers
[_player setMuted:NO];
}

[self setBufferConfig:_bufferConfig];
[self setSelectedAudioTrack:_selectedAudioTrack];
[self setSelectedTextTrack:_selectedTextTrack];
[self setResizeMode:_resizeMode];
Expand Down Expand Up @@ -888,6 +926,10 @@ - (void)setMediaSelectionTrackForCharacteristic:(AVMediaCharacteristic)character
[_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group];
}

- (void)setBufferConfig:(NSDictionary *)bufferConfig {
_bufferConfig = bufferConfig;
}

- (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack {
_selectedAudioTrack = selectedAudioTrack;
[self setMediaSelectionTrackForCharacteristic:AVMediaCharacteristicAudible
Expand Down
1 change: 1 addition & 0 deletions ios/RCTVideoManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ - (dispatch_queue_t)methodQueue
RCT_EXPORT_VIEW_PROPERTY(currentTime, float);
RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL);
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
RCT_EXPORT_VIEW_PROPERTY(bufferConfig, NSDictionary);
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
Expand Down