Merge pull request #1627 from jenshandersson/posterfix
Fixes bug where poster and video was displayed simultaneously
This commit is contained in:
commit
3a7be63de3
@ -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)
|
* 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)
|
* 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 `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)
|
* 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
|
### Version 4.4.1
|
||||||
|
12
README.md
12
README.md
@ -302,6 +302,7 @@ var styles = StyleSheet.create({
|
|||||||
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
||||||
* [onLoad](#onload)
|
* [onLoad](#onload)
|
||||||
* [onLoadStart](#onloadstart)
|
* [onLoadStart](#onloadstart)
|
||||||
|
* [onReadyForDisplay](#onreadyfordisplay)
|
||||||
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
|
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
|
||||||
* [onPlaybackRateChange](#onplaybackratechange)
|
* [onPlaybackRateChange](#onplaybackratechange)
|
||||||
* [onProgress](#onprogress)
|
* [onProgress](#onprogress)
|
||||||
@ -954,6 +955,17 @@ Example:
|
|||||||
|
|
||||||
Platforms: all
|
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
|
#### onPictureInPictureStatusChanged
|
||||||
Callback function that is called when picture in picture becomes active or inactive.
|
Callback function that is called when picture in picture becomes active or inactive.
|
||||||
|
|
||||||
|
34
Video.js
34
Video.js
@ -20,7 +20,7 @@ export default class Video extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showPoster: true,
|
showPoster: !!props.poster
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +86,12 @@ export default class Video extends Component {
|
|||||||
this._root = component;
|
this._root = component;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_hidePoster = () => {
|
||||||
|
if (this.state.showPoster) {
|
||||||
|
this.setState({showPoster: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onLoadStart = (event) => {
|
_onLoadStart = (event) => {
|
||||||
if (this.props.onLoadStart) {
|
if (this.props.onLoadStart) {
|
||||||
this.props.onLoadStart(event.nativeEvent);
|
this.props.onLoadStart(event.nativeEvent);
|
||||||
@ -93,6 +99,10 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onLoad = (event) => {
|
_onLoad = (event) => {
|
||||||
|
// Need to hide poster here for windows as onReadyForDisplay is not implemented
|
||||||
|
if (Platform.OS === 'windows') {
|
||||||
|
this._hidePoster();
|
||||||
|
}
|
||||||
if (this.props.onLoad) {
|
if (this.props.onLoad) {
|
||||||
this.props.onLoad(event.nativeEvent);
|
this.props.onLoad(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -117,10 +127,6 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onSeek = (event) => {
|
_onSeek = (event) => {
|
||||||
if (this.state.showPoster && !this.props.audioOnly) {
|
|
||||||
this.setState({showPoster: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onSeek) {
|
if (this.props.onSeek) {
|
||||||
this.props.onSeek(event.nativeEvent);
|
this.props.onSeek(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -163,6 +169,7 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onReadyForDisplay = (event) => {
|
_onReadyForDisplay = (event) => {
|
||||||
|
this._hidePoster();
|
||||||
if (this.props.onReadyForDisplay) {
|
if (this.props.onReadyForDisplay) {
|
||||||
this.props.onReadyForDisplay(event.nativeEvent);
|
this.props.onReadyForDisplay(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -181,10 +188,6 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onPlaybackRateChange = (event) => {
|
_onPlaybackRateChange = (event) => {
|
||||||
if (this.state.showPoster && event.nativeEvent.playbackRate !== 0 && !this.props.audioOnly) {
|
|
||||||
this.setState({showPoster: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onPlaybackRateChange) {
|
if (this.props.onPlaybackRateChange) {
|
||||||
this.props.onPlaybackRateChange(event.nativeEvent);
|
this.props.onPlaybackRateChange(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -308,15 +311,16 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
|
||||||
<RCTVideo ref={this._assignRoot} {...nativeProps} />
|
|
||||||
{this.props.poster &&
|
|
||||||
this.state.showPoster && (
|
|
||||||
<View style={nativeProps.style}>
|
<View style={nativeProps.style}>
|
||||||
|
<RCTVideo
|
||||||
|
ref={this._assignRoot}
|
||||||
|
{...nativeProps}
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
/>
|
||||||
|
{this.state.showPoster && (
|
||||||
<Image style={posterStyle} source={{ uri: this.props.poster }} />
|
<Image style={posterStyle} source={{ uri: this.props.poster }} />
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ class RCTVideo extends RCTView {
|
|||||||
this.videoElement = this.initializeVideoElement();
|
this.videoElement = this.initializeVideoElement();
|
||||||
this.videoElement.addEventListener("ended", this.onEnd);
|
this.videoElement.addEventListener("ended", this.onEnd);
|
||||||
this.videoElement.addEventListener("loadeddata", this.onLoad);
|
this.videoElement.addEventListener("loadeddata", this.onLoad);
|
||||||
|
this.videoElement.addEventListener("canplay", this.onReadyForDisplay);
|
||||||
this.videoElement.addEventListener("loadstart", this.onLoadStart);
|
this.videoElement.addEventListener("loadstart", this.onLoadStart);
|
||||||
this.videoElement.addEventListener("pause", this.onPause);
|
this.videoElement.addEventListener("pause", this.onPause);
|
||||||
this.videoElement.addEventListener("play", this.onPlay);
|
this.videoElement.addEventListener("play", this.onPlay);
|
||||||
@ -51,6 +52,7 @@ class RCTVideo extends RCTView {
|
|||||||
detachFromView(view: UIView) {
|
detachFromView(view: UIView) {
|
||||||
this.videoElement.removeEventListener("ended", this.onEnd);
|
this.videoElement.removeEventListener("ended", this.onEnd);
|
||||||
this.videoElement.removeEventListener("loadeddata", this.onLoad);
|
this.videoElement.removeEventListener("loadeddata", this.onLoad);
|
||||||
|
this.videoElement.removeEventListener("canplay", this.onReadyForDisplay);
|
||||||
this.videoElement.removeEventListener("loadstart", this.onLoadStart);
|
this.videoElement.removeEventListener("loadstart", this.onLoadStart);
|
||||||
this.videoElement.removeEventListener("pause", this.onPause);
|
this.videoElement.removeEventListener("pause", this.onPause);
|
||||||
this.videoElement.removeEventListener("play", this.onPlay);
|
this.videoElement.removeEventListener("play", this.onPlay);
|
||||||
@ -203,6 +205,10 @@ class RCTVideo extends RCTView {
|
|||||||
this.sendEvent("topVideoLoad", payload);
|
this.sendEvent("topVideoLoad", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onReadyForDisplay = () => {
|
||||||
|
this.sendEvent("onReadyForDisplay");
|
||||||
|
}
|
||||||
|
|
||||||
onLoadStart = () => {
|
onLoadStart = () => {
|
||||||
const src = this.videoElement.currentSrc;
|
const src = this.videoElement.currentSrc;
|
||||||
const payload = {
|
const payload = {
|
||||||
|
@ -356,8 +356,6 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self setMaxBitRate:_maxBitRate];
|
[self setMaxBitRate:_maxBitRate];
|
||||||
|
|
||||||
[_player pause];
|
[_player pause];
|
||||||
[_playerViewController.view removeFromSuperview];
|
|
||||||
_playerViewController = nil;
|
|
||||||
|
|
||||||
if (_playbackRateObserverRegistered) {
|
if (_playbackRateObserverRegistered) {
|
||||||
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
||||||
@ -600,7 +598,10 @@ static int const RCTVideoUnset = -1;
|
|||||||
} else
|
} else
|
||||||
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
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) {
|
if (object == _playerItem) {
|
||||||
// When timeMetadata is read the event onTimedMetadata is triggered
|
// When timeMetadata is read the event onTimedMetadata is triggered
|
||||||
if ([keyPath isEqualToString:timedMetadata]) {
|
if ([keyPath isEqualToString:timedMetadata]) {
|
||||||
@ -692,12 +693,6 @@ static int const RCTVideoUnset = -1;
|
|||||||
_playerBufferEmpty = NO;
|
_playerBufferEmpty = NO;
|
||||||
self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag});
|
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) {
|
} else if (object == _player) {
|
||||||
if([keyPath isEqualToString:playbackRate]) {
|
if([keyPath isEqualToString:playbackRate]) {
|
||||||
if(self.onPlaybackRateChange) {
|
if(self.onPlaybackRateChange) {
|
||||||
@ -1285,7 +1280,9 @@ static int const RCTVideoUnset = -1;
|
|||||||
{
|
{
|
||||||
if( _player )
|
if( _player )
|
||||||
{
|
{
|
||||||
|
if (!_playerViewController) {
|
||||||
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
|
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
|
||||||
|
}
|
||||||
// to prevent video from being animated when resizeMode is 'cover'
|
// to prevent video from being animated when resizeMode is 'cover'
|
||||||
// resize mode must be set before subview is added
|
// resize mode must be set before subview is added
|
||||||
[self setResizeMode:_resizeMode];
|
[self setResizeMode:_resizeMode];
|
||||||
@ -1296,6 +1293,8 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self addSubview:_playerViewController.view];
|
[self addSubview:_playerViewController.view];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[_playerViewController addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil];
|
||||||
|
|
||||||
[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
|
[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1490,6 +1489,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self removePlayerLayer];
|
[self removePlayerLayer];
|
||||||
|
|
||||||
[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
|
[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
|
||||||
|
[_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath];
|
||||||
[_playerViewController.view removeFromSuperview];
|
[_playerViewController.view removeFromSuperview];
|
||||||
_playerViewController = nil;
|
_playerViewController = nil;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user