Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 2.11.1
## 2.12.0

* Optimizes caption retrieval with binary search.
* Adds `VideoPlayerAndroidOptions` to configure Android renderer settings during player creation.

## 2.11.0

Expand Down
2 changes: 2 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export 'package:video_player_platform_interface/video_player_platform_interface.
DataSourceType,
DurationRange,
VideoFormat,
VideoPlayerAndroidOptions,
VideoPlayerOptions,
VideoPlayerWebOptions,
VideoPlayerWebOptionsControls,
Expand Down Expand Up @@ -576,6 +577,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
final creationOptions = platform_interface.VideoCreationOptions(
dataSource: dataSourceDescription,
viewType: viewType,
androidOptions: videoPlayerOptions?.androidOptions,
);

if (videoPlayerOptions?.mixWithOthers != null) {
Expand Down
6 changes: 3 additions & 3 deletions packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
widgets on Android, iOS, macOS and web.
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
version: 2.11.1
version: 2.12.0

environment:
sdk: ^3.10.0
Expand All @@ -26,9 +26,9 @@ dependencies:
flutter:
sdk: flutter
html: ^0.15.0
video_player_android: ^2.9.1
video_player_android: ^2.10.0
video_player_avfoundation: ^2.9.0
video_player_platform_interface: ^6.6.0
video_player_platform_interface: ^6.7.0
video_player_web: ^2.1.0

dev_dependencies:
Expand Down
35 changes: 28 additions & 7 deletions packages/video_player/video_player/test/video_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1976,14 +1976,33 @@ void main() {
);
addTearDown(controller.dispose);

await controller.initialize();
await controller.play();
verifyPlayStateRespondsToLifecycle(
controller,
shouldPlayInBackground: false,
);
});
await controller.initialize();
await controller.play();
verifyPlayStateRespondsToLifecycle(
controller,
shouldPlayInBackground: false,
);
});

test('androidOptions are forwarded during player creation', () async {
const androidOptions = VideoPlayerAndroidOptions(
enableDecoderFallback: true,
disableMediaCodecAsyncQueueing: true,
);
final controller = VideoPlayerController.networkUrl(
_localhostUri,
videoPlayerOptions: VideoPlayerOptions(androidOptions: androidOptions),
);
addTearDown(controller.dispose);

await controller.initialize();

expect(
fakeVideoPlayerPlatform.creationOptions.single.androidOptions,
androidOptions,
);
});
});

test('VideoProgressColors', () {
const playedColor = Color.fromRGBO(0, 0, 255, 0.75);
Expand Down Expand Up @@ -2108,6 +2127,7 @@ void main() {
class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
Completer<bool> initialized = Completer<bool>();
List<String> calls = <String>[];
List<VideoCreationOptions> creationOptions = <VideoCreationOptions>[];
List<DataSource> dataSources = <DataSource>[];
List<VideoViewType> viewTypes = <VideoViewType>[];
final Map<int, StreamController<VideoEvent>> streams =
Expand Down Expand Up @@ -2146,6 +2166,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
@override
Future<int?> createWithOptions(VideoCreationOptions options) async {
calls.add('createWithOptions');
creationOptions.add(options);
final stream = StreamController<VideoEvent>();
streams[nextPlayerId] = stream;
if (forceInitError) {
Expand Down
4 changes: 4 additions & 0 deletions packages/video_player/video_player_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.10.0

* Adds per-player Android renderer options for decoder fallback and MediaCodec asynchronous queueing.

## 2.9.4

* Updates `androidx.media3` to 1.9.2.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import static androidx.media3.common.Player.REPEAT_MODE_ALL;
import static androidx.media3.common.Player.REPEAT_MODE_OFF;

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.Format;
Expand All @@ -18,6 +20,7 @@
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.Tracks;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.DefaultRenderersFactory;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import io.flutter.view.TextureRegistry.SurfaceProducer;
Expand Down Expand Up @@ -87,6 +90,24 @@ public void setDisposeHandler(@Nullable DisposeHandler handler) {
protected abstract ExoPlayerEventListener createExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer);

@UnstableApi
protected static DefaultRenderersFactory createRenderersFactory(
@NonNull Context context, @NonNull VideoPlayerOptions options) {
final DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context);
configureRenderersFactory(renderersFactory, options);
return renderersFactory;
}

@VisibleForTesting
@UnstableApi
static void configureRenderersFactory(
@NonNull DefaultRenderersFactory renderersFactory, @NonNull VideoPlayerOptions options) {
renderersFactory.setEnableDecoderFallback(options.enableDecoderFallback);
if (options.disableMediaCodecAsyncQueueing) {
renderersFactory.forceDisableMediaCodecAsynchronousQueueing();
}
}

private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) {
exoPlayer.setAudioAttributes(
new AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

public class VideoPlayerOptions {
public boolean mixWithOthers;
public boolean enableDecoderFallback;
public boolean disableMediaCodecAsyncQueueing;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public void initialize() {
@Override
public long createForPlatformView(@NonNull CreationOptions options) {
final VideoAsset videoAsset = videoAssetWithOptions(options);
final VideoPlayerOptions playerOptions = videoPlayerOptionsWithCreationOptions(options);

long id = nextPlayerIdentifier++;
final String streamInstance = Long.toString(id);
Expand All @@ -92,7 +93,7 @@ public long createForPlatformView(@NonNull CreationOptions options) {
flutterState.applicationContext,
VideoPlayerEventCallbacks.bindTo(flutterState.binaryMessenger, streamInstance),
videoAsset,
sharedOptions);
playerOptions);

registerPlayerInstance(videoPlayer, id);
return id;
Expand All @@ -102,6 +103,7 @@ public long createForPlatformView(@NonNull CreationOptions options) {
@Override
public @NonNull TexturePlayerIds createForTextureView(@NonNull CreationOptions options) {
final VideoAsset videoAsset = videoAssetWithOptions(options);
final VideoPlayerOptions playerOptions = videoPlayerOptionsWithCreationOptions(options);

long id = nextPlayerIdentifier++;
final String streamInstance = Long.toString(id);
Expand All @@ -112,7 +114,7 @@ public long createForPlatformView(@NonNull CreationOptions options) {
VideoPlayerEventCallbacks.bindTo(flutterState.binaryMessenger, streamInstance),
handle,
videoAsset,
sharedOptions);
playerOptions);

registerPlayerInstance(videoPlayer, id);
return new TexturePlayerIds(id, handle.id());
Expand Down Expand Up @@ -145,6 +147,15 @@ public long createForPlatformView(@NonNull CreationOptions options) {
}
}

private @NonNull VideoPlayerOptions videoPlayerOptionsWithCreationOptions(
@NonNull CreationOptions options) {
final VideoPlayerOptions playerOptions = new VideoPlayerOptions();
playerOptions.mixWithOthers = sharedOptions.mixWithOthers;
playerOptions.enableDecoderFallback = options.getEnableDecoderFallback();
playerOptions.disableMediaCodecAsyncQueueing = options.getDisableMediaCodecAsyncQueueing();
return playerOptions;
}

private void registerPlayerInstance(VideoPlayer player, long id) {
// Set up the instance-specific API handler, and make sure it is removed when the player is
// disposed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static PlatformViewVideoPlayer create(
new androidx.media3.exoplayer.trackselection.DefaultTrackSelector(context);
ExoPlayer.Builder builder =
new ExoPlayer.Builder(context)
.setRenderersFactory(createRenderersFactory(context, options))
.setTrackSelector(trackSelector)
.setMediaSourceFactory(asset.getMediaSourceFactory(context));
return builder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static TextureVideoPlayer create(
new androidx.media3.exoplayer.trackselection.DefaultTrackSelector(context);
ExoPlayer.Builder builder =
new ExoPlayer.Builder(context)
.setRenderersFactory(createRenderersFactory(context, options))
.setTrackSelector(trackSelector)
.setMediaSourceFactory(asset.getMediaSourceFactory(context));
return builder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,25 @@ data class CreationOptions(
val uri: String,
val formatHint: PlatformVideoFormat? = null,
val httpHeaders: Map<String, String>,
val userAgent: String? = null
val userAgent: String? = null,
val enableDecoderFallback: Boolean,
val disableMediaCodecAsyncQueueing: Boolean
) {
companion object {
fun fromList(pigeonVar_list: List<Any?>): CreationOptions {
val uri = pigeonVar_list[0] as String
val formatHint = pigeonVar_list[1] as PlatformVideoFormat?
val httpHeaders = pigeonVar_list[2] as Map<String, String>
val userAgent = pigeonVar_list[3] as String?
return CreationOptions(uri, formatHint, httpHeaders, userAgent)
val enableDecoderFallback = pigeonVar_list[4] as Boolean
val disableMediaCodecAsyncQueueing = pigeonVar_list[5] as Boolean
return CreationOptions(
uri,
formatHint,
httpHeaders,
userAgent,
enableDecoderFallback,
disableMediaCodecAsyncQueueing)
}
}

Expand All @@ -318,6 +328,8 @@ data class CreationOptions(
formatHint,
httpHeaders,
userAgent,
enableDecoderFallback,
disableMediaCodecAsyncQueueing,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public void createsPlatformViewVideoPlayer() throws Exception {
"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4",
null,
new HashMap<>(),
null);
null,
false,
false);

final long playerId = plugin.createForPlatformView(options);

Expand All @@ -103,12 +105,50 @@ public void createsTextureVideoPlayer() throws Exception {
"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4",
null,
new HashMap<>(),
null);
null,
false,
false);

final TexturePlayerIds ids = plugin.createForTextureView(options);

final LongSparseArray<VideoPlayer> videoPlayers = getVideoPlayers();
assertTrue(videoPlayers.get(ids.getPlayerId()) instanceof TextureVideoPlayer);
}
}

@Test
public void createsTextureVideoPlayerWithCreationScopedAndroidOptions() {
try (MockedStatic<TextureVideoPlayer> mockedTextureVideoPlayerStatic =
mockStatic(TextureVideoPlayer.class)) {
mockedTextureVideoPlayerStatic
.when(() -> TextureVideoPlayer.create(any(), any(), any(), any(), any()))
.thenReturn(mock(TextureVideoPlayer.class));

plugin.setMixWithOthers(true);

final CreationOptions options =
new CreationOptions(
"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4",
null,
new HashMap<>(),
null,
true,
true);

plugin.createForTextureView(options);

mockedTextureVideoPlayerStatic.verify(
() ->
TextureVideoPlayer.create(
any(),
any(),
any(),
any(),
argThat(
(VideoPlayerOptions playerOptions) ->
playerOptions.mixWithOthers
&& playerOptions.enableDecoderFallback
&& playerOptions.disableMediaCodecAsyncQueueing)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.Tracks;
import androidx.media3.exoplayer.DefaultRenderersFactory;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -61,6 +62,7 @@ public final class VideoPlayerTest {

@Mock private VideoPlayerCallbacks mockEvents;
@Mock private ExoPlayer mockExoPlayer;
@Mock private DefaultRenderersFactory mockRenderersFactory;
@Captor private ArgumentCaptor<AudioAttributes> attributesCaptor;
@Captor private ArgumentCaptor<Player.Listener> listenerCaptor;

Expand Down Expand Up @@ -126,6 +128,26 @@ public void loadsAndPreparesProvidedMediaDisablesAudioFocusWhenMixModeSet() {
videoPlayer.dispose();
}

@Test
public void configureRenderersFactoryAppliesDefaults() {
VideoPlayer.configureRenderersFactory(mockRenderersFactory, new VideoPlayerOptions());

verify(mockRenderersFactory).setEnableDecoderFallback(false);
verify(mockRenderersFactory, never()).forceDisableMediaCodecAsynchronousQueueing();
}

@Test
public void configureRenderersFactoryAppliesAndroidOptions() {
VideoPlayerOptions options = new VideoPlayerOptions();
options.enableDecoderFallback = true;
options.disableMediaCodecAsyncQueueing = true;

VideoPlayer.configureRenderersFactory(mockRenderersFactory, options);

verify(mockRenderersFactory).setEnableDecoderFallback(true);
verify(mockRenderersFactory).forceDisableMediaCodecAsynchronousQueueing();
}

@Test
public void playsAndPausesProvidedMedia() {
VideoPlayer videoPlayer = createVideoPlayer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
@override
Future<int?> createWithOptions(VideoCreationOptions options) async {
final DataSource dataSource = options.dataSource;
final VideoPlayerAndroidOptions androidOptions =
options.androidOptions ?? const VideoPlayerAndroidOptions();

String? uri;
PlatformVideoFormat? formatHint;
Expand Down Expand Up @@ -114,6 +116,9 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
httpHeaders: httpHeaders,
userAgent: userAgent,
formatHint: formatHint,
enableDecoderFallback: androidOptions.enableDecoderFallback,
disableMediaCodecAsyncQueueing:
androidOptions.disableMediaCodecAsyncQueueing,
);

final int playerId;
Expand Down
Loading