Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes bug where poster and video was displayed simultaneously #1627

Merged
merged 8 commits into from
Jun 22, 2019
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Change compileOnly to implementation on gradle (for newer gradle versions and react-native 0.59 support) [#1592](https://github.com/react-native-community/react-native-video/pull/1592)
* Replaced RCTBubblingEventBlock events by RCTDirectEventBlock to avoid event name collisions [#1625](https://github.com/react-native-community/react-native-video/pull/1625)
* Added `onPlaybackRateChange` to README [#1578](https://github.com/react-native-community/react-native-video/pull/1578)
* Added `onReadyForDisplay` to README [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
* Improved handling of poster image. Fixes bug with displaying video and poster simultaneously. [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
* Fix background audio stopping on iOS when using `controls` [#1614](https://github.com/react-native-community/react-native-video/pull/1614)

### Version 4.4.1
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ var styles = StyleSheet.create({
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
* [onLoad](#onload)
* [onLoadStart](#onloadstart)
* [onReadyForDisplay](#onreadyfordisplay)
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
* [onPlaybackRateChange](#onplaybackratechange)
* [onProgress](#onprogress)
Expand Down Expand Up @@ -954,6 +955,17 @@ Example:

Platforms: all

#### onReadyForDisplay
Callback function that is called when the first video frame is ready for display. This is when the poster is removed.

Payload: none

* iOS: [readyForDisplay](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/1615830-readyfordisplay?language=objc)
* Android: [MEDIA_INFO_VIDEO_RENDERING_START](https://developer.android.com/reference/android/media/MediaPlayer#MEDIA_INFO_VIDEO_RENDERING_START)
* Android ExoPlayer [STATE_READY](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#STATE_READY)

Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Web

#### onPictureInPictureStatusChanged
Callback function that is called when picture in picture becomes active or inactive.

Expand Down
40 changes: 22 additions & 18 deletions Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default class Video extends Component {
super(props);

this.state = {
showPoster: true,
showPoster: !!props.poster
};
}

Expand Down Expand Up @@ -86,13 +86,23 @@ export default class Video extends Component {
this._root = component;
};

_hidePoster = () => {
if (this.state.showPoster) {
this.setState({showPoster: false});
}
}

_onLoadStart = (event) => {
if (this.props.onLoadStart) {
this.props.onLoadStart(event.nativeEvent);
}
};

_onLoad = (event) => {
// Need to hide poster here for windows as onReadyForDisplay is not implemented
if (Platform.OS === 'windows') {
this._hidePoster();
}
if (this.props.onLoad) {
this.props.onLoad(event.nativeEvent);
}
Expand All @@ -117,10 +127,6 @@ export default class Video extends Component {
};

_onSeek = (event) => {
if (this.state.showPoster && !this.props.audioOnly) {
this.setState({showPoster: false});
}

if (this.props.onSeek) {
this.props.onSeek(event.nativeEvent);
}
Expand Down Expand Up @@ -163,6 +169,7 @@ export default class Video extends Component {
};

_onReadyForDisplay = (event) => {
this._hidePoster();
if (this.props.onReadyForDisplay) {
this.props.onReadyForDisplay(event.nativeEvent);
}
Expand All @@ -181,10 +188,6 @@ export default class Video extends Component {
};

_onPlaybackRateChange = (event) => {
if (this.state.showPoster && event.nativeEvent.playbackRate !== 0 && !this.props.audioOnly) {
this.setState({showPoster: false});
}

if (this.props.onPlaybackRateChange) {
this.props.onPlaybackRateChange(event.nativeEvent);
}
Expand Down Expand Up @@ -308,15 +311,16 @@ export default class Video extends Component {
};

return (
<React.Fragment>
<RCTVideo ref={this._assignRoot} {...nativeProps} />
{this.props.poster &&
this.state.showPoster && (
<View style={nativeProps.style}>
<Image style={posterStyle} source={{ uri: this.props.poster }} />
</View>
)}
</React.Fragment>
<View style={nativeProps.style}>
<RCTVideo
ref={this._assignRoot}
{...nativeProps}
style={StyleSheet.absoluteFill}
/>
{this.state.showPoster && (
<Image style={posterStyle} source={{ uri: this.props.poster }} />
)}
</View>
);
}
}
Expand Down
6 changes: 6 additions & 0 deletions dom/RCTVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class RCTVideo extends RCTView {
this.videoElement = this.initializeVideoElement();
this.videoElement.addEventListener("ended", this.onEnd);
this.videoElement.addEventListener("loadeddata", this.onLoad);
this.videoElement.addEventListener("canplay", this.onReadyForDisplay);
this.videoElement.addEventListener("loadstart", this.onLoadStart);
this.videoElement.addEventListener("pause", this.onPause);
this.videoElement.addEventListener("play", this.onPlay);
Expand All @@ -51,6 +52,7 @@ class RCTVideo extends RCTView {
detachFromView(view: UIView) {
this.videoElement.removeEventListener("ended", this.onEnd);
this.videoElement.removeEventListener("loadeddata", this.onLoad);
this.videoElement.removeEventListener("canplay", this.onReadyForDisplay);
this.videoElement.removeEventListener("loadstart", this.onLoadStart);
this.videoElement.removeEventListener("pause", this.onPause);
this.videoElement.removeEventListener("play", this.onPlay);
Expand Down Expand Up @@ -203,6 +205,10 @@ class RCTVideo extends RCTView {
this.sendEvent("topVideoLoad", payload);
}

onReadyForDisplay = () => {
this.sendEvent("onReadyForDisplay");
}

onLoadStart = () => {
const src = this.videoElement.currentSrc;
const payload = {
Expand Down
20 changes: 10 additions & 10 deletions ios/Video/RCTVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,6 @@ - (void)setSrc:(NSDictionary *)source
[self setMaxBitRate:_maxBitRate];

[_player pause];
[_playerViewController.view removeFromSuperview];
_playerViewController = nil;

if (_playbackRateObserverRegistered) {
[_player removeObserver:self forKeyPath:playbackRate context:nil];
Expand Down Expand Up @@ -600,7 +598,10 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
} else
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}

if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) {
self.onReadyForDisplay(@{@"target": self.reactTag});
return;
}
if (object == _playerItem) {
// When timeMetadata is read the event onTimedMetadata is triggered
if ([keyPath isEqualToString:timedMetadata]) {
Expand Down Expand Up @@ -692,12 +693,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
_playerBufferEmpty = NO;
self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag});
}
} else if (object == _playerLayer) {
if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) {
if([change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) {
self.onReadyForDisplay(@{@"target": self.reactTag});
}
}
} else if (object == _player) {
if([keyPath isEqualToString:playbackRate]) {
if(self.onPlaybackRateChange) {
Expand Down Expand Up @@ -1285,7 +1280,9 @@ - (void)usePlayerViewController
{
if( _player )
{
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
if (!_playerViewController) {
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
}
// to prevent video from being animated when resizeMode is 'cover'
// resize mode must be set before subview is added
[self setResizeMode:_resizeMode];
Expand All @@ -1295,6 +1292,8 @@ - (void)usePlayerViewController
[viewController addChildViewController:_playerViewController];
[self addSubview:_playerViewController.view];
}

[_playerViewController addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil];

[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
}
Expand Down Expand Up @@ -1490,6 +1489,7 @@ - (void)removeFromSuperview
[self removePlayerLayer];

[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
[_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath];
[_playerViewController.view removeFromSuperview];
_playerViewController = nil;

Expand Down