From e4da5c97734862e6a3585d06088b7aed336166e8 Mon Sep 17 00:00:00 2001 From: benlime Date: Sun, 30 Apr 2017 13:43:01 +0200 Subject: [PATCH 001/336] adds resizeMode to poster image --- Video.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Video.js b/Video.js index 4d69b3331c..ed55b7fc52 100644 --- a/Video.js +++ b/Video.js @@ -217,7 +217,6 @@ export default class Video extends Component { top: 0, right: 0, bottom: 0, - resizeMode: 'contain', }; return ( @@ -229,6 +228,7 @@ export default class Video extends Component { ); @@ -271,6 +271,7 @@ Video.propTypes = { ]), resizeMode: PropTypes.string, poster: PropTypes.string, + posterResizeMode: Image.propTypes.resizeMode, repeat: PropTypes.bool, paused: PropTypes.bool, muted: PropTypes.bool, From b6afaa3378fed72d6bd356f9a76dce965e350cf7 Mon Sep 17 00:00:00 2001 From: Nicolas Ngomai Date: Thu, 11 May 2017 13:46:04 +0200 Subject: [PATCH 002/336] Add link to propTypes Some props are missing in the documentation, the propTypes provides a full list of those. I think it would help a lot of developers to add a link here. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e473a766f6..11819fb270 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,8 @@ var styles = StyleSheet.create({ - * *For iOS you also need to specify muted for this to work* +To see full list of available props, you can check [the propTypes](https://github.com/react-native-community/react-native-video/blob/master/Video.js#L246) of the Video.js component. + ## Android Expansion File Usage ```javascript From 38d225294f7d0d499f0a4bf2770f14e14f073134 Mon Sep 17 00:00:00 2001 From: RWOverdijk Date: Tue, 25 Jul 2017 16:41:20 +0200 Subject: [PATCH 003/336] add support for onTimedMetaData for android --- .../com/brentvatne/react/ReactVideoView.java | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 94a25a0e99..62d530879f 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -4,6 +4,7 @@ import android.content.res.AssetFileDescriptor; import android.graphics.Matrix; import android.media.MediaPlayer; +import android.media.TimedMetaData; import android.net.Uri; import android.os.Handler; import android.util.Log; @@ -16,6 +17,8 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; 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.events.RCTEventEmitter; import com.yqritc.scalablevideoview.ScalableType; @@ -24,19 +27,28 @@ import com.yqritc.scalablevideoview.Size; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import java.lang.Math; @SuppressLint("ViewConstructor") -public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer - .OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, LifecycleEventListener, MediaController.MediaPlayerControl { +public class ReactVideoView extends ScalableVideoView implements + MediaPlayer.OnPreparedListener, + MediaPlayer.OnErrorListener, + MediaPlayer.OnBufferingUpdateListener, + MediaPlayer.OnTimedMetaDataAvailableListener, + MediaPlayer.OnCompletionListener, + MediaPlayer.OnInfoListener, + LifecycleEventListener, + MediaController.MediaPlayerControl { public enum Events { EVENT_LOAD_START("onVideoLoadStart"), EVENT_LOAD("onVideoLoad"), EVENT_ERROR("onVideoError"), EVENT_PROGRESS("onVideoProgress"), + EVENT_TIMED_METADATA("onTimedMetadata"), EVENT_SEEK("onVideoSeek"), EVENT_END("onVideoEnd"), EVENT_STALLED("onPlaybackStalled"), @@ -70,6 +82,7 @@ public String toString() { public static final String EVENT_PROP_WIDTH = "width"; public static final String EVENT_PROP_HEIGHT = "height"; public static final String EVENT_PROP_ORIENTATION = "orientation"; + public static final String EVENT_PROP_METADATA = "metadata"; public static final String EVENT_PROP_ERROR = "error"; public static final String EVENT_PROP_WHAT = "what"; @@ -181,6 +194,7 @@ private void initializeMediaPlayerIfNeeded() { mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setOnCompletionListener(this); + mMediaPlayer.setOnTimedMetaDataAvailableListener(this); mMediaPlayer.setOnInfoListener(this); } } @@ -444,6 +458,9 @@ public void run() { } }); } + + // Select track (so we can use it to listen to timed meta data updates) + mp.selectTrack(0); } @Override @@ -478,6 +495,9 @@ public boolean onInfo(MediaPlayer mp, int what, int extra) { @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); + mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0); } @@ -529,6 +549,30 @@ public void onCompletion(MediaPlayer mp) { mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null); } + @Override + 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("value", rawMeta.substring(rawMeta.lastIndexOf("\u0003") + 1)); + id3.putString("identifier", "id3/TDEN"); + + WritableArray metadata = new WritableNativeArray(); + + metadata.pushMap(id3); + + event.putArray(EVENT_PROP_METADATA, metadata); + event.putDouble("target", getId()); + } catch(UnsupportedEncodingException e) { + e.printStackTrace(); + } + + mEventEmitter.receiveEvent(getId(), Events.EVENT_TIMED_METADATA.toString(), event); + } + @Override protected void onDetachedFromWindow() { From aa89683785999f978fa7d60ca1f9a61b6b13f2d0 Mon Sep 17 00:00:00 2001 From: RWOverdijk Date: Tue, 25 Jul 2017 16:53:04 +0200 Subject: [PATCH 004/336] move prop keys for metadata to constants --- .../main/java/com/brentvatne/react/ReactVideoView.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 62d530879f..dc51346933 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -83,6 +83,9 @@ public String toString() { public static final String EVENT_PROP_HEIGHT = "height"; 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_WHAT = "what"; @@ -557,15 +560,15 @@ public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data) { String rawMeta = new String(data.getMetaData(), "UTF-8"); WritableMap id3 = Arguments.createMap(); - id3.putString("value", rawMeta.substring(rawMeta.lastIndexOf("\u0003") + 1)); - id3.putString("identifier", "id3/TDEN"); + 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("target", getId()); + event.putDouble(EVENT_PROP_TARGET, getId()); } catch(UnsupportedEncodingException e) { e.printStackTrace(); } From a4ada46b3069ee300df2b68d89ef2ecbde7f0469 Mon Sep 17 00:00:00 2001 From: Landon Sherwood Date: Tue, 29 Aug 2017 16:39:18 -0500 Subject: [PATCH 005/336] Show iOS controls if not fullscreen Show controls without calling the present full screen method. --- ios/RCTVideo.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 16174add4d..134cc0ae67 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -90,7 +90,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher - (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem { RCTVideoPlayerViewController* playerLayer= [[RCTVideoPlayerViewController alloc] init]; - playerLayer.showsPlaybackControls = NO; + playerLayer.showsPlaybackControls = YES; playerLayer.rctDelegate = self; playerLayer.view.frame = self.bounds; playerLayer.player = _player; From 451f8d0919e94e44d7b294b187ac527533d06b82 Mon Sep 17 00:00:00 2001 From: Alex Fox Date: Thu, 7 Sep 2017 13:16:44 +0100 Subject: [PATCH 006/336] Fixed rate not being respected after seeking Referenced in issue => https://github.com/react-native-community/react-native-video/issues/763 --- ios/RCTVideo.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 9962c2bb14..097e16c897 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -561,7 +561,9 @@ - (void)setSeek:(float)seekTime if (CMTimeCompare(current, cmSeekTime) != 0) { if (!wasPaused) [_player pause]; [_player seekToTime:cmSeekTime toleranceBefore:tolerance toleranceAfter:tolerance completionHandler:^(BOOL finished) { - if (!wasPaused) [_player play]; + if (!wasPaused) { + [self setPaused:false]; + } if(self.onVideoSeek) { self.onVideoSeek(@{@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(item.currentTime)], @"seekTime": [NSNumber numberWithFloat:seekTime], From 1bdd8720fbc08b2a04edf2b59e8e03063ed41c48 Mon Sep 17 00:00:00 2001 From: Anne Glines Date: Mon, 11 Sep 2017 22:30:17 -0700 Subject: [PATCH 007/336] Reseting isCompleted flag on source change --- android/src/main/java/com/brentvatne/react/ReactVideoView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 94a25a0e99..f426c30ec4 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -297,6 +297,7 @@ public void setSrc(final String uriString, final String type, final boolean isNe WritableMap event = Arguments.createMap(); event.putMap(ReactVideoViewManager.PROP_SRC, src); mEventEmitter.receiveEvent(getId(), Events.EVENT_LOAD_START.toString(), event); + isCompleted = false; try { prepareAsync(this); From 3e0f084c621e7af0a60a13c701cb94cd3b211041 Mon Sep 17 00:00:00 2001 From: Louis Capitanchik Date: Wed, 27 Sep 2017 16:13:29 +0100 Subject: [PATCH 008/336] Implement 'rate' prop for android devices - Version locked to 6.0+ because that is the version that introduced setPlaybackParams - Ignores rate prop as before on android versions lower than 6.0 --- .../main/java/com/brentvatne/react/ReactVideoView.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 94a25a0e99..099bec3331 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -5,6 +5,7 @@ import android.graphics.Matrix; import android.media.MediaPlayer; import android.net.Uri; +import android.os.Build; import android.os.Handler; import android.util.Log; import android.view.MotionEvent; @@ -377,8 +378,11 @@ public void setRateModifier(final float rate) { mRate = rate; if (mMediaPlayerValid) { - // TODO: Implement this. - Log.e(ReactVideoViewManager.REACT_CLASS, "Setting playback rate is not yet supported on Android"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mMediaPlayer.setPlaybackParams(mMediaPlayer.getPlaybackParams().setSpeed(rate)); + } else { + Log.e(ReactVideoViewManager.REACT_CLASS, "Setting playback rate is not yet supported on Android versions below 6.0"); + } } } @@ -388,7 +392,7 @@ public void applyModifiers() { setPausedModifier(mPaused); setMutedModifier(mMuted); setProgressUpdateInterval(mProgressUpdateInterval); -// setRateModifier(mRate); + setRateModifier(mRate); } public void setPlayInBackground(final boolean playInBackground) { From 435669a944430fa51b8bbd640c6bf66c7756a1ce Mon Sep 17 00:00:00 2001 From: Dan Hodos Date: Thu, 28 Sep 2017 21:37:26 -0400 Subject: [PATCH 009/336] Extract method to add observer for progress update --- ios/RCTVideo.m | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 9962c2bb14..88f3a640b1 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -124,6 +124,16 @@ - (CMTimeRange)playerItemSeekableTimeRange return (kCMTimeRangeZero); } +-(void)addPlayerTimeObserver +{ + const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; + // @see endScrubbing in AVPlayerDemoPlaybackViewController.m of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html + __weak RCTVideo *weakSelf = self; + _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(progressUpdateIntervalMS, NSEC_PER_SEC) + queue:NULL + usingBlock:^(CMTime time) { [weakSelf sendProgressUpdate]; } + ]; +} /* Cancels the previously registered time observer. */ -(void)removePlayerTimeObserver @@ -283,13 +293,7 @@ - (void)setSrc:(NSDictionary *)source [_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; _playbackRateObserverRegistered = YES; - const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; - // @see endScrubbing in AVPlayerDemoPlaybackViewController.m of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html - __weak RCTVideo *weakSelf = self; - _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(progressUpdateIntervalMS, NSEC_PER_SEC) - queue:NULL - usingBlock:^(CMTime time) { [weakSelf sendProgressUpdate]; } - ]; + [self addPlayerTimeObserver]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //Perform on next run loop, otherwise onVideoLoadStart is nil From 10cba5ad5c545a1ea6a6e7d53020f834466aa72e Mon Sep 17 00:00:00 2001 From: Dan Hodos Date: Thu, 28 Sep 2017 21:37:52 -0400 Subject: [PATCH 010/336] Reset progress observer on update interval changes --- ios/RCTVideo.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 88f3a640b1..68e52e3103 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -725,6 +725,11 @@ - (void)setControls:(BOOL)controls - (void)setProgressUpdateInterval:(float)progressUpdateInterval { _progressUpdateInterval = progressUpdateInterval; + + if (_timeObserver) { + [self removePlayerTimeObserver]; + [self addPlayerTimeObserver]; + } } - (void)removePlayerLayer From 270fdfb657bb298a2c381aa5ff0e455cb2a95423 Mon Sep 17 00:00:00 2001 From: Emrah Kaya Date: Mon, 2 Oct 2017 20:11:41 +0200 Subject: [PATCH 011/336] added optional request headers for remote assests (android & ios) --- Video.js | 11 +++++ .../brentvatne/exoplayer/DataSourceUtil.java | 25 +++++++--- .../exoplayer/ReactExoplayerView.java | 10 ++-- .../exoplayer/ReactExoplayerViewManager.java | 30 +++++++++++- .../com/brentvatne/react/ReactVideoView.java | 48 +++++++++++++++++-- .../react/ReactVideoViewManager.java | 7 ++- ios/RCTVideo.m | 7 ++- 7 files changed, 119 insertions(+), 19 deletions(-) diff --git a/Video.js b/Video.js index b90c030e4f..b835664d2c 100644 --- a/Video.js +++ b/Video.js @@ -24,6 +24,16 @@ export default class Video extends Component { this._root.setNativeProps(nativeProps); } + stringsOnlyObject(obj) { + const strObj = {}; + + Object.keys(obj).forEach(x => { + strObj[x] = obj[x].toString(); + }); + + return strObj; + } + seek = (time) => { this.setNativeProps({ seek: time }); }; @@ -190,6 +200,7 @@ export default class Video extends Component { type: source.type || '', mainVer: source.mainVer || 0, patchVer: source.patchVer || 0, + requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {} }, onVideoLoadStart: this._onLoadStart, onVideoLoad: this._onLoad, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java index 0077c3b0cd..2c3af70872 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java @@ -1,7 +1,10 @@ package com.brentvatne.exoplayer; import android.content.Context; +import android.util.ArrayMap; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.modules.network.OkHttpClientProvider; import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DataSource; @@ -10,6 +13,10 @@ import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Util; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + public class DataSourceUtil { private DataSourceUtil() { @@ -41,9 +48,9 @@ public static void setRawDataSourceFactory(DataSource.Factory factory) { DataSourceUtil.rawDataSourceFactory = factory; } - public static DataSource.Factory getDefaultDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { + public static DataSource.Factory getDefaultDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) { if (defaultDataSourceFactory == null) { - defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter); + defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter, requestHeaders); } return defaultDataSourceFactory; } @@ -56,14 +63,18 @@ private static DataSource.Factory buildRawDataSourceFactory(Context context) { return new RawResourceDataSourceFactory(context.getApplicationContext()); } - private static DataSource.Factory buildDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { + private static DataSource.Factory buildDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) { Context appContext = context.getApplicationContext(); return new DefaultDataSourceFactory(appContext, bandwidthMeter, - buildHttpDataSourceFactory(appContext, bandwidthMeter)); + buildHttpDataSourceFactory(appContext, bandwidthMeter, requestHeaders)); } - private static HttpDataSource.Factory buildHttpDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { - return new OkHttpDataSourceFactory(OkHttpClientProvider.getOkHttpClient(), getUserAgent(context), bandwidthMeter); - } + private static HttpDataSource.Factory buildHttpDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) { + OkHttpDataSourceFactory okHttpDataSourceFactory = new OkHttpDataSourceFactory(OkHttpClientProvider.getOkHttpClient(), getUserAgent(context), bandwidthMeter); + if (requestHeaders != null) + okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders); + + return okHttpDataSourceFactory; + } } 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 52bf24e64c..dc48de31b8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -14,6 +14,7 @@ import com.brentvatne.receiver.AudioBecomingNoisyReceiver; import com.brentvatne.receiver.BecomingNoisyListener; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.ThemedReactContext; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; @@ -52,6 +53,7 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.lang.Math; +import java.util.Map; @SuppressLint("ViewConstructor") class ReactExoplayerView extends FrameLayout implements @@ -96,6 +98,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean disableFocus; private float mProgressUpdateInterval = 250.0f; private boolean playInBackground = false; + private Map requestHeaders; // \ End props // React @@ -353,7 +356,7 @@ private void clearResumePosition() { * @return A new DataSource factory. */ private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) { - return DataSourceUtil.getDefaultDataSourceFactory(getContext(), useBandwidthMeter ? BANDWIDTH_METER : null); + return DataSourceUtil.getDefaultDataSourceFactory(getContext(), useBandwidthMeter ? BANDWIDTH_METER : null, requestHeaders); } // AudioManager.OnAudioFocusChangeListener implementation @@ -537,14 +540,15 @@ public void onMetadata(Metadata metadata) { // ReactExoplayerViewManager public api - public void setSrc(final Uri uri, final String extension) { + public void setSrc(final Uri uri, final String extension, Map headers) { if (uri != null) { boolean isOriginalSourceNull = srcUri == null; boolean isSourceEqual = uri.equals(srcUri); this.srcUri = uri; this.extension = extension; - this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(getContext(), BANDWIDTH_METER); + this.requestHeaders = headers; + this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(getContext(), BANDWIDTH_METER, requestHeaders); if (!isOriginalSourceNull && !isSourceEqual) { reloadSource(); 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 dda8d00036..cc0bee6447 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -11,6 +11,7 @@ import com.facebook.react.uimanager.annotations.ReactProp; import com.google.android.exoplayer2.upstream.RawResourceDataSource; +import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; @@ -22,6 +23,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null; + if (TextUtils.isEmpty(uriString)) { return; @@ -81,7 +85,7 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src Uri srcUri = Uri.parse(uriString); if (srcUri != null) { - videoView.setSrc(srcUri, extension); + videoView.setSrc(srcUri, extension, headers); } } else { int identifier = context.getResources().getIdentifier( @@ -170,4 +174,28 @@ private boolean startsWithValidScheme(String uriString) { } return ResizeMode.RESIZE_MODE_FIT; } + + /** + * toStringMap converts a {@link ReadableMap} into a HashMap. + * + * @param readableMap The ReadableMap to be conveted. + * @return A HashMap containing the data that was in the ReadableMap. + * @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java' + */ + public static Map toStringMap(@Nullable ReadableMap readableMap) { + if (readableMap == null) + return null; + + com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + if (!iterator.hasNextKey()) + return null; + + Map result = new HashMap<>(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + result.put(key, readableMap.getString(key)); + } + + return result; + } } diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 94a25a0e99..17a8fbb04c 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -15,6 +15,7 @@ import com.android.vending.expansion.zipfile.ZipResourceFile; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.events.RCTEventEmitter; @@ -28,6 +29,8 @@ import java.util.Map; import java.lang.Math; +import javax.annotation.Nullable; + @SuppressLint("ViewConstructor") public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer .OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, LifecycleEventListener, MediaController.MediaPlayerControl { @@ -86,6 +89,7 @@ public String toString() { private String mSrcUriString = null; private String mSrcType = "mp4"; + private ReadableMap mRequestHeaders = null; private boolean mSrcIsNetwork = false; private boolean mSrcIsAsset = false; private ScalableType mResizeMode = ScalableType.LEFT_TOP; @@ -201,16 +205,17 @@ public void cleanupMediaPlayerResources() { } } - public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset) { - setSrc(uriString,type,isNetwork,isAsset,0,0); + public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders) { + setSrc(uriString, type, isNetwork, isAsset, requestHeaders, 0, 0); } - public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final int expansionMainVersion, final int expansionPatchVersion) { + public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders, final int expansionMainVersion, final int expansionPatchVersion) { mSrcUriString = uriString; mSrcType = type; mSrcIsNetwork = isNetwork; mSrcIsAsset = isAsset; + mRequestHeaders = requestHeaders; mMainVer = expansionMainVersion; mPatchVer = expansionPatchVersion; @@ -239,6 +244,10 @@ public void setSrc(final String uriString, final String type, final boolean isNe headers.put("Cookie", cookie); } + if (mRequestHeaders != null) { + headers.putAll(toStringMap(mRequestHeaders)); + } + setDataSource(uriString); } else if (isAsset) { if (uriString.startsWith("content://")) { @@ -285,8 +294,13 @@ public void setSrc(final String uriString, final String type, final boolean isNe } WritableMap src = Arguments.createMap(); + + WritableMap wRequestHeaders = Arguments.createMap(); + wRequestHeaders.merge(mRequestHeaders); + src.putString(ReactVideoViewManager.PROP_SRC_URI, uriString); src.putString(ReactVideoViewManager.PROP_SRC_TYPE, type); + src.putMap(ReactVideoViewManager.PROP_SRC_HEADERS, wRequestHeaders); src.putBoolean(ReactVideoViewManager.PROP_SRC_IS_NETWORK, isNetwork); if(mMainVer>0) { src.putInt(ReactVideoViewManager.PROP_SRC_MAINVER, mMainVer); @@ -542,10 +556,10 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); if(mMainVer>0) { - setSrc(mSrcUriString, mSrcType, mSrcIsNetwork,mSrcIsAsset,mMainVer,mPatchVer); + setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders, mMainVer, mPatchVer); } else { - setSrc(mSrcUriString, mSrcType, mSrcIsNetwork,mSrcIsAsset); + setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders); } } @@ -577,4 +591,28 @@ public void run() { @Override public void onHostDestroy() { } + + /** + * toStringMap converts a {@link ReadableMap} into a HashMap. + * + * @param readableMap The ReadableMap to be conveted. + * @return A HashMap containing the data that was in the ReadableMap. + * @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java' + */ + public static Map toStringMap(@Nullable ReadableMap readableMap) { + if (readableMap == null) + return null; + + com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + if (!iterator.hasNextKey()) + return null; + + Map result = new HashMap<>(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + result.put(key, readableMap.getString(key)); + } + + return result; + } } diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java b/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java index 983113d55e..404a16efce 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java @@ -21,6 +21,7 @@ public class ReactVideoViewManager extends SimpleViewManager { public static final String PROP_SRC = "src"; public static final String PROP_SRC_URI = "uri"; public static final String PROP_SRC_TYPE = "type"; + public static final String PROP_SRC_HEADERS = "headers"; public static final String PROP_SRC_IS_NETWORK = "isNetwork"; public static final String PROP_SRC_MAINVER = "mainVer"; public static final String PROP_SRC_PATCHVER = "patchVer"; @@ -85,6 +86,7 @@ public void setSrc(final ReactVideoView videoView, @Nullable ReadableMap src) { src.getString(PROP_SRC_TYPE), src.getBoolean(PROP_SRC_IS_NETWORK), src.getBoolean(PROP_SRC_IS_ASSET), + src.getMap(PROP_SRC_HEADERS), mainVer, patchVer ); @@ -94,8 +96,9 @@ public void setSrc(final ReactVideoView videoView, @Nullable ReadableMap src) { src.getString(PROP_SRC_URI), src.getString(PROP_SRC_TYPE), src.getBoolean(PROP_SRC_IS_NETWORK), - src.getBoolean(PROP_SRC_IS_ASSET) - ); + src.getBoolean(PROP_SRC_IS_ASSET), + src.getMap(PROP_SRC_HEADERS) + ); } } diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 9962c2bb14..8538b7429a 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -312,12 +312,17 @@ - (AVPlayerItem*)playerItemForSource:(NSDictionary *)source bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; NSString *uri = [source objectForKey:@"uri"]; NSString *type = [source objectForKey:@"type"]; - + NSDictionary *headers = [source objectForKey:@"requestHeaders"]; + NSURL *url = (isNetwork || isAsset) ? [NSURL URLWithString:uri] : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; if (isNetwork) { + if ([headers count] > 0) { + AVURLAsset* asset = [AVURLAsset URLAssetWithURL:url options:@{@"AVURLAssetHTTPHeaderFieldsKey": headers}]; + return [AVPlayerItem playerItemWithAsset:asset]; + } NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetHTTPCookiesKey : cookies}]; return [AVPlayerItem playerItemWithAsset:asset]; From 2ae99bd484486f468932db7830b7db8b69ccc507 Mon Sep 17 00:00:00 2001 From: Jan Lievens Date: Tue, 24 Oct 2017 09:47:43 +0200 Subject: [PATCH 012/336] remove observers before adding thus preventing multiple observers for the same notification --- ios/RCTVideo.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 9962c2bb14..ab0927c630 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -454,10 +454,17 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N - (void)attachListeners { // listen for end of file + [[NSNotificationCenter defaultCenter] removeObserver:self + name:AVPlayerItemDidPlayToEndTimeNotification + object:[_player currentItem]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:[_player currentItem]]; + + [[NSNotificationCenter defaultCenter] removeObserver:self + name:AVPlayerItemPlaybackStalledNotification + object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackStalled:) name:AVPlayerItemPlaybackStalledNotification From f7b7f2666a206bc92faa94028b26d65b45a7300c Mon Sep 17 00:00:00 2001 From: Marc-Olivier Duval Date: Tue, 24 Oct 2017 21:49:46 -0400 Subject: [PATCH 013/336] Apply fix in playInBackground props --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 52bf24e64c..c12b2a48dd 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -167,6 +167,9 @@ protected void onAttachedToWindow() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + if (playInBackground) { + return; + } stopPlayback(); } @@ -174,9 +177,6 @@ protected void onDetachedFromWindow() { @Override public void onHostResume() { - if (playInBackground) { - return; - } setPlayWhenReady(!isPaused); } From ee5818b6ff4e39b1d5bdc5d59616f0f5f255ad7c Mon Sep 17 00:00:00 2001 From: Marc-Olivier Duval Date: Tue, 24 Oct 2017 22:42:41 -0400 Subject: [PATCH 014/336] Fix bug with playInBackground --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 c12b2a48dd..34df00319c 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -167,9 +167,6 @@ protected void onAttachedToWindow() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (playInBackground) { - return; - } stopPlayback(); } @@ -209,12 +206,14 @@ private void initializePlayer() { player.setMetadataOutput(this); exoPlayerView.setPlayer(player); audioBecomingNoisyReceiver.setListener(this); - setPlayWhenReady(!isPaused); playerNeedsSource = true; PlaybackParameters params = new PlaybackParameters(rate, 1f); player.setPlaybackParameters(params); } + + setPlayWhenReady(!isPaused); + if (playerNeedsSource && srcUri != null) { MediaSource mediaSource = buildMediaSource(srcUri, extension); mediaSource = repeat ? new LoopingMediaSource(mediaSource) : mediaSource; From 0fe621a26db4f9e091649b0304a5715d4d8b2947 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Duval Date: Tue, 24 Oct 2017 22:42:51 -0400 Subject: [PATCH 015/336] Updated exoplayer readme --- android-exoplayer/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android-exoplayer/README.md b/android-exoplayer/README.md index e2d4f9faef..9396763b26 100644 --- a/android-exoplayer/README.md +++ b/android-exoplayer/README.md @@ -42,6 +42,5 @@ https://github.com/google/ExoPlayer ## Unimplemented props -- `playInBackground={true}` -- `rate={1.0}` - Expansion file - `source={{ mainVer: 1, patchVer: 0 }}` + From 3e293407e8b15cb97b9ed475a0d65e40b079c466 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Duval Date: Tue, 24 Oct 2017 22:50:20 -0400 Subject: [PATCH 016/336] updated readme to with a guid to setup exoplayer --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 597b71ec0e..89e46a2fd9 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,16 @@ Toggles a fullscreen player. Access using a ref to the component. - [Lumpen Radio](https://github.com/jhabdas/lumpen-radio) contains another example integration using local files and full screen background video. +## Use ExoPlayer on Android + +To use ExoPlayer instead of the default player, you have to change android to android-exoplayer in settings.gradle + +**android/settings.gradle** + +```gradle +project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer') +``` + ## TODOS - [ ] Add support for captions From fe69081576017200c02d54425ebf7bf2aa30e68a Mon Sep 17 00:00:00 2001 From: Mathias Scherer Date: Fri, 3 Nov 2017 12:08:30 +0100 Subject: [PATCH 017/336] adds supported file extensions Android Expansion files adds mp4 as the only supported file extension for Android Expansion files --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 597b71ec0e..62fe5ba9c3 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ var styles = StyleSheet.create({ - * *For iOS you also need to specify muted for this to work* ## Android Expansion File Usage - +Only supports .mp4 files ```javascript // Within your render function, assuming you have a file called // "background.mp4" in your expansion file. Just add your main and (if applicable) patch version From 132d420b6ad28f54652778c08b762b4dfca372cc Mon Sep 17 00:00:00 2001 From: Mathias Scherer Date: Mon, 6 Nov 2017 13:17:56 +0100 Subject: [PATCH 018/336] updates readme with additional infos for expansion files adds the info that the mp4 files inside expansion files have not to be compressed to get it working --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 62fe5ba9c3..55a759281c 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,12 @@ var styles = StyleSheet.create({ - * *For iOS you also need to specify muted for this to work* ## Android Expansion File Usage -Only supports .mp4 files +Only supports .mp4 files. +The video files have not to be compressed. +Linux command example to exclude .mp4 files from zip compression: +```bash +zip -r -n.mp4 *.mp4 main.1.com.exmample.com +``` ```javascript // Within your render function, assuming you have a file called // "background.mp4" in your expansion file. Just add your main and (if applicable) patch version From c0a8f7c0c9ebf1a82a6f37381de9d96754c931c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kay=20Pl=C3=B6=C3=9Fer?= Date: Fri, 24 Nov 2017 16:22:41 +0100 Subject: [PATCH 019/336] Document fullscreen callbacks fix #861 --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 597b71ec0e..01470edf6f 100644 --- a/README.md +++ b/README.md @@ -165,12 +165,16 @@ using System.Collections.Generic; playWhenInactive={false} // [iOS] Video continues to play when control or notification center are shown. ignoreSilentSwitch={"ignore"} // [iOS] ignore | obey - When 'ignore', audio will still play with the iOS hard silent switch set to silent. When 'obey', audio will toggle with the switch. When not specified, will inherit audio settings as usual. progressUpdateInterval={250.0} // [iOS] Interval to fire onProgress (default to ~250ms) + onBuffer={this.onBuffer} // Callback when remote video is buffering + onEnd={this.onEnd} // Callback when playback finishes + onError={this.videoError} // Callback when video cannot be loaded + onFullscreenPlayerWillPresent={this.fullScreenPlayerWillPresent} // Callback before fullscreen starts + onFullscreenPlayerDidPresent={this.fullScreenPlayerDidPresent} // Callback after fullscreen started + onFullscreenPlayerWillDismiss={this.fullScreenPlayerWillDismiss} // Callback before fullscreen stops + onFullscreenPlayerDidDismiss={this.fullScreenPlayerDidDissmiss} // Callback after fullscreen stopped onLoadStart={this.loadStart} // Callback when video starts to load onLoad={this.setDuration} // Callback when video loads onProgress={this.setTime} // Callback every ~250ms with currentTime - onEnd={this.onEnd} // Callback when playback finishes - onError={this.videoError} // Callback when video cannot be loaded - onBuffer={this.onBuffer} // Callback when remote video is buffering onTimedMetadata={this.onTimedMetadata} // Callback when the stream receive some metadata style={styles.backgroundVideo} /> From ce7c732453476cf77b8583981601f521cbb83410 Mon Sep 17 00:00:00 2001 From: Jordan Becker Date: Thu, 7 Dec 2017 19:35:32 +0100 Subject: [PATCH 020/336] Add requiresMainQueueSetup method Since RN 0.49, `requiresMainQueueSetup` needs to be defined if the module overrides `constantsToExport`. --- ios/RCTVideoManager.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ios/RCTVideoManager.m b/ios/RCTVideoManager.m index a5cf85e0b6..c1edb9b782 100644 --- a/ios/RCTVideoManager.m +++ b/ios/RCTVideoManager.m @@ -62,4 +62,9 @@ - (NSDictionary *)constantsToExport }; } ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + @end From 64191298e514ab52b9cb4fcabcb7e38b40a698fe Mon Sep 17 00:00:00 2001 From: sm2017 Date: Sun, 24 Dec 2017 14:44:00 +0330 Subject: [PATCH 021/336] Update ReactVideoView.cs Fix 'Event' does not contain a constructor that takes 2 arguments ReactNativeVideo --- windows/ReactNativeVideo.Net46/ReactVideoView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/ReactNativeVideo.Net46/ReactVideoView.cs b/windows/ReactNativeVideo.Net46/ReactVideoView.cs index 8a8b1b294c..643965eed6 100644 --- a/windows/ReactNativeVideo.Net46/ReactVideoView.cs +++ b/windows/ReactNativeVideo.Net46/ReactVideoView.cs @@ -328,7 +328,7 @@ class ReactVideoEvent : Event private readonly JObject _eventData; public ReactVideoEvent(string eventName, int viewTag, JObject eventData) - : base(viewTag, TimeSpan.FromTicks(Environment.TickCount)) + : base(viewTag) { _eventName = eventName; _eventData = eventData; From dd242476a3615055343c0c06a119713f9df6fb0e Mon Sep 17 00:00:00 2001 From: sm2017 Date: Sun, 24 Dec 2017 14:46:02 +0330 Subject: [PATCH 022/336] Update ReactVideoView.cs Fix 'Event' does not contain a constructor that takes 2 arguments ReactNativeVideo --- windows/ReactNativeVideo/ReactVideoView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/ReactNativeVideo/ReactVideoView.cs b/windows/ReactNativeVideo/ReactVideoView.cs index 22063affaa..5a6c103fd3 100644 --- a/windows/ReactNativeVideo/ReactVideoView.cs +++ b/windows/ReactNativeVideo/ReactVideoView.cs @@ -334,7 +334,7 @@ class ReactVideoEvent : Event private readonly JObject _eventData; public ReactVideoEvent(string eventName, int viewTag, JObject eventData) - : base(viewTag, TimeSpan.FromTicks(Environment.TickCount)) + : base(viewTag) { _eventName = eventName; _eventData = eventData; From 7d48f22d9871bf98f4a1208e53d218954135f6fa Mon Sep 17 00:00:00 2001 From: Alejandro Rangel Date: Thu, 11 Jan 2018 09:16:29 -0800 Subject: [PATCH 023/336] add seekableDuration to android ReactVideoView --- android/src/main/java/com/brentvatne/react/ReactVideoView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java index 94a25a0e99..f609bcc422 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoView.java @@ -64,6 +64,7 @@ public String toString() { public static final String EVENT_PROP_DURATION = "duration"; public static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration"; + public static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration"; public static final String EVENT_PROP_CURRENT_TIME = "currentTime"; public static final String EVENT_PROP_SEEK_TIME = "seekTime"; public static final String EVENT_PROP_NATURALSIZE = "naturalSize"; @@ -127,6 +128,7 @@ public void run() { WritableMap event = Arguments.createMap(); event.putDouble(EVENT_PROP_CURRENT_TIME, mMediaPlayer.getCurrentPosition() / 1000.0); event.putDouble(EVENT_PROP_PLAYABLE_DURATION, mVideoBufferedDuration / 1000.0); //TODO:mBufferUpdateRunnable + event.putDouble(EVENT_PROP_SEEKABLE_DURATION, mVideoDuration / 1000.0); mEventEmitter.receiveEvent(getId(), Events.EVENT_PROGRESS.toString(), event); // Check for update after an interval From 57a16b23c57b600dd446298a26fdf1eb627d0955 Mon Sep 17 00:00:00 2001 From: Emrah Kaya Date: Sat, 13 Jan 2018 21:28:24 +0100 Subject: [PATCH 024/336] stringsOnlyObject does typechecking before coercion to string --- Video.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Video.js b/Video.js index b835664d2c..2db74fbd65 100644 --- a/Video.js +++ b/Video.js @@ -23,6 +23,29 @@ export default class Video extends Component { setNativeProps(nativeProps) { this._root.setNativeProps(nativeProps); } + + toTypeString(x) { + switch (typeof x) { + case "object": + return x instanceof Date + ? x.toISOString() + : JSON.stringify(x); // object, null + case "undefined": + return ""; + default: // boolean, number, string + return x.toString(); + } + } + + stringsOnlyObject(obj) { + const strObj = {}; + + Object.keys(obj).forEach(x => { + strObj[x] = this.toTypeString(obj[x]); + }); + + return strObj; + } stringsOnlyObject(obj) { const strObj = {}; From db1d83b83e68ed02160d4c8c6eb35a650a29709e Mon Sep 17 00:00:00 2001 From: Emrah Kaya Date: Sat, 13 Jan 2018 21:29:53 +0100 Subject: [PATCH 025/336] constants renamed to requestheader --- .../com/brentvatne/exoplayer/ReactExoplayerViewManager.java | 2 +- .../main/java/com/brentvatne/react/ReactVideoViewManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 cc0bee6447..634aefd25b 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -23,7 +23,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager { public static final String PROP_SRC = "src"; public static final String PROP_SRC_URI = "uri"; public static final String PROP_SRC_TYPE = "type"; - public static final String PROP_SRC_HEADERS = "headers"; + public static final String PROP_SRC_HEADERS = "requestHeaders"; public static final String PROP_SRC_IS_NETWORK = "isNetwork"; public static final String PROP_SRC_MAINVER = "mainVer"; public static final String PROP_SRC_PATCHVER = "patchVer"; From 5c8e1bd6d4828842f160bd66eb6c8c6aabb92f47 Mon Sep 17 00:00:00 2001 From: emrah88 Date: Mon, 15 Jan 2018 16:15:25 +0100 Subject: [PATCH 026/336] ensuring datasource is build with headers --- .../src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java index 2c3af70872..91bee912b8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java @@ -49,7 +49,7 @@ public static void setRawDataSourceFactory(DataSource.Factory factory) { } public static DataSource.Factory getDefaultDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) { - if (defaultDataSourceFactory == null) { + if (defaultDataSourceFactory == null || !requestHeaders.isEmpty()) { defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter, requestHeaders); } return defaultDataSourceFactory; From 12f93b5463a525562eb1edff9117d257de0220e8 Mon Sep 17 00:00:00 2001 From: emrah88 Date: Mon, 15 Jan 2018 17:10:54 +0100 Subject: [PATCH 027/336] null pointer --- .../src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java index 91bee912b8..47d179d7e1 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java @@ -49,7 +49,7 @@ public static void setRawDataSourceFactory(DataSource.Factory factory) { } public static DataSource.Factory getDefaultDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) { - if (defaultDataSourceFactory == null || !requestHeaders.isEmpty()) { + if (defaultDataSourceFactory == null || (requestHeaders != null && !requestHeaders.isEmpty())) { defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter, requestHeaders); } return defaultDataSourceFactory; From 5d274631c8d742a5faa49532bf0a967da760be2c Mon Sep 17 00:00:00 2001 From: Brandon Moon Date: Mon, 29 Jan 2018 13:25:58 -0700 Subject: [PATCH 028/336] Link up cookies so exoplayer can use them --- .../brentvatne/exoplayer/DataSourceUtil.java | 31 +++++++++++++------ .../exoplayer/ReactExoplayerView.java | 16 +++------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java index 0077c3b0cd..c486bf0b0c 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java @@ -1,7 +1,11 @@ package com.brentvatne.exoplayer; import android.content.Context; +import android.content.ContextWrapper; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.CookieJarContainer; +import com.facebook.react.modules.network.ForwardingCookieHandler; import com.facebook.react.modules.network.OkHttpClientProvider; import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DataSource; @@ -10,6 +14,10 @@ import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Util; +import okhttp3.Cookie; +import okhttp3.JavaNetCookieJar; +import okhttp3.OkHttpClient; + public class DataSourceUtil { private DataSourceUtil() { @@ -23,14 +31,14 @@ public static void setUserAgent(String userAgent) { DataSourceUtil.userAgent = userAgent; } - public static String getUserAgent(Context context) { + public static String getUserAgent(ReactContext context) { if (userAgent == null) { - userAgent = Util.getUserAgent(context.getApplicationContext(), "ReactNativeVideo"); + userAgent = Util.getUserAgent(context, "ReactNativeVideo"); } return userAgent; } - public static DataSource.Factory getRawDataSourceFactory(Context context) { + public static DataSource.Factory getRawDataSourceFactory(ReactContext context) { if (rawDataSourceFactory == null) { rawDataSourceFactory = buildRawDataSourceFactory(context); } @@ -41,7 +49,7 @@ public static void setRawDataSourceFactory(DataSource.Factory factory) { DataSourceUtil.rawDataSourceFactory = factory; } - public static DataSource.Factory getDefaultDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { + public static DataSource.Factory getDefaultDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) { if (defaultDataSourceFactory == null) { defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter); } @@ -52,17 +60,20 @@ public static void setDefaultDataSourceFactory(DataSource.Factory factory) { DataSourceUtil.defaultDataSourceFactory = factory; } - private static DataSource.Factory buildRawDataSourceFactory(Context context) { + private static DataSource.Factory buildRawDataSourceFactory(ReactContext context) { return new RawResourceDataSourceFactory(context.getApplicationContext()); } - private static DataSource.Factory buildDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { - Context appContext = context.getApplicationContext(); - return new DefaultDataSourceFactory(appContext, bandwidthMeter, - buildHttpDataSourceFactory(appContext, bandwidthMeter)); + private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) { + return new DefaultDataSourceFactory(context, bandwidthMeter, + buildHttpDataSourceFactory(context, bandwidthMeter)); } - private static HttpDataSource.Factory buildHttpDataSourceFactory(Context context, DefaultBandwidthMeter bandwidthMeter) { + private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) { + OkHttpClient client = OkHttpClientProvider.getOkHttpClient(); + CookieJarContainer container = (CookieJarContainer) client.cookieJar(); + ForwardingCookieHandler handler = new ForwardingCookieHandler(context); + container.setCookieJar(new JavaNetCookieJar(handler)); return new OkHttpDataSourceFactory(OkHttpClientProvider.getOkHttpClient(), getUserAgent(context), bandwidthMeter); } 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 f24d94c2c0..074531af00 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -124,14 +124,13 @@ public void handleMessage(Message msg) { public ReactExoplayerView(ThemedReactContext context) { super(context); + this.themedReactContext = context; createViews(); this.eventEmitter = new VideoEventEmitter(context); - this.themedReactContext = context; audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); themedReactContext.addLifecycleEventListener(this); audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); - initializePlayer(); } @@ -353,7 +352,7 @@ private void clearResumePosition() { * @return A new DataSource factory. */ private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) { - return DataSourceUtil.getDefaultDataSourceFactory(getContext(), useBandwidthMeter ? BANDWIDTH_METER : null); + return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, useBandwidthMeter ? BANDWIDTH_METER : null); } // AudioManager.OnAudioFocusChangeListener implementation @@ -482,7 +481,6 @@ public void onPlaybackParametersChanged(PlaybackParameters params) { @Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; - Exception ex = e; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { @@ -505,12 +503,8 @@ public void onPlayerError(ExoPlaybackException e) { } } } - else if (e.type == ExoPlaybackException.TYPE_SOURCE) { - ex = e.getSourceException(); - errorString = getResources().getString(R.string.unrecognized_media_format); - } if (errorString != null) { - eventEmitter.error(errorString, ex); + eventEmitter.error(errorString, e); } playerNeedsSource = true; if (isBehindLiveWindow(e)) { @@ -549,7 +543,7 @@ public void setSrc(final Uri uri, final String extension) { this.srcUri = uri; this.extension = extension; - this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(getContext(), BANDWIDTH_METER); + this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER); if (!isOriginalSourceNull && !isSourceEqual) { reloadSource(); @@ -568,7 +562,7 @@ public void setRawSrc(final Uri uri, final String extension) { this.srcUri = uri; this.extension = extension; - this.mediaDataSourceFactory = DataSourceUtil.getRawDataSourceFactory(getContext()); + this.mediaDataSourceFactory = DataSourceUtil.getRawDataSourceFactory(this.themedReactContext); if (!isOriginalSourceNull && !isSourceEqual) { reloadSource(); From f2e182addc70cb380b56db190b62cca56d3dcac4 Mon Sep 17 00:00:00 2001 From: Brandon Moon Date: Mon, 29 Jan 2018 13:32:31 -0700 Subject: [PATCH 029/336] Bring things up to date with master from copied code --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 074531af00..cf8ebc2841 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -131,6 +131,7 @@ public ReactExoplayerView(ThemedReactContext context) { themedReactContext.addLifecycleEventListener(this); audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); + initializePlayer(); } @@ -481,6 +482,7 @@ public void onPlaybackParametersChanged(PlaybackParameters params) { @Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; + Exception ex = e; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { @@ -503,8 +505,12 @@ public void onPlayerError(ExoPlaybackException e) { } } } + else if (e.type == ExoPlaybackException.TYPE_SOURCE) { + ex = e.getSourceException(); + errorString = getResources().getString(R.string.unrecognized_media_format); + } if (errorString != null) { - eventEmitter.error(errorString, e); + eventEmitter.error(errorString, ex); } playerNeedsSource = true; if (isBehindLiveWindow(e)) { From 723df5d4ae825c2ffbbb20f0d856ee5d105a6af0 Mon Sep 17 00:00:00 2001 From: YasirYang Date: Wed, 31 Jan 2018 20:23:19 +0800 Subject: [PATCH 030/336] add ipod-library support --- Video.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Video.js b/Video.js index feb79861d9..2fa6ddfa2a 100644 --- a/Video.js +++ b/Video.js @@ -166,7 +166,7 @@ export default class Video extends Component { } const isNetwork = !!(uri && uri.match(/^https?:/)); - const isAsset = !!(uri && uri.match(/^(assets-library|file|content|ms-appx|ms-appdata):/)); + const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/)); let nativeResizeMode; if (resizeMode === VideoResizeMode.stretch) { From cf9a5fc63596d6095506b01a988af7067e907205 Mon Sep 17 00:00:00 2001 From: Andy Stanberry Date: Wed, 31 Jan 2018 17:53:37 -0500 Subject: [PATCH 031/336] Document poster prop --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 597b71ec0e..9a6da0f430 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ using System.Collections.Generic; // on a single screen if you like.