Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
11e39d8
Implements background playback functionality using allowBackgroundPla…
ArvidNy May 5, 2025
bce7066
Remove unnecessary iOS code
ArvidNy May 6, 2025
ea276cc
Use path-based dependency overrides
ArvidNy May 7, 2025
c3821a1
Add missing dependency overrides
ArvidNy May 8, 2025
77c89ee
Fix incorrect dependency paths
ArvidNy May 8, 2025
19cea1e
Revert dependency overrides for non-video_player packages
ArvidNy May 8, 2025
d3b421a
Revert dependency overrides for non-video_player packages
ArvidNy May 8, 2025
e3cc2c0
Revert pubspec.yaml changes
ArvidNy May 9, 2025
d4bed81
Fix version mismatch
ArvidNy Jun 4, 2025
98f55a1
Remove unused options field from TextureVideoPlayer
ArvidNy Jun 9, 2025
c835f14
Update README with WAKE_LOCK permissions info
ArvidNy Sep 1, 2025
bc798f2
Add wake mode tests
ArvidNy Sep 1, 2025
27bcb3b
Format code and regenerate mocks after rebase
ArvidNy Oct 16, 2025
7c41c4e
Fixes rebase merge errors
ArvidNy Oct 16, 2025
b820a88
Remove dependency_overrides and update to published platform interfac…
ArvidNy Oct 16, 2025
7ed672c
Bump video_player_avfoundation to 2.9.0 and video_player_web to 2.5.0
ArvidNy Oct 16, 2025
9f65b1e
Update video_player CHANGELOG to version 2.11.0
ArvidNy Oct 16, 2025
121c597
Format code with dart format and clang-format
ArvidNy Oct 16, 2025
d338d1c
Revert dependency versions to published versions in video_player package
ArvidNy Oct 16, 2025
df5b91f
Fix TextureVideoPlayer formatting and update video_player_platform_in…
ArvidNy Oct 16, 2025
fddf604
Remove no-op setAllowBackgroundPlayback method and related references…
ArvidNy Oct 20, 2025
b23067a
Merge remote-tracking branch 'upstream/main'
ArvidNy Oct 20, 2025
513f2e0
Refactor setAllowBackgroundPlayback method to use primitive boolean t…
ArvidNy Oct 20, 2025
b6417cf
Fix use contains instead of containsKey for map comparison
ArvidNy Oct 20, 2025
c52e368
Merge branch 'main' into main
ArvidNy Oct 20, 2025
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 @@
## NEXT
## 2.11.0

* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.
* Implements background playback functionality using allowBackgroundPlayback option.

## 2.10.0

Expand Down
15 changes: 15 additions & 0 deletions packages/video_player/video_player/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ in `<project root>/ios/Runner/Info.plist`. See
[Apple's documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity)
to determine the right combination of entries for your use case and supported iOS versions.

If you plan to use background playback, you will also need to add the following permission in your Info.plist file.

```xml
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
```

### Android

If you are using network-based videos, ensure that the following permission is present in your
Expand All @@ -31,6 +40,12 @@ Android Manifest file, located in `<project root>/android/app/src/main/AndroidMa
<uses-permission android:name="android.permission.INTERNET"/>
```

If you plan to use background playback, you will also need to add the following permission in your Android Manifest file.

```xml
<uses-permission android:name="android.permission.WAKE_LOCK"/>
```

### macOS

If you are using network-based videos, you will need to [add the
Expand Down
6 changes: 6 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,12 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
);
}

if (videoPlayerOptions?.allowBackgroundPlayback != null) {
await _videoPlayerPlatform.setAllowBackgroundPlayback(
videoPlayerOptions!.allowBackgroundPlayback,
);
}

_playerId =
(await _videoPlayerPlatform.createWithOptions(creationOptions)) ??
kUninitializedPlayerId;
Expand Down
4 changes: 2 additions & 2 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.10.0
version: 2.11.0

environment:
sdk: ^3.7.0
Expand All @@ -27,7 +27,7 @@ dependencies:
html: ^0.15.0
video_player_android: ^2.8.1
video_player_avfoundation: ^2.7.0
video_player_platform_interface: ^6.3.0
video_player_platform_interface: ^6.5.0
video_player_web: ^2.1.0

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1572,8 +1572,13 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
}

@override
Widget buildView(int playerId) {
return Texture(textureId: playerId);
Future<void> setAllowBackgroundPlayback(bool allowBackgroundPlayback) async {
calls.add('setAllowBackgroundPlayback');
}

@override
Widget buildView(int textureId) {
return Texture(textureId: textureId);
}

@override
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.9.0

* Implements background playback functionality using allowBackgroundPlayback option.

## 2.8.17

* Moves video event processing logic to Dart, and fixes an issue where buffer
Expand Down
6 changes: 6 additions & 0 deletions packages/video_player/video_player_android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ so you do not need to add it to your `pubspec.yaml`.
However, if you `import` this package to use any of its APIs directly, you
should add it to your `pubspec.yaml` as usual.

If you plan to use background playback, you will also need to add the following permission in your Android Manifest file.

```xml
<uses-permission android:name="android.permission.WAKE_LOCK"/>
```

## Known issues

Using `VideoViewType.platformView` is not currently recommended on Android due to a known [issue][3] affecting platform views on Android.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ public VideoPlayer(
exoPlayer.prepare();
exoPlayer.addListener(createExoPlayerEventListener(exoPlayer, surfaceProducer));
setAudioAttributes(exoPlayer, options.mixWithOthers);

if (options.allowBackgroundPlayback) {
// This flag allows playback to continue when the device is in Doze mode.
// WAKE_MODE_NETWORK is used to allow network streaming to continue.
exoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
exoPlayer.setAudioAttributes(
new AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).build(),
/* handleAudioFocus= */ !options.mixWithOthers);
}
}

public void setDisposeHandler(@Nullable DisposeHandler handler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@

public class VideoPlayerOptions {
public boolean mixWithOthers;
public boolean allowBackgroundPlayback;
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ public void setMixWithOthers(boolean mixWithOthers) {
: flutterState.keyForAssetAndPackageName.get(asset, packageName);
}

@Override
public void setAllowBackgroundPlayback(boolean allowBackgroundPlayback) {
sharedOptions.allowBackgroundPlayback = allowBackgroundPlayback;
}

private interface KeyForAssetFn {
String get(String asset);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ interface AndroidVideoPlayerApi {

fun setMixWithOthers(mixWithOthers: Boolean)

fun setAllowBackgroundPlayback(allowBackgroundPlayback: Boolean)

fun getLookupKeyForAsset(asset: String, packageName: String?): String

companion object {
Expand Down Expand Up @@ -540,6 +542,29 @@ interface AndroidVideoPlayerApi {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback$separatedMessageChannelSuffix",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val allowBackgroundPlaybackArg = args[0] as Boolean
val wrapped: List<Any?> =
try {
api.setAllowBackgroundPlayback(allowBackgroundPlaybackArg)
listOf(null)
} catch (exception: Throwable) {
MessagesPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,27 @@ public void disposeReleasesExoPlayer() {

verify(mockExoPlayer).release();
}

@Test
public void loadsAndPreparesProvidedMediaWithoutWakeModeByDefault() {
VideoPlayerOptions options = new VideoPlayerOptions();

VideoPlayer videoPlayer = createVideoPlayer(options);

verify(mockExoPlayer, never()).setWakeMode(anyInt());

videoPlayer.dispose();
}

@Test
public void loadsAndPreparesProvidedMediaWakeModeWhenAllowBackgroundPlaybackSet() {
VideoPlayerOptions options = new VideoPlayerOptions();
options.allowBackgroundPlayback = true;

VideoPlayer videoPlayer = createVideoPlayer(options);

verify(mockExoPlayer).setWakeMode(C.WAKE_MODE_NETWORK);

videoPlayer.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
video_player_platform_interface: ^6.3.0
video_player_platform_interface: ^6.5.0

dev_dependencies:
espresso: ^0.4.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
return _api.setMixWithOthers(mixWithOthers);
}

@override
Future<void> setAllowBackgroundPlayback(bool allowBackgroundPlayback) {
return _api.setAllowBackgroundPlayback(allowBackgroundPlayback);
}

_PlayerInstance _playerWith({required int id}) {
final _PlayerInstance? player = _players[id];
return player ?? (throw StateError('No active player with ID $id.'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,33 @@ class AndroidVideoPlayerApi {
}
}

Future<void> setAllowBackgroundPlayback(bool allowBackgroundPlayback) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[allowBackgroundPlayback],
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

Future<String> getLookupKeyForAsset(String asset, String? packageName) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.getLookupKeyForAsset$pigeonVar_messageChannelSuffix';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ abstract class AndroidVideoPlayerApi {
TexturePlayerIds createForTextureView(CreationOptions options);
void dispose(int playerId);
void setMixWithOthers(bool mixWithOthers);
void setAllowBackgroundPlayback(bool allowBackgroundPlayback);
String getLookupKeyForAsset(String asset, String? packageName);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/video_player/video_player_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: video_player_android
description: Android implementation of the video_player plugin.
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
version: 2.8.17
version: 2.9.0

environment:
sdk: ^3.9.0
Expand All @@ -20,7 +20,7 @@ flutter:
dependencies:
flutter:
sdk: flutter
video_player_platform_interface: ^6.3.0
video_player_platform_interface: ^6.5.0

dev_dependencies:
build_runner: ^2.3.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,24 @@ void main() {
});
});

group('setAllowBackgroundPlayback', () {
test('passes true', () async {
final (AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _) =
setUpMockPlayer(playerId: 1);
await player.setAllowBackgroundPlayback(true);

verify(api.setAllowBackgroundPlayback(true));
});

test('passes false', () async {
final (AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _) =
setUpMockPlayer(playerId: 1);
await player.setAllowBackgroundPlayback(false);

verify(api.setAllowBackgroundPlayback(false));
});
});

test('setVolume', () async {
final (
AndroidVideoPlayer player,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ class MockAndroidVideoPlayerApi extends _i1.Mock
)
as _i4.Future<void>);

@override
_i4.Future<void> setAllowBackgroundPlayback(bool? allowBackgroundPlayback) =>
(super.noSuchMethod(
Invocation.method(#setAllowBackgroundPlayback, [
allowBackgroundPlayback,
]),
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
)
as _i4.Future<void>);

@override
_i4.Future<String> getLookupKeyForAsset(String? asset, String? packageName) =>
(super.noSuchMethod(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.9.0

* Implements background playback functionality using allowBackgroundPlayback option.

## 2.8.5

* Updates minimum supported version to iOS 13 and macOS 10.15.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
video_player_platform_interface: ^6.3.0
video_player_platform_interface: ^6.5.0

dev_dependencies:
flutter_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform {
return _api.setMixWithOthers(mixWithOthers);
}

@override
Future<void> setAllowBackgroundPlayback(bool allowBackgroundPlayback) {
// No-op on iOS as background playback already works
return Future<void>.value();
}

@override
Widget buildView(int playerId) {
return buildViewWithOptions(VideoViewOptions(playerId: playerId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: video_player_avfoundation
description: iOS and macOS implementation of the video_player plugin.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
version: 2.8.5
version: 2.9.0

environment:
sdk: ^3.9.0
Expand All @@ -24,7 +24,7 @@ flutter:
dependencies:
flutter:
sdk: flutter
video_player_platform_interface: ^6.3.0
video_player_platform_interface: ^6.5.0

dev_dependencies:
build_runner: ^2.3.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,15 @@ void main() {
});
});

test('setAllowBackgroundPlayback does nothing', () async {
final (AVFoundationVideoPlayer player, _, _) = setUpMockPlayer(
playerId: 1,
);
// Should complete without error, even though it's a no-op on iOS
await player.setAllowBackgroundPlayback(true);
await player.setAllowBackgroundPlayback(false);
});

test('setVolume', () async {
final (
AVFoundationVideoPlayer player,
Expand Down
Loading