Skip to content

[video_player] Implements background playback functionality #9212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
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
5 changes: 3 additions & 2 deletions packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## NEXT
## 2.10.0

* Updates README to indicate that Andoid SDK <21 is no longer supported.
* Implements background playback functionality using allowBackgroundPlayback option.
* Updates README to indicate that Android SDK <21 is no longer supported.

## 2.9.5

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
7 changes: 7 additions & 0 deletions packages/video_player/video_player/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ flutter:
- assets/bumble_bee_captions.srt
- assets/bumble_bee_captions.vtt
- assets/Audio.mp3
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
video_player_android: {path: ../../../../packages/video_player/video_player_android}
video_player_avfoundation: {path: ../../../../packages/video_player/video_player_avfoundation}
video_player_platform_interface: {path: ../../../../packages/video_player/video_player_platform_interface}
video_player_web: {path: ../../../../packages/video_player/video_player_web}
5 changes: 5 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,11 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
.setMixWithOthers(videoPlayerOptions!.mixWithOthers);
}

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

_textureId = (await _videoPlayerPlatform.create(dataSourceDescription)) ??
kUninitializedTextureId;
_creatingCompleter!.complete(null);
Expand Down
9 changes: 8 additions & 1 deletion 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, 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.9.5
version: 2.10.0

environment:
sdk: ^3.4.0
Expand Down Expand Up @@ -38,3 +38,10 @@ dev_dependencies:
topics:
- video
- video-player
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
video_player_android: {path: ../../../packages/video_player/video_player_android}
video_player_avfoundation: {path: ../../../packages/video_player/video_player_avfoundation}
video_player_platform_interface: {path: ../../../packages/video_player/video_player_platform_interface}
video_player_web: {path: ../../../packages/video_player/video_player_web}
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,11 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
calls.add('setMixWithOthers');
}

@override
Future<void> setAllowBackgroundPlayback(bool allowBackgroundPlayback) async {
calls.add('setAllowBackgroundPlayback');
}

@override
Widget buildView(int textureId) {
return Texture(textureId: textureId);
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.2

* Fixes a [bug](https://github.com/flutter/flutter/issues/164689) that can cause video to
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon

package io.flutter.plugins.videoplayer;
Expand Down Expand Up @@ -397,6 +397,8 @@ public interface AndroidVideoPlayerApi {

void setMixWithOthers(@NonNull Boolean mixWithOthers);

void setAllowBackgroundPlayback(@NonNull Boolean allowBackgroundPlayback);

/** The codec used by AndroidVideoPlayerApi. */
static @NonNull MessageCodec<Object> getCodec() {
return PigeonCodec.INSTANCE;
Expand Down Expand Up @@ -693,6 +695,31 @@ static void setUp(
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback"
+ messageChannelSuffix,
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<>();
ArrayList<Object> args = (ArrayList<Object>) message;
Boolean allowBackgroundPlaybackArg = (Boolean) args.get(0);
try {
api.setAllowBackgroundPlayback(allowBackgroundPlaybackArg);
wrapped.add(0, null);
} catch (Throwable exception) {
wrapped = wrapError(exception);
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ protected ExoPlayer createVideoPlayer() {
exoPlayer.addListener(createExoPlayerEventListener(exoPlayer));
setAudioAttributes(exoPlayer, options.mixWithOthers);

if (options.allowBackgroundPlayback) {
exoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
exoPlayer.setAudioAttributes(
new AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).build(),
/* handleAudioFocus= */ !options.mixWithOthers);
}

return exoPlayer;
}

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 @@ -223,6 +223,11 @@ public void setMixWithOthers(@NonNull Boolean mixWithOthers) {
options.mixWithOthers = mixWithOthers;
}

@Override
public void setAllowBackgroundPlayback(@NonNull Boolean allowBackgroundPlayback) {
options.allowBackgroundPlayback = allowBackgroundPlayback;
}

private interface KeyForAssetFn {
String get(String asset);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class TextureVideoPlayer extends VideoPlayer
implements TextureRegistry.SurfaceProducer.Callback {
@NonNull private final TextureRegistry.SurfaceProducer surfaceProducer;
@Nullable private ExoPlayerState savedStateDuring;
@Nullable private final VideoPlayerOptions options;

/**
* Creates a texture video player.
Expand Down Expand Up @@ -72,6 +73,7 @@ public TextureVideoPlayer(

this.surfaceProducer = surfaceProducer;
surfaceProducer.setCallback(this);
this.options = options;

this.exoPlayer.setVideoSurface(surfaceProducer.getSurface());
}
Expand Down Expand Up @@ -100,8 +102,13 @@ public void onSurfaceAvailable() {
public void onSurfaceDestroyed() {
// Intentionally do not call pause/stop here, because the surface has already been released
// at this point (see https://github.com/flutter/flutter/issues/156451).
savedStateDuring = ExoPlayerState.save(exoPlayer);
exoPlayer.release();
if (options == null || !options.allowBackgroundPlayback) {
savedStateDuring = ExoPlayerState.save(exoPlayer);
exoPlayer.release();
} else {
savedStateDuring = null;
exoPlayer.setVideoSurface(null);
}
}

private boolean playerHasBeenSuspended() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ flutter:
assets:
- assets/flutter-mark-square-64.png
- assets/Butterfly-209.mp4
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
video_player_platform_interface: {path: ../../../../packages/video_player/video_player_platform_interface}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
return _api.setMixWithOthers(mixWithOthers);
}

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

EventChannel _eventChannelFor(int playerId) {
return EventChannel('flutter.io/videoPlayer/videoEvents$playerId');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers

Expand Down Expand Up @@ -429,4 +429,28 @@ class AndroidVideoPlayerApi {
return;
}
}

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 List<Object?>? pigeonVar_replyList = await pigeonVar_channel
.send(<Object?>[allowBackgroundPlayback]) 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ abstract class AndroidVideoPlayerApi {
void seekTo(int playerId, int position);
void pause(int playerId);
void setMixWithOthers(bool mixWithOthers);
void setAllowBackgroundPlayback(bool allowBackgroundPlayback);
}
6 changes: 5 additions & 1 deletion 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.2
version: 2.9.0

environment:
sdk: ^3.6.0
Expand Down Expand Up @@ -30,3 +30,7 @@ dev_dependencies:
topics:
- video
- video-player
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
video_player_platform_interface: {path: ../../../packages/video_player/video_player_platform_interface}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class _ApiLogger implements TestHostVideoPlayerApi {
double? passedVolume;
double? passedPlaybackSpeed;
bool? passedMixWithOthers;
bool? passedAllowBackgroundPlayback;

@override
int create(CreateMessage arg) {
Expand Down Expand Up @@ -58,6 +59,12 @@ class _ApiLogger implements TestHostVideoPlayerApi {
passedMixWithOthers = mixWithOthers;
}

@override
void setAllowBackgroundPlayback(bool allowBackgroundPlayback) {
log.add('setAllowBackgroundPlayback');
passedAllowBackgroundPlayback = allowBackgroundPlayback;
}

@override
int position(int playerId) {
log.add('position');
Expand Down Expand Up @@ -349,6 +356,16 @@ void main() {
expect(log.passedMixWithOthers, false);
});

test('setAllowBackgroundPlayback', () async {
await player.setAllowBackgroundPlayback(true);
expect(log.log.last, 'setAllowBackgroundPlayback');
expect(log.passedAllowBackgroundPlayback, true);

await player.setAllowBackgroundPlayback(false);
expect(log.log.last, 'setAllowBackgroundPlayback');
expect(log.passedAllowBackgroundPlayback, false);
});

test('setVolume', () async {
await player.setVolume(1, 0.7);
expect(log.log.last, 'setVolume');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers
// ignore_for_file: avoid_relative_lib_imports
Expand Down Expand Up @@ -77,6 +77,8 @@ abstract class TestHostVideoPlayerApi {

void setMixWithOthers(bool mixWithOthers);

void setAllowBackgroundPlayback(bool allowBackgroundPlayback);

static void setUp(
TestHostVideoPlayerApi? api, {
BinaryMessenger? binaryMessenger,
Expand Down Expand Up @@ -442,5 +444,37 @@ abstract class TestHostVideoPlayerApi {
});
}
}
{
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(pigeonVar_channel, null);
} else {
_testBinaryMessengerBinding!.defaultBinaryMessenger
.setMockDecodedMessageHandler<Object?>(pigeonVar_channel,
(Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback was null.');
final List<Object?> args = (message as List<Object?>?)!;
final bool? arg_allowBackgroundPlayback = (args[0] as bool?);
assert(arg_allowBackgroundPlayback != null,
'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback was null, expected non-null bool.');
try {
api.setAllowBackgroundPlayback(arg_allowBackgroundPlayback!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.8.0

* Implements background playback functionality using allowBackgroundPlayback option.

## 2.7.1

* Adds possibility to play videos at more than 30 FPS.
Expand Down
Loading