diff --git a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 2b730214068d..4e7a975afd29 100644 --- a/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -11,6 +11,7 @@ import android.media.AudioManager; import android.net.Uri; import android.os.Build; +import android.support.annotation.Nullable; import android.view.Surface; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -32,7 +33,9 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource.RequestProperties; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Util; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; @@ -51,6 +54,56 @@ public class VideoPlayerPlugin implements MethodCallHandler { + // near copy and paste from final class DefaultHttpDataSourceFactory + private static class VideoPlayerHttpDataSourceFactory extends BaseFactory { + private final String userAgent; + private final @Nullable TransferListener listener; + private final int connectTimeoutMillis; + private final int readTimeoutMillis; + private final boolean allowCrossProtocolRedirects; + private final Map headers; + + public VideoPlayerHttpDataSourceFactory( + String userAgent, + @Nullable TransferListener listener, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects, + Map headers) { + this.userAgent = userAgent; + this.listener = listener; + this.connectTimeoutMillis = connectTimeoutMillis; + this.readTimeoutMillis = readTimeoutMillis; + this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + this.headers = headers; + } + + @Override + protected DefaultHttpDataSource createDataSourceInternal( + RequestProperties defaultRequestProperties) { + if (this.headers != null) { + if (defaultRequestProperties == null) { + defaultRequestProperties = new RequestProperties(); + } + for (Map.Entry header : this.headers.entrySet()) { + defaultRequestProperties.set(header.getKey(), header.getValue()); + } + } + DefaultHttpDataSource dataSource = + new DefaultHttpDataSource( + userAgent, + /* contentTypePredicate= */ null, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects, + defaultRequestProperties); + if (listener != null) { + dataSource.addTransferListener(listener); + } + return dataSource; + } + } + private static class VideoPlayer { private SimpleExoPlayer exoPlayer; @@ -70,6 +123,7 @@ private static class VideoPlayer { EventChannel eventChannel, TextureRegistry.SurfaceTextureEntry textureEntry, String dataSource, + Map headers, Result result) { this.eventChannel = eventChannel; this.textureEntry = textureEntry; @@ -84,12 +138,13 @@ private static class VideoPlayer { dataSourceFactory = new DefaultDataSourceFactory(context, "ExoPlayer"); } else { dataSourceFactory = - new DefaultHttpDataSourceFactory( + new VideoPlayerHttpDataSourceFactory( "ExoPlayer", null, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, - true); + true, + headers); } MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, context); @@ -315,6 +370,7 @@ public void onMethodCall(MethodCall call, Result result) { eventChannel, handle, "asset:///" + assetLookupKey, + null, result); videoPlayers.put(handle.id(), player); } else { @@ -324,6 +380,7 @@ public void onMethodCall(MethodCall call, Result result) { eventChannel, handle, (String) call.argument("uri"), + (Map) call.argument("headers"), result); videoPlayers.put(handle.id(), player); } diff --git a/packages/video_player/ios/Classes/VideoPlayerPlugin.m b/packages/video_player/ios/Classes/VideoPlayerPlugin.m index aeec622ce7b6..5d936659f5f8 100644 --- a/packages/video_player/ios/Classes/VideoPlayerPlugin.m +++ b/packages/video_player/ios/Classes/VideoPlayerPlugin.m @@ -36,7 +36,9 @@ @interface FLTVideoPlayer : NSObject @property(nonatomic, readonly) bool isPlaying; @property(nonatomic, readonly) bool isLooping; @property(nonatomic, readonly) bool isInitialized; -- (instancetype)initWithURL:(NSURL*)url frameUpdater:(FLTFrameUpdater*)frameUpdater; +- (instancetype)initWithURL:(NSURL*)url + headers:(NSDictionary*)headers + frameUpdater:(FLTFrameUpdater*)frameUpdater; - (void)play; - (void)pause; - (void)setIsLooping:(bool)isLooping; @@ -52,17 +54,26 @@ - (void)updatePlayingState; @implementation FLTVideoPlayer - (instancetype)initWithAsset:(NSString*)asset frameUpdater:(FLTFrameUpdater*)frameUpdater { NSString* path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; - return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater]; + return [self initWithURL:[NSURL fileURLWithPath:path] headers:nil frameUpdater:frameUpdater]; } -- (instancetype)initWithURL:(NSURL*)url frameUpdater:(FLTFrameUpdater*)frameUpdater { +- (instancetype)initWithURL:(NSURL*)url + headers:(NSDictionary*)headers + frameUpdater:(FLTFrameUpdater*)frameUpdater { self = [super init]; NSAssert(self, @"super init cannot be nil"); _isInitialized = false; _isPlaying = false; _disposed = false; - AVPlayerItem* item = [AVPlayerItem playerItemWithURL:url]; + AVPlayerItem* item; + if (headers) { + AVURLAsset* asset = [AVURLAsset URLAssetWithURL:url + options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}]; + item = [AVPlayerItem playerItemWithAsset:asset]; + } else { + item = [AVPlayerItem playerItemWithURL:url]; + } [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -340,7 +351,9 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { player = [[FLTVideoPlayer alloc] initWithAsset:assetPath frameUpdater:frameUpdater]; } else { dataSource = argsMap[@"uri"]; + NSDictionary* headers = argsMap[@"headers"]; player = [[FLTVideoPlayer alloc] initWithURL:[NSURL URLWithString:dataSource] + headers:headers frameUpdater:frameUpdater]; } int64_t textureId = [_registry registerTexture:player]; diff --git a/packages/video_player/lib/video_player.dart b/packages/video_player/lib/video_player.dart index c297078bf8f9..0b1e5fb5fe06 100644 --- a/packages/video_player/lib/video_player.dart +++ b/packages/video_player/lib/video_player.dart @@ -148,6 +148,7 @@ class VideoPlayerController extends ValueNotifier { /// package and null otherwise. VideoPlayerController.asset(this.dataSource, {this.package}) : dataSourceType = DataSourceType.asset, + headers = null, super(VideoPlayerValue(duration: null)); /// Constructs a [VideoPlayerController] playing a video from obtained from @@ -155,7 +156,7 @@ class VideoPlayerController extends ValueNotifier { /// /// The URI for the video is given by the [dataSource] argument and must not be /// null. - VideoPlayerController.network(this.dataSource) + VideoPlayerController.network(this.dataSource, {this.headers}) : dataSourceType = DataSourceType.network, package = null, super(VideoPlayerValue(duration: null)); @@ -168,10 +169,12 @@ class VideoPlayerController extends ValueNotifier { : dataSource = 'file://${file.path}', dataSourceType = DataSourceType.file, package = null, + headers = null, super(VideoPlayerValue(duration: null)); int _textureId; final String dataSource; + final Map headers; /// Describes the type of data source this [VideoPlayerController] /// is constructed with. @@ -200,7 +203,10 @@ class VideoPlayerController extends ValueNotifier { }; break; case DataSourceType.network: - dataSourceDescription = {'uri': dataSource}; + dataSourceDescription = { + 'uri': dataSource, + 'headers': headers + }; break; case DataSourceType.file: dataSourceDescription = {'uri': dataSource}; diff --git a/packages/video_player/test/video_player_test.dart b/packages/video_player/test/video_player_test.dart index 22dd9a2e3e67..1400d4a86858 100644 --- a/packages/video_player/test/video_player_test.dart +++ b/packages/video_player/test/video_player_test.dart @@ -19,6 +19,8 @@ class FakeController extends ValueNotifier @override String get dataSource => ''; @override + Map get headers => null; + @override DataSourceType get dataSourceType => DataSourceType.file; @override String get package => null;