Merge branch 'upstream-master'
This commit is contained in:
commit
5b3f08781e
@ -1,8 +1,17 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### Next Version
|
### Next Version
|
||||||
|
* Partial support for timed metadata on Android MediaPlayer [#707](https://github.com/react-native-community/react-native-video/pull/707)
|
||||||
|
* Update to ExoPlayer 2.8.2. Android SDK 26 now required [#1170](https://github.com/react-native-community/react-native-video/pull/1170)
|
||||||
|
|
||||||
|
### Version 3.2.0
|
||||||
* Basic fullscreen support for Android MediaPlayer [#1138](https://github.com/react-native-community/react-native-video/pull/1138)
|
* Basic fullscreen support for Android MediaPlayer [#1138](https://github.com/react-native-community/react-native-video/pull/1138)
|
||||||
* Simplify default Android SDK code [#1145](https://github.com/react-native-community/react-native-video/pull/1145) [#1146](https://github.com/react-native-community/react-native-video/pull/1146)
|
* Simplify default Android SDK code [#1145](https://github.com/react-native-community/react-native-video/pull/1145) [#1146](https://github.com/react-native-community/react-native-video/pull/1146)
|
||||||
|
* Various iOS sideloaded text track fixes [#1157](https://github.com/react-native-community/react-native-video/pull/1157)
|
||||||
|
* Fix #1150 where assets with bundled assets don't work on iOS in release mode [#1162](https://github.com/react-native-community/react-native-video/pull/1162)
|
||||||
|
* Support configuring the buffer on Android ExoPlayer [#1160](https://github.com/react-native-community/react-native-video/pull/1160)
|
||||||
|
* Prevent sleep from sleeping while videos are playing on Android MediaPlayer [#1117](https://github.com/react-native-community/react-native-video/pull/1117)
|
||||||
|
* Update NewtonSoft JSON to match react-native-windows version [#1169](https://github.com/react-native-community/react-native-video/pull/1169)
|
||||||
|
|
||||||
### Version 3.1.0
|
### Version 3.1.0
|
||||||
* Support sidecar text tracks on iOS [#1109](https://github.com/react-native-community/react-native-video/pull/1109)
|
* Support sidecar text tracks on iOS [#1109](https://github.com/react-native-community/react-native-video/pull/1109)
|
||||||
|
36
README.md
36
README.md
@ -3,9 +3,12 @@
|
|||||||
A `<Video>` component for react-native, as seen in
|
A `<Video>` component for react-native, as seen in
|
||||||
[react-native-login](https://github.com/brentvatne/react-native-login)!
|
[react-native-login](https://github.com/brentvatne/react-native-login)!
|
||||||
|
|
||||||
Requires react-native >= 0.40.0, for RN support of 0.19.0 - 0.39.0 please use a pre 1.0 version.
|
Requires react-native >= 0.40.0
|
||||||
|
|
||||||
### Version 3.0 breaking changes
|
### Version 4.0.0 breaking changes
|
||||||
|
Version 4.0.0 now requires Android SDK 26 or higher to use ExoPlayer. This is the default version as of React Native 0.56 and will be required by Google for all apps in October 2018.
|
||||||
|
|
||||||
|
### Version 3.0.0 breaking changes
|
||||||
Version 3.0 features a number of changes to existing behavior. See [Updating](#updating) for changes.
|
Version 3.0 features a number of changes to existing behavior. See [Updating](#updating) for changes.
|
||||||
|
|
||||||
## TOC
|
## TOC
|
||||||
@ -210,6 +213,7 @@ var styles = StyleSheet.create({
|
|||||||
### Configurable props
|
### Configurable props
|
||||||
* [allowsExternalPlayback](#allowsexternalplayback)
|
* [allowsExternalPlayback](#allowsexternalplayback)
|
||||||
* [audioOnly](#audioonly)
|
* [audioOnly](#audioonly)
|
||||||
|
* [bufferConfig](#bufferconfig)
|
||||||
* [ignoreSilentSwitch](#ignoresilentswitch)
|
* [ignoreSilentSwitch](#ignoresilentswitch)
|
||||||
* [muted](#muted)
|
* [muted](#muted)
|
||||||
* [paused](#paused)
|
* [paused](#paused)
|
||||||
@ -262,6 +266,30 @@ For this to work, the poster prop must be set.
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### bufferConfig
|
||||||
|
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
|
||||||
|
|
||||||
|
Property | Type | Description
|
||||||
|
--- | --- | ---
|
||||||
|
minBufferMs | number | The default minimum duration of media that the player will attempt to ensure is buffered at all times, in milliseconds.
|
||||||
|
maxBufferMs | number | The default maximum duration of media that the player will attempt to buffer, in milliseconds.
|
||||||
|
bufferForPlaybackMs | number | The default duration of media that must be buffered for playback to start or resume following a user action such as a seek, in milliseconds.
|
||||||
|
playbackAfterRebufferMs | number | The default duration of media that must be buffered for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Example with default values:
|
||||||
|
```
|
||||||
|
bufferConfig={{
|
||||||
|
minBufferMs: 15000,
|
||||||
|
maxBufferMs: 50000,
|
||||||
|
bufferForPlaybackMs: 2500,
|
||||||
|
bufferForPlaybackAfterRebufferMs: 5000
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer
|
||||||
|
|
||||||
#### ignoreSilentSwitch
|
#### ignoreSilentSwitch
|
||||||
Controls the iOS silent switch behavior
|
Controls the iOS silent switch behavior
|
||||||
* **"inherit" (default)** - Use the default AVPlayer behavior
|
* **"inherit" (default)** - Use the default AVPlayer behavior
|
||||||
@ -617,7 +645,9 @@ Example:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Platforms: Android ExoPlayer, iOS
|
Support for timed metadata on Android MediaPlayer is limited at best and only compatible with some videos. It requires a target SDK of 23 or higher.
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer, Android MediaPlayer, iOS
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
Methods operate on a ref to the Video element. You can create a ref using code like:
|
Methods operate on a ref to the Video element. You can create a ref using code like:
|
||||||
|
6
Video.js
6
Video.js
@ -350,6 +350,12 @@ Video.propTypes = {
|
|||||||
paused: PropTypes.bool,
|
paused: PropTypes.bool,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
|
bufferConfig: PropTypes.shape({
|
||||||
|
minBufferMs: PropTypes.number,
|
||||||
|
maxBufferMs: PropTypes.number,
|
||||||
|
bufferForPlaybackMs: PropTypes.number,
|
||||||
|
bufferForPlaybackAfterRebufferMs: PropTypes.number,
|
||||||
|
}),
|
||||||
stereoPan: PropTypes.number,
|
stereoPan: PropTypes.number,
|
||||||
rate: PropTypes.number,
|
rate: PropTypes.number,
|
||||||
playInBackground: PropTypes.bool,
|
playInBackground: PropTypes.bool,
|
||||||
|
@ -19,9 +19,18 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
provided "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
provided "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
||||||
compile 'com.google.android.exoplayer:exoplayer:2.7.3'
|
compile('com.google.android.exoplayer:exoplayer:2.8.2') {
|
||||||
compile('com.google.android.exoplayer:extension-okhttp:2.7.3') {
|
exclude group: 'com.android.support'
|
||||||
|
}
|
||||||
|
|
||||||
|
// All support libs must use the same version
|
||||||
|
compile "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '+')}"
|
||||||
|
compile "com.android.support:support-compat:${safeExtGet('supportLibVersion', '+')}"
|
||||||
|
compile "com.android.support:support-media-compat:${safeExtGet('supportLibVersion', '+')}"
|
||||||
|
|
||||||
|
compile('com.google.android.exoplayer:extension-okhttp:2.8.2') {
|
||||||
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
||||||
}
|
}
|
||||||
compile 'com.squareup.okhttp3:okhttp:3.9.1'
|
compile 'com.squareup.okhttp3:okhttp:3.11.0'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
|||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -97,7 +98,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
private DataSource.Factory mediaDataSourceFactory;
|
private DataSource.Factory mediaDataSourceFactory;
|
||||||
private SimpleExoPlayer player;
|
private SimpleExoPlayer player;
|
||||||
private MappingTrackSelector trackSelector;
|
private DefaultTrackSelector trackSelector;
|
||||||
private boolean playerNeedsSource;
|
private boolean playerNeedsSource;
|
||||||
|
|
||||||
private int resumeWindow;
|
private int resumeWindow;
|
||||||
@ -109,6 +110,11 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean isBuffering;
|
private boolean isBuffering;
|
||||||
private float rate = 1f;
|
private float rate = 1f;
|
||||||
|
|
||||||
|
private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
||||||
|
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
|
||||||
|
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
||||||
|
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
||||||
|
|
||||||
// Props from React
|
// Props from React
|
||||||
private Uri srcUri;
|
private Uri srcUri;
|
||||||
private String extension;
|
private String extension;
|
||||||
@ -234,7 +240,9 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
if (player == null) {
|
if (player == null) {
|
||||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
||||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||||
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, new DefaultLoadControl());
|
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);
|
||||||
player.addListener(this);
|
player.addListener(this);
|
||||||
player.setMetadataOutput(this);
|
player.setMetadataOutput(this);
|
||||||
exoPlayerView.setPlayer(player);
|
exoPlayerView.setPlayer(player);
|
||||||
@ -320,7 +328,6 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
private void releasePlayer() {
|
private void releasePlayer() {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
isPaused = player.getPlayWhenReady();
|
|
||||||
updateResumePosition();
|
updateResumePosition();
|
||||||
player.release();
|
player.release();
|
||||||
player.setMetadataOutput(null);
|
player.setMetadataOutput(null);
|
||||||
@ -526,12 +533,12 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
TrackGroupArray groups = info.getTrackGroups(index);
|
TrackGroupArray groups = info.getTrackGroups(index);
|
||||||
for (int i = 0; i < groups.length; ++i) {
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
Format format = groups.get(i).getFormat(0);
|
Format format = groups.get(i).getFormat(0);
|
||||||
WritableMap textTrack = Arguments.createMap();
|
WritableMap audioTrack = Arguments.createMap();
|
||||||
textTrack.putInt("index", i);
|
audioTrack.putInt("index", i);
|
||||||
textTrack.putString("title", format.id != null ? format.id : "");
|
audioTrack.putString("title", format.id != null ? format.id : "");
|
||||||
textTrack.putString("type", format.sampleMimeType);
|
audioTrack.putString("type", format.sampleMimeType);
|
||||||
textTrack.putString("language", format.language != null ? format.language : "");
|
audioTrack.putString("language", format.language != null ? format.language : "");
|
||||||
audioTracks.pushMap(textTrack);
|
audioTracks.pushMap(audioTrack);
|
||||||
}
|
}
|
||||||
return audioTracks;
|
return audioTracks;
|
||||||
}
|
}
|
||||||
@ -767,8 +774,13 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
type = "default";
|
type = "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters()
|
||||||
|
.buildUpon()
|
||||||
|
.setRendererDisabled(rendererIndex, true)
|
||||||
|
.build();
|
||||||
|
|
||||||
if (type.equals("disabled")) {
|
if (type.equals("disabled")) {
|
||||||
trackSelector.setSelectionOverride(rendererIndex, groups, null);
|
trackSelector.setParameters(disableParameters);
|
||||||
return;
|
return;
|
||||||
} else if (type.equals("language")) {
|
} else if (type.equals("language")) {
|
||||||
for (int i = 0; i < groups.length; ++i) {
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
@ -791,17 +803,12 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
trackIndex = value.asInt();
|
trackIndex = value.asInt();
|
||||||
}
|
}
|
||||||
} else { // default
|
} else { // default
|
||||||
if (rendererIndex == C.TRACK_TYPE_TEXT) { // Use system settings if possible
|
if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18 && groups.length > 0) {
|
||||||
int sdk = android.os.Build.VERSION.SDK_INT;
|
// Use system settings if possible
|
||||||
if (sdk > 18 && groups.length > 0) {
|
CaptioningManager captioningManager
|
||||||
CaptioningManager captioningManager
|
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
|
||||||
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
|
if (captioningManager != null && captioningManager.isEnabled()) {
|
||||||
if (captioningManager != null && captioningManager.isEnabled()) {
|
trackIndex = getTrackIndexForDefaultLocale(groups);
|
||||||
trackIndex = getTrackIndexForDefaultLocale(groups);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trackSelector.setSelectionOverride(rendererIndex, groups, null);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) {
|
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) {
|
||||||
trackIndex = getTrackIndexForDefaultLocale(groups);
|
trackIndex = getTrackIndexForDefaultLocale(groups);
|
||||||
@ -809,14 +816,17 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trackIndex == C.INDEX_UNSET) {
|
if (trackIndex == C.INDEX_UNSET) {
|
||||||
trackSelector.clearSelectionOverrides(trackIndex);
|
trackSelector.setParameters(disableParameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingTrackSelector.SelectionOverride override
|
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
|
||||||
= new MappingTrackSelector.SelectionOverride(
|
.buildUpon()
|
||||||
new FixedTrackSelection.Factory(), trackIndex, 0);
|
.setRendererDisabled(rendererIndex, false)
|
||||||
trackSelector.setSelectionOverride(rendererIndex, groups, override);
|
.setSelectionOverride(rendererIndex, groups,
|
||||||
|
new DefaultTrackSelector.SelectionOverride(trackIndex, 0))
|
||||||
|
.build();
|
||||||
|
trackSelector.setParameters(selectionParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTrackIndexForDefaultLocale(TrackGroupArray groups) {
|
private int getTrackIndexForDefaultLocale(TrackGroupArray groups) {
|
||||||
@ -931,4 +941,13 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
public void setUseTextureView(boolean useTextureView) {
|
public void setUseTextureView(boolean useTextureView) {
|
||||||
exoPlayerView.setUseTextureView(useTextureView);
|
exoPlayerView.setUseTextureView(useTextureView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) {
|
||||||
|
minBufferMs = newMinBufferMs;
|
||||||
|
maxBufferMs = newMaxBufferMs;
|
||||||
|
bufferForPlaybackMs = newBufferForPlaybackMs;
|
||||||
|
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
|
||||||
|
releasePlayer();
|
||||||
|
initializePlayer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import com.facebook.react.common.MapBuilder;
|
|||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.facebook.react.uimanager.ViewGroupManager;
|
import com.facebook.react.uimanager.ViewGroupManager;
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -38,6 +39,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_PAUSED = "paused";
|
private static final String PROP_PAUSED = "paused";
|
||||||
private static final String PROP_MUTED = "muted";
|
private static final String PROP_MUTED = "muted";
|
||||||
private static final String PROP_VOLUME = "volume";
|
private static final String PROP_VOLUME = "volume";
|
||||||
|
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
||||||
|
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
|
||||||
|
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
|
||||||
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
|
||||||
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
|
||||||
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
|
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
|
||||||
private static final String PROP_SEEK = "seek";
|
private static final String PROP_SEEK = "seek";
|
||||||
private static final String PROP_RATE = "rate";
|
private static final String PROP_RATE = "rate";
|
||||||
@ -214,6 +220,25 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setUseTextureView(useTextureView);
|
videoView.setUseTextureView(useTextureView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_BUFFER_CONFIG)
|
||||||
|
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
|
||||||
|
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
||||||
|
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
|
||||||
|
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
||||||
|
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
||||||
|
if (bufferConfig != null) {
|
||||||
|
minBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MS)
|
||||||
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MIN_BUFFER_MS) : minBufferMs;
|
||||||
|
maxBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MAX_BUFFER_MS)
|
||||||
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MAX_BUFFER_MS) : maxBufferMs;
|
||||||
|
bufferForPlaybackMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS)
|
||||||
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS) : bufferForPlaybackMs;
|
||||||
|
bufferForPlaybackAfterRebufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
|
||||||
|
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) : bufferForPlaybackAfterRebufferMs;
|
||||||
|
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean startsWithValidScheme(String uriString) {
|
private boolean startsWithValidScheme(String uriString) {
|
||||||
return uriString.startsWith("http://")
|
return uriString.startsWith("http://")
|
||||||
|| uriString.startsWith("https://")
|
|| uriString.startsWith("https://")
|
||||||
|
@ -5,11 +5,13 @@ import android.app.Activity;
|
|||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
|
import android.media.TimedMetaData;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
@ -21,6 +23,8 @@ import com.facebook.react.bridge.Arguments;
|
|||||||
import com.facebook.react.bridge.LifecycleEventListener;
|
import com.facebook.react.bridge.LifecycleEventListener;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.bridge.WritableArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeArray;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||||
import com.yqritc.scalablevideoview.ScalableType;
|
import com.yqritc.scalablevideoview.ScalableType;
|
||||||
@ -29,6 +33,7 @@ import com.yqritc.scalablevideoview.ScaleManager;
|
|||||||
import com.yqritc.scalablevideoview.Size;
|
import com.yqritc.scalablevideoview.Size;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.lang.Math;
|
import java.lang.Math;
|
||||||
@ -37,14 +42,21 @@ import java.math.BigDecimal;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer
|
public class ReactVideoView extends ScalableVideoView implements
|
||||||
.OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, LifecycleEventListener, MediaController.MediaPlayerControl {
|
MediaPlayer.OnPreparedListener,
|
||||||
|
MediaPlayer.OnErrorListener,
|
||||||
|
MediaPlayer.OnBufferingUpdateListener,
|
||||||
|
MediaPlayer.OnCompletionListener,
|
||||||
|
MediaPlayer.OnInfoListener,
|
||||||
|
LifecycleEventListener,
|
||||||
|
MediaController.MediaPlayerControl {
|
||||||
|
|
||||||
public enum Events {
|
public enum Events {
|
||||||
EVENT_LOAD_START("onVideoLoadStart"),
|
EVENT_LOAD_START("onVideoLoadStart"),
|
||||||
EVENT_LOAD("onVideoLoad"),
|
EVENT_LOAD("onVideoLoad"),
|
||||||
EVENT_ERROR("onVideoError"),
|
EVENT_ERROR("onVideoError"),
|
||||||
EVENT_PROGRESS("onVideoProgress"),
|
EVENT_PROGRESS("onVideoProgress"),
|
||||||
|
EVENT_TIMED_METADATA("onTimedMetadata"),
|
||||||
EVENT_SEEK("onVideoSeek"),
|
EVENT_SEEK("onVideoSeek"),
|
||||||
EVENT_END("onVideoEnd"),
|
EVENT_END("onVideoEnd"),
|
||||||
EVENT_STALLED("onPlaybackStalled"),
|
EVENT_STALLED("onPlaybackStalled"),
|
||||||
@ -83,6 +95,10 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
public static final String EVENT_PROP_WIDTH = "width";
|
public static final String EVENT_PROP_WIDTH = "width";
|
||||||
public static final String EVENT_PROP_HEIGHT = "height";
|
public static final String EVENT_PROP_HEIGHT = "height";
|
||||||
public static final String EVENT_PROP_ORIENTATION = "orientation";
|
public static final String EVENT_PROP_ORIENTATION = "orientation";
|
||||||
|
public static final String EVENT_PROP_METADATA = "metadata";
|
||||||
|
public static final String EVENT_PROP_TARGET = "target";
|
||||||
|
public static final String EVENT_PROP_METADATA_IDENTIFIER = "identifier";
|
||||||
|
public static final String EVENT_PROP_METADATA_VALUE = "value";
|
||||||
|
|
||||||
public static final String EVENT_PROP_ERROR = "error";
|
public static final String EVENT_PROP_ERROR = "error";
|
||||||
public static final String EVENT_PROP_WHAT = "what";
|
public static final String EVENT_PROP_WHAT = "what";
|
||||||
@ -96,7 +112,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
private Handler videoControlHandler = new Handler();
|
private Handler videoControlHandler = new Handler();
|
||||||
private MediaController mediaController;
|
private MediaController mediaController;
|
||||||
|
|
||||||
|
|
||||||
private String mSrcUriString = null;
|
private String mSrcUriString = null;
|
||||||
private String mSrcType = "mp4";
|
private String mSrcType = "mp4";
|
||||||
private ReadableMap mRequestHeaders = null;
|
private ReadableMap mRequestHeaders = null;
|
||||||
@ -199,6 +214,9 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
mMediaPlayer.setOnBufferingUpdateListener(this);
|
mMediaPlayer.setOnBufferingUpdateListener(this);
|
||||||
mMediaPlayer.setOnCompletionListener(this);
|
mMediaPlayer.setOnCompletionListener(this);
|
||||||
mMediaPlayer.setOnInfoListener(this);
|
mMediaPlayer.setOnInfoListener(this);
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
mMediaPlayer.setOnTimedMetaDataAvailableListener(new TimedMetaDataAvailableListener());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +377,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setPausedModifier(final boolean paused) {
|
public void setPausedModifier(final boolean paused) {
|
||||||
|
|
||||||
mPaused = paused;
|
mPaused = paused;
|
||||||
|
|
||||||
if (!mMediaPlayerValid) {
|
if (!mMediaPlayerValid) {
|
||||||
@ -382,6 +399,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
mProgressUpdateHandler.post(mProgressUpdateRunnable);
|
mProgressUpdateHandler.post(mProgressUpdateRunnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setKeepScreenOn(!mPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduces the volume based on stereoPan
|
// reduces the volume based on stereoPan
|
||||||
@ -501,7 +519,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
this.mUseNativeControls = controls;
|
this.mUseNativeControls = controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepared(MediaPlayer mp) {
|
public void onPrepared(MediaPlayer mp) {
|
||||||
|
|
||||||
@ -545,6 +562,9 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select track (so we can use it to listen to timed meta data updates)
|
||||||
|
mp.selectTrack(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -579,6 +599,9 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
||||||
|
// Select track (so we can use it to listen to timed meta data updates)
|
||||||
|
mp.selectTrack(0);
|
||||||
|
|
||||||
mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0);
|
mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -625,21 +648,51 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCompletion(MediaPlayer mp) {
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null);
|
mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null);
|
||||||
|
if (!mRepeat) {
|
||||||
|
setKeepScreenOn(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not fully tested and does not work for all forms of timed metadata
|
||||||
|
@TargetApi(23) // 6.0
|
||||||
|
public class TimedMetaDataAvailableListener
|
||||||
|
implements MediaPlayer.OnTimedMetaDataAvailableListener
|
||||||
|
{
|
||||||
|
public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String rawMeta = new String(data.getMetaData(), "UTF-8");
|
||||||
|
WritableMap id3 = Arguments.createMap();
|
||||||
|
|
||||||
|
id3.putString(EVENT_PROP_METADATA_VALUE, rawMeta.substring(rawMeta.lastIndexOf("\u0003") + 1));
|
||||||
|
id3.putString(EVENT_PROP_METADATA_IDENTIFIER, "id3/TDEN");
|
||||||
|
|
||||||
|
WritableArray metadata = new WritableNativeArray();
|
||||||
|
|
||||||
|
metadata.pushMap(id3);
|
||||||
|
|
||||||
|
event.putArray(EVENT_PROP_METADATA, metadata);
|
||||||
|
event.putDouble(EVENT_PROP_TARGET, getId());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
mEventEmitter.receiveEvent(getId(), Events.EVENT_TIMED_METADATA.toString(), event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow() {
|
protected void onDetachedFromWindow() {
|
||||||
|
|
||||||
mMediaPlayerValid = false;
|
mMediaPlayerValid = false;
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
|
setKeepScreenOn(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow() {
|
protected void onAttachedToWindow() {
|
||||||
|
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
if(mMainVer>0) {
|
if(mMainVer>0) {
|
||||||
@ -648,7 +701,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
|||||||
else {
|
else {
|
||||||
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders);
|
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders);
|
||||||
}
|
}
|
||||||
|
setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-video",
|
"name": "react-native-video",
|
||||||
"version": "3.1.0",
|
"version": "3.2.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",
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net46" />
|
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net46" />
|
||||||
</packages>
|
</packages>
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2",
|
"Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2",
|
||||||
"Newtonsoft.Json": "9.0.1"
|
"Newtonsoft.Json": "10.0.3"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"uap10.0": {}
|
"uap10.0": {}
|
||||||
|
Loading…
Reference in New Issue
Block a user