diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 1b3caf42876..a8f4f1c51d9 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -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 diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index dcf1a6018bf..4bec02069e5 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -22,6 +22,15 @@ in `/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 +UIBackgroundModes + + audio + +``` + ### Android If you are using network-based videos, ensure that the following permission is present in your @@ -31,6 +40,12 @@ Android Manifest file, located in `/android/app/src/main/AndroidMa ``` +If you plan to use background playback, you will also need to add the following permission in your Android Manifest file. + +```xml + +``` + ### macOS If you are using network-based videos, you will need to [add the diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 5755db029dd..6f2917e8430 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -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} diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 12b30c2a146..169b8dbe850 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -436,6 +436,11 @@ class VideoPlayerController extends ValueNotifier { .setMixWithOthers(videoPlayerOptions!.mixWithOthers); } + if (videoPlayerOptions?.allowBackgroundPlayback != null) { + await _videoPlayerPlatform.setAllowBackgroundPlayback( + videoPlayerOptions!.allowBackgroundPlayback); + } + _textureId = (await _videoPlayerPlatform.create(dataSourceDescription)) ?? kUninitializedTextureId; _creatingCompleter!.complete(null); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0f80d59e6a9..61d3a455c26 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -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 @@ -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} 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 a7b0888faa6..cad9cbeb77e 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -1498,6 +1498,11 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { calls.add('setMixWithOthers'); } + @override + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) async { + calls.add('setAllowBackgroundPlayback'); + } + @override Widget buildView(int textureId) { return Texture(textureId: textureId); diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index f9a2064447e..1634c1d6786 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -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 diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index 581cb7b80f6..e1082d06501 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -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; @@ -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 getCodec() { return PigeonCodec.INSTANCE; @@ -693,6 +695,31 @@ static void setUp( channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) 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); + } + } } } } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 8040e86419c..ff6ed387b5c 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -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; } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java index 7c8a6aab726..c793b503a85 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java @@ -6,4 +6,5 @@ public class VideoPlayerOptions { public boolean mixWithOthers; + public boolean allowBackgroundPlayback; } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 3db5fd42a26..200e6f08978 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -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); } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java index 535c302921d..69fc38a0331 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/texture/TextureVideoPlayer.java @@ -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. @@ -72,6 +73,7 @@ public TextureVideoPlayer( this.surfaceProducer = surfaceProducer; surfaceProducer.setCallback(this); + this.options = options; this.exoPlayer.setVideoSurface(surfaceProducer.getSurface()); } @@ -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() { diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml index a08ae689747..d585f8e00f7 100644 --- a/packages/video_player/video_player_android/example/pubspec.yaml +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -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} diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 2df0a0ebc36..389e23f1940 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -198,6 +198,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { return _api.setMixWithOthers(mixWithOthers); } + @override + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) { + return _api.setAllowBackgroundPlayback(allowBackgroundPlayback); + } + EventChannel _eventChannelFor(int playerId) { return EventChannel('flutter.io/videoPlayer/videoEvents$playerId'); } diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart index 717e8e020d0..d33efc352c8 100644 --- a/packages/video_player/video_player_android/lib/src/messages.g.dart +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -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 @@ -429,4 +429,28 @@ class AndroidVideoPlayerApi { return; } } + + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([allowBackgroundPlayback]) as List?; + 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; + } + } } diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index a1f1211e533..a6a7ca67fc2 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -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); } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 40b0c33388c..30716835377 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -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 @@ -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} diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index ddc6617fc4e..80ba87f4e0b 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -21,6 +21,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { double? passedVolume; double? passedPlaybackSpeed; bool? passedMixWithOthers; + bool? passedAllowBackgroundPlayback; @override int create(CreateMessage arg) { @@ -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'); @@ -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'); diff --git a/packages/video_player/video_player_android/test/test_api.g.dart b/packages/video_player/video_player_android/test/test_api.g.dart index f3ac1b7fb73..4fd2e6d9fd9 100644 --- a/packages/video_player/video_player_android/test/test_api.g.dart +++ b/packages/video_player/video_player_android/test/test_api.g.dart @@ -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 @@ -77,6 +77,8 @@ abstract class TestHostVideoPlayerApi { void setMixWithOthers(bool mixWithOthers); + void setAllowBackgroundPlayback(bool allowBackgroundPlayback); + static void setUp( TestHostVideoPlayerApi? api, { BinaryMessenger? binaryMessenger, @@ -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(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setAllowBackgroundPlayback was null.'); + final List args = (message as List?)!; + 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())); + } + }); + } + } } } diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 992c015df1b..6f6d6e4afc4 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -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. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m index 1936435ffd0..c7678135233 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m @@ -322,4 +322,10 @@ - (void)setMixWithOthers:(BOOL)mixWithOthers #endif } +- (void)setAllowBackgroundPlayback:(BOOL)allowBackgroundPlayback error:(FlutterError **)error { +#if TARGET_OS_IOS + // Do nothing here, the background playback still works +#endif +} + @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h index 6a115c6b9ac..b89c75d17f9 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h @@ -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 #import @@ -80,6 +80,8 @@ NSObject *FVPGetMessagesCodec(void); completion:(void (^)(FlutterError *_Nullable))completion; - (void)pausePlayer:(NSInteger)playerId error:(FlutterError *_Nullable *_Nonnull)error; - (void)setMixWithOthers:(BOOL)mixWithOthers error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowBackgroundPlayback:(BOOL)allowBackgroundPlayback + error:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFVPAVFoundationVideoPlayerApi( diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m index 83c41211f74..5af7f9740f8 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m @@ -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 #import "./include/video_player_avfoundation/messages.g.h" @@ -458,4 +458,29 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.setAllowBackgroundPlayback", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FVPGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setAllowBackgroundPlayback:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setAllowBackgroundPlayback:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + BOOL arg_allowBackgroundPlayback = [GetNullableObjectAtIndex(args, 0) boolValue]; + FlutterError *error; + [api setAllowBackgroundPlayback:arg_allowBackgroundPlayback error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml index 9441576e59d..8d7a9f337a1 100644 --- a/packages/video_player/video_player_avfoundation/example/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml @@ -31,3 +31,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} diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index ec790187abf..3facf2fb2bf 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -179,6 +179,11 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return _api.setMixWithOthers(mixWithOthers); } + @override + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) { + return _api.setAllowBackgroundPlayback(allowBackgroundPlayback); + } + @override Widget buildView(int playerId) { return buildViewWithOptions( diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index c1d16ecce5e..eac3f83f8c7 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -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 @@ -429,4 +429,28 @@ class AVFoundationVideoPlayerApi { return; } } + + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setAllowBackgroundPlayback$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([allowBackgroundPlayback]) as List?; + 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; + } + } } diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index ffa2f968c32..0abb3b86ae8 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -73,4 +73,6 @@ abstract class AVFoundationVideoPlayerApi { void pause(int playerId); @ObjCSelector('setMixWithOthers:') void setMixWithOthers(bool mixWithOthers); + @ObjCSelector('setAllowBackgroundPlayback:') + void setAllowBackgroundPlayback(bool allowBackgroundPlayback); } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 20087db42cc..dee7b47433d 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -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.7.1 +version: 2.8.0 environment: sdk: ^3.4.0 @@ -34,3 +34,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} diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 3d02cef3cbe..e25a6cbb554 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -20,6 +20,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { double? volume; double? playbackSpeed; bool? mixWithOthers; + bool? allowBackgroundPlayback; @override int create(CreationOptions options) { @@ -57,6 +58,12 @@ class _ApiLogger implements TestHostVideoPlayerApi { mixWithOthers = enabled; } + @override + void setAllowBackgroundPlayback(bool enabled) { + log.add('setAllowBackgroundPlayback'); + allowBackgroundPlayback = enabled; + } + @override int getPosition(int playerId) { log.add('position'); @@ -335,6 +342,16 @@ void main() { expect(log.mixWithOthers, false); }); + test('setAllowBackgroundPlayback', () async { + await player.setAllowBackgroundPlayback(true); + expect(log.log.last, 'setAllowBackgroundPlayback'); + expect(log.allowBackgroundPlayback, true); + + await player.setAllowBackgroundPlayback(false); + expect(log.log.last, 'setAllowBackgroundPlayback'); + expect(log.allowBackgroundPlayback, false); + }); + test('setVolume', () async { await player.setVolume(1, 0.7); expect(log.log.last, 'setVolume'); diff --git a/packages/video_player/video_player_avfoundation/test/test_api.g.dart b/packages/video_player/video_player_avfoundation/test/test_api.g.dart index 38ed42f1f9e..5e93f96f6fc 100644 --- a/packages/video_player/video_player_avfoundation/test/test_api.g.dart +++ b/packages/video_player/video_player_avfoundation/test/test_api.g.dart @@ -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 @@ -77,6 +77,8 @@ abstract class TestHostVideoPlayerApi { void setMixWithOthers(bool mixWithOthers); + void setAllowBackgroundPlayback(bool allowBackgroundPlayback); + static void setUp( TestHostVideoPlayerApi? api, { BinaryMessenger? binaryMessenger, @@ -443,5 +445,37 @@ abstract class TestHostVideoPlayerApi { }); } } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setAllowBackgroundPlayback$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setAllowBackgroundPlayback was null.'); + final List args = (message as List?)!; + final bool? arg_allowBackgroundPlayback = (args[0] as bool?); + assert(arg_allowBackgroundPlayback != null, + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.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())); + } + }); + } + } } } diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 5e8f06df267..edd0d04de1f 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.4.0 + +* Implements background playback functionality using allowBackgroundPlayback option. + ## 6.3.0 * Adds support for platform views as an optional way of displaying a video. diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 13c9cf55aa7..2e676b3bc9c 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -117,6 +117,12 @@ abstract class VideoPlayerPlatform extends PlatformInterface { throw UnimplementedError('setMixWithOthers() has not been implemented.'); } + /// Sets the audio mode to allow background playback. + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) { + throw UnimplementedError( + 'setAllowBackgroundPlayback() has not been implemented.'); + } + /// Sets additional options on web. Future setWebOptions(int playerId, VideoPlayerWebOptions options) { throw UnimplementedError('setWebOptions() has not been implemented.'); diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 66b12850703..53bab651f16 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/video_player/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 6.3.0 +version: 6.4.0 environment: sdk: ^3.4.0 diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 1db5ab25811..3b37db875ea 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Implements background playback functionality using allowBackgroundPlayback option. + ## 2.3.5 * Fixes blank first frame on iOS. diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index f2d5193640e..a989afde933 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -169,6 +169,14 @@ void main() { expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes); }); + testWidgets('ignores setting allowBackgroundPlayback', + (WidgetTester tester) async { + expect(VideoPlayerPlatform.instance.setAllowBackgroundPlayback(true), + completes); + expect(VideoPlayerPlatform.instance.setAllowBackgroundPlayback(false), + completes); + }); + testWidgets( 'double call to play will emit a single isPlayingStateUpdate event', (WidgetTester tester) async { diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 1ebae929e19..a315716f24c 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -18,3 +18,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter +# 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} diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index d65eabacfcc..c84d21c6fc9 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -163,4 +163,9 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { /// Sets the audio mode to mix with other sources (ignored). @override Future setMixWithOthers(bool mixWithOthers) => Future.value(); + + /// Sets the audio mode to allow background playback (ignored). + @override + Future setAllowBackgroundPlayback(bool allowBackgroundPlayback) => + Future.value(); } diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 67c7628130b..8952db043a1 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.5 +version: 2.4.0 environment: sdk: ^3.4.0 @@ -31,3 +31,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}