mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
Reland: [video_player] Passing http headers to file constructor (#3425)
Reland: [video_player] Passing http headers to file constructor
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
## NEXT
|
## 2.6.0
|
||||||
|
|
||||||
|
* Adds option to configure HTTP headers via `VideoPlayerController` to fix access to M3U8 files on Android.
|
||||||
* Aligns Dart and Flutter SDK constraints.
|
* Aligns Dart and Flutter SDK constraints.
|
||||||
|
|
||||||
## 2.5.3
|
## 2.5.3
|
||||||
|
@ -230,7 +230,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
|
|||||||
/// null.
|
/// null.
|
||||||
/// **Android only**: The [formatHint] option allows the caller to override
|
/// **Android only**: The [formatHint] option allows the caller to override
|
||||||
/// the video format detection code.
|
/// the video format detection code.
|
||||||
/// [httpHeaders] option allows to specify HTTP headers
|
/// [httpHeaders] option allows to specify HTTP headers.
|
||||||
/// for the request to the [dataSource].
|
/// for the request to the [dataSource].
|
||||||
VideoPlayerController.network(
|
VideoPlayerController.network(
|
||||||
this.dataSource, {
|
this.dataSource, {
|
||||||
@ -246,14 +246,16 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
|
|||||||
/// Constructs a [VideoPlayerController] playing a video from a file.
|
/// Constructs a [VideoPlayerController] playing a video from a file.
|
||||||
///
|
///
|
||||||
/// This will load the file from a file:// URI constructed from [file]'s path.
|
/// This will load the file from a file:// URI constructed from [file]'s path.
|
||||||
|
/// [httpHeaders] option allows to specify HTTP headers, mainly used for hls files like (m3u8).
|
||||||
VideoPlayerController.file(File file,
|
VideoPlayerController.file(File file,
|
||||||
{Future<ClosedCaptionFile>? closedCaptionFile, this.videoPlayerOptions})
|
{Future<ClosedCaptionFile>? closedCaptionFile,
|
||||||
|
this.videoPlayerOptions,
|
||||||
|
this.httpHeaders = const <String, String>{}})
|
||||||
: _closedCaptionFileFuture = closedCaptionFile,
|
: _closedCaptionFileFuture = closedCaptionFile,
|
||||||
dataSource = Uri.file(file.absolute.path).toString(),
|
dataSource = Uri.file(file.absolute.path).toString(),
|
||||||
dataSourceType = DataSourceType.file,
|
dataSourceType = DataSourceType.file,
|
||||||
package = null,
|
package = null,
|
||||||
formatHint = null,
|
formatHint = null,
|
||||||
httpHeaders = const <String, String>{},
|
|
||||||
super(VideoPlayerValue(duration: Duration.zero));
|
super(VideoPlayerValue(duration: Duration.zero));
|
||||||
|
|
||||||
/// Constructs a [VideoPlayerController] playing a video from a contentUri.
|
/// Constructs a [VideoPlayerController] playing a video from a contentUri.
|
||||||
@ -344,6 +346,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
|
|||||||
dataSourceDescription = DataSource(
|
dataSourceDescription = DataSource(
|
||||||
sourceType: DataSourceType.file,
|
sourceType: DataSourceType.file,
|
||||||
uri: dataSource,
|
uri: dataSource,
|
||||||
|
httpHeaders: httpHeaders,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case DataSourceType.contentUri:
|
case DataSourceType.contentUri:
|
||||||
|
@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
|
|||||||
widgets on Android, iOS, and web.
|
widgets on Android, iOS, and web.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player
|
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player
|
||||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
||||||
version: 2.5.3
|
version: 2.6.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
@ -358,6 +358,22 @@ void main() {
|
|||||||
reason: 'Actual string: $uri');
|
reason: 'Actual string: $uri');
|
||||||
}, skip: kIsWeb /* Web does not support file assets. */);
|
}, skip: kIsWeb /* Web does not support file assets. */);
|
||||||
|
|
||||||
|
test('file with headers (m3u8)', () async {
|
||||||
|
final VideoPlayerController controller = VideoPlayerController.file(
|
||||||
|
File('a.avi'),
|
||||||
|
httpHeaders: <String, String>{'Authorization': 'Bearer token'},
|
||||||
|
);
|
||||||
|
await controller.initialize();
|
||||||
|
|
||||||
|
final String uri = fakeVideoPlayerPlatform.dataSources[0].uri!;
|
||||||
|
expect(uri.startsWith('file:///'), true, reason: 'Actual string: $uri');
|
||||||
|
expect(uri.endsWith('/a.avi'), true, reason: 'Actual string: $uri');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
fakeVideoPlayerPlatform.dataSources[0].httpHeaders,
|
||||||
|
<String, String>{'Authorization': 'Bearer token'},
|
||||||
|
);
|
||||||
|
}, skip: kIsWeb /* Web does not support file assets. */);
|
||||||
test('successful initialize on controller with error clears error',
|
test('successful initialize on controller with error clears error',
|
||||||
() async {
|
() async {
|
||||||
final VideoPlayerController controller = VideoPlayerController.network(
|
final VideoPlayerController controller = VideoPlayerController.network(
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
* Allows setting the ExoPlayer user agent by passing a User-Agent HTTP header.
|
||||||
|
|
||||||
## 2.3.12
|
## 2.3.12
|
||||||
|
|
||||||
* Clarifies explanation of endorsement in README.
|
* Clarifies explanation of endorsement in README.
|
||||||
|
@ -56,10 +56,14 @@ final class VideoPlayer {
|
|||||||
|
|
||||||
private final EventChannel eventChannel;
|
private final EventChannel eventChannel;
|
||||||
|
|
||||||
|
private static final String USER_AGENT = "User-Agent";
|
||||||
|
|
||||||
@VisibleForTesting boolean isInitialized = false;
|
@VisibleForTesting boolean isInitialized = false;
|
||||||
|
|
||||||
private final VideoPlayerOptions options;
|
private final VideoPlayerOptions options;
|
||||||
|
|
||||||
|
private DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory();
|
||||||
|
|
||||||
VideoPlayer(
|
VideoPlayer(
|
||||||
Context context,
|
Context context,
|
||||||
EventChannel eventChannel,
|
EventChannel eventChannel,
|
||||||
@ -73,23 +77,11 @@ final class VideoPlayer {
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
|
ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
|
||||||
|
|
||||||
Uri uri = Uri.parse(dataSource);
|
Uri uri = Uri.parse(dataSource);
|
||||||
DataSource.Factory dataSourceFactory;
|
|
||||||
|
|
||||||
if (isHTTP(uri)) {
|
buildHttpDataSourceFactory(httpHeaders);
|
||||||
DefaultHttpDataSource.Factory httpDataSourceFactory =
|
DataSource.Factory dataSourceFactory =
|
||||||
new DefaultHttpDataSource.Factory()
|
new DefaultDataSource.Factory(context, httpDataSourceFactory);
|
||||||
.setUserAgent("ExoPlayer")
|
|
||||||
.setAllowCrossProtocolRedirects(true);
|
|
||||||
|
|
||||||
if (httpHeaders != null && !httpHeaders.isEmpty()) {
|
|
||||||
httpDataSourceFactory.setDefaultRequestProperties(httpHeaders);
|
|
||||||
}
|
|
||||||
dataSourceFactory = httpDataSourceFactory;
|
|
||||||
} else {
|
|
||||||
dataSourceFactory = new DefaultDataSource.Factory(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context);
|
MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context);
|
||||||
|
|
||||||
@ -106,20 +98,29 @@ final class VideoPlayer {
|
|||||||
EventChannel eventChannel,
|
EventChannel eventChannel,
|
||||||
TextureRegistry.SurfaceTextureEntry textureEntry,
|
TextureRegistry.SurfaceTextureEntry textureEntry,
|
||||||
VideoPlayerOptions options,
|
VideoPlayerOptions options,
|
||||||
QueuingEventSink eventSink) {
|
QueuingEventSink eventSink,
|
||||||
|
DefaultHttpDataSource.Factory httpDataSourceFactory) {
|
||||||
this.eventChannel = eventChannel;
|
this.eventChannel = eventChannel;
|
||||||
this.textureEntry = textureEntry;
|
this.textureEntry = textureEntry;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
this.httpDataSourceFactory = httpDataSourceFactory;
|
||||||
|
|
||||||
setUpVideoPlayer(exoPlayer, eventSink);
|
setUpVideoPlayer(exoPlayer, eventSink);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isHTTP(Uri uri) {
|
@VisibleForTesting
|
||||||
if (uri == null || uri.getScheme() == null) {
|
public void buildHttpDataSourceFactory(@NonNull Map<String, String> httpHeaders) {
|
||||||
return false;
|
final boolean httpHeadersNotEmpty = !httpHeaders.isEmpty();
|
||||||
|
final String userAgent =
|
||||||
|
httpHeadersNotEmpty && httpHeaders.containsKey(USER_AGENT)
|
||||||
|
? httpHeaders.get(USER_AGENT)
|
||||||
|
: "ExoPlayer";
|
||||||
|
|
||||||
|
httpDataSourceFactory.setUserAgent(userAgent).setAllowCrossProtocolRedirects(true);
|
||||||
|
|
||||||
|
if (httpHeadersNotEmpty) {
|
||||||
|
httpDataSourceFactory.setDefaultRequestProperties(httpHeaders);
|
||||||
}
|
}
|
||||||
String scheme = uri.getScheme();
|
|
||||||
return scheme.equals("http") || scheme.equals("https");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSource buildMediaSource(
|
private MediaSource buildMediaSource(
|
||||||
@ -149,13 +150,11 @@ final class VideoPlayer {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case C.CONTENT_TYPE_SS:
|
case C.CONTENT_TYPE_SS:
|
||||||
return new SsMediaSource.Factory(
|
return new SsMediaSource.Factory(
|
||||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mediaDataSourceFactory)
|
||||||
new DefaultDataSource.Factory(context, mediaDataSourceFactory))
|
|
||||||
.createMediaSource(MediaItem.fromUri(uri));
|
.createMediaSource(MediaItem.fromUri(uri));
|
||||||
case C.CONTENT_TYPE_DASH:
|
case C.CONTENT_TYPE_DASH:
|
||||||
return new DashMediaSource.Factory(
|
return new DashMediaSource.Factory(
|
||||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mediaDataSourceFactory)
|
||||||
new DefaultDataSource.Factory(context, mediaDataSourceFactory))
|
|
||||||
.createMediaSource(MediaItem.fromUri(uri));
|
.createMediaSource(MediaItem.fromUri(uri));
|
||||||
case C.CONTENT_TYPE_HLS:
|
case C.CONTENT_TYPE_HLS:
|
||||||
return new HlsMediaSource.Factory(mediaDataSourceFactory)
|
return new HlsMediaSource.Factory(mediaDataSourceFactory)
|
||||||
|
@ -23,6 +23,7 @@ import io.flutter.plugins.videoplayer.Messages.VolumeMessage;
|
|||||||
import io.flutter.view.TextureRegistry;
|
import io.flutter.view.TextureRegistry;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi {
|
|||||||
handle,
|
handle,
|
||||||
"asset:///" + assetLookupKey,
|
"asset:///" + assetLookupKey,
|
||||||
null,
|
null,
|
||||||
null,
|
new HashMap<String, String>(),
|
||||||
options);
|
options);
|
||||||
} else {
|
} else {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -5,15 +5,20 @@
|
|||||||
package io.flutter.plugins.videoplayer;
|
package io.flutter.plugins.videoplayer;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||||
import io.flutter.plugin.common.EventChannel;
|
import io.flutter.plugin.common.EventChannel;
|
||||||
import io.flutter.view.TextureRegistry;
|
import io.flutter.view.TextureRegistry;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -29,6 +34,7 @@ public class VideoPlayerTest {
|
|||||||
private TextureRegistry.SurfaceTextureEntry fakeSurfaceTextureEntry;
|
private TextureRegistry.SurfaceTextureEntry fakeSurfaceTextureEntry;
|
||||||
private VideoPlayerOptions fakeVideoPlayerOptions;
|
private VideoPlayerOptions fakeVideoPlayerOptions;
|
||||||
private QueuingEventSink fakeEventSink;
|
private QueuingEventSink fakeEventSink;
|
||||||
|
private DefaultHttpDataSource.Factory httpDataSourceFactorySpy;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<HashMap<String, Object>> eventCaptor;
|
@Captor private ArgumentCaptor<HashMap<String, Object>> eventCaptor;
|
||||||
|
|
||||||
@ -41,6 +47,76 @@ public class VideoPlayerTest {
|
|||||||
fakeSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class);
|
fakeSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class);
|
||||||
fakeVideoPlayerOptions = mock(VideoPlayerOptions.class);
|
fakeVideoPlayerOptions = mock(VideoPlayerOptions.class);
|
||||||
fakeEventSink = mock(QueuingEventSink.class);
|
fakeEventSink = mock(QueuingEventSink.class);
|
||||||
|
httpDataSourceFactorySpy = spy(new DefaultHttpDataSource.Factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNull() {
|
||||||
|
VideoPlayer videoPlayer =
|
||||||
|
new VideoPlayer(
|
||||||
|
fakeExoPlayer,
|
||||||
|
fakeEventChannel,
|
||||||
|
fakeSurfaceTextureEntry,
|
||||||
|
fakeVideoPlayerOptions,
|
||||||
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
|
|
||||||
|
videoPlayer.buildHttpDataSourceFactory(new HashMap<>());
|
||||||
|
|
||||||
|
verify(httpDataSourceFactorySpy).setUserAgent("ExoPlayer");
|
||||||
|
verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true);
|
||||||
|
verify(httpDataSourceFactorySpy, never()).setDefaultRequestProperties(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNonNullAndUserAgentSpecified() {
|
||||||
|
VideoPlayer videoPlayer =
|
||||||
|
new VideoPlayer(
|
||||||
|
fakeExoPlayer,
|
||||||
|
fakeEventChannel,
|
||||||
|
fakeSurfaceTextureEntry,
|
||||||
|
fakeVideoPlayerOptions,
|
||||||
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
|
Map<String, String> httpHeaders =
|
||||||
|
new HashMap<String, String>() {
|
||||||
|
{
|
||||||
|
put("header", "value");
|
||||||
|
put("User-Agent", "userAgent");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
videoPlayer.buildHttpDataSourceFactory(httpHeaders);
|
||||||
|
|
||||||
|
verify(httpDataSourceFactorySpy).setUserAgent("userAgent");
|
||||||
|
verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true);
|
||||||
|
verify(httpDataSourceFactorySpy).setDefaultRequestProperties(httpHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNonNullAndUserAgentNotSpecified() {
|
||||||
|
VideoPlayer videoPlayer =
|
||||||
|
new VideoPlayer(
|
||||||
|
fakeExoPlayer,
|
||||||
|
fakeEventChannel,
|
||||||
|
fakeSurfaceTextureEntry,
|
||||||
|
fakeVideoPlayerOptions,
|
||||||
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
|
Map<String, String> httpHeaders =
|
||||||
|
new HashMap<String, String>() {
|
||||||
|
{
|
||||||
|
put("header", "value");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
videoPlayer.buildHttpDataSourceFactory(httpHeaders);
|
||||||
|
|
||||||
|
verify(httpDataSourceFactorySpy).setUserAgent("ExoPlayer");
|
||||||
|
verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true);
|
||||||
|
verify(httpDataSourceFactorySpy).setDefaultRequestProperties(httpHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -51,7 +127,8 @@ public class VideoPlayerTest {
|
|||||||
fakeEventChannel,
|
fakeEventChannel,
|
||||||
fakeSurfaceTextureEntry,
|
fakeSurfaceTextureEntry,
|
||||||
fakeVideoPlayerOptions,
|
fakeVideoPlayerOptions,
|
||||||
fakeEventSink);
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
Format testFormat =
|
Format testFormat =
|
||||||
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(90).build();
|
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(90).build();
|
||||||
|
|
||||||
@ -79,7 +156,8 @@ public class VideoPlayerTest {
|
|||||||
fakeEventChannel,
|
fakeEventChannel,
|
||||||
fakeSurfaceTextureEntry,
|
fakeSurfaceTextureEntry,
|
||||||
fakeVideoPlayerOptions,
|
fakeVideoPlayerOptions,
|
||||||
fakeEventSink);
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
Format testFormat =
|
Format testFormat =
|
||||||
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(270).build();
|
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(270).build();
|
||||||
|
|
||||||
@ -107,7 +185,8 @@ public class VideoPlayerTest {
|
|||||||
fakeEventChannel,
|
fakeEventChannel,
|
||||||
fakeSurfaceTextureEntry,
|
fakeSurfaceTextureEntry,
|
||||||
fakeVideoPlayerOptions,
|
fakeVideoPlayerOptions,
|
||||||
fakeEventSink);
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
Format testFormat =
|
Format testFormat =
|
||||||
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(0).build();
|
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(0).build();
|
||||||
|
|
||||||
@ -135,7 +214,8 @@ public class VideoPlayerTest {
|
|||||||
fakeEventChannel,
|
fakeEventChannel,
|
||||||
fakeSurfaceTextureEntry,
|
fakeSurfaceTextureEntry,
|
||||||
fakeVideoPlayerOptions,
|
fakeVideoPlayerOptions,
|
||||||
fakeEventSink);
|
fakeEventSink,
|
||||||
|
httpDataSourceFactorySpy);
|
||||||
Format testFormat =
|
Format testFormat =
|
||||||
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(180).build();
|
new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(180).build();
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
|
|||||||
break;
|
break;
|
||||||
case DataSourceType.file:
|
case DataSourceType.file:
|
||||||
uri = dataSource.uri;
|
uri = dataSource.uri;
|
||||||
|
httpHeaders = dataSource.httpHeaders;
|
||||||
break;
|
break;
|
||||||
case DataSourceType.contentUri:
|
case DataSourceType.contentUri:
|
||||||
uri = dataSource.uri;
|
uri = dataSource.uri;
|
||||||
|
@ -2,7 +2,7 @@ name: video_player_android
|
|||||||
description: Android implementation of the video_player plugin.
|
description: Android implementation of the video_player plugin.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android
|
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android
|
||||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
||||||
version: 2.3.12
|
version: 2.4.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
@ -176,6 +176,18 @@ void main() {
|
|||||||
expect(textureId, 3);
|
expect(textureId, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('create with file (some headers)', () async {
|
||||||
|
final int? textureId = await player.create(DataSource(
|
||||||
|
sourceType: DataSourceType.file,
|
||||||
|
uri: 'someUri',
|
||||||
|
httpHeaders: <String, String>{'Authorization': 'Bearer token'},
|
||||||
|
));
|
||||||
|
expect(log.log.last, 'create');
|
||||||
|
expect(log.createMessage?.uri, 'someUri');
|
||||||
|
expect(log.createMessage?.httpHeaders,
|
||||||
|
<String, String>{'Authorization': 'Bearer token'});
|
||||||
|
expect(textureId, 3);
|
||||||
|
});
|
||||||
test('setLooping', () async {
|
test('setLooping', () async {
|
||||||
await player.setLooping(1, true);
|
await player.setLooping(1, true);
|
||||||
expect(log.log.last, 'setLooping');
|
expect(log.log.last, 'setLooping');
|
||||||
|
Reference in New Issue
Block a user