[video_player] add http headers (#3671)
This enables to pass HTTP headers to VideoPlayerController.network.
Fixes: flutter/flutter#16466
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 08a5e44..6b20fd9 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.1.0
+
+* Add `httpHeaders` option to `VideoPlayerController.network`
+
## 2.0.2
* Fix `VideoPlayerValue` size and aspect ratio documentation
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
index f1a9095..e0a4a3b 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Autogenerated from Pigeon (v0.1.19), do not edit directly.
+// Autogenerated from Pigeon (v0.1.21), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package io.flutter.plugins.videoplayer;
@@ -87,12 +87,23 @@
this.formatHint = setterArg;
}
+ private HashMap httpHeaders;
+
+ public HashMap getHttpHeaders() {
+ return httpHeaders;
+ }
+
+ public void setHttpHeaders(HashMap setterArg) {
+ this.httpHeaders = setterArg;
+ }
+
HashMap toMap() {
HashMap<String, Object> toMapResult = new HashMap<>();
toMapResult.put("asset", asset);
toMapResult.put("uri", uri);
toMapResult.put("packageName", packageName);
toMapResult.put("formatHint", formatHint);
+ toMapResult.put("httpHeaders", httpHeaders);
return toMapResult;
}
@@ -106,6 +117,8 @@
fromMapResult.packageName = (String) packageName;
Object formatHint = map.get("formatHint");
fromMapResult.formatHint = (String) formatHint;
+ Object httpHeaders = map.get("httpHeaders");
+ fromMapResult.httpHeaders = (HashMap) httpHeaders;
return fromMapResult;
}
}
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
index 840b146..87784ee 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
@@ -65,6 +65,7 @@
TextureRegistry.SurfaceTextureEntry textureEntry,
String dataSource,
String formatHint,
+ Map<String, String> httpHeaders,
VideoPlayerOptions options) {
this.eventChannel = eventChannel;
this.textureEntry = textureEntry;
@@ -76,13 +77,17 @@
DataSource.Factory dataSourceFactory;
if (isHTTP(uri)) {
- dataSourceFactory =
+ DefaultHttpDataSourceFactory httpDataSourceFactory =
new DefaultHttpDataSourceFactory(
"ExoPlayer",
null,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true);
+ if (httpHeaders != null && !httpHeaders.isEmpty()) {
+ httpDataSourceFactory.getDefaultRequestProperties().set(httpHeaders);
+ }
+ dataSourceFactory = httpDataSourceFactory;
} else {
dataSourceFactory = new DefaultDataSourceFactory(context, "ExoPlayer");
}
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
index 2895db2..d77b45e 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
@@ -23,6 +23,7 @@
import io.flutter.view.TextureRegistry;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
+import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
/** Android platform implementation of the VideoPlayerPlugin. */
@@ -138,8 +139,11 @@
handle,
"asset:///" + assetLookupKey,
null,
+ null,
options);
} else {
+ @SuppressWarnings("unchecked")
+ Map<String, String> httpHeaders = arg.getHttpHeaders();
player =
new VideoPlayer(
flutterState.applicationContext,
@@ -147,6 +151,7 @@
handle,
arg.getUri(),
arg.getFormatHint(),
+ httpHeaders,
options);
}
videoPlayers.put(handle.id(), player);
diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
index 83144a9..b359c1b 100644
--- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
+++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
@@ -46,7 +46,9 @@
@property(nonatomic, readonly) bool isPlaying;
@property(nonatomic) bool isLooping;
@property(nonatomic, readonly) bool isInitialized;
-- (instancetype)initWithURL:(NSURL*)url frameUpdater:(FLTFrameUpdater*)frameUpdater;
+- (instancetype)initWithURL:(NSURL*)url
+ frameUpdater:(FLTFrameUpdater*)frameUpdater
+ httpHeaders:(NSDictionary<NSString*, NSString*>*)headers;
- (void)play;
- (void)pause;
- (void)setIsLooping:(bool)isLooping;
@@ -62,7 +64,7 @@
@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] frameUpdater:frameUpdater httpHeaders:nil];
}
- (void)addObservers:(AVPlayerItem*)item {
@@ -162,8 +164,15 @@
_displayLink.paused = YES;
}
-- (instancetype)initWithURL:(NSURL*)url frameUpdater:(FLTFrameUpdater*)frameUpdater {
- AVPlayerItem* item = [AVPlayerItem playerItemWithURL:url];
+- (instancetype)initWithURL:(NSURL*)url
+ frameUpdater:(FLTFrameUpdater*)frameUpdater
+ httpHeaders:(NSDictionary<NSString*, NSString*>*)headers {
+ NSDictionary<NSString*, id>* options = nil;
+ if (headers != nil && [headers count] != 0) {
+ options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers};
+ }
+ AVURLAsset* urlAsset = [AVURLAsset URLAssetWithURL:url options:options];
+ AVPlayerItem* item = [AVPlayerItem playerItemWithAsset:urlAsset];
return [self initWithPlayerItem:item frameUpdater:frameUpdater];
}
@@ -522,7 +531,8 @@
return [self onPlayerSetup:player frameUpdater:frameUpdater];
} else if (input.uri) {
player = [[FLTVideoPlayer alloc] initWithURL:[NSURL URLWithString:input.uri]
- frameUpdater:frameUpdater];
+ frameUpdater:frameUpdater
+ httpHeaders:input.httpHeaders];
return [self onPlayerSetup:player frameUpdater:frameUpdater];
} else {
*error = [FlutterError errorWithCode:@"video_player" message:@"not implemented" details:nil];
diff --git a/packages/video_player/video_player/ios/Classes/messages.h b/packages/video_player/video_player/ios/Classes/messages.h
index 9717f65..e21e786 100644
--- a/packages/video_player/video_player/ios/Classes/messages.h
+++ b/packages/video_player/video_player/ios/Classes/messages.h
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Autogenerated from Pigeon (v0.1.19), do not edit directly.
+// Autogenerated from Pigeon (v0.1.21), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#import <Foundation/Foundation.h>
@protocol FlutterBinaryMessenger;
@@ -28,6 +28,7 @@
@property(nonatomic, copy, nullable) NSString *uri;
@property(nonatomic, copy, nullable) NSString *packageName;
@property(nonatomic, copy, nullable) NSString *formatHint;
+@property(nonatomic, strong, nullable) NSDictionary *httpHeaders;
@end
@interface FLTLoopingMessage : NSObject
diff --git a/packages/video_player/video_player/ios/Classes/messages.m b/packages/video_player/video_player/ios/Classes/messages.m
index 0993c94..14e375b 100644
--- a/packages/video_player/video_player/ios/Classes/messages.m
+++ b/packages/video_player/video_player/ios/Classes/messages.m
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Autogenerated from Pigeon (v0.1.19), do not edit directly.
+// Autogenerated from Pigeon (v0.1.21), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#import "messages.h"
#import <Flutter/Flutter.h>
@@ -11,18 +11,19 @@
#error File requires ARC to be enabled.
#endif
-#ifndef __clang_analyzer__
-static NSDictionary *wrapResult(NSDictionary *result, FlutterError *error) {
+static NSDictionary<NSString *, id> *wrapResult(NSDictionary *result, FlutterError *error) {
NSDictionary *errorDict = (NSDictionary *)[NSNull null];
if (error) {
- errorDict = [NSDictionary
- dictionaryWithObjectsAndKeys:(error.code ? error.code : [NSNull null]), @"code",
- (error.message ? error.message : [NSNull null]), @"message",
- (error.details ? error.details : [NSNull null]), @"details",
- nil];
+ errorDict = @{
+ @"code" : (error.code ? error.code : [NSNull null]),
+ @"message" : (error.message ? error.message : [NSNull null]),
+ @"details" : (error.details ? error.details : [NSNull null]),
+ };
}
- return [NSDictionary dictionaryWithObjectsAndKeys:(result ? result : [NSNull null]), @"result",
- errorDict, @"error", nil];
+ return @{
+ @"result" : (result ? result : [NSNull null]),
+ @"error" : errorDict,
+ };
}
@interface FLTTextureMessage ()
@@ -89,6 +90,10 @@
if ((NSNull *)result.formatHint == [NSNull null]) {
result.formatHint = nil;
}
+ result.httpHeaders = dict[@"httpHeaders"];
+ if ((NSNull *)result.httpHeaders == [NSNull null]) {
+ result.httpHeaders = nil;
+ }
return result;
}
- (NSDictionary *)toMap {
@@ -98,7 +103,9 @@
(self.packageName ? self.packageName : [NSNull null]),
@"packageName",
(self.formatHint ? self.formatHint : [NSNull null]),
- @"formatHint", nil];
+ @"formatHint",
+ (self.httpHeaders ? self.httpHeaders : [NSNull null]),
+ @"httpHeaders", nil];
}
@end
@@ -221,8 +228,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTCreateMessage *input = [FLTCreateMessage fromMap:message];
+ FlutterError *error;
FLTTextureMessage *output = [api create:input error:&error];
callback(wrapResult([output toMap], error));
}];
@@ -236,8 +243,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTTextureMessage *input = [FLTTextureMessage fromMap:message];
+ FlutterError *error;
[api dispose:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -251,8 +258,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTLoopingMessage *input = [FLTLoopingMessage fromMap:message];
+ FlutterError *error;
[api setLooping:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -266,8 +273,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTVolumeMessage *input = [FLTVolumeMessage fromMap:message];
+ FlutterError *error;
[api setVolume:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -281,8 +288,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTPlaybackSpeedMessage *input = [FLTPlaybackSpeedMessage fromMap:message];
+ FlutterError *error;
[api setPlaybackSpeed:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -296,8 +303,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTTextureMessage *input = [FLTTextureMessage fromMap:message];
+ FlutterError *error;
[api play:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -311,8 +318,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTTextureMessage *input = [FLTTextureMessage fromMap:message];
+ FlutterError *error;
FLTPositionMessage *output = [api position:input error:&error];
callback(wrapResult([output toMap], error));
}];
@@ -326,8 +333,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTPositionMessage *input = [FLTPositionMessage fromMap:message];
+ FlutterError *error;
[api seekTo:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -341,8 +348,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTTextureMessage *input = [FLTTextureMessage fromMap:message];
+ FlutterError *error;
[api pause:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -356,8 +363,8 @@
binaryMessenger:binaryMessenger];
if (api) {
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- FlutterError *error;
FLTMixWithOthersMessage *input = [FLTMixWithOthersMessage fromMap:message];
+ FlutterError *error;
[api setMixWithOthers:input error:&error];
callback(wrapResult(nil, error));
}];
@@ -366,4 +373,3 @@
}
}
}
-#endif
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index 08bd1d4..d5bd7d2 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -187,6 +187,7 @@
{this.package, this.closedCaptionFile, this.videoPlayerOptions})
: dataSourceType = DataSourceType.asset,
formatHint = null,
+ httpHeaders = const {},
super(VideoPlayerValue(duration: Duration.zero));
/// Constructs a [VideoPlayerController] playing a video from obtained from
@@ -196,9 +197,15 @@
/// null.
/// **Android only**: The [formatHint] option allows the caller to override
/// the video format detection code.
- VideoPlayerController.network(this.dataSource,
- {this.formatHint, this.closedCaptionFile, this.videoPlayerOptions})
- : dataSourceType = DataSourceType.network,
+ /// [httpHeaders] option allows to specify HTTP headers
+ /// for the request to the [dataSource].
+ VideoPlayerController.network(
+ this.dataSource, {
+ this.formatHint,
+ this.closedCaptionFile,
+ this.videoPlayerOptions,
+ this.httpHeaders = const {},
+ }) : dataSourceType = DataSourceType.network,
package = null,
super(VideoPlayerValue(duration: Duration.zero));
@@ -212,12 +219,18 @@
dataSourceType = DataSourceType.file,
package = null,
formatHint = null,
+ httpHeaders = const {},
super(VideoPlayerValue(duration: Duration.zero));
/// The URI to the video file. This will be in different formats depending on
/// the [DataSourceType] of the original video.
final String dataSource;
+ /// HTTP headers used for the request to the [dataSource].
+ /// Only for [VideoPlayerController.network].
+ /// Always empty for other video types.
+ final Map<String, String> httpHeaders;
+
/// **Android only**. Will override the platform's generic file format
/// detection with whatever is set here.
final VideoFormat? formatHint;
@@ -276,6 +289,7 @@
sourceType: DataSourceType.network,
uri: dataSource,
formatHint: formatHint,
+ httpHeaders: httpHeaders,
);
break;
case DataSourceType.file:
diff --git a/packages/video_player/video_player/pigeons/messages.dart b/packages/video_player/video_player/pigeons/messages.dart
index c0a76dd..e893aaa 100644
--- a/packages/video_player/video_player/pigeons/messages.dart
+++ b/packages/video_player/video_player/pigeons/messages.dart
@@ -35,6 +35,7 @@
String uri;
String packageName;
String formatHint;
+ Map<String, String> httpHeaders;
}
class MixWithOthersMessage {
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 17442d7..0215ead 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -1,7 +1,7 @@
name: video_player
description: Flutter plugin for displaying inline video with other Flutter
widgets on Android, iOS, and web.
-version: 2.0.2
+version: 2.1.0
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
flutter:
@@ -17,7 +17,7 @@
dependencies:
meta: ^1.3.0
- video_player_platform_interface: ^4.0.0
+ video_player_platform_interface: ^4.1.0
# The design on https://flutter.dev/go/federated-plugins was to leave
# this constraint as "any". We cannot do it right now as it fails pub publish
diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart
index 580c9ad..e17dac7 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -31,6 +31,9 @@
String get dataSource => '';
@override
+ Map<String, String> get httpHeaders => {};
+
+ @override
DataSourceType get dataSourceType => DataSourceType.file;
@override
@@ -200,22 +203,60 @@
);
await controller.initialize();
- expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri,
- 'https://127.0.0.1');
expect(
- fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint, null);
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri,
+ 'https://127.0.0.1',
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint,
+ null,
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders,
+ {},
+ );
});
test('network with hint', () async {
final VideoPlayerController controller = VideoPlayerController.network(
- 'https://127.0.0.1',
- formatHint: VideoFormat.dash);
+ 'https://127.0.0.1',
+ formatHint: VideoFormat.dash,
+ );
await controller.initialize();
- expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri,
- 'https://127.0.0.1');
- expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint,
- 'dash');
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri,
+ 'https://127.0.0.1',
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint,
+ 'dash',
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders,
+ {},
+ );
+ });
+
+ test('network with some headers', () async {
+ final VideoPlayerController controller = VideoPlayerController.network(
+ 'https://127.0.0.1',
+ httpHeaders: {'Authorization': 'Bearer token'},
+ );
+ await controller.initialize();
+
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri,
+ 'https://127.0.0.1',
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint,
+ null,
+ );
+ expect(
+ fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders,
+ {'Authorization': 'Bearer token'},
+ );
});
test('init errors', () async {