diff --git a/Video.js b/Video.js index b90c030e..b835664d 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 0077c3b0..2c3af708 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.DefaultDataSourceFactory; 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 class DataSourceUtil { 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 @@ public class DataSourceUtil { 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 52bf24e6..dc48de31 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.react.R; 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.CookieHandler; 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 @@ class ReactExoplayerView extends FrameLayout implements * @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 @@ class ReactExoplayerView extends FrameLayout implements // 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 dda8d000..cc0bee64 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.ViewGroupManager; 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 class ReactExoplayerViewManager extends ViewGroupManager 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 94a25a0e..17a8fbb0 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.APKExpansionSupport; 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.HashMap; 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 class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP 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 class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP } } - 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 class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP 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 class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP } 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 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP 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 class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP @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 983113d5..404a16ef 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 class ReactVideoViewManager extends SimpleViewManager { 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 class ReactVideoViewManager extends SimpleViewManager { 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 9962c2bb..8538b742 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -312,12 +312,17 @@ static NSString *const timedMetadata = @"timedMetadata"; 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];