diff --git a/CHANGELOG.md b/CHANGELOG.md index de721463..78f91cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ ## Changelog +### Version 4.2.0 +* 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) +* Support setting the maxBitRate [#1310](https://github.com/react-native-community/react-native-video/pull/1310) +* Fix useTextureView not defaulting to true [#1383](https://github.com/react-native-community/react-native-video/pull/1383) +* Fix crash on MediaPlayer w/ Android 4.4 & avoid memory leak [#1328](https://github.com/react-native-community/react-native-video/pull/1328) + +### 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 * Add missing files to package.json [#1342](https://github.com/react-native-community/react-native-video/pull/1342) diff --git a/README.md b/README.md index 5dc8affc..f8fb354d 100644 --- a/README.md +++ b/README.md @@ -265,8 +265,10 @@ var styles = StyleSheet.create({ * [fullscreenAutorotate](#fullscreenautorotate) * [fullscreenOrientation](#fullscreenorientation) * [headers](#headers) +* [hideShutterView](#hideshutterview) * [id](#id) * [ignoreSilentSwitch](#ignoresilentswitch) +* [maxBitRate](#maxbitrate) * [muted](#muted) * [paused](#paused) * [playInBackground](#playinbackground) @@ -427,6 +429,14 @@ headers={{ 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 Set the DOM id element so you can use document.getElementById on web platforms. Accepts string values. @@ -445,6 +455,18 @@ Controls the iOS silent switch behavior Platforms: iOS +#### maxBitRate +Sets the desired limit, in bits per second, of network bandwidth consumption when multiple video streams are available for a playlist. + +Default: 0. Don't limit the maxBitRate. + +Example: +``` +maxBitRate={2000000} // 2 megabits +``` + +Platforms: Android ExoPlayer, iOS + #### muted Controls whether the audio is muted * **false (default)** - Don't mute audio @@ -594,6 +616,7 @@ Sets the media source. You can pass an asset loaded via require or an object wit The docs for this prop are incomplete and will be updated as each option is investigated and tested. + ##### Asset loaded via require Example: @@ -666,6 +689,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. +Note: Due to iOS limitations, sidecar text tracks are not compatible with Airplay. If textTracks are specified, AirPlay support will be automatically disabled. + Example: ``` import { TextTrackType }, Video from 'react-native-video'; diff --git a/Video.js b/Video.js index a0ca9574..e726f401 100644 --- a/Video.js +++ b/Video.js @@ -331,6 +331,7 @@ Video.propTypes = { // Opaque type returned by require('./video.mp4') PropTypes.number ]), + maxBitRate: PropTypes.number, resizeMode: PropTypes.string, poster: PropTypes.string, posterResizeMode: Image.propTypes.resizeMode, @@ -384,6 +385,7 @@ Video.propTypes = { fullscreenOrientation: PropTypes.oneOf(['all','landscape','portrait']), progressUpdateInterval: PropTypes.number, useTextureView: PropTypes.bool, + hideShutterView: PropTypes.bool, onLoadStart: PropTypes.func, onLoad: PropTypes.func, onBuffer: PropTypes.func, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index 85876709..fc966005 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -38,7 +38,8 @@ public final class ExoPlayerView extends FrameLayout { private Context context; private ViewGroup.LayoutParams layoutParams; - private boolean useTextureView = false; + private boolean useTextureView = true; + private boolean hideShutterView = false; public ExoPlayerView(Context context) { 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 * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous @@ -157,8 +162,15 @@ public final class ExoPlayerView extends FrameLayout { } public void setUseTextureView(boolean useTextureView) { - this.useTextureView = useTextureView; - updateSurfaceView(); + if (useTextureView != this.useTextureView) { + this.useTextureView = useTextureView; + updateSurfaceView(); + } + } + + public void setHideShutterView(boolean hideShutterView) { + this.hideShutterView = hideShutterView; + updateShutterViewVisibility(); } private final Runnable measureAndLayout = new Runnable() { diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index dc49402d..1197a8d8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -110,6 +110,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean isBuffering; private float rate = 1f; private float audioVolume = 1f; + private int maxBitRate = 0; private long seekTime = C.TIME_UNSET; private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; @@ -130,7 +131,6 @@ class ReactExoplayerView extends FrameLayout implements private boolean disableFocus; private float mProgressUpdateInterval = 250.0f; private boolean playInBackground = false; - private boolean useTextureView = false; private Map requestHeaders; // \ End props @@ -245,6 +245,9 @@ class ReactExoplayerView extends FrameLayout implements if (player == null) { TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + trackSelector.setParameters(trackSelector.buildUponParameters() + .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); + DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); DefaultLoadControl defaultLoadControl = new DefaultLoadControl(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, -1, true); player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, defaultLoadControl); @@ -908,6 +911,14 @@ class ReactExoplayerView extends FrameLayout implements } } + public void setMaxBitRateModifier(int newMaxBitRate) { + maxBitRate = newMaxBitRate; + if (player != null) { + trackSelector.setParameters(trackSelector.buildUponParameters() + .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); + } + } + public void setPlayInBackground(boolean playInBackground) { this.playInBackground = playInBackground; @@ -954,6 +965,10 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setUseTextureView(useTextureView); } + public void setHideShutterView(boolean hideShutterView) { + exoPlayerView.setHideShutterView(hideShutterView); + } + public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) { minBufferMs = newMinBufferMs; maxBufferMs = newMaxBufferMs; diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 4d1ec286..84cc620a 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -47,10 +47,12 @@ public class ReactExoplayerViewManager extends ViewGroupManager= Build.VERSION_CODES.M) { + mMediaPlayer.setOnTimedMetaDataAvailableListener(null); + } mMediaPlayerValid = false; release(); } if (mIsFullscreen) { setFullscreen(false); } + if (mThemedReactContext != null) { + mThemedReactContext.removeLifecycleEventListener(this); + mThemedReactContext = null; + } } public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders) { @@ -567,8 +574,7 @@ public class ReactVideoView extends ScalableVideoView implements }); } - // Select track (so we can use it to listen to timed meta data updates) - mp.selectTrack(0); + selectTimedMetadataTrack(mp); } @Override @@ -603,9 +609,7 @@ public class ReactVideoView extends ScalableVideoView implements @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { - // Select track (so we can use it to listen to timed meta data updates) - mp.selectTrack(0); - + selectTimedMetadataTrack(mp); mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0); } @@ -761,4 +765,20 @@ public class ReactVideoView extends ScalableVideoView implements return result; } + + // Select track (so we can use it to listen to timed meta data updates) + private void selectTimedMetadataTrack(MediaPlayer mp) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return; + } + try { // It's possible this could throw an exception if the framework doesn't support getting track info + MediaPlayer.TrackInfo[] trackInfo = mp.getTrackInfo(); + for (int i = 0; i < trackInfo.length; ++i) { + if (trackInfo[i].getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { + mp.selectTrack(i); + break; + } + } + } catch (Exception e) {} + } } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index d9578dc3..f7e6666a 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -19,6 +19,7 @@ RCT_EXPORT_MODULE(); } RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary); +RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float); RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString); RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL); RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, BOOL); diff --git a/package.json b/package.json index 8419ce14..0c690a04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "4.0.2", + "version": "4.2.1", "description": "A