Merge branch 'master' into maximumBitRate-adaptive-streaming

This commit is contained in:
Hampton Maxwell 2018-12-12 22:30:52 -08:00 committed by GitHub
commit a43f9c7ce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 41 deletions

View File

@ -1,5 +1,14 @@
## Changelog ## Changelog
### Version 4.1.1
* Don't initialize filters on iOS unless a filter is set. This was causing a startup performance regression [#1360](https://github.com/react-native-community/react-native-video/pull/1360)
### Version 4.1.0
* Generate onSeek on Android ExoPlayer & MediaPlayer after seek completes [#1351](https://github.com/react-native-community/react-native-video/pull/1351)
* Remove unneeded onVideoSaved event [#1350](https://github.com/react-native-community/react-native-video/pull/1350)
* Disable AirPlay if sidecar text tracks are enabled [#1304](https://github.com/react-native-community/react-native-video/pull/1304)
* Add possibility to remove black screen while video is loading in Exoplayer [#1355](https://github.com/react-native-community/react-native-video/pull/1355)
### Version 4.0.1 ### Version 4.0.1
* Add missing files to package.json [#1342](https://github.com/react-native-community/react-native-video/pull/1342) * Add missing files to package.json [#1342](https://github.com/react-native-community/react-native-video/pull/1342)

View File

@ -264,6 +264,7 @@ var styles = StyleSheet.create({
* [fullscreenAutorotate](#fullscreenautorotate) * [fullscreenAutorotate](#fullscreenautorotate)
* [fullscreenOrientation](#fullscreenorientation) * [fullscreenOrientation](#fullscreenorientation)
* [headers](#headers) * [headers](#headers)
* [hideShutterView](#hideshutterview)
* [id](#id) * [id](#id)
* [ignoreSilentSwitch](#ignoresilentswitch) * [ignoreSilentSwitch](#ignoresilentswitch)
* [maxBitRate](#maxbitrate) * [maxBitRate](#maxbitrate)
@ -296,6 +297,7 @@ var styles = StyleSheet.create({
* [onLoad](#onload) * [onLoad](#onload)
* [onLoadStart](#onloadstart) * [onLoadStart](#onloadstart)
* [onProgress](#onprogress) * [onProgress](#onprogress)
* [onSeek](#onseek)
* [onTimedMetadata](#ontimedmetadata) * [onTimedMetadata](#ontimedmetadata)
### Methods ### Methods
@ -417,6 +419,14 @@ headers={{
Platforms: Android ExoPlayer Platforms: Android ExoPlayer
#### hideShutterView
Controls whether the ExoPlayer shutter view (black screen while loading) is enabled.
* **false (default)** - Show shutter view
* **true** - Hide shutter view
Platforms: Android ExoPlayer
#### id #### id
Set the DOM id element so you can use document.getElementById on web platforms. Accepts string values. Set the DOM id element so you can use document.getElementById on web platforms. Accepts string values.
@ -667,6 +677,8 @@ uri | URL for the text track. Currently, only tracks hosted on a webserver are s
On iOS, sidecar text tracks are only supported for individual files, not HLS playlists. For HLS, you should include the text tracks as part of the playlist. On iOS, sidecar text tracks are only supported for individual files, not HLS playlists. For HLS, you should include the text tracks as part of the playlist.
Note: Due to iOS limitations, sidecar text tracks are not compatible with Airplay. If textTracks are specified, AirPlay support will be automatically disabled.
Example: Example:
``` ```
import { TextTrackType }, Video from 'react-native-video'; import { TextTrackType }, Video from 'react-native-video';
@ -860,6 +872,29 @@ Example:
Platforms: all Platforms: all
#### onSeek
Callback function that is called when a seek completes.
Payload:
Property | Type | Description
--- | --- | ---
currentTime | number | The current time after the seek
seekTime | number | The requested time
Example:
```
{
currentTime: 100.5
seekTime: 100
}
```
Both the currentTime & seekTime are reported because the video player may not seek to the exact requested position in order to improve seek performance.
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP
#### onTimedMetadata #### onTimedMetadata
Callback function that is called when timed metadata becomes available Callback function that is called when timed metadata becomes available
@ -953,7 +988,7 @@ Platforms: iOS
Seek to the specified position represented by seconds. seconds is a float value. Seek to the specified position represented by seconds. seconds is a float value.
`seek()` can only be called after the `onLoad` event has fired. `seek()` can only be called after the `onLoad` event has fired. Once completed, the [onSeek](#onseek) event will be called.
Example: Example:
``` ```

View File

@ -384,6 +384,7 @@ Video.propTypes = {
fullscreenOrientation: PropTypes.oneOf(['all','landscape','portrait']), fullscreenOrientation: PropTypes.oneOf(['all','landscape','portrait']),
progressUpdateInterval: PropTypes.number, progressUpdateInterval: PropTypes.number,
useTextureView: PropTypes.bool, useTextureView: PropTypes.bool,
hideShutterView: PropTypes.bool,
onLoadStart: PropTypes.func, onLoadStart: PropTypes.func,
onLoad: PropTypes.func, onLoad: PropTypes.func,
onBuffer: PropTypes.func, onBuffer: PropTypes.func,

View File

@ -39,6 +39,7 @@ public final class ExoPlayerView extends FrameLayout {
private ViewGroup.LayoutParams layoutParams; private ViewGroup.LayoutParams layoutParams;
private boolean useTextureView = false; private boolean useTextureView = false;
private boolean hideShutterView = false;
public ExoPlayerView(Context context) { public ExoPlayerView(Context context) {
this(context, null); this(context, null);
@ -106,6 +107,10 @@ public final class ExoPlayerView extends FrameLayout {
} }
} }
private void updateShutterViewVisibility() {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
/** /**
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
@ -161,6 +166,11 @@ public final class ExoPlayerView extends FrameLayout {
updateSurfaceView(); updateSurfaceView();
} }
public void setHideShutterView(boolean hideShutterView) {
this.hideShutterView = hideShutterView;
updateShutterViewVisibility();
}
private final Runnable measureAndLayout = new Runnable() { private final Runnable measureAndLayout = new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -111,6 +111,7 @@ class ReactExoplayerView extends FrameLayout implements
private float rate = 1f; private float rate = 1f;
private float audioVolume = 1f; private float audioVolume = 1f;
private int maxBitRate = 0; private int maxBitRate = 0;
private long seekTime = C.TIME_UNSET;
private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
@ -131,6 +132,7 @@ class ReactExoplayerView extends FrameLayout implements
private float mProgressUpdateInterval = 250.0f; private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false; private boolean playInBackground = false;
private boolean useTextureView = false; private boolean useTextureView = false;
private boolean hideShutterView = false;
private Map<String, String> requestHeaders; private Map<String, String> requestHeaders;
// \ End props // \ End props
@ -609,7 +611,8 @@ class ReactExoplayerView extends FrameLayout implements
@Override @Override
public void onSeekProcessed() { public void onSeekProcessed() {
// Do nothing. eventEmitter.seek(player.getCurrentPosition(), seekTime);
seekTime = C.TIME_UNSET;
} }
@Override @Override
@ -896,7 +899,7 @@ class ReactExoplayerView extends FrameLayout implements
public void seekTo(long positionMs) { public void seekTo(long positionMs) {
if (player != null) { if (player != null) {
eventEmitter.seek(player.getCurrentPosition(), positionMs); seekTime = positionMs;
player.seekTo(positionMs); player.seekTo(positionMs);
} }
} }
@ -966,6 +969,10 @@ class ReactExoplayerView extends FrameLayout implements
exoPlayerView.setUseTextureView(useTextureView); exoPlayerView.setUseTextureView(useTextureView);
} }
public void setHideShutterView(boolean hideShutterView) {
exoPlayerView.setHideShutterView(hideShutterView);
}
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) { public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) {
minBufferMs = newMinBufferMs; minBufferMs = newMinBufferMs;
maxBufferMs = newMaxBufferMs; maxBufferMs = newMaxBufferMs;

View File

@ -52,6 +52,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_DISABLE_FOCUS = "disableFocus"; private static final String PROP_DISABLE_FOCUS = "disableFocus";
private static final String PROP_FULLSCREEN = "fullscreen"; private static final String PROP_FULLSCREEN = "fullscreen";
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView"; private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
@Override @Override
public String getName() { public String getName() {
@ -226,6 +227,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
videoView.setUseTextureView(useTextureView); videoView.setUseTextureView(useTextureView);
} }
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)
public void setHideShutterView(final ReactExoplayerView videoView, final boolean hideShutterView) {
videoView.setHideShutterView(hideShutterView);
}
@ReactProp(name = PROP_BUFFER_CONFIG) @ReactProp(name = PROP_BUFFER_CONFIG)
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) { public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;

View File

@ -47,6 +47,7 @@ public class ReactVideoView extends ScalableVideoView implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnBufferingUpdateListener,
MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnCompletionListener, MediaPlayer.OnCompletionListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnInfoListener,
LifecycleEventListener, LifecycleEventListener,
@ -127,6 +128,7 @@ public class ReactVideoView extends ScalableVideoView implements
private float mProgressUpdateInterval = 250.0f; private float mProgressUpdateInterval = 250.0f;
private float mRate = 1.0f; private float mRate = 1.0f;
private float mActiveRate = 1.0f; private float mActiveRate = 1.0f;
private long mSeekTime = 0;
private boolean mPlayInBackground = false; private boolean mPlayInBackground = false;
private boolean mBackgroundPaused = false; private boolean mBackgroundPaused = false;
private boolean mIsFullscreen = false; private boolean mIsFullscreen = false;
@ -213,6 +215,7 @@ public class ReactVideoView extends ScalableVideoView implements
mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnSeekCompleteListener(this);
mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnInfoListener(this); mMediaPlayer.setOnInfoListener(this);
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
@ -606,15 +609,18 @@ public class ReactVideoView extends ScalableVideoView implements
mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0); mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0);
} }
public void onSeekComplete(MediaPlayer mp) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_CURRENT_TIME, getCurrentPosition() / 1000.0);
event.putDouble(EVENT_PROP_SEEK_TIME, mSeekTime / 1000.0);
mEventEmitter.receiveEvent(getId(), Events.EVENT_SEEK.toString(), event);
mSeekTime = 0;
}
@Override @Override
public void seekTo(int msec) { public void seekTo(int msec) {
if (mMediaPlayerValid) { if (mMediaPlayerValid) {
WritableMap event = Arguments.createMap(); mSeekTime = msec;
event.putDouble(EVENT_PROP_CURRENT_TIME, getCurrentPosition() / 1000.0);
event.putDouble(EVENT_PROP_SEEK_TIME, msec / 1000.0);
mEventEmitter.receiveEvent(getId(), Events.EVENT_SEEK.toString(), event);
super.seekTo(msec); super.seekTo(msec);
if (isCompleted && mVideoDuration != 0 && msec < mVideoDuration) { if (isCompleted && mVideoDuration != 0 && msec < mVideoDuration) {
isCompleted = false; isCompleted = false;

View File

@ -404,10 +404,13 @@ static int const RCTVideoUnset = -1;
- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler - (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler
{ {
if (!_textTracks) { if (!_textTracks || _textTracks.count==0) {
handler([AVPlayerItem playerItemWithAsset:asset]); handler([AVPlayerItem playerItemWithAsset:asset]);
return; return;
} }
// AVPlayer can't airplay AVMutableCompositions
_allowsExternalPlayback = NO;
// sideload text tracks // sideload text tracks
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
@ -1276,39 +1279,29 @@ static int const RCTVideoUnset = -1;
} }
- (void)setFilter:(NSString *)filterName { - (void)setFilter:(NSString *)filterName {
_filterName = filterName; _filterName = filterName;
AVAsset *asset = _playerItem.asset; AVAsset *asset = _playerItem.asset;
if (asset != nil) { if (!asset) {
return;
CIFilter *filter = [CIFilter filterWithName:filterName]; } else if (!_playerItem.videoComposition && (filterName == nil || [filterName isEqualToString:@""])) {
return; // Setting up an empty filter has a cost so avoid whenever possible
_playerItem.videoComposition = [AVVideoComposition
videoCompositionWithAsset:asset
applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest *_Nonnull request) {
if (filter == nil) {
[request finishWithImage:request.sourceImage context:nil];
} else {
CIImage *image = request.sourceImage.imageByClampingToExtent;
[filter setValue:image forKey:kCIInputImageKey];
CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent];
[request finishWithImage:output context:nil];
}
}];
} }
// TODO: filters don't work for HLS, check & return
CIFilter *filter = [CIFilter filterWithName:filterName];
_playerItem.videoComposition = [AVVideoComposition
videoCompositionWithAsset:asset
applyingCIFiltersWithHandler:^(AVAsynchronousCIImageFilteringRequest *_Nonnull request) {
if (filter == nil) {
[request finishWithImage:request.sourceImage context:nil];
} else {
CIImage *image = request.sourceImage.imageByClampingToExtent;
[filter setValue:image forKey:kCIInputImageKey];
CIImage *output = [filter.outputImage imageByCroppingToRect:request.sourceImage.extent];
[request finishWithImage:output context:nil];
}
}];
} }
#pragma mark - React View Management #pragma mark - React View Management

View File

@ -60,7 +60,6 @@ RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onVideoSaved, RCTBubblingEventBlock);
RCT_REMAP_METHOD(save, RCT_REMAP_METHOD(save,
options:(NSDictionary *)options options:(NSDictionary *)options
reactTag:(nonnull NSNumber *)reactTag reactTag:(nonnull NSNumber *)reactTag

View File

@ -1,6 +1,6 @@
{ {
"name": "react-native-video", "name": "react-native-video",
"version": "4.0.1", "version": "4.1.0",
"description": "A <Video /> element for react-native", "description": "A <Video /> element for react-native",
"main": "Video.js", "main": "Video.js",
"license": "MIT", "license": "MIT",