Merge remote-tracking branch 'upstream/master' into implement-ios-caching
This commit is contained in:
commit
8fcdc6e02a
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,9 +1,17 @@
|
||||
## Changelog
|
||||
|
||||
### Next Version
|
||||
|
||||
* Support video cachging for iOS ([#955](https://github.com/react-native-community/react-native-video/pull/955))
|
||||
|
||||
### Version 3.2.0
|
||||
* 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)
|
||||
* Support video cachging for iOS ([#955](https://github.com/react-native-community/react-native-video/pull/955))
|
||||
* 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
|
||||
* Support sidecar text tracks on iOS [#1109](https://github.com/react-native-community/react-native-video/pull/1109)
|
||||
|
25
README.md
25
README.md
@ -236,6 +236,7 @@ var styles = StyleSheet.create({
|
||||
### Configurable props
|
||||
* [allowsExternalPlayback](#allowsexternalplayback)
|
||||
* [audioOnly](#audioonly)
|
||||
* [bufferConfig](#bufferconfig)
|
||||
* [ignoreSilentSwitch](#ignoresilentswitch)
|
||||
* [muted](#muted)
|
||||
* [paused](#paused)
|
||||
@ -288,6 +289,30 @@ For this to work, the poster prop must be set.
|
||||
|
||||
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
|
||||
Controls the iOS silent switch behavior
|
||||
* **"inherit" (default)** - Use the default AVPlayer behavior
|
||||
|
6
Video.js
6
Video.js
@ -345,6 +345,12 @@ Video.propTypes = {
|
||||
paused: PropTypes.bool,
|
||||
muted: PropTypes.bool,
|
||||
volume: PropTypes.number,
|
||||
bufferConfig: PropTypes.shape({
|
||||
minBufferMs: PropTypes.number,
|
||||
maxBufferMs: PropTypes.number,
|
||||
bufferForPlaybackMs: PropTypes.number,
|
||||
bufferForPlaybackAfterRebufferMs: PropTypes.number,
|
||||
}),
|
||||
stereoPan: PropTypes.number,
|
||||
rate: PropTypes.number,
|
||||
playInBackground: PropTypes.bool,
|
||||
|
@ -58,6 +58,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
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.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
@ -109,6 +110,11 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
private boolean isBuffering;
|
||||
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
|
||||
private Uri srcUri;
|
||||
private String extension;
|
||||
@ -234,7 +240,9 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
if (player == null) {
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
||||
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.setMetadataOutput(this);
|
||||
exoPlayerView.setPlayer(player);
|
||||
@ -320,7 +328,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
private void releasePlayer() {
|
||||
if (player != null) {
|
||||
isPaused = player.getPlayWhenReady();
|
||||
updateResumePosition();
|
||||
player.release();
|
||||
player.setMetadataOutput(null);
|
||||
@ -931,4 +938,13 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
public void setUseTextureView(boolean 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.ViewGroupManager;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
||||
|
||||
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_MUTED = "muted";
|
||||
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_SEEK = "seek";
|
||||
private static final String PROP_RATE = "rate";
|
||||
@ -214,6 +220,25 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
||||
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) {
|
||||
return uriString.startsWith("http://")
|
||||
|| uriString.startsWith("https://")
|
||||
|
@ -10,6 +10,7 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.webkit.CookieManager;
|
||||
@ -96,7 +97,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
private Handler videoControlHandler = new Handler();
|
||||
private MediaController mediaController;
|
||||
|
||||
|
||||
private String mSrcUriString = null;
|
||||
private String mSrcType = "mp4";
|
||||
private ReadableMap mRequestHeaders = null;
|
||||
@ -359,7 +359,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
}
|
||||
|
||||
public void setPausedModifier(final boolean paused) {
|
||||
|
||||
mPaused = paused;
|
||||
|
||||
if (!mMediaPlayerValid) {
|
||||
@ -382,6 +381,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
mProgressUpdateHandler.post(mProgressUpdateRunnable);
|
||||
}
|
||||
}
|
||||
setKeepScreenOn(!mPaused);
|
||||
}
|
||||
|
||||
// reduces the volume based on stereoPan
|
||||
@ -501,7 +501,6 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
this.mUseNativeControls = controls;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
|
||||
@ -625,21 +624,22 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
|
||||
isCompleted = true;
|
||||
mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null);
|
||||
if (!mRepeat) {
|
||||
setKeepScreenOn(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
|
||||
mMediaPlayerValid = false;
|
||||
super.onDetachedFromWindow();
|
||||
setKeepScreenOn(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
|
||||
super.onAttachedToWindow();
|
||||
|
||||
if(mMainVer>0) {
|
||||
@ -648,7 +648,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
|
||||
else {
|
||||
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders);
|
||||
}
|
||||
|
||||
setKeepScreenOn(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -313,7 +313,7 @@ static int const RCTVideoUnset = -1;
|
||||
[self removePlayerItemObservers];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
|
||||
|
||||
// perform on next run loop, otherwise other passed react-props may not be set
|
||||
[self playerItemForSource:source withCallback:^(AVPlayerItem * playerItem) {
|
||||
_playerItem = playerItem;
|
||||
@ -353,8 +353,12 @@ static int const RCTVideoUnset = -1;
|
||||
}
|
||||
|
||||
- (NSURL*) urlFilePath:(NSString*) filepath {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
if ([filepath containsString:@"file://"]) {
|
||||
return [NSURL URLWithString:filepath];
|
||||
}
|
||||
|
||||
// code to support local caching
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString* relativeFilePath = [filepath lastPathComponent];
|
||||
// the file may be multiple levels below the documents directory
|
||||
NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"];
|
||||
@ -392,6 +396,7 @@ static int const RCTVideoUnset = -1;
|
||||
atTime:kCMTimeZero
|
||||
error:nil];
|
||||
|
||||
NSMutableArray* validTextTracks = [NSMutableArray array];
|
||||
for (int i = 0; i < _textTracks.count; ++i) {
|
||||
AVURLAsset *textURLAsset;
|
||||
NSString *textUri = [_textTracks objectAtIndex:i][@"uri"];
|
||||
@ -401,6 +406,8 @@ static int const RCTVideoUnset = -1;
|
||||
textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil];
|
||||
}
|
||||
AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject;
|
||||
if (!textTrackAsset) continue; // fix when there's no textTrackAsset
|
||||
[validTextTracks addObject:[_textTracks objectAtIndex:i]];
|
||||
AVMutableCompositionTrack *textCompTrack = [mixComposition
|
||||
addMutableTrackWithMediaType:AVMediaTypeText
|
||||
preferredTrackID:kCMPersistentTrackID_Invalid];
|
||||
@ -409,6 +416,9 @@ static int const RCTVideoUnset = -1;
|
||||
atTime:kCMTimeZero
|
||||
error:nil];
|
||||
}
|
||||
if (validTextTracks.count != _textTracks.count) {
|
||||
[self setTextTracks:validTextTracks];
|
||||
}
|
||||
|
||||
handler([AVPlayerItem playerItemWithAsset:mixComposition]);
|
||||
}
|
||||
@ -892,7 +902,10 @@ static int const RCTVideoUnset = -1;
|
||||
selectedTrackIndex = index;
|
||||
}
|
||||
}
|
||||
} else { // type "system"
|
||||
}
|
||||
|
||||
// in the situation that a selected text track is not available (eg. specifies a textTrack not available)
|
||||
if (![type isEqualToString:@"disabled"] && selectedTrackIndex == RCTVideoUnset) {
|
||||
CFArrayRef captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser);
|
||||
NSArray *captionSettings = (__bridge NSArray*)captioningMediaCharacteristics;
|
||||
if ([captionSettings containsObject:AVMediaCharacteristicTranscribesSpokenDialogForAccessibility]) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-video",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"description": "A <Video /> element for react-native",
|
||||
"main": "Video.js",
|
||||
"license": "MIT",
|
||||
|
@ -29,8 +29,8 @@
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
@ -71,4 +71,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net46" />
|
||||
</packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net46" />
|
||||
</packages>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2",
|
||||
"Newtonsoft.Json": "9.0.1"
|
||||
"Newtonsoft.Json": "10.0.3"
|
||||
},
|
||||
"frameworks": {
|
||||
"uap10.0": {}
|
||||
@ -14,4 +14,4 @@
|
||||
"win10-x64": {},
|
||||
"win10-x64-aot": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user