From 07a1e0668719529f15c1155a3f9cc000ffd35723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 18 Nov 2024 15:15:36 +0100 Subject: [PATCH 01/92] Add FVPNativeVideoViewFactory and FVPNativeVideoView (everything in one file for now) --- .../FVPVideoPlayerPlugin.m | 4 ++++ .../FVPVideoPlayerPlugin.h | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) 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 cc81b3e6dd7f..4caf3c564cb8 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 @@ -55,6 +55,10 @@ @implementation FVPVideoPlayerPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FVPVideoPlayerPlugin *instance = [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; + FVPNativeVideoViewFactory *factory = + [[FVPNativeVideoViewFactory alloc] initWithMessenger:registrar.messenger + playersByTextureId:instance.playersByTextureId]; + [registrar registerViewFactory:factory withId:@"plugins.flutter.dev/video_player_ios"]; SetUpFVPAVFoundationVideoPlayerApi(registrar.messenger, instance); } diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h index b64a173fe0cd..9e2f30289391 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h @@ -8,6 +8,22 @@ #import #endif +#import + @interface FVPVideoPlayerPlugin : NSObject - (instancetype)initWithRegistrar:(NSObject *)registrar; @end + +@interface FVPNativeVideoViewFactory : NSObject +- (instancetype)initWithMessenger:(NSObject *)messenger; +@end + +@interface FVPNativeVideoView : NSObject +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject *)messenger + player:(AVPlayer *)player; + +- (UIView *)view; +@end From acc49919ae24f2e425dc002eda2672bfe51f7ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 18 Nov 2024 15:16:20 +0100 Subject: [PATCH 02/92] Use platform view in Dart Code (hardcoded) --- .../lib/src/avfoundation_video_player.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 ace8f749a6b1..b964d5e38f56 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 @@ -141,6 +141,21 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Widget buildView(int textureId) { + // FIXME Use platform view or texture view based on a param. + // FIXME Use a separate class for creation. + + final Map creationParams = { + 'viewId': textureId, + }; + return IgnorePointer( + // IgnorePointer so that GestureDetector can be used above the platform view. + child: UiKitView( + viewType: 'plugins.flutter.dev/video_player_ios', + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ), + ); + return Texture(textureId: textureId); } From 4184fc542c56f367c9fe8117f45832f99774734d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 18 Nov 2024 15:54:32 +0100 Subject: [PATCH 03/92] Add PlatformVideoViewType enum to allow choosing between texture view and platform view for each video --- .../video_player_avfoundation/messages.g.h | 64 ++--- .../video_player_avfoundation/messages.g.m | 225 +++++++-------- .../lib/src/avfoundation_video_player.dart | 41 ++- .../lib/src/messages.g.dart | 126 ++++----- .../pigeons/messages.dart | 8 + .../test/test_api.g.dart | 257 ++++++------------ .../lib/video_player_platform_interface.dart | 10 +- 7 files changed, 319 insertions(+), 412 deletions(-) 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 afb118f14cd0..81c227e3c135 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -13,21 +13,35 @@ NS_ASSUME_NONNULL_BEGIN +/// Pigeon equivalent of VideoViewType. +typedef NS_ENUM(NSUInteger, FVPPlatformVideoViewType) { + FVPPlatformVideoViewTypeTextureView = 0, + FVPPlatformVideoViewTypePlatformView = 1, +}; + +/// Wrapper for FVPPlatformVideoViewType to allow for nullability. +@interface FVPPlatformVideoViewTypeBox : NSObject +@property(nonatomic, assign) FVPPlatformVideoViewType value; +- (instancetype)initWithValue:(FVPPlatformVideoViewType)value; +@end + @class FVPCreationOptions; @interface FVPCreationOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders; -@property(nonatomic, copy, nullable) NSString *asset; -@property(nonatomic, copy, nullable) NSString *uri; -@property(nonatomic, copy, nullable) NSString *packageName; -@property(nonatomic, copy, nullable) NSString *formatHint; -@property(nonatomic, copy) NSDictionary *httpHeaders; + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders + viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType; +@property(nonatomic, copy, nullable) NSString * asset; +@property(nonatomic, copy, nullable) NSString * uri; +@property(nonatomic, copy, nullable) NSString * packageName; +@property(nonatomic, copy, nullable) NSString * formatHint; +@property(nonatomic, copy) NSDictionary * httpHeaders; +@property(nonatomic, strong, nullable) FVPPlatformVideoViewTypeBox * viewType; @end /// The codec used by all APIs. @@ -36,35 +50,21 @@ NSObject *FVPGetMessagesCodec(void); @protocol FVPAVFoundationVideoPlayerApi - (void)initialize:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)createWithOptions:(FVPCreationOptions *)creationOptions - error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSNumber *)createWithOptions:(FVPCreationOptions *)creationOptions error:(FlutterError *_Nullable *_Nonnull)error; - (void)disposePlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setLooping:(BOOL)isLooping - forPlayer:(NSInteger)textureId - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setVolume:(double)volume - forPlayer:(NSInteger)textureId - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setPlaybackSpeed:(double)speed - forPlayer:(NSInteger)textureId - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setLooping:(BOOL)isLooping forPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setVolume:(double)volume forPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setPlaybackSpeed:(double)speed forPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; - (void)playPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)positionForPlayer:(NSInteger)textureId - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)seekTo:(NSInteger)position - forPlayer:(NSInteger)textureId - completion:(void (^)(FlutterError *_Nullable))completion; +- (nullable NSNumber *)positionForPlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)seekTo:(NSInteger)position forPlayer:(NSInteger)textureId completion:(void (^)(FlutterError *_Nullable))completion; - (void)pausePlayer:(NSInteger)textureId error:(FlutterError *_Nullable *_Nonnull)error; - (void)setMixWithOthers:(BOOL)mixWithOthers error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpFVPAVFoundationVideoPlayerApi( - id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, NSObject *_Nullable api); -extern void SetUpFVPAVFoundationVideoPlayerApiWithSuffix( - id binaryMessenger, - NSObject *_Nullable api, NSString *messageChannelSuffix); +extern void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); NS_ASSUME_NONNULL_END 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 9749b125a9ee..c26cfb0227f4 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "./include/video_player_avfoundation/messages.g.h" @@ -30,6 +30,17 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { return (result == [NSNull null]) ? nil : result; } +/// Pigeon equivalent of VideoViewType. +@implementation FVPPlatformVideoViewTypeBox +- (instancetype)initWithValue:(FVPPlatformVideoViewType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + @interface FVPCreationOptions () + (FVPCreationOptions *)fromList:(NSArray *)list; + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list; @@ -38,16 +49,18 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list; @implementation FVPCreationOptions + (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders { - FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders + viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType { + FVPCreationOptions* pigeonResult = [[FVPCreationOptions alloc] init]; pigeonResult.asset = asset; pigeonResult.uri = uri; pigeonResult.packageName = packageName; pigeonResult.formatHint = formatHint; pigeonResult.httpHeaders = httpHeaders; + pigeonResult.viewType = viewType; return pigeonResult; } + (FVPCreationOptions *)fromList:(NSArray *)list { @@ -57,6 +70,7 @@ + (FVPCreationOptions *)fromList:(NSArray *)list { pigeonResult.packageName = GetNullableObjectAtIndex(list, 2); pigeonResult.formatHint = GetNullableObjectAtIndex(list, 3); pigeonResult.httpHeaders = GetNullableObjectAtIndex(list, 4); + pigeonResult.viewType = GetNullableObjectAtIndex(list, 5); return pigeonResult; } + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list { @@ -69,6 +83,7 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list { self.packageName ?: [NSNull null], self.formatHint ?: [NSNull null], self.httpHeaders ?: [NSNull null], + self.viewType ?: [NSNull null], ]; } @end @@ -78,7 +93,11 @@ @interface FVPMessagesPigeonCodecReader : FlutterStandardReader @implementation FVPMessagesPigeonCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 129: + case 129: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 130: return [FVPCreationOptions fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -90,8 +109,12 @@ @interface FVPMessagesPigeonCodecWriter : FlutterStandardWriter @end @implementation FVPMessagesPigeonCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FVPCreationOptions class]]) { + if ([value isKindOfClass:[FVPPlatformVideoViewTypeBox class]]) { + FVPPlatformVideoViewTypeBox *box = (FVPPlatformVideoViewTypeBox *)value; [self writeByte:129]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[FVPCreationOptions class]]) { + [self writeByte:130]; [self writeValue:[value toList]]; } else { [super writeValue:value]; @@ -114,35 +137,25 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FVPMessagesPigeonCodecReaderWriter *readerWriter = - [[FVPMessagesPigeonCodecReaderWriter alloc] init]; + FVPMessagesPigeonCodecReaderWriter *readerWriter = [[FVPMessagesPigeonCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, - NSObject *api) { +void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, NSObject *api) { SetUpFVPAVFoundationVideoPlayerApiWithSuffix(binaryMessenger, api, @""); } -void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, - NSObject *api, - NSString *messageChannelSuffix) { - messageChannelSuffix = messageChannelSuffix.length > 0 - ? [NSString stringWithFormat:@".%@", messageChannelSuffix] - : @""; +void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.initialize", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(initialize:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", - api); + NSCAssert([api respondsToSelector:@selector(initialize:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api initialize:&error]; @@ -153,18 +166,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.create", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(createWithOptions:error:)", - api); + NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(createWithOptions:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FVPCreationOptions *arg_creationOptions = GetNullableObjectAtIndex(args, 0); @@ -177,18 +185,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.dispose", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(disposePlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(disposePlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -201,18 +204,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setLooping", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setLooping:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_isLooping = [GetNullableObjectAtIndex(args, 0) boolValue]; @@ -226,18 +224,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setVolume", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setVolume:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_volume = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -251,18 +244,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setPlaybackSpeed", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setPlaybackSpeed:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_speed = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -276,18 +264,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.play", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(playPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(playPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -300,18 +283,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.getPosition", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(positionForPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(positionForPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -324,45 +302,33 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.seekTo", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(seekTo:forPlayer:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:forPlayer:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_position = [GetNullableObjectAtIndex(args, 0) integerValue]; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 1) integerValue]; - [api seekTo:arg_position - forPlayer:arg_textureId - completion:^(FlutterError *_Nullable error) { - callback(wrapResult(nil, error)); - }]; + [api seekTo:arg_position forPlayer:arg_textureId completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.pause", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(pausePlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(pausePlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -375,18 +341,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setMixWithOthers", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setMixWithOthers:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_mixWithOthers = [GetNullableObjectAtIndex(args, 0) boolValue]; 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 b964d5e38f56..5f2f60c8e323 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 @@ -31,7 +31,10 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } @override - Future create(DataSource dataSource) async { + Future create( + DataSource dataSource, + VideoViewType viewType, + ) async { String? asset; String? packageName; String? uri; @@ -56,6 +59,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { uri: uri, httpHeaders: httpHeaders, formatHint: formatHint, + viewType: _platformVideoViewTypeFromVideoViewType(viewType), ); return _api.create(options); @@ -140,12 +144,23 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } @override - Widget buildView(int textureId) { - // FIXME Use platform view or texture view based on a param. - // FIXME Use a separate class for creation. + Future setMixWithOthers(bool mixWithOthers) { + return _api.setMixWithOthers(mixWithOthers); + } + + @override + Widget buildView(int textureId, VideoViewType viewType) { + return switch (viewType) { + VideoViewType.textureView => Texture(textureId: textureId), + VideoViewType.platformView => _buildPlatformView(textureId, viewType), + }; + } + + Widget _buildPlatformView(int viewId, VideoViewType viewType) { + // FIXME Use a separate class for creation params. final Map creationParams = { - 'viewId': textureId, + 'viewId': viewId, }; return IgnorePointer( // IgnorePointer so that GestureDetector can be used above the platform view. @@ -155,13 +170,6 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { creationParamsCodec: const StandardMessageCodec(), ), ); - - return Texture(textureId: textureId); - } - - @override - Future setMixWithOthers(bool mixWithOthers) { - return _api.setMixWithOthers(mixWithOthers); } EventChannel _eventChannelFor(int textureId) { @@ -184,3 +192,12 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { ); } } + +PlatformVideoViewType _platformVideoViewTypeFromVideoViewType( + VideoViewType viewType, +) { + return switch (viewType) { + VideoViewType.textureView => PlatformVideoViewType.textureView, + VideoViewType.platformView => PlatformVideoViewType.platformView, + }; +} 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 ea4a93fdaba1..39c988dec634 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), 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 @@ -18,8 +18,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -29,6 +28,12 @@ List wrapResponse( return [error.code, error.message, error.details]; } +/// Pigeon equivalent of VideoViewType. +enum PlatformVideoViewType { + textureView, + platformView, +} + class CreationOptions { CreationOptions({ this.asset, @@ -36,6 +41,7 @@ class CreationOptions { this.packageName, this.formatHint, required this.httpHeaders, + this.viewType, }); String? asset; @@ -48,6 +54,8 @@ class CreationOptions { Map httpHeaders; + PlatformVideoViewType? viewType; + Object encode() { return [ asset, @@ -55,6 +63,7 @@ class CreationOptions { packageName, formatHint, httpHeaders, + viewType, ]; } @@ -65,12 +74,13 @@ class CreationOptions { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: - (result[4] as Map?)!.cast(), + httpHeaders: (result[4] as Map?)!.cast(), + viewType: result[5] as PlatformVideoViewType?, ); } } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -78,8 +88,11 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is CreationOptions) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is CreationOptions) { + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -89,7 +102,10 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformVideoViewType.values[value]; + case 130: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -101,11 +117,9 @@ class AVFoundationVideoPlayerApi { /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AVFoundationVideoPlayerApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -113,10 +127,8 @@ class AVFoundationVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -137,16 +149,14 @@ class AVFoundationVideoPlayerApi { } Future create(CreationOptions creationOptions) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([creationOptions]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([creationOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -166,10 +176,8 @@ class AVFoundationVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -190,16 +198,14 @@ class AVFoundationVideoPlayerApi { } Future setLooping(bool isLooping, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([isLooping, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([isLooping, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -214,16 +220,14 @@ class AVFoundationVideoPlayerApi { } Future setVolume(double volume, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([volume, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([volume, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -238,16 +242,14 @@ class AVFoundationVideoPlayerApi { } Future setPlaybackSpeed(double speed, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([speed, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([speed, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -262,10 +264,8 @@ class AVFoundationVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -286,10 +286,8 @@ class AVFoundationVideoPlayerApi { } Future getPosition(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -315,16 +313,14 @@ class AVFoundationVideoPlayerApi { } Future seekTo(int position, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([position, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([position, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -339,10 +335,8 @@ class AVFoundationVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -363,16 +357,14 @@ class AVFoundationVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 7a2d2957be16..91190861f52e 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -17,6 +17,13 @@ import 'package:pigeon/pigeon.dart'; ), copyrightHeader: 'pigeons/copyright.txt', )) + +/// Pigeon equivalent of VideoViewType. +enum PlatformVideoViewType { + textureView, + platformView, +} + class CreationOptions { CreationOptions({required this.httpHeaders}); String? asset; @@ -24,6 +31,7 @@ class CreationOptions { String? packageName; String? formatHint; Map httpHeaders; + PlatformVideoViewType? viewType; } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') 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 6dbbb53d233e..b888c96aa981 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.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), 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 @@ -13,6 +13,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_avfoundation/src/messages.g.dart'; + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -20,8 +21,11 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is CreationOptions) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is CreationOptions) { + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -31,7 +35,10 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformVideoViewType.values[value]; + case 130: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -40,8 +47,7 @@ class _PigeonCodec extends StandardMessageCodec { } abstract class TestHostVideoPlayerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); void initialize(); @@ -66,58 +72,39 @@ abstract class TestHostVideoPlayerApi { void setMixWithOthers(bool mixWithOthers); - static void setUp( - TestHostVideoPlayerApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp(TestHostVideoPlayerApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { try { api.initialize(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null.'); final List args = (message as List?)!; - final CreationOptions? arg_creationOptions = - (args[0] as CreationOptions?); + final CreationOptions? arg_creationOptions = (args[0] as CreationOptions?); assert(arg_creationOptions != null, 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null, expected non-null CreationOptions.'); try { @@ -125,29 +112,22 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -157,29 +137,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final bool? arg_isLooping = (args[0] as bool?); assert(arg_isLooping != null, @@ -192,29 +165,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final double? arg_volume = (args[0] as double?); assert(arg_volume != null, @@ -227,29 +193,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final double? arg_speed = (args[0] as double?); assert(arg_speed != null, @@ -262,29 +221,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -294,29 +246,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -326,29 +271,22 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final int? arg_position = (args[0] as int?); assert(arg_position != null, @@ -361,29 +299,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -393,29 +324,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final bool? arg_mixWithOthers = (args[0] as bool?); assert(arg_mixWithOthers != null, @@ -425,9 +349,8 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } 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 d169c5f16d45..dbd8921b5f9c 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 @@ -49,7 +49,7 @@ abstract class VideoPlayerPlatform extends PlatformInterface { } /// Creates an instance of a video player and returns its textureId. - Future create(DataSource dataSource) { + Future create(DataSource dataSource, VideoViewType viewType) { throw UnimplementedError('create() has not been implemented.'); } @@ -94,7 +94,7 @@ abstract class VideoPlayerPlatform extends PlatformInterface { } /// Returns a widget displaying the video with a given textureID. - Widget buildView(int textureId) { + Widget buildView(int textureId, VideoViewType viewType) { throw UnimplementedError('buildView() has not been implemented.'); } @@ -111,6 +111,12 @@ abstract class VideoPlayerPlatform extends PlatformInterface { class _PlaceholderImplementation extends VideoPlayerPlatform {} +// FIXME Add docs +enum VideoViewType { + textureView, + platformView, +} + /// Description of the data source used to create an instance of /// the video player. class DataSource { From 5d7dfc7791b573ecefbc318e8f8d10a9e4f332d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 18 Nov 2024 16:58:49 +0100 Subject: [PATCH 04/92] Pass video view type to controller in iOS example project --- .../video_player_avfoundation/example/lib/main.dart | 8 +++++++- .../example/lib/mini_controller.dart | 13 ++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index 313ae7f50dc3..a0b73ce3edeb 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -5,6 +5,7 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'mini_controller.dart'; @@ -63,7 +64,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { @override void initState() { super.initState(); - _controller = MiniController.asset('assets/Butterfly-209.mp4'); + _controller = MiniController.asset( + 'assets/Butterfly-209.mp4', + viewType: VideoViewType.platformView, + ); _controller.addListener(() { setState(() {}); @@ -120,6 +124,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + viewType: VideoViewType.platformView, ); _controller.addListener(() { @@ -176,6 +181,7 @@ class _BumbleBeeEncryptedLiveStreamState super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/encrypted_bee.m3u8', + viewType: VideoViewType.textureView, ); _controller.addListener(() { diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index a38013533d62..bf08307e1d8d 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -167,19 +167,19 @@ class MiniController extends ValueNotifier { /// The name of the asset is given by the [dataSource] argument and must not be /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. - MiniController.asset(this.dataSource, {this.package}) + MiniController.asset(this.dataSource, {this.package, required this.viewType}) : dataSourceType = DataSourceType.asset, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. - MiniController.network(this.dataSource) + MiniController.network(this.dataSource, {required this.viewType}) : dataSourceType = DataSourceType.network, package = null, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. - MiniController.file(File file) + MiniController.file(File file, {required this.viewType}) : dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, @@ -196,6 +196,9 @@ class MiniController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; + // FIXME Document + final VideoViewType viewType; + Timer? _timer; Completer? _creatingCompleter; StreamSubscription? _eventSubscription; @@ -239,7 +242,7 @@ class MiniController extends ValueNotifier { ); } - _textureId = (await _platform.create(dataSourceDescription)) ?? + _textureId = (await _platform.create(dataSourceDescription, viewType)) ?? kUninitializedTextureId; _creatingCompleter!.complete(null); final Completer initializingCompleter = Completer(); @@ -422,7 +425,7 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == MiniController.kUninitializedTextureId ? Container() - : _platform.buildView(_textureId); + : _platform.buildView(_textureId, widget.controller.viewType); } } From 0d91c35f7a66cedd4261d1ded95fcd85bf77378e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 18 Nov 2024 19:17:35 +0100 Subject: [PATCH 05/92] Add two FVPVideoPlayer implementations - basic and texture-approach oriented --- .../FVPVideoPlayerPlugin.m | 71 +++++++++++++------ 1 file changed, 49 insertions(+), 22 deletions(-) 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 4caf3c564cb8..1a4a82a47088 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 @@ -89,8 +89,14 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr } - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater { - int64_t textureId = [self.registry registerTexture:player]; - frameUpdater.textureId = textureId; + int64_t textureId; + if (frameUpdater != nil) { + textureId = [self.registry registerTexture:player]; + frameUpdater.textureId = textureId; + } else { + textureId = arc4random(); + } + FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:[NSString stringWithFormat:@"flutter.io/videoPlayer/videoEvents%lld", textureId] @@ -99,9 +105,13 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater player.eventChannel = eventChannel; self.playersByTextureId[@(textureId)] = player; - // Ensure that the first frame is drawn once available, even if the video isn't played, since - // the engine is now expecting the texture to be populated. - [player expectFrame]; + // FIXME Does it need to be here? + if (frameUpdater != nil) { + // Ensure that the first frame is drawn once available, even if the video isn't played, since + // the engine is now expecting the texture to be populated. + // We can safely cast to FVPVideoPlayerTextureApproach since frameUpdater is non-nil. + [(FVPVideoPlayerTextureApproach *)player expectFrame]; + } return textureId; } @@ -122,12 +132,15 @@ - (void)initialize:(FlutterError *__autoreleasing *)error { - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options error:(FlutterError **)error { - FVPFrameUpdater *frameUpdater = [[FVPFrameUpdater alloc] initWithRegistry:_registry]; - FVPDisplayLink *displayLink = - [self.displayLinkFactory displayLinkWithRegistrar:_registrar - callback:^() { - [frameUpdater displayLinkFired]; - }]; + FVPFrameUpdater *frameUpdater; + FVPDisplayLink *displayLink; + if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + frameUpdater = [[FVPFrameUpdater alloc] initWithRegistry:_registry]; + displayLink = [self.displayLinkFactory displayLinkWithRegistrar:_registrar + callback:^() { + [frameUpdater displayLinkFired]; + }]; + } FVPVideoPlayer *player; if (options.asset) { @@ -138,23 +151,36 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options assetPath = [_registrar lookupKeyForAsset:options.asset]; } @try { - player = [[FVPVideoPlayer alloc] initWithAsset:assetPath - frameUpdater:frameUpdater - displayLink:displayLink - avFactory:_avFactory - registrar:self.registrar]; + if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + player = [[FVPVideoPlayerTextureApproach alloc] initWithAsset:assetPath + frameUpdater:frameUpdater + displayLink:displayLink + avFactory:_avFactory + registrar:self.registrar]; + } else { + player = [[FVPVideoPlayer alloc] initWithAsset:assetPath + avFactory:_avFactory + registrar:self.registrar]; + } return @([self onPlayerSetup:player frameUpdater:frameUpdater]); } @catch (NSException *exception) { *error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil]; return nil; } } else if (options.uri) { - player = [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] - frameUpdater:frameUpdater - displayLink:displayLink - httpHeaders:options.httpHeaders - avFactory:_avFactory - registrar:self.registrar]; + if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + player = [[FVPVideoPlayerTextureApproach alloc] initWithURL:[NSURL URLWithString:options.uri] + frameUpdater:frameUpdater + displayLink:displayLink + httpHeaders:options.httpHeaders + avFactory:_avFactory + registrar:self.registrar]; + } else { + player = [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] + httpHeaders:options.httpHeaders + avFactory:_avFactory + registrar:self.registrar]; + } return @([self onPlayerSetup:player frameUpdater:frameUpdater]); } else { *error = [FlutterError errorWithCode:@"video_player" message:@"not implemented" details:nil]; @@ -165,6 +191,7 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options - (void)disposePlayer:(NSInteger)textureId error:(FlutterError **)error { NSNumber *playerKey = @(textureId); FVPVideoPlayer *player = self.playersByTextureId[playerKey]; + // FIXME Only unregister texture when using texture approach [self.registry unregisterTexture:textureId]; [self.playersByTextureId removeObjectForKey:playerKey]; if (!player.disposed) { From 9b688b3d42b0fc486c6321cb4077a60df11970d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 10:49:31 +0100 Subject: [PATCH 06/92] Split iOS native classes into multiple files --- .../FVPNativeVideoView.m | 43 +++++ .../FVPNativeVideoViewFactory.m | 35 ++++ .../FVPVideoPlayer.m | 135 ++------------ .../FVPVideoPlayerPlugin.m | 2 + .../FVPVideoPlayerTextureApproach.m | 164 ++++++++++++++++++ .../FVPNativeVideoView.h | 11 ++ .../FVPNativeVideoViewFactory.h | 13 ++ .../FVPVideoPlayerPlugin.h | 14 -- .../FVPVideoPlayerTextureApproach.h | 33 ++++ 9 files changed, 312 insertions(+), 138 deletions(-) create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m new file mode 100644 index 000000000000..07b269842af5 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m @@ -0,0 +1,43 @@ +#import + +#import "FVPNativeVideoView.h" + +@interface FVPPlayerView : UIView +@end + +@implementation FVPPlayerView ++ (Class)layerClass { + return [AVPlayerLayer class]; +} + +- (void)setPlayer:(AVPlayer *)player { + [(AVPlayerLayer *)[self layer] setPlayer:player]; +} +@end + +@implementation FVPNativeVideoView { + FVPPlayerView *_view; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject *)messenger + player:(AVPlayer *)player { + if (self = [super init]) { + _view = [[FVPPlayerView alloc] init]; + [_view setPlayer:player]; + } + return self; +} + +- (FVPPlayerView *)view { + return _view; +} + +- (void)dealloc { + [_view setPlayer:nil]; + _view = nil; +} + +@end \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m new file mode 100644 index 000000000000..e90f1e2117be --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m @@ -0,0 +1,35 @@ +#import "FVPNativeVideoViewFactory.h" +#import "FVPNativeVideoView.h" +#import "FVPVideoPlayer.h" + +@implementation FVPNativeVideoViewFactory { + NSObject *_messenger; + NSMutableDictionary *_playersByTextureId; +} +- (instancetype)initWithMessenger:(NSObject *)messenger + playersByTextureId: + (NSMutableDictionary *)playersByTextureId { + self = [super init]; + if (self) { + _messenger = messenger; + _playersByTextureId = playersByTextureId; + } + return self; +} + +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + NSNumber *viewIdFromArgs = args[@"viewId"]; + FVPVideoPlayer *player = _playersByTextureId[viewIdFromArgs]; + return [[FVPNativeVideoView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger + player:player.player]; +} + +- (NSObject *)createArgsCodec { + return [FlutterStandardMessageCodec sharedInstance]; +} +@end \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index 61d737b868c4..6201e6ae93b8 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -16,8 +16,6 @@ @implementation FVPVideoPlayer - (instancetype)initWithAsset:(NSString *)asset - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink avFactory:(id)avFactory registrar:(NSObject *)registrar { NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; @@ -29,8 +27,6 @@ - (instancetype)initWithAsset:(NSString *)asset } #endif return [self initWithURL:[NSURL fileURLWithPath:path] - frameUpdater:frameUpdater - displayLink:displayLink httpHeaders:@{} avFactory:avFactory registrar:registrar]; @@ -144,8 +140,6 @@ - (AVMutableVideoComposition *)getVideoCompositionWithTransform:(CGAffineTransfo } - (instancetype)initWithURL:(NSURL *)url - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink httpHeaders:(nonnull NSDictionary *)headers avFactory:(id)avFactory registrar:(NSObject *)registrar { @@ -155,23 +149,16 @@ - (instancetype)initWithURL:(NSURL *)url } AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; - return [self initWithPlayerItem:item - frameUpdater:frameUpdater - displayLink:(FVPDisplayLink *)displayLink - avFactory:avFactory - registrar:registrar]; + return [self initWithPlayerItem:item avFactory:avFactory registrar:registrar]; } - (instancetype)initWithPlayerItem:(AVPlayerItem *)item - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink avFactory:(id)avFactory registrar:(NSObject *)registrar { self = [super init]; NSAssert(self, @"super init cannot be nil"); _registrar = registrar; - _frameUpdater = frameUpdater; AVAsset *asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ @@ -205,22 +192,12 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item _player = [avFactory playerWithPlayerItem:item]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - // This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 - // (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some - // video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An - // invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams - // for issue #1, and restore the correct width and height for issue #2. - _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; - [self.flutterViewLayer addSublayer:_playerLayer]; - // Configure output. - _displayLink = displayLink; NSDictionary *pixBuffAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA), (id)kCVPixelBufferIOSurfacePropertiesKey : @{} }; _videoOutput = [avFactory videoOutputWithPixelBufferAttributes:pixBuffAttributes]; - frameUpdater.videoOutput = _videoOutput; [self addObserversForItem:item player:_player]; @@ -247,7 +224,13 @@ - (void)observeValueForKeyPath:(NSString *)path AVPlayerItem *item = (AVPlayerItem *)object; switch (item.status) { case AVPlayerItemStatusFailed: - [self sendFailedToLoadVideoEvent]; + if (_eventSink != nil) { + _eventSink([FlutterError + errorWithCode:@"VideoError" + message:[@"Failed to load video: " + stringByAppendingString:[item.error localizedDescription]] + details:nil]); + } break; case AVPlayerItemStatusUnknown: break; @@ -297,35 +280,6 @@ - (void)updatePlayingState { } else { [_player pause]; } - // If the texture is still waiting for an expected frame, the display link needs to keep - // running until it arrives regardless of the play/pause state. - _displayLink.running = _isPlaying || self.waitingForFrame; -} - -- (void)sendFailedToLoadVideoEvent { - if (_eventSink == nil) { - return; - } - // Prefer more detailed error information from tracks loading. - NSError *error; - if ([self.player.currentItem.asset statusOfValueForKey:@"tracks" - error:&error] != AVKeyValueStatusFailed) { - error = self.player.currentItem.error; - } - __block NSMutableOrderedSet *details = - [NSMutableOrderedSet orderedSetWithObject:@"Failed to load video"]; - void (^add)(NSString *) = ^(NSString *detail) { - if (detail != nil) { - [details addObject:detail]; - } - }; - NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey]; - add(error.localizedDescription); - add(error.localizedFailureReason); - add(underlyingError.localizedDescription); - add(underlyingError.localizedFailureReason); - NSString *message = [details.array componentsJoinedByString:@": "]; - _eventSink([FlutterError errorWithCode:@"VideoError" message:message details:nil]); } - (void)setupEventSinkIfReadyToPlay { @@ -411,27 +365,12 @@ - (void)seekTo:(int64_t)location completionHandler:(void (^)(BOOL))completionHan toleranceBefore:tolerance toleranceAfter:tolerance completionHandler:^(BOOL completed) { - if (CMTimeCompare(self.player.currentTime, previousCMTime) != 0) { - // Ensure that a frame is drawn once available, even if currently paused. In theory a race - // is possible here where the new frame has already drawn by the time this code runs, and - // the display link stays on indefinitely, but that should be relatively harmless. This - // must use the display link rather than just informing the engine that a new frame is - // available because the seek completing doesn't guarantee that the pixel buffer is - // already available. - [self expectFrame]; - } - if (completionHandler) { completionHandler(completed); } }]; } -- (void)expectFrame { - self.waitingForFrame = YES; - self.displayLink.running = YES; -} - - (void)setIsLooping:(BOOL)isLooping { _isLooping = isLooping; } @@ -464,38 +403,6 @@ - (void)setPlaybackSpeed:(double)speed { _player.rate = speed; } -- (CVPixelBufferRef)copyPixelBuffer { - CVPixelBufferRef buffer = NULL; - CMTime outputItemTime = [_videoOutput itemTimeForHostTime:CACurrentMediaTime()]; - if ([_videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { - buffer = [_videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL]; - } else { - // If the current time isn't available yet, use the time that was checked when informing the - // engine that a frame was available (if any). - CMTime lastAvailableTime = self.frameUpdater.lastKnownAvailableTime; - if (CMTIME_IS_VALID(lastAvailableTime)) { - buffer = [_videoOutput copyPixelBufferForItemTime:lastAvailableTime itemTimeForDisplay:NULL]; - } - } - - if (self.waitingForFrame && buffer) { - self.waitingForFrame = NO; - // If the display link was only running temporarily to pick up a new frame while the video was - // paused, stop it again. - if (!self.isPlaying) { - self.displayLink.running = NO; - } - } - - return buffer; -} - -- (void)onTextureUnregistered:(NSObject *)texture { - dispatch_async(dispatch_get_main_queue(), ^{ - [self dispose]; - }); -} - - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { _eventSink = nil; return nil; @@ -509,17 +416,13 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments // This line ensures the 'initialized' event is sent when the event // 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this function // onListenWithArguments is called) - // and also send error in similar case with 'AVPlayerItemStatusFailed' - // https://github.com/flutter/flutter/issues/151475 - // https://github.com/flutter/flutter/issues/147707 - if (self.player.currentItem.status == AVPlayerItemStatusFailed) { - [self sendFailedToLoadVideoEvent]; - return nil; - } [self setupEventSinkIfReadyToPlay]; return nil; } +/// This method allows you to dispose without touching the event channel. This +/// is useful for the case where the Engine is in the process of deconstruction +/// so the channel is going to die or is already dead. - (void)disposeSansEventChannel { // This check prevents the crash caused by removing the KVO observers twice. // When performing a Hot Restart, the leftover players are disposed once directly @@ -530,8 +433,6 @@ - (void)disposeSansEventChannel { } _disposed = YES; - [_playerLayer removeFromSuperlayer]; - _displayLink = nil; [self removeKeyValueObservers]; [self.player replaceCurrentItemWithPlayerItem:nil]; @@ -543,20 +444,6 @@ - (void)dispose { [_eventChannel setStreamHandler:nil]; } -- (CALayer *)flutterViewLayer { -#if TARGET_OS_OSX - return self.registrar.view.layer; -#else -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // TODO(hellohuanlin): Provide a non-deprecated codepath. See - // https://github.com/flutter/flutter/issues/104117 - UIViewController *root = UIApplication.sharedApplication.keyWindow.rootViewController; -#pragma clang diagnostic pop - return root.view.layer; -#endif -} - /// Removes all key-value observers set up for the player. /// /// This is called from dealloc, so must not use any methods on self. 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 1a4a82a47088..09acddbec7d2 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 @@ -8,8 +8,10 @@ #import "./include/video_player_avfoundation/FVPDisplayLink.h" #import "./include/video_player_avfoundation/FVPFrameUpdater.h" +#import "./include/video_player_avfoundation/FVPNativeVideoViewFactory.h" #import "./include/video_player_avfoundation/FVPVideoPlayer.h" #import "./include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h" +#import "./include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h" #import "./include/video_player_avfoundation/messages.g.h" #if !__has_feature(objc_arc) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m new file mode 100644 index 000000000000..fbf77d323159 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m @@ -0,0 +1,164 @@ +#import "FVPVideoPlayerTextureApproach.h" + +@implementation FVPVideoPlayerTextureApproach +- (instancetype)initWithAsset:(NSString *)asset + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + avFactory:(id)avFactory + registrar:(NSObject *)registrar { + NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; +#if TARGET_OS_OSX + // See https://github.com/flutter/flutter/issues/135302 + // TODO(stuartmorgan): Remove this if the asset APIs are adjusted to work better for macOS. + if (!path) { + path = [NSURL URLWithString:asset relativeToURL:NSBundle.mainBundle.bundleURL].path; + } +#endif + return [self initWithURL:[NSURL fileURLWithPath:path] + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + httpHeaders:@{} + avFactory:avFactory + registrar:registrar]; +} + +- (instancetype)initWithURL:(NSURL *)url + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + httpHeaders:(nonnull NSDictionary *)headers + avFactory:(id)avFactory + registrar:(NSObject *)registrar { + NSDictionary *options = nil; + if ([headers count] != 0) { + options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; + } + AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; + AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; + return [self initWithPlayerItem:item + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + avFactory:avFactory + registrar:registrar]; +} + +- (instancetype)initWithPlayerItem:(AVPlayerItem *)item + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + avFactory:(id)avFactory + registrar:(NSObject *)registrar { + self = [super initWithPlayerItem:item avFactory:avFactory registrar:registrar]; + + if (self) { + _frameUpdater = frameUpdater; + _displayLink = displayLink; + _frameUpdater.videoOutput = self.videoOutput; + + // This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 + // (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some + // video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An + // invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams + // for issue #1, and restore the correct width and height for issue #2. + self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; + [self.flutterViewLayer addSublayer:self.playerLayer]; + } + return self; +} + +- (void)updatePlayingState { + [super updatePlayingState]; + // If the texture is still waiting for an expected frame, the display link needs to keep + // running until it arrives regardless of the play/pause state. + _displayLink.running = self.isPlaying || self.waitingForFrame; +} + +- (void)seekTo:(int64_t)location completionHandler:(void (^)(BOOL))completionHandler { + // FIXME Rethink if it's possible to reuse it (same logic in super class) + CMTime previousCMTime = self.player.currentTime; + CMTime targetCMTime = CMTimeMake(location, 1000); + CMTimeValue duration = self.player.currentItem.asset.duration.value; + // Without adding tolerance when seeking to duration, + // seekToTime will never complete, and this call will hang. + // see issue https://github.com/flutter/flutter/issues/124475. + CMTime tolerance = location == duration ? CMTimeMake(1, 1000) : kCMTimeZero; + [self.player seekToTime:targetCMTime + toleranceBefore:tolerance + toleranceAfter:tolerance + completionHandler:^(BOOL completed) { + if (CMTimeCompare(self.player.currentTime, previousCMTime) != 0) { + // Ensure that a frame is drawn once available, even if currently paused. In theory a + // race is possible here where the new frame has already drawn by the time this code + // runs, and the display link stays on indefinitely, but that should be relatively + // harmless. This must use the display link rather than just informing the engine that a + // new frame is available because the seek completing doesn't guarantee that the pixel + // buffer is already available. + [self expectFrame]; + } + + if (completionHandler) { + completionHandler(completed); + } + }]; +} + +- (void)expectFrame { + self.waitingForFrame = YES; + + self.displayLink.running = YES; +} + +- (CVPixelBufferRef)copyPixelBuffer { + CVPixelBufferRef buffer = NULL; + CMTime outputItemTime = [self.videoOutput itemTimeForHostTime:CACurrentMediaTime()]; + if ([self.videoOutput hasNewPixelBufferForItemTime:outputItemTime]) { + buffer = [self.videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL]; + } else { + // If the current time isn't available yet, use the time that was checked when informing the + // engine that a frame was available (if any). + CMTime lastAvailableTime = self.frameUpdater.lastKnownAvailableTime; + if (CMTIME_IS_VALID(lastAvailableTime)) { + buffer = [self.videoOutput copyPixelBufferForItemTime:lastAvailableTime + itemTimeForDisplay:NULL]; + } + } + + if (self.waitingForFrame && buffer) { + self.waitingForFrame = NO; + // If the display link was only running temporarily to pick up a new frame while the video was + // paused, stop it again. + if (!self.isPlaying) { + self.displayLink.running = NO; + } + } + + return buffer; +} + +- (void)onTextureUnregistered:(NSObject *)texture { + dispatch_async(dispatch_get_main_queue(), ^{ + [self dispose]; + }); +} + +- (void)disposeSansEventChannel { + [super disposeSansEventChannel]; + + [self.playerLayer removeFromSuperlayer]; + + _displayLink = nil; +} + +- (CALayer *)flutterViewLayer { +#if TARGET_OS_OSX + return self.registrar.view.layer; +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(hellohuanlin): Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 + UIViewController *root = UIApplication.sharedApplication.keyWindow.rootViewController; +#pragma clang diagnostic pop + return root.view.layer; +#endif +} + +@end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h new file mode 100644 index 000000000000..299aa17c113e --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h @@ -0,0 +1,11 @@ +#import + +@interface FVPNativeVideoView : NSObject +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject *)messenger + player:(AVPlayer *)player; + +- (UIView *)view; +@end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h new file mode 100644 index 000000000000..a119258efc7c --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h @@ -0,0 +1,13 @@ +#if TARGET_OS_OSX +#import +#else +#import +#endif + +#import "FVPVideoPlayer.h" + +@interface FVPNativeVideoViewFactory : NSObject +- (instancetype)initWithMessenger:(NSObject *)messenger + playersByTextureId: + (NSMutableDictionary *)playersByTextureId; +@end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h index 9e2f30289391..9a2d168337d8 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h @@ -13,17 +13,3 @@ @interface FVPVideoPlayerPlugin : NSObject - (instancetype)initWithRegistrar:(NSObject *)registrar; @end - -@interface FVPNativeVideoViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject *)messenger; -@end - -@interface FVPNativeVideoView : NSObject -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger - player:(AVPlayer *)player; - -- (UIView *)view; -@end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h new file mode 100644 index 000000000000..1b29cb3ece5a --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -0,0 +1,33 @@ +#import "FVPDisplayLink.h" +#import "FVPFrameUpdater.h" +#import "FVPVideoPlayer.h" + +@interface FVPVideoPlayerTextureApproach : FVPVideoPlayer +// The CALayer associated with the Flutter view this plugin is associated with, if any. +@property(nonatomic, readonly) CALayer *flutterViewLayer; +// The updater that drives callbacks to the engine to indicate that a new frame is ready. +@property(nonatomic) FVPFrameUpdater *frameUpdater; +// The display link that drives frameUpdater. +@property(nonatomic) FVPDisplayLink *displayLink; +// Whether a new frame needs to be provided to the engine regardless of the current play/pause state +// (e.g., after a seek while paused). If YES, the display link should continue to run until the next +// frame is successfully provided. +@property(nonatomic, assign) BOOL waitingForFrame; + +- (instancetype)initWithURL:(NSURL *)url + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + httpHeaders:(nonnull NSDictionary *)headers + avFactory:(id)avFactory + registrar:(NSObject *)registrar; + +- (instancetype)initWithAsset:(AVPlayerItem *)item + frameUpdater:(FVPFrameUpdater *)frameUpdater + displayLink:(FVPDisplayLink *)displayLink + avFactory:(id)avFactory + registrar:(NSObject *)registrar; + +// Tells the player to run its frame updater until it receives a frame, regardless of the +// play/pause state. +- (void)expectFrame; +@end From 9b97010e5332b57ac71c2cbfb20c0f49cc47177a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 12:16:31 +0100 Subject: [PATCH 07/92] Only register native view factory for iOS, not MacOS --- .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 7 ++++++- .../video_player_avfoundation/FVPNativeVideoView.h | 12 ++++++++++++ .../FVPNativeVideoView.m | 2 +- .../FVPNativeVideoViewFactory.m | 0 4 files changed, 19 insertions(+), 2 deletions(-) rename packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/{video_player_avfoundation => video_player_avfoundation_ios}/FVPNativeVideoView.m (89%) rename packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/{video_player_avfoundation => video_player_avfoundation_ios}/FVPNativeVideoViewFactory.m (100%) 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 09acddbec7d2..84a6932fee36 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 @@ -8,12 +8,15 @@ #import "./include/video_player_avfoundation/FVPDisplayLink.h" #import "./include/video_player_avfoundation/FVPFrameUpdater.h" -#import "./include/video_player_avfoundation/FVPNativeVideoViewFactory.h" #import "./include/video_player_avfoundation/FVPVideoPlayer.h" #import "./include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h" #import "./include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h" #import "./include/video_player_avfoundation/messages.g.h" +#if TARGET_OS_IOS +#import "./include/video_player_avfoundation/FVPNativeVideoViewFactory.h" +#endif + #if !__has_feature(objc_arc) #error Code Requires ARC. #endif @@ -57,10 +60,12 @@ @implementation FVPVideoPlayerPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FVPVideoPlayerPlugin *instance = [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; +#if TARGET_OS_IOS FVPNativeVideoViewFactory *factory = [[FVPNativeVideoViewFactory alloc] initWithMessenger:registrar.messenger playersByTextureId:instance.playersByTextureId]; [registrar registerViewFactory:factory withId:@"plugins.flutter.dev/video_player_ios"]; +#endif SetUpFVPAVFoundationVideoPlayerApi(registrar.messenger, instance); } diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h index 299aa17c113e..1ed6f019edfa 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h @@ -1,11 +1,23 @@ +#if TARGET_OS_OSX +#import +#else #import +#endif +#if TARGET_OS_OSX +@interface FVPNativeVideoView : NSView +#else @interface FVPNativeVideoView : NSObject +#endif - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject *)messenger player:(AVPlayer *)player; +#if TARGET_OS_OSX +- (NSView *)view; +#else - (UIView *)view; +#endif @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m similarity index 89% rename from packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m rename to packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index 07b269842af5..bb208d7cc213 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -1,6 +1,6 @@ #import -#import "FVPNativeVideoView.h" +#import "../video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h" @interface FVPPlayerView : UIView @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m similarity index 100% rename from packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPNativeVideoViewFactory.m rename to packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m From 26e99d5c880bbf0cd00a69498e54b54b2ea2657d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 12:17:26 +0100 Subject: [PATCH 08/92] Use only textue view on MacOS in example app --- .../video_player_avfoundation/example/lib/main.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index a0b73ce3edeb..72b36df8ed27 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -4,6 +4,8 @@ // ignore_for_file: public_member_api_docs +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -66,7 +68,9 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { super.initState(); _controller = MiniController.asset( 'assets/Butterfly-209.mp4', - viewType: VideoViewType.platformView, + viewType: Platform.isIOS + ? VideoViewType.platformView + : VideoViewType.textureView, ); _controller.addListener(() { @@ -124,7 +128,9 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', - viewType: VideoViewType.platformView, + viewType: Platform.isIOS + ? VideoViewType.platformView + : VideoViewType.textureView, ); _controller.addListener(() { From f3b3320dd6d9f98be7f3cbe9ea8b9fe7462955c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 12:33:54 +0100 Subject: [PATCH 09/92] Refactor imports --- .../FVPVideoPlayerPlugin.m | 2 + .../video_player_avfoundation/messages.g.m | 210 ++++++++++++------ .../FVPDisplayLink.m | 4 +- .../FVPNativeVideoView.m | 2 +- 4 files changed, 143 insertions(+), 75 deletions(-) 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 84a6932fee36..374053bcb1c9 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 @@ -11,6 +11,8 @@ #import "./include/video_player_avfoundation/FVPVideoPlayer.h" #import "./include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h" #import "./include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h" +// Relative path is needed for messages.g.h. See: +// https://github.com/flutter/packages/pull/6675/#discussion_r1591210702 #import "./include/video_player_avfoundation/messages.g.h" #if TARGET_OS_IOS 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 c26cfb0227f4..255fbd8bbbac 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 @@ -4,14 +4,16 @@ // Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import "./include/video_player_avfoundation/messages.g.h" - #if TARGET_OS_OSX #import #else #import #endif +// Relative path is needed for messages.g.h. See: +// https://github.com/flutter/packages/pull/6675/#discussion_r1591210702 +#import "./include/video_player_avfoundation/messages.g.h" + #if !__has_feature(objc_arc) #error File requires ARC to be enabled. #endif @@ -49,12 +51,12 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list; @implementation FVPCreationOptions + (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders - viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType { - FVPCreationOptions* pigeonResult = [[FVPCreationOptions alloc] init]; + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders + viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType { + FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; pigeonResult.asset = asset; pigeonResult.uri = uri; pigeonResult.packageName = packageName; @@ -95,9 +97,11 @@ - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 129: { NSNumber *enumAsNumber = [self readValue]; - return enumAsNumber == nil ? nil : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + return enumAsNumber == nil + ? nil + : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; } - case 130: + case 130: return [FVPCreationOptions fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -137,25 +141,35 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FVPMessagesPigeonCodecReaderWriter *readerWriter = [[FVPMessagesPigeonCodecReaderWriter alloc] init]; + FVPMessagesPigeonCodecReaderWriter *readerWriter = + [[FVPMessagesPigeonCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, NSObject *api) { +void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, + NSObject *api) { SetUpFVPAVFoundationVideoPlayerApiWithSuffix(binaryMessenger, api, @""); } -void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { - messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; +void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.initialize", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(initialize:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); + NSCAssert([api respondsToSelector:@selector(initialize:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api initialize:&error]; @@ -166,13 +180,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.create", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(createWithOptions:error:)", api); + NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(createWithOptions:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FVPCreationOptions *arg_creationOptions = GetNullableObjectAtIndex(args, 0); @@ -185,13 +204,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.dispose", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(disposePlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(disposePlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -204,13 +228,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.setLooping", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:forPlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setLooping:forPlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_isLooping = [GetNullableObjectAtIndex(args, 0) boolValue]; @@ -224,13 +253,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.setVolume", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:forPlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setVolume:forPlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_volume = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -244,13 +278,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.setPlaybackSpeed", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:forPlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setPlaybackSpeed:forPlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_speed = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -264,13 +303,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.play", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(playPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(playPlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -283,13 +327,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.getPosition", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(positionForPlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(positionForPlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -302,33 +351,45 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.seekTo", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:forPlayer:completion:)", api); + NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(seekTo:forPlayer:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_position = [GetNullableObjectAtIndex(args, 0) integerValue]; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 1) integerValue]; - [api seekTo:arg_position forPlayer:arg_textureId completion:^(FlutterError *_Nullable error) { - callback(wrapResult(nil, error)); - }]; + [api seekTo:arg_position + forPlayer:arg_textureId + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.pause", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(pausePlayer:error:)", api); + NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(pausePlayer:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -341,13 +402,18 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers", messageChannelSuffix] + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.setMixWithOthers", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", api); + NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setMixWithOthers:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_mixWithOthers = [GetNullableObjectAtIndex(args, 0) boolValue]; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m index 9bdb321ae164..c3921496a353 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "../video_player_avfoundation/include/video_player_avfoundation/FVPDisplayLink.h" - #import #import +#import "FVPDisplayLink.h" + /// A proxy object to act as a CADisplayLink target, to avoid retain loops, since FVPDisplayLink /// owns its CADisplayLink, but CADisplayLink retains its target. @interface FVPDisplayLinkTarget : NSObject diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index bb208d7cc213..07b269842af5 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -1,6 +1,6 @@ #import -#import "../video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h" +#import "FVPNativeVideoView.h" @interface FVPPlayerView : UIView @end From 08e46fe1d6124644a80de2443c9d7b57808770c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 13:20:07 +0100 Subject: [PATCH 10/92] Allow choosing between texture view and platform view in example app --- .../example/lib/main.dart | 98 ++++++++++++++++--- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index 72b36df8ed27..e043e05b8822 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -4,8 +4,6 @@ // ignore_for_file: public_member_api_docs -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -45,9 +43,18 @@ class _App extends StatelessWidget { ), body: TabBarView( children: [ - _BumbleBeeRemoteVideo(), - _BumbleBeeEncryptedLiveStream(), - _ButterFlyAssetVideo(), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeRemoteVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeEncryptedLiveStream(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _ButterFlyAssetVideo(viewType), + ), ], ), ), @@ -55,7 +62,70 @@ class _App extends StatelessWidget { } } +class _ViewTypeTabBar extends StatefulWidget { + const _ViewTypeTabBar({ + required this.builder, + }); + + final Widget Function(VideoViewType) builder; + + @override + State<_ViewTypeTabBar> createState() => _ViewTypeTabBarState(); +} + +class _ViewTypeTabBarState extends State<_ViewTypeTabBar> + with SingleTickerProviderStateMixin { + late final TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TabBar( + controller: _tabController, + isScrollable: true, + tabs: const [ + Tab( + icon: Icon(Icons.texture), + text: 'Texture view', + ), + Tab( + icon: Icon(Icons.construction), + text: 'Platform view', + ), + ], + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + widget.builder(VideoViewType.textureView), + widget.builder(VideoViewType.platformView), + ], + ), + ), + ], + ); + } +} + class _ButterFlyAssetVideo extends StatefulWidget { + const _ButterFlyAssetVideo(this.viewType); + + final VideoViewType viewType; + @override _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); } @@ -68,9 +138,7 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { super.initState(); _controller = MiniController.asset( 'assets/Butterfly-209.mp4', - viewType: Platform.isIOS - ? VideoViewType.platformView - : VideoViewType.textureView, + viewType: widget.viewType, ); _controller.addListener(() { @@ -116,6 +184,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { } class _BumbleBeeRemoteVideo extends StatefulWidget { + const _BumbleBeeRemoteVideo(this.viewType); + + final VideoViewType viewType; + @override _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); } @@ -128,9 +200,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', - viewType: Platform.isIOS - ? VideoViewType.platformView - : VideoViewType.textureView, + viewType: widget.viewType, ); _controller.addListener(() { @@ -173,6 +243,10 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { } class _BumbleBeeEncryptedLiveStream extends StatefulWidget { + const _BumbleBeeEncryptedLiveStream(this.viewType); + + final VideoViewType viewType; + @override _BumbleBeeEncryptedLiveStreamState createState() => _BumbleBeeEncryptedLiveStreamState(); @@ -187,7 +261,7 @@ class _BumbleBeeEncryptedLiveStreamState super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/encrypted_bee.m3u8', - viewType: VideoViewType.textureView, + viewType: widget.viewType, ); _controller.addListener(() { From 28b61a9c0a8d52cf5cad67777d69cbd40be8997c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 13:38:08 +0100 Subject: [PATCH 11/92] Remove redundant import --- .../include/video_player_avfoundation/FVPVideoPlayerPlugin.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h index 9a2d168337d8..b64a173fe0cd 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin.h @@ -8,8 +8,6 @@ #import #endif -#import - @interface FVPVideoPlayerPlugin : NSObject - (instancetype)initWithRegistrar:(NSObject *)registrar; @end From 8d86527ea4e9028b170b3408f143f5af20b129b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 13:38:24 +0100 Subject: [PATCH 12/92] Add some fixme comments for later --- .../include/video_player_avfoundation/FVPNativeVideoView.h | 3 +++ .../video_player_avfoundation/FVPVideoPlayerTextureApproach.h | 1 + .../Sources/video_player_avfoundation_ios/FVPNativeVideoView.m | 1 + 3 files changed, 5 insertions(+) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h index 1ed6f019edfa..d969355da0dc 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h @@ -4,6 +4,9 @@ #import #endif +// FIXME Do we even need ifs below? MacOS currently doesn't support FlutterPlatformView. +// But can we complie code without it? And is it good to put iOS-specific code in the main +// directory (video_player_avfoundation? #if TARGET_OS_OSX @interface FVPNativeVideoView : NSView #else diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index 1b29cb3ece5a..aa6f55b18a90 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -2,6 +2,7 @@ #import "FVPFrameUpdater.h" #import "FVPVideoPlayer.h" +// FIXME Add docs - what functionality this class adds. @interface FVPVideoPlayerTextureApproach : FVPVideoPlayer // The CALayer associated with the Flutter view this plugin is associated with, if any. @property(nonatomic, readonly) CALayer *flutterViewLayer; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index 07b269842af5..c40331700a38 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -2,6 +2,7 @@ #import "FVPNativeVideoView.h" +// FIXME Add some docs for FVPNativeVideoView and possibly FVPPlayerView @interface FVPPlayerView : UIView @end From 643caa8a67e0ae7aa08c4232d10097d9a6c95f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 13:40:05 +0100 Subject: [PATCH 13/92] Add copyright to each native iOS file --- .../video_player_avfoundation/FVPVideoPlayerTextureApproach.m | 4 ++++ .../include/video_player_avfoundation/FVPNativeVideoView.h | 4 ++++ .../video_player_avfoundation/FVPNativeVideoViewFactory.h | 4 ++++ .../video_player_avfoundation/FVPVideoPlayerTextureApproach.h | 4 ++++ .../video_player_avfoundation_ios/FVPNativeVideoView.m | 4 ++++ .../video_player_avfoundation_ios/FVPNativeVideoViewFactory.m | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m index fbf77d323159..c94cb556edbb 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerTextureApproach.m @@ -1,3 +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. + #import "FVPVideoPlayerTextureApproach.h" @implementation FVPVideoPlayerTextureApproach diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h index d969355da0dc..871ac36fcfa3 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h @@ -1,3 +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. + #if TARGET_OS_OSX #import #else diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h index a119258efc7c..64fb7c7a1f81 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h @@ -1,3 +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. + #if TARGET_OS_OSX #import #else diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index aa6f55b18a90..bfad965a4a34 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -1,3 +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. + #import "FVPDisplayLink.h" #import "FVPFrameUpdater.h" #import "FVPVideoPlayer.h" diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index c40331700a38..f8050466ec32 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -1,3 +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. + #import #import "FVPNativeVideoView.h" diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index e90f1e2117be..741179f2e8e7 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -1,3 +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. + #import "FVPNativeVideoViewFactory.h" #import "FVPNativeVideoView.h" #import "FVPVideoPlayer.h" From 8acf58e44b99404d470c91972adac0f8a5029805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 13:45:06 +0100 Subject: [PATCH 14/92] Rename viewId to playerId in params --- .../FVPNativeVideoViewFactory.m | 2 +- .../lib/src/avfoundation_video_player.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index 741179f2e8e7..be036f43296b 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -24,7 +24,7 @@ - (instancetype)initWithMessenger:(NSObject *)messenger - (NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args { - NSNumber *viewIdFromArgs = args[@"viewId"]; + NSNumber *viewIdFromArgs = args[@"playerId"]; FVPVideoPlayer *player = _playersByTextureId[viewIdFromArgs]; return [[FVPNativeVideoView alloc] initWithFrame:frame viewIdentifier:viewId 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 5f2f60c8e323..f1fd6fb42740 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 @@ -25,6 +25,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return _api.initialize(); } + // FIXME Rename textureId everywhere to playerId. @override Future dispose(int textureId) { return _api.dispose(textureId); @@ -156,11 +157,10 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { }; } - Widget _buildPlatformView(int viewId, VideoViewType viewType) { + Widget _buildPlatformView(int playerId, VideoViewType viewType) { // FIXME Use a separate class for creation params. - final Map creationParams = { - 'viewId': viewId, + 'playerId': playerId, }; return IgnorePointer( // IgnorePointer so that GestureDetector can be used above the platform view. From faaba934318fcadd05fde61d378b99b379cdfc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 14:36:54 +0100 Subject: [PATCH 15/92] Unregister texture from registry only when using texture approach --- .../Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 374053bcb1c9..8001a17554bf 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 @@ -200,8 +200,9 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options - (void)disposePlayer:(NSInteger)textureId error:(FlutterError **)error { NSNumber *playerKey = @(textureId); FVPVideoPlayer *player = self.playersByTextureId[playerKey]; - // FIXME Only unregister texture when using texture approach - [self.registry unregisterTexture:textureId]; + if ([player isKindOfClass:[FVPVideoPlayerTextureApproach class]]) { + [self.registry unregisterTexture:textureId]; + } [self.playersByTextureId removeObjectForKey:playerKey]; if (!player.disposed) { [player dispose]; From 122fd4a0c96aa6f175c88756b7ac94d4ed38ec3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 14:37:26 +0100 Subject: [PATCH 16/92] Clean up code, add more context in comments --- .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 8001a17554bf..a321195084cb 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 @@ -16,6 +16,7 @@ #import "./include/video_player_avfoundation/messages.g.h" #if TARGET_OS_IOS +// Platform views are only supported on iOS as of now. #import "./include/video_player_avfoundation/FVPNativeVideoViewFactory.h" #endif @@ -63,6 +64,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FVPVideoPlayerPlugin *instance = [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; #if TARGET_OS_IOS + // Platform views are only supported on iOS as of now. FVPNativeVideoViewFactory *factory = [[FVPNativeVideoViewFactory alloc] initWithMessenger:registrar.messenger playersByTextureId:instance.playersByTextureId]; @@ -98,11 +100,14 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr } - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater { + // FIXME Rename textureId to playerId, in all other places as well. int64_t textureId; - if (frameUpdater != nil) { + if (frameUpdater) { textureId = [self.registry registerTexture:player]; frameUpdater.textureId = textureId; } else { + // FIXME Possibly start with a predefined prefix and then increment it to avoid + // collisions withtextureId. textureId = arc4random(); } @@ -115,7 +120,7 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater self.playersByTextureId[@(textureId)] = player; // FIXME Does it need to be here? - if (frameUpdater != nil) { + if (frameUpdater) { // Ensure that the first frame is drawn once available, even if the video isn't played, since // the engine is now expecting the texture to be populated. // We can safely cast to FVPVideoPlayerTextureApproach since frameUpdater is non-nil. From e2b60931ff93735625675403b0b330f88c0e42a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 17:27:57 +0100 Subject: [PATCH 17/92] Fix most of existing unit tests by creating a player with texture view approach, like it happened up to this point --- .../darwin/RunnerTests/VideoPlayerTests.m | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index 02b9199a74fa..036897a213ff 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -179,7 +179,9 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureId); @@ -217,7 +219,9 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -283,7 +287,9 @@ - (void)testInitStartsDisplayLinkTemporarily { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -333,7 +339,9 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -395,7 +403,9 @@ - (void)testPauseWhileWaitingForFrameDoesNotStopDisplayLink { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -423,7 +433,9 @@ - (void)testDeregistersFromPlayer { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureId); @@ -455,7 +467,9 @@ - (void)testBufferingStateFromPlayer { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureId); @@ -564,7 +578,9 @@ - (void)testSeekToleranceWhenNotSeekingToEnd { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [pluginWithMockAVPlayer createWithOptions:create error:&createError]; @@ -602,7 +618,9 @@ - (void)testSeekToleranceWhenSeekingToEnd { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; FlutterError *createError; NSNumber *textureId = [pluginWithMockAVPlayer createWithOptions:create error:&createError]; @@ -625,11 +643,14 @@ - (void)testSeekToleranceWhenSeekingToEnd { [videoPlayerPlugin initialize:&error]; XCTAssertNil(error); - FVPCreationOptions *create = [FVPCreationOptions makeWithAsset:nil - uri:uri - packageName:nil - formatHint:nil - httpHeaders:@{}]; + FVPCreationOptions *create = + [FVPCreationOptions makeWithAsset:nil + uri:uri + packageName:nil + formatHint:nil + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; @@ -694,7 +715,9 @@ - (void)testDoesNotCrashOnRateObservationAfterDisposal { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureId); @@ -748,7 +771,9 @@ - (void)testHotReloadDoesNotCrash { uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" packageName:nil formatHint:nil - httpHeaders:@{}]; + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypeTextureView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureId); @@ -782,6 +807,8 @@ - (void)testPublishesInRegistration { NSObject *registry = GetPluginRegistry(); NSObject *registrar = [registry registrarForPlugin:pluginKey]; + // FIXME This line makes the test fail (or rather run forever) if native view factory is + // registered. [FVPVideoPlayerPlugin registerWithRegistrar:registrar]; id publishedValue = [registry valuePublishedByPlugin:pluginKey]; From 60892bf606553fe8fce601bcb74462d1185825a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 19 Nov 2024 18:18:15 +0100 Subject: [PATCH 18/92] Check platform view in UI test --- .../ios/RunnerUITests/VideoPlayerUITests.m | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 54c97030c3ae..778caa0c3088 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -21,30 +21,39 @@ - (void)setUp { - (void)testPlayVideo { XCUIApplication *app = self.app; - - XCUIElement *remoteTab = [app.otherElements - elementMatchingPredicate:[NSPredicate predicateWithFormat:@"selected == YES"]]; + XCUIElement *remoteTab = + [[app.otherElements matchingPredicate:[NSPredicate predicateWithFormat:@"selected == YES"]] + elementBoundByIndex:0]; XCTAssertTrue([remoteTab waitForExistenceWithTimeout:30.0]); XCTAssertTrue([remoteTab.label containsString:@"Remote"]); - XCUIElement *playButton = app.staticTexts[@"Play"]; - XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); - [playButton tap]; + // FIXME Is this a good way to check platform view? + for (NSString *tabName in @[ @"Platform view", @"Texture view" ]) { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; + XCUIElement *viewTypeTab = [app.staticTexts elementMatchingPredicate:predicate]; + XCTAssertTrue([viewTypeTab waitForExistenceWithTimeout:30.0]); + XCTAssertFalse(viewTypeTab.isSelected); + [viewTypeTab tap]; + + XCUIElement *playButton = app.staticTexts[@"Play"]; + XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); + [playButton tap]; - NSPredicate *find1xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '1.0x'"]; - XCUIElement *playbackSpeed1x = [app.staticTexts elementMatchingPredicate:find1xButton]; - BOOL foundPlaybackSpeed1x = [playbackSpeed1x waitForExistenceWithTimeout:30.0]; - XCTAssertTrue(foundPlaybackSpeed1x); - [playbackSpeed1x tap]; + NSPredicate *find1xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '1.0x'"]; + XCUIElement *playbackSpeed1x = [app.staticTexts elementMatchingPredicate:find1xButton]; + BOOL foundPlaybackSpeed1x = [playbackSpeed1x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed1x); + [playbackSpeed1x tap]; - XCUIElement *playbackSpeed5xButton = app.buttons[@"5.0x"]; - XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]); - [playbackSpeed5xButton tap]; + XCUIElement *playbackSpeed5xButton = app.buttons[@"5.0x"]; + XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]); + [playbackSpeed5xButton tap]; - NSPredicate *find5xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '5.0x'"]; - XCUIElement *playbackSpeed5x = [app.staticTexts elementMatchingPredicate:find5xButton]; - BOOL foundPlaybackSpeed5x = [playbackSpeed5x waitForExistenceWithTimeout:30.0]; - XCTAssertTrue(foundPlaybackSpeed5x); + NSPredicate *find5xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '5.0x'"]; + XCUIElement *playbackSpeed5x = [app.staticTexts elementMatchingPredicate:find5xButton]; + BOOL foundPlaybackSpeed5x = [playbackSpeed5x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed5x); + } // Cycle through tabs. for (NSString *tabName in @[ @"Asset mp4", @"Remote mp4" ]) { From e3f7c86bca2ea7635c29cf24804abd813e87dfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 10:32:33 +0100 Subject: [PATCH 19/92] Only allow choosing between texture view and platform view in example on iOS --- .../example/lib/main.dart | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index e043e05b8822..d195f91a9b59 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -4,6 +4,8 @@ // ignore_for_file: public_member_api_docs +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -42,20 +44,27 @@ class _App extends StatelessWidget { ), ), body: TabBarView( - children: [ - _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _BumbleBeeRemoteVideo(viewType), - ), - _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _BumbleBeeEncryptedLiveStream(viewType), - ), - _ViewTypeTabBar( - builder: (VideoViewType viewType) => - _ButterFlyAssetVideo(viewType), - ), - ], + children: Platform.isIOS + ? [ + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeRemoteVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeEncryptedLiveStream(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _ButterFlyAssetVideo(viewType), + ), + ] + // We don't support platform views on MacOS yet. + : const [ + _BumbleBeeRemoteVideo(VideoViewType.textureView), + _BumbleBeeEncryptedLiveStream(VideoViewType.textureView), + _ButterFlyAssetVideo(VideoViewType.textureView), + ], ), ), ); From 612d9cbe85f6a94c26ec3796fe4c307f196156c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 10:59:20 +0100 Subject: [PATCH 20/92] Only include native view files on iOS --- .../darwin/video_player_avfoundation.podspec | 1 + .../FVPVideoPlayerPlugin.m | 2 +- .../FVPNativeVideoView.m | 2 +- .../FVPNativeVideoViewFactory.m | 7 ++++--- .../include}/FVPNativeVideoView.h | 20 +++++-------------- .../include}/FVPNativeVideoViewFactory.h | 4 ---- 6 files changed, 12 insertions(+), 24 deletions(-) rename packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/{video_player_avfoundation/include/video_player_avfoundation => video_player_avfoundation_ios/include}/FVPNativeVideoView.h (63%) rename packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/{video_player_avfoundation/include/video_player_avfoundation => video_player_avfoundation_ios/include}/FVPNativeVideoViewFactory.h (88%) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec index 81e8d2f8e4ef..94281a6dc0d7 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec @@ -18,6 +18,7 @@ Downloaded by pub (not CocoaPods). s.ios.source_files = 'video_player_avfoundation/Sources/video_player_avfoundation_ios/*' s.osx.source_files = 'video_player_avfoundation/Sources/video_player_avfoundation_macos/*' s.public_header_files = 'video_player_avfoundation/Sources/video_player_avfoundation/include/**/*.h' + s.ios.public_header_files = 'video_player_avfoundation/Sources/video_player_avfoundation_ios/include/*.h' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' 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 a321195084cb..7da65f029304 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 @@ -17,7 +17,7 @@ #if TARGET_OS_IOS // Platform views are only supported on iOS as of now. -#import "./include/video_player_avfoundation/FVPNativeVideoViewFactory.h" +#import "../video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h" #endif #if !__has_feature(objc_arc) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index f8050466ec32..1ad0994b864a 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -4,7 +4,7 @@ #import -#import "FVPNativeVideoView.h" +#import "./include/FVPNativeVideoView.h" // FIXME Add some docs for FVPNativeVideoView and possibly FVPPlayerView @interface FVPPlayerView : UIView diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index be036f43296b..fbc327dd354e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FVPNativeVideoViewFactory.h" -#import "FVPNativeVideoView.h" -#import "FVPVideoPlayer.h" +#import "../video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h" + +#import "./include/FVPNativeVideoView.h" +#import "./include/FVPNativeVideoViewFactory.h" @implementation FVPNativeVideoViewFactory { NSObject *_messenger; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h similarity index 63% rename from packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h rename to packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h index 871ac36fcfa3..daba6464189f 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h @@ -2,29 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if TARGET_OS_OSX -#import -#else +// FIXME Do we even need if statement below? MacOS currently doesn't support +// FlutterPlatformView. +// But can we complie code without it? And is it good to put iOS-specific code +// in the main directory (video_player_avfoundation? + #import -#endif -// FIXME Do we even need ifs below? MacOS currently doesn't support FlutterPlatformView. -// But can we complie code without it? And is it good to put iOS-specific code in the main -// directory (video_player_avfoundation? -#if TARGET_OS_OSX -@interface FVPNativeVideoView : NSView -#else @interface FVPNativeVideoView : NSObject -#endif - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject *)messenger player:(AVPlayer *)player; -#if TARGET_OS_OSX -- (NSView *)view; -#else - (UIView *)view; -#endif @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h similarity index 88% rename from packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h rename to packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h index 64fb7c7a1f81..fe04fd2967fb 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPNativeVideoViewFactory.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h @@ -2,11 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if TARGET_OS_OSX -#import -#else #import -#endif #import "FVPVideoPlayer.h" From f29b3cda998ff388ea4c179f5b258c60b7a6e89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 13:05:35 +0100 Subject: [PATCH 21/92] Make comments less ambiguous --- .../Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7da65f029304..ab3bb746fa94 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 @@ -16,7 +16,7 @@ #import "./include/video_player_avfoundation/messages.g.h" #if TARGET_OS_IOS -// Platform views are only supported on iOS as of now. +// We only support platform views on iOS as of now. #import "../video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h" #endif @@ -64,7 +64,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FVPVideoPlayerPlugin *instance = [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; #if TARGET_OS_IOS - // Platform views are only supported on iOS as of now. + // We only support platform views on iOS as of now. FVPNativeVideoViewFactory *factory = [[FVPNativeVideoViewFactory alloc] initWithMessenger:registrar.messenger playersByTextureId:instance.playersByTextureId]; From 491d294eedd200bee1c409c25c2dfca21f76aed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 13:07:05 +0100 Subject: [PATCH 22/92] Add unit tests for factory registration and lack of texture registration for platform view --- .../darwin/RunnerTests/VideoPlayerTests.m | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index 036897a213ff..afa3eaeb4cb3 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -192,6 +192,42 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom XCTAssertNotNil(player.playerLayer.superlayer, @"AVPlayerLayer should be added on screen."); } +- (void)testPlayerForPlatformViewDoesNotRegisterTexture { + NSObject *mockTextureRegistry = + OCMProtocolMock(@protocol(FlutterTextureRegistry)); + NSObject *registrar = + [GetPluginRegistry() registrarForPlugin:@"testPlayerTypeForPlatformView"]; + NSObject *partialRegistrar = OCMPartialMock(registrar); + OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); + FVPDisplayLink *mockDisplayLink = + OCMPartialMock([[FVPDisplayLink alloc] initWithRegistrar:registrar + callback:^(){ + }]); + StubFVPDisplayLinkFactory *stubDisplayLinkFactory = + [[StubFVPDisplayLinkFactory alloc] initWithDisplayLink:mockDisplayLink]; + AVPlayerItemVideoOutput *mockVideoOutput = OCMPartialMock([[AVPlayerItemVideoOutput alloc] init]); + FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc] + initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:mockVideoOutput] + displayLinkFactory:stubDisplayLinkFactory + registrar:partialRegistrar]; + + FlutterError *initalizationError; + [videoPlayerPlugin initialize:&initalizationError]; + XCTAssertNil(initalizationError); + FVPCreationOptions *create = [FVPCreationOptions + makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + packageName:nil + formatHint:nil + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypePlatformView]]; + FlutterError *createError; + [videoPlayerPlugin createWithOptions:create error:&createError]; + + OCMVerify(never(), [mockTextureRegistry registerTexture:[OCMArg any]]); +} + - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily { NSObject *mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); @@ -802,6 +838,21 @@ - (void)testHotReloadDoesNotCrash { handler:nil]; // No assertions needed. Lack of crash is a success. } +#if TARGET_OS_IOS +- (void)testNativeVideoViewFactoryRegistration { + NSObject *registrar = + [GetPluginRegistry() registrarForPlugin:@"testNativeVideoViewFactoryRegistration"]; + id mockRegistrar = OCMPartialMock(registrar); + + OCMExpect([mockRegistrar + registerViewFactory:[OCMArg isKindOfClass:[FVPNativeVideoViewFactory class]] + withId:@"plugins.flutter.dev/video_player_ios"]); + [FVPVideoPlayerPlugin registerWithRegistrar:mockRegistrar]; + + OCMVerifyAll(mockRegistrar); +} +#endif + - (void)testPublishesInRegistration { NSString *pluginKey = @"TestRegistration"; NSObject *registry = GetPluginRegistry(); From f7cd1e8940781ef84f4484211e0faa5c0f60c61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 13:21:05 +0100 Subject: [PATCH 23/92] Make viewType an optional parameter and default to textureView --- .../example/lib/mini_controller.dart | 24 ++++++++++++------- .../lib/src/avfoundation_video_player.dart | 6 ++--- .../lib/video_player_platform_interface.dart | 5 +++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index bf08307e1d8d..9150255ac3fd 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -167,20 +167,27 @@ class MiniController extends ValueNotifier { /// The name of the asset is given by the [dataSource] argument and must not be /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. - MiniController.asset(this.dataSource, {this.package, required this.viewType}) - : dataSourceType = DataSourceType.asset, + MiniController.asset( + this.dataSource, { + this.package, + this.viewType = VideoViewType.textureView, + }) : dataSourceType = DataSourceType.asset, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. - MiniController.network(this.dataSource, {required this.viewType}) - : dataSourceType = DataSourceType.network, + MiniController.network( + this.dataSource, { + this.viewType = VideoViewType.textureView, + }) : dataSourceType = DataSourceType.network, package = null, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. - MiniController.file(File file, {required this.viewType}) - : dataSource = Uri.file(file.absolute.path).toString(), + MiniController.file( + File file, { + this.viewType = VideoViewType.textureView, + }) : dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, super(const VideoPlayerValue(duration: Duration.zero)); @@ -242,8 +249,9 @@ class MiniController extends ValueNotifier { ); } - _textureId = (await _platform.create(dataSourceDescription, viewType)) ?? - kUninitializedTextureId; + _textureId = + (await _platform.create(dataSourceDescription, viewType: viewType)) ?? + kUninitializedTextureId; _creatingCompleter!.complete(null); final Completer initializingCompleter = Completer(); 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 f1fd6fb42740..43186863ca20 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 @@ -33,9 +33,9 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future create( - DataSource dataSource, - VideoViewType viewType, - ) async { + DataSource dataSource, { + VideoViewType viewType = VideoViewType.textureView, + }) async { String? asset; String? packageName; String? uri; 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 dbd8921b5f9c..8cd94f4e8296 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 @@ -49,7 +49,10 @@ abstract class VideoPlayerPlatform extends PlatformInterface { } /// Creates an instance of a video player and returns its textureId. - Future create(DataSource dataSource, VideoViewType viewType) { + Future create( + DataSource dataSource, { + VideoViewType viewType = VideoViewType.textureView, + }) { throw UnimplementedError('create() has not been implemented.'); } From b76c721ae7aad72457179d49172075f7969bbd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 13:42:41 +0100 Subject: [PATCH 24/92] Document new classes in native iOS code --- .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 1 - .../FVPVideoPlayerTextureApproach.h | 4 +++- .../video_player_avfoundation_ios/FVPNativeVideoView.m | 1 - .../include/FVPNativeVideoView.h | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) 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 ab3bb746fa94..115c7bdc03e0 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 @@ -119,7 +119,6 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater player.eventChannel = eventChannel; self.playersByTextureId[@(textureId)] = player; - // FIXME Does it need to be here? if (frameUpdater) { // Ensure that the first frame is drawn once available, even if the video isn't played, since // the engine is now expecting the texture to be populated. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index bfad965a4a34..96d926e941fa 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -6,7 +6,9 @@ #import "FVPFrameUpdater.h" #import "FVPVideoPlayer.h" -// FIXME Add docs - what functionality this class adds. +/// A subclass of FVPVideoPlayer that adds functionality related to texture-based view as a way of +/// displaying the video in the app. It manages the CALayer associated with the Flutter view, +/// updates frames, and handles display link callbacks. @interface FVPVideoPlayerTextureApproach : FVPVideoPlayer // The CALayer associated with the Flutter view this plugin is associated with, if any. @property(nonatomic, readonly) CALayer *flutterViewLayer; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index 1ad0994b864a..771aa8b1a826 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -6,7 +6,6 @@ #import "./include/FVPNativeVideoView.h" -// FIXME Add some docs for FVPNativeVideoView and possibly FVPPlayerView @interface FVPPlayerView : UIView @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h index daba6464189f..f850c5346e64 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// FIXME Do we even need if statement below? MacOS currently doesn't support -// FlutterPlatformView. -// But can we complie code without it? And is it good to put iOS-specific code -// in the main directory (video_player_avfoundation? - #import +/// A class used to create a native video view that can be embedded in a Flutter app. +/// This class wraps an AVPlayer instance and displays its video content. @interface FVPNativeVideoView : NSObject +/// Initializes a new instance of a native view. +/// It creates a video view instance and sets the provided AVPlayer instance to it. - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject *)messenger player:(AVPlayer *)player; +/// Returns the native UIView that displays the video content. - (UIView *)view; @end From 11d1a857570b0199ab4a4031a187ca18d6d08294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 14:00:49 +0100 Subject: [PATCH 25/92] Document VideoViewType on Dart side --- .../example/lib/mini_controller.dart | 2 +- .../lib/video_player_platform_interface.dart | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 9150255ac3fd..e00929610f8c 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -203,7 +203,7 @@ class MiniController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; - // FIXME Document + /// The type of view used to display the video. final VideoViewType viewType; Timer? _timer; 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 8cd94f4e8296..7b080e3edcfa 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 @@ -114,9 +114,12 @@ abstract class VideoPlayerPlatform extends PlatformInterface { class _PlaceholderImplementation extends VideoPlayerPlatform {} -// FIXME Add docs +/// Enum representing the type of video view to be used. enum VideoViewType { + /// Uses a texture view for rendering video. textureView, + + /// Uses a platform view for rendering video. platformView, } From 6610d898687a9f6f45f60784e912be88f2db4591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 14:45:50 +0100 Subject: [PATCH 26/92] Fix variable name --- .../video_player_avfoundation_ios/FVPNativeVideoViewFactory.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index fbc327dd354e..01f715c21a03 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -25,8 +25,8 @@ - (instancetype)initWithMessenger:(NSObject *)messenger - (NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args { - NSNumber *viewIdFromArgs = args[@"playerId"]; - FVPVideoPlayer *player = _playersByTextureId[viewIdFromArgs]; + NSNumber *playerId = args[@"playerId"]; + FVPVideoPlayer *player = _playersByTextureId[playerId]; return [[FVPNativeVideoView alloc] initWithFrame:frame viewIdentifier:viewId arguments:args From 16fe8ddda9db6b709bd85ff35f5052f61eaf1657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 17:05:12 +0100 Subject: [PATCH 27/92] Format files generated by pigeon --- .../video_player_avfoundation/messages.g.m | 6 +- .../lib/src/messages.g.dart | 111 +++++--- .../test/test_api.g.dart | 253 ++++++++++++------ 3 files changed, 238 insertions(+), 132 deletions(-) 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 255fbd8bbbac..5b238679a22f 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 @@ -4,16 +4,14 @@ // Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon +#import "./include/video_player_avfoundation/messages.g.h" + #if TARGET_OS_OSX #import #else #import #endif -// Relative path is needed for messages.g.h. See: -// https://github.com/flutter/packages/pull/6675/#discussion_r1591210702 -#import "./include/video_player_avfoundation/messages.g.h" - #if !__has_feature(objc_arc) #error File requires ARC to be enabled. #endif 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 39c988dec634..3775c094e02c 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 @@ -18,7 +18,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -74,13 +75,13 @@ class CreationOptions { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: (result[4] as Map?)!.cast(), + httpHeaders: + (result[4] as Map?)!.cast(), viewType: result[5] as PlatformVideoViewType?, ); } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -88,10 +89,10 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is CreationOptions) { + } else if (value is CreationOptions) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -102,10 +103,10 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; - case 130: + case 130: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -117,9 +118,11 @@ class AVFoundationVideoPlayerApi { /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AVFoundationVideoPlayerApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -127,8 +130,10 @@ class AVFoundationVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -149,14 +154,16 @@ class AVFoundationVideoPlayerApi { } Future create(CreationOptions creationOptions) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([creationOptions]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([creationOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -176,8 +183,10 @@ class AVFoundationVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -198,14 +207,16 @@ class AVFoundationVideoPlayerApi { } Future setLooping(bool isLooping, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([isLooping, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([isLooping, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -220,14 +231,16 @@ class AVFoundationVideoPlayerApi { } Future setVolume(double volume, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([volume, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([volume, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -242,14 +255,16 @@ class AVFoundationVideoPlayerApi { } Future setPlaybackSpeed(double speed, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([speed, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([speed, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -264,8 +279,10 @@ class AVFoundationVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -286,8 +303,10 @@ class AVFoundationVideoPlayerApi { } Future getPosition(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -313,14 +332,16 @@ class AVFoundationVideoPlayerApi { } Future seekTo(int position, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([position, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([position, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -335,8 +356,10 @@ class AVFoundationVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -357,14 +380,16 @@ class AVFoundationVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { 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 b888c96aa981..c65a595e4f76 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 @@ -13,7 +13,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_avfoundation/src/messages.g.dart'; - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -21,10 +20,10 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is CreationOptions) { + } else if (value is CreationOptions) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -35,10 +34,10 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; - case 130: + case 130: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -47,7 +46,8 @@ class _PigeonCodec extends StandardMessageCodec { } abstract class TestHostVideoPlayerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); void initialize(); @@ -72,39 +72,58 @@ abstract class TestHostVideoPlayerApi { void setMixWithOthers(bool mixWithOthers); - static void setUp(TestHostVideoPlayerApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + TestHostVideoPlayerApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { try { api.initialize(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null.'); final List args = (message as List?)!; - final CreationOptions? arg_creationOptions = (args[0] as CreationOptions?); + final CreationOptions? arg_creationOptions = + (args[0] as CreationOptions?); assert(arg_creationOptions != null, 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create was null, expected non-null CreationOptions.'); try { @@ -112,22 +131,29 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -137,22 +163,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final bool? arg_isLooping = (args[0] as bool?); assert(arg_isLooping != null, @@ -165,22 +198,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final double? arg_volume = (args[0] as double?); assert(arg_volume != null, @@ -193,22 +233,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final double? arg_speed = (args[0] as double?); assert(arg_speed != null, @@ -221,22 +268,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -246,22 +300,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -271,22 +332,29 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final int? arg_position = (args[0] as int?); assert(arg_position != null, @@ -299,22 +367,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -324,22 +399,29 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final bool? arg_mixWithOthers = (args[0] as bool?); assert(arg_mixWithOthers != null, @@ -349,8 +431,9 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } From f126edf4375eac6fbb1cc73566631f758cd6d990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 20 Nov 2024 17:35:03 +0100 Subject: [PATCH 28/92] Use a class for platform view creation params --- .../video_player_avfoundation/messages.g.h | 9 + .../video_player_avfoundation/messages.g.m | 238 ++++++++---------- .../FVPNativeVideoView.m | 6 +- .../FVPNativeVideoViewFactory.m | 12 +- .../include/FVPNativeVideoView.h | 6 +- .../lib/src/avfoundation_video_player.dart | 8 +- .../lib/src/messages.g.dart | 138 +++++----- .../pigeons/messages.dart | 9 + .../test/test_api.g.dart | 7 +- 9 files changed, 206 insertions(+), 227 deletions(-) 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 81c227e3c135..7f545d7575cd 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 @@ -25,8 +25,17 @@ typedef NS_ENUM(NSUInteger, FVPPlatformVideoViewType) { - (instancetype)initWithValue:(FVPPlatformVideoViewType)value; @end +@class FVPPlatformVideoViewCreationParams; @class FVPCreationOptions; +/// Information passed to the platform view creation. +@interface FVPPlatformVideoViewCreationParams : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithPlayerId:(NSInteger )playerId; +@property(nonatomic, assign) NSInteger playerId; +@end + @interface FVPCreationOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; 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 5b238679a22f..3cf2c9d15bd7 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 @@ -41,20 +41,47 @@ - (instancetype)initWithValue:(FVPPlatformVideoViewType)value { } @end +@interface FVPPlatformVideoViewCreationParams () ++ (FVPPlatformVideoViewCreationParams *)fromList:(NSArray *)list; ++ (nullable FVPPlatformVideoViewCreationParams *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FVPCreationOptions () + (FVPCreationOptions *)fromList:(NSArray *)list; + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@implementation FVPPlatformVideoViewCreationParams ++ (instancetype)makeWithPlayerId:(NSInteger )playerId { + FVPPlatformVideoViewCreationParams* pigeonResult = [[FVPPlatformVideoViewCreationParams alloc] init]; + pigeonResult.playerId = playerId; + return pigeonResult; +} ++ (FVPPlatformVideoViewCreationParams *)fromList:(NSArray *)list { + FVPPlatformVideoViewCreationParams *pigeonResult = [[FVPPlatformVideoViewCreationParams alloc] init]; + pigeonResult.playerId = [GetNullableObjectAtIndex(list, 0) integerValue]; + return pigeonResult; +} ++ (nullable FVPPlatformVideoViewCreationParams *)nullableFromList:(NSArray *)list { + return (list) ? [FVPPlatformVideoViewCreationParams fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.playerId), + ]; +} +@end + @implementation FVPCreationOptions + (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders - viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType { - FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders + viewType:(nullable FVPPlatformVideoViewTypeBox *)viewType { + FVPCreationOptions* pigeonResult = [[FVPCreationOptions alloc] init]; pigeonResult.asset = asset; pigeonResult.uri = uri; pigeonResult.packageName = packageName; @@ -95,11 +122,11 @@ - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 129: { NSNumber *enumAsNumber = [self readValue]; - return enumAsNumber == nil - ? nil - : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + return enumAsNumber == nil ? nil : [[FVPPlatformVideoViewTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; } - case 130: + case 130: + return [FVPPlatformVideoViewCreationParams fromList:[self readValue]]; + case 131: return [FVPCreationOptions fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -115,9 +142,12 @@ - (void)writeValue:(id)value { FVPPlatformVideoViewTypeBox *box = (FVPPlatformVideoViewTypeBox *)value; [self writeByte:129]; [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; - } else if ([value isKindOfClass:[FVPCreationOptions class]]) { + } else if ([value isKindOfClass:[FVPPlatformVideoViewCreationParams class]]) { [self writeByte:130]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FVPCreationOptions class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -139,35 +169,25 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FVPMessagesPigeonCodecReaderWriter *readerWriter = - [[FVPMessagesPigeonCodecReaderWriter alloc] init]; + FVPMessagesPigeonCodecReaderWriter *readerWriter = [[FVPMessagesPigeonCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, - NSObject *api) { +void SetUpFVPAVFoundationVideoPlayerApi(id binaryMessenger, NSObject *api) { SetUpFVPAVFoundationVideoPlayerApiWithSuffix(binaryMessenger, api, @""); } -void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, - NSObject *api, - NSString *messageChannelSuffix) { - messageChannelSuffix = messageChannelSuffix.length > 0 - ? [NSString stringWithFormat:@".%@", messageChannelSuffix] - : @""; +void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.initialize", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(initialize:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", - api); + NSCAssert([api respondsToSelector:@selector(initialize:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api initialize:&error]; @@ -178,18 +198,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.create", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(createWithOptions:error:)", - api); + NSCAssert([api respondsToSelector:@selector(createWithOptions:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(createWithOptions:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FVPCreationOptions *arg_creationOptions = GetNullableObjectAtIndex(args, 0); @@ -202,18 +217,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.dispose", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(disposePlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(disposePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(disposePlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -226,18 +236,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setLooping", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setLooping:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setLooping:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_isLooping = [GetNullableObjectAtIndex(args, 0) boolValue]; @@ -251,18 +256,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setVolume", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setVolume:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setVolume:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_volume = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -276,18 +276,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setPlaybackSpeed", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setPlaybackSpeed:forPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:forPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:forPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_speed = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -301,18 +296,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.play", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(playPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(playPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(playPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -325,18 +315,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.getPosition", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(positionForPlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(positionForPlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(positionForPlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -349,45 +334,33 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.seekTo", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(seekTo:forPlayer:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(seekTo:forPlayer:completion:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:forPlayer:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_position = [GetNullableObjectAtIndex(args, 0) integerValue]; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 1) integerValue]; - [api seekTo:arg_position - forPlayer:arg_textureId - completion:^(FlutterError *_Nullable error) { - callback(wrapResult(nil, error)); - }]; + [api seekTo:arg_position forPlayer:arg_textureId completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.pause", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(pausePlayer:error:)", - api); + NSCAssert([api respondsToSelector:@selector(pausePlayer:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(pausePlayer:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_textureId = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -400,18 +373,13 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.video_player_avfoundation." - @"AVFoundationVideoPlayerApi.setMixWithOthers", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FVPGetMessagesCodec()]; + codec:FVPGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], - @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " - @"@selector(setMixWithOthers:error:)", - api); + NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; BOOL arg_mixWithOthers = [GetNullableObjectAtIndex(args, 0) boolValue]; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m index 771aa8b1a826..14472cfc8b5e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoView.m @@ -23,11 +23,7 @@ @implementation FVPNativeVideoView { FVPPlayerView *_view; } -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger - player:(AVPlayer *)player { +- (instancetype)initWithPlayer:(AVPlayer *)player { if (self = [super init]) { _view = [[FVPPlayerView alloc] init]; [_view setPlayer:player]; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index 01f715c21a03..c700c57203b5 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -24,17 +24,13 @@ - (instancetype)initWithMessenger:(NSObject *)messenger - (NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - NSNumber *playerId = args[@"playerId"]; + arguments:(FVPPlatformVideoViewCreationParams *)args { + NSNumber *playerId = @(args.playerId); FVPVideoPlayer *player = _playersByTextureId[playerId]; - return [[FVPNativeVideoView alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger - player:player.player]; + return [[FVPNativeVideoView alloc] initWithPlayer:player.player]; } - (NSObject *)createArgsCodec { - return [FlutterStandardMessageCodec sharedInstance]; + return FVPGetMessagesCodec(); } @end \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h index f850c5346e64..ed5f4d6d157d 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h @@ -9,11 +9,7 @@ @interface FVPNativeVideoView : NSObject /// Initializes a new instance of a native view. /// It creates a video view instance and sets the provided AVPlayer instance to it. -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger - player:(AVPlayer *)player; +- (instancetype)initWithPlayer:(AVPlayer *)player; /// Returns the native UIView that displays the video content. - (UIView *)view; 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 43186863ca20..e82d331dc225 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 @@ -158,16 +158,14 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } Widget _buildPlatformView(int playerId, VideoViewType viewType) { - // FIXME Use a separate class for creation params. - final Map creationParams = { - 'playerId': playerId, - }; + final PlatformVideoViewCreationParams creationParams = + PlatformVideoViewCreationParams(playerId: playerId); return IgnorePointer( // IgnorePointer so that GestureDetector can be used above the platform view. child: UiKitView( viewType: 'plugins.flutter.dev/video_player_ios', creationParams: creationParams, - creationParamsCodec: const StandardMessageCodec(), + creationParamsCodec: AVFoundationVideoPlayerApi.pigeonChannelCodec, ), ); } 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 3775c094e02c..d69dc2136cdd 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 @@ -18,8 +18,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -35,6 +34,28 @@ enum PlatformVideoViewType { platformView, } +/// Information passed to the platform view creation. +class PlatformVideoViewCreationParams { + PlatformVideoViewCreationParams({ + required this.playerId, + }); + + int playerId; + + Object encode() { + return [ + playerId, + ]; + } + + static PlatformVideoViewCreationParams decode(Object result) { + result as List; + return PlatformVideoViewCreationParams( + playerId: result[0]! as int, + ); + } +} + class CreationOptions { CreationOptions({ this.asset, @@ -75,13 +96,13 @@ class CreationOptions { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: - (result[4] as Map?)!.cast(), + httpHeaders: (result[4] as Map?)!.cast(), viewType: result[5] as PlatformVideoViewType?, ); } } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -89,12 +110,15 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is CreationOptions) { + } else if (value is PlatformVideoViewCreationParams) { buffer.putUint8(130); writeValue(buffer, value.encode()); + } else if (value is CreationOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -103,10 +127,12 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; - case 130: + case 130: + return PlatformVideoViewCreationParams.decode(readValue(buffer)!); + case 131: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -118,11 +144,9 @@ class AVFoundationVideoPlayerApi { /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AVFoundationVideoPlayerApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -130,10 +154,8 @@ class AVFoundationVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -154,16 +176,14 @@ class AVFoundationVideoPlayerApi { } Future create(CreationOptions creationOptions) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([creationOptions]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([creationOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -183,10 +203,8 @@ class AVFoundationVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -207,16 +225,14 @@ class AVFoundationVideoPlayerApi { } Future setLooping(bool isLooping, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([isLooping, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([isLooping, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -231,16 +247,14 @@ class AVFoundationVideoPlayerApi { } Future setVolume(double volume, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([volume, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([volume, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -255,16 +269,14 @@ class AVFoundationVideoPlayerApi { } Future setPlaybackSpeed(double speed, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([speed, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([speed, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -279,10 +291,8 @@ class AVFoundationVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -303,10 +313,8 @@ class AVFoundationVideoPlayerApi { } Future getPosition(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -332,16 +340,14 @@ class AVFoundationVideoPlayerApi { } Future seekTo(int position, int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([position, textureId]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([position, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -356,10 +362,8 @@ class AVFoundationVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -380,16 +384,14 @@ class AVFoundationVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 91190861f52e..80fef03f8604 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -24,6 +24,15 @@ enum PlatformVideoViewType { platformView, } +/// Information passed to the platform view creation. +class PlatformVideoViewCreationParams { + const PlatformVideoViewCreationParams({ + required this.playerId, + }); + + final int playerId; +} + class CreationOptions { CreationOptions({required this.httpHeaders}); String? asset; 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 c65a595e4f76..5fc69e63ccd8 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 @@ -23,9 +23,12 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is CreationOptions) { + } else if (value is PlatformVideoViewCreationParams) { buffer.putUint8(130); writeValue(buffer, value.encode()); + } else if (value is CreationOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -38,6 +41,8 @@ class _PigeonCodec extends StandardMessageCodec { final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; case 130: + return PlatformVideoViewCreationParams.decode(readValue(buffer)!); + case 131: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); From 0a6fafef9ddebea328e822affc4c40874b711bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 21 Nov 2024 08:53:41 +0100 Subject: [PATCH 29/92] Remove unused param from method --- .../lib/src/avfoundation_video_player.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e82d331dc225..7fadd3258d5d 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 @@ -153,11 +153,11 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { Widget buildView(int textureId, VideoViewType viewType) { return switch (viewType) { VideoViewType.textureView => Texture(textureId: textureId), - VideoViewType.platformView => _buildPlatformView(textureId, viewType), + VideoViewType.platformView => _buildPlatformView(textureId), }; } - Widget _buildPlatformView(int playerId, VideoViewType viewType) { + Widget _buildPlatformView(int playerId) { final PlatformVideoViewCreationParams creationParams = PlatformVideoViewCreationParams(playerId: playerId); return IgnorePointer( From baf00e398e18bac0deb269b0e7b6cc41a413b4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 21 Nov 2024 12:01:38 +0100 Subject: [PATCH 30/92] Fix failing unit test, prevent double factory registration --- .../darwin/RunnerTests/VideoPlayerTests.m | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index afa3eaeb4cb3..ac018f75014f 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -840,8 +840,9 @@ - (void)testHotReloadDoesNotCrash { #if TARGET_OS_IOS - (void)testNativeVideoViewFactoryRegistration { + NSObject *registry = GetPluginRegistry(); NSObject *registrar = - [GetPluginRegistry() registrarForPlugin:@"testNativeVideoViewFactoryRegistration"]; + [registry registrarForPlugin:@"testNativeVideoViewFactoryRegistration"]; id mockRegistrar = OCMPartialMock(registrar); OCMExpect([mockRegistrar @@ -857,10 +858,14 @@ - (void)testPublishesInRegistration { NSString *pluginKey = @"TestRegistration"; NSObject *registry = GetPluginRegistry(); NSObject *registrar = [registry registrarForPlugin:pluginKey]; + id mockRegistrar = OCMPartialMock(registrar); + // Empty stub to pass a check in Flutter's engine (double factory registration). + // registerWithRegistrar gets called at the beginning of the test, and factory is registered + // there. Additional call would try to register the same factory another time, which would fail a + // check in the engine. + OCMStub([mockRegistrar registerViewFactory:[OCMArg any] withId:[OCMArg any]]); - // FIXME This line makes the test fail (or rather run forever) if native view factory is - // registered. - [FVPVideoPlayerPlugin registerWithRegistrar:registrar]; + [FVPVideoPlayerPlugin registerWithRegistrar:mockRegistrar]; id publishedValue = [registry valuePublishedByPlugin:pluginKey]; From 80e8573777e11f7c3de48b341ef147aa36155af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 21 Nov 2024 12:27:56 +0100 Subject: [PATCH 31/92] Format pigeon generated file --- .../lib/src/messages.g.dart | 115 +++++++++++------- 1 file changed, 70 insertions(+), 45 deletions(-) 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 d69dc2136cdd..1b3f0e089509 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 @@ -18,7 +18,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -96,13 +97,13 @@ class CreationOptions { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: (result[4] as Map?)!.cast(), + httpHeaders: + (result[4] as Map?)!.cast(), viewType: result[5] as PlatformVideoViewType?, ); } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -110,13 +111,13 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is PlatformVideoViewCreationParams) { + } else if (value is PlatformVideoViewCreationParams) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is CreationOptions) { + } else if (value is CreationOptions) { buffer.putUint8(131); writeValue(buffer, value.encode()); } else { @@ -127,12 +128,12 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; - case 130: + case 130: return PlatformVideoViewCreationParams.decode(readValue(buffer)!); - case 131: + case 131: return CreationOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -144,9 +145,11 @@ class AVFoundationVideoPlayerApi { /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AVFoundationVideoPlayerApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -154,8 +157,10 @@ class AVFoundationVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -176,14 +181,16 @@ class AVFoundationVideoPlayerApi { } Future create(CreationOptions creationOptions) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([creationOptions]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([creationOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -203,8 +210,10 @@ class AVFoundationVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -225,14 +234,16 @@ class AVFoundationVideoPlayerApi { } Future setLooping(bool isLooping, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([isLooping, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([isLooping, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -247,14 +258,16 @@ class AVFoundationVideoPlayerApi { } Future setVolume(double volume, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([volume, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([volume, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -269,14 +282,16 @@ class AVFoundationVideoPlayerApi { } Future setPlaybackSpeed(double speed, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([speed, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([speed, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -291,8 +306,10 @@ class AVFoundationVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -313,8 +330,10 @@ class AVFoundationVideoPlayerApi { } Future getPosition(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getPosition$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -340,14 +359,16 @@ class AVFoundationVideoPlayerApi { } Future seekTo(int position, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([position, textureId]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([position, textureId]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -362,8 +383,10 @@ class AVFoundationVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -384,14 +407,16 @@ class AVFoundationVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { From 28c5792b7730d650ece9c4735ec2ddcdf4318d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 21 Nov 2024 13:05:52 +0100 Subject: [PATCH 32/92] Use correct plugin key in test function --- .../darwin/RunnerTests/VideoPlayerTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index ac018f75014f..2ef43ad0be7a 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -196,7 +196,7 @@ - (void)testPlayerForPlatformViewDoesNotRegisterTexture { NSObject *mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); NSObject *registrar = - [GetPluginRegistry() registrarForPlugin:@"testPlayerTypeForPlatformView"]; + [GetPluginRegistry() registrarForPlugin:@"testPlayerForPlatformViewDoesNotRegisterTexture"]; NSObject *partialRegistrar = OCMPartialMock(registrar); OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); FVPDisplayLink *mockDisplayLink = From 3c24ef2691ed6d814cffd38be959ebc419b1d476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 22 Nov 2024 11:21:53 +0100 Subject: [PATCH 33/92] Update dartdocs and comments --- .../lib/src/avfoundation_video_player.dart | 2 +- .../lib/video_player_platform_interface.dart | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 7fadd3258d5d..ad9672aaf821 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 @@ -25,7 +25,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return _api.initialize(); } - // FIXME Rename textureId everywhere to playerId. + // FIXME Rename textureId to playerId everywhere. @override Future dispose(int textureId) { return _api.dispose(textureId); 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 7b080e3edcfa..54325a2598a9 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 @@ -48,7 +48,9 @@ abstract class VideoPlayerPlatform extends PlatformInterface { throw UnimplementedError('dispose() has not been implemented.'); } - /// Creates an instance of a video player and returns its textureId. + /// Creates an instance of a video player with and returns its textureId. + /// The [viewType] parameter specifies the type of view to be used for the + /// video player. Future create( DataSource dataSource, { VideoViewType viewType = VideoViewType.textureView, @@ -96,7 +98,8 @@ abstract class VideoPlayerPlatform extends PlatformInterface { throw UnimplementedError('getPosition() has not been implemented.'); } - /// Returns a widget displaying the video with a given textureID. + /// Returns a widget displaying the video with a given textureID, using given + /// viewType. Widget buildView(int textureId, VideoViewType viewType) { throw UnimplementedError('buildView() has not been implemented.'); } From bf55efafeaa0a1f2f7118232732cea353cec1877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 25 Nov 2024 16:18:49 +0100 Subject: [PATCH 34/92] Adjust FVPVideoPlayer interface after rebase --- .../FVPVideoPlayer.h | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 3cb94b4b74cd..6b2b33384f10 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -22,14 +22,15 @@ @property(readonly, nonatomic, nonnull) AVPlayerItemVideoOutput *videoOutput; /// The plugin registrar, to obtain view information from. @property(nonatomic, weak, nullable) NSObject *registrar; -/// The CALayer associated with the Flutter view this plugin is associated with, if any. -@property(nonatomic, readonly, nullable) CALayer *flutterViewLayer; /// The Flutter event channel used to communicate with the Flutter engine. @property(nonatomic, nonnull) FlutterEventChannel *eventChannel; /// The Flutter event sink used to send events to the Flutter engine. @property(nonatomic, nonnull) FlutterEventSink eventSink; /// The preferred transform for the video. It can be used to handle the rotation of the video. @property(nonatomic) CGAffineTransform preferredTransform; +/// The layer used to display the video content from the AVPlayer. It's responsible for rendering +/// the video output of the associated AVPlayer. +@property(nonatomic) AVPlayerLayer *playerLayer; /// Indicates whether the video player has been disposed. @property(nonatomic, readonly) BOOL disposed; /// Indicates whether the video player is currently playing. @@ -38,22 +39,12 @@ @property(nonatomic) BOOL isLooping; /// Indicates whether the video player has been initialized. @property(nonatomic, readonly) BOOL isInitialized; -/// The updater that drives callbacks to the engine to indicate that a new frame is ready. -@property(nonatomic, nullable) FVPFrameUpdater *frameUpdater; -/// The display link that drives frameUpdater. -@property(nonatomic, nullable) FVPDisplayLink *displayLink; -/// Whether a new frame needs to be provided to the engine regardless of the current play/pause -/// state (e.g., after a seek while paused). If YES, the display link should continue to run until -/// the next frame is successfully provided. -@property(nonatomic, assign) BOOL waitingForFrame; NS_ASSUME_NONNULL_BEGIN /// Initializes a new instance of FVPVideoPlayer with the given URL, frame updater, display link, /// HTTP headers, AV factory, and registrar. - (instancetype)initWithURL:(NSURL *)url - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink httpHeaders:(nonnull NSDictionary *)headers avFactory:(id)avFactory registrar:(NSObject *)registrar; @@ -61,16 +52,12 @@ NS_ASSUME_NONNULL_BEGIN /// Initializes a new instance of FVPVideoPlayer with the given AVPlayerItem, frame updater, display /// link, AV factory, and registrar. - (instancetype)initWithPlayerItem:(AVPlayerItem *)item - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink avFactory:(id)avFactory registrar:(NSObject *)registrar; /// Initializes a new instance of FVPVideoPlayer with the given asset, frame updater, display link, /// AV factory, and registrar. - (instancetype)initWithAsset:(NSString *)asset - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(FVPDisplayLink *)displayLink avFactory:(id)avFactory registrar:(NSObject *)registrar; From fc88fc81cfa9690e437f687af7ef4d03c684c53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 25 Nov 2024 16:40:23 +0100 Subject: [PATCH 35/92] Fix Xcode analysis issues --- .../video_player_avfoundation/FVPVideoPlayer.m | 1 - .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 7 ++++--- .../video_player_avfoundation/FVPVideoPlayer.h | 6 +----- .../FVPVideoPlayerTextureApproach.h | 12 ++++++++---- .../FVPNativeVideoViewFactory.m | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index 6201e6ae93b8..b9c316d21bc2 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -354,7 +354,6 @@ - (int64_t)duration { } - (void)seekTo:(int64_t)location completionHandler:(void (^)(BOOL))completionHandler { - CMTime previousCMTime = _player.currentTime; CMTime targetCMTime = CMTimeMake(location, 1000); CMTimeValue duration = _player.currentItem.asset.duration.value; // Without adding tolerance when seeking to duration, 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 115c7bdc03e0..a1b54ea1ab6f 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 @@ -145,9 +145,10 @@ - (void)initialize:(FlutterError *__autoreleasing *)error { - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options error:(FlutterError **)error { + BOOL usesTextureApproach = options.viewType.value == FVPPlatformVideoViewTypeTextureView; FVPFrameUpdater *frameUpdater; FVPDisplayLink *displayLink; - if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + if (usesTextureApproach) { frameUpdater = [[FVPFrameUpdater alloc] initWithRegistry:_registry]; displayLink = [self.displayLinkFactory displayLinkWithRegistrar:_registrar callback:^() { @@ -164,7 +165,7 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options assetPath = [_registrar lookupKeyForAsset:options.asset]; } @try { - if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + if (usesTextureApproach) { player = [[FVPVideoPlayerTextureApproach alloc] initWithAsset:assetPath frameUpdater:frameUpdater displayLink:displayLink @@ -181,7 +182,7 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options return nil; } } else if (options.uri) { - if (options.viewType.value == FVPPlatformVideoViewTypeTextureView) { + if (usesTextureApproach) { player = [[FVPVideoPlayerTextureApproach alloc] initWithURL:[NSURL URLWithString:options.uri] frameUpdater:frameUpdater displayLink:displayLink diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 6b2b33384f10..4965ab61010e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -30,7 +30,7 @@ @property(nonatomic) CGAffineTransform preferredTransform; /// The layer used to display the video content from the AVPlayer. It's responsible for rendering /// the video output of the associated AVPlayer. -@property(nonatomic) AVPlayerLayer *playerLayer; +@property(nonatomic, nonnull) AVPlayerLayer *playerLayer; /// Indicates whether the video player has been disposed. @property(nonatomic, readonly) BOOL disposed; /// Indicates whether the video player is currently playing. @@ -89,8 +89,4 @@ NS_ASSUME_NONNULL_END /// Seeks to the specified location in the video and calls the completion handler when done, if one /// is supplied. - (void)seekTo:(int64_t)location completionHandler:(void (^_Nullable)(BOOL))completionHandler; - -/// Tells the player to run its frame updater until it receives a frame, regardless of the -/// play/pause state. -- (void)expectFrame; @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index 96d926e941fa..6eb43096bf84 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -11,16 +11,18 @@ /// updates frames, and handles display link callbacks. @interface FVPVideoPlayerTextureApproach : FVPVideoPlayer // The CALayer associated with the Flutter view this plugin is associated with, if any. -@property(nonatomic, readonly) CALayer *flutterViewLayer; +@property(nonatomic, readonly, nullable) CALayer *flutterViewLayer; // The updater that drives callbacks to the engine to indicate that a new frame is ready. -@property(nonatomic) FVPFrameUpdater *frameUpdater; +@property(nonatomic, nullable) FVPFrameUpdater *frameUpdater; // The display link that drives frameUpdater. -@property(nonatomic) FVPDisplayLink *displayLink; +@property(nonatomic, nullable) FVPDisplayLink *displayLink; // Whether a new frame needs to be provided to the engine regardless of the current play/pause state // (e.g., after a seek while paused). If YES, the display link should continue to run until the next // frame is successfully provided. @property(nonatomic, assign) BOOL waitingForFrame; +NS_ASSUME_NONNULL_BEGIN + - (instancetype)initWithURL:(NSURL *)url frameUpdater:(FVPFrameUpdater *)frameUpdater displayLink:(FVPDisplayLink *)displayLink @@ -28,12 +30,14 @@ avFactory:(id)avFactory registrar:(NSObject *)registrar; -- (instancetype)initWithAsset:(AVPlayerItem *)item +- (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FVPFrameUpdater *)frameUpdater displayLink:(FVPDisplayLink *)displayLink avFactory:(id)avFactory registrar:(NSObject *)registrar; +NS_ASSUME_NONNULL_END + // Tells the player to run its frame updater until it receives a frame, regardless of the // play/pause state. - (void)expectFrame; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index c700c57203b5..2a9c6285edb9 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -33,4 +33,4 @@ - (instancetype)initWithMessenger:(NSObject *)messenger - (NSObject *)createArgsCodec { return FVPGetMessagesCodec(); } -@end \ No newline at end of file +@end From 282e7463074394638c338d8eb5046fb29c878fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 13:04:58 +0100 Subject: [PATCH 36/92] Split code into fiels and fix implementations to separate texture approach from platform view approach --- .../video_player_avfoundation/FVPAVFactory.m | 13 ++++++++ .../FVPVideoPlayerPlugin.m | 18 ++--------- .../video_player_avfoundation/FVPAVFactory.h | 12 +++++++ .../FVPVideoPlayer.h | 8 +++-- .../FVPVideoPlayerPlugin_Test.h | 31 ++----------------- .../FVPVideoPlayerTextureApproach.h | 2 +- .../FVPNativeVideoViewFactory.m | 1 + 7 files changed, 39 insertions(+), 46 deletions(-) create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPAVFactory.m create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPAVFactory.h diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPAVFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPAVFactory.m new file mode 100644 index 000000000000..f29130e41784 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPAVFactory.m @@ -0,0 +1,13 @@ +#import + +#import "FVPAVFactory.h" + +@implementation FVPDefaultAVFactory +- (AVPlayer *)playerWithPlayerItem:(AVPlayerItem *)playerItem { + return [AVPlayer playerWithPlayerItem:playerItem]; +} +- (AVPlayerItemVideoOutput *)videoOutputWithPixelBufferAttributes: + (NSDictionary *)attributes { + return [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]; +} +@end \ No newline at end of file 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 a1b54ea1ab6f..561bb6f69532 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 @@ -6,6 +6,7 @@ #import +#import "./include/video_player_avfoundation/FVPAVFactory.h" #import "./include/video_player_avfoundation/FVPDisplayLink.h" #import "./include/video_player_avfoundation/FVPFrameUpdater.h" #import "./include/video_player_avfoundation/FVPVideoPlayer.h" @@ -24,19 +25,6 @@ #error Code Requires ARC. #endif -@interface FVPDefaultAVFactory : NSObject -@end - -@implementation FVPDefaultAVFactory -- (AVPlayer *)playerWithPlayerItem:(AVPlayerItem *)playerItem { - return [AVPlayer playerWithPlayerItem:playerItem]; -} -- (AVPlayerItemVideoOutput *)videoOutputWithPixelBufferAttributes: - (NSDictionary *)attributes { - return [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]; -} -@end - /// Non-test implementation of the diplay link factory. @interface FVPDefaultDisplayLinkFactory : NSObject @end @@ -102,8 +90,8 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater { // FIXME Rename textureId to playerId, in all other places as well. int64_t textureId; - if (frameUpdater) { - textureId = [self.registry registerTexture:player]; + if (frameUpdater && [player isKindOfClass:[FVPVideoPlayerTextureApproach class]]) { + textureId = [self.registry registerTexture:(FVPVideoPlayerTextureApproach *)player]; frameUpdater.textureId = textureId; } else { // FIXME Possibly start with a predefined prefix and then increment it to avoid diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPAVFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPAVFactory.h new file mode 100644 index 000000000000..dfc7c5e39ce7 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPAVFactory.h @@ -0,0 +1,12 @@ +#import + +// Protocol for AVFoundation object instance factory. Used for injecting framework objects in tests. +@protocol FVPAVFactory +@required +- (AVPlayer *)playerWithPlayerItem:(AVPlayerItem *)playerItem; +- (AVPlayerItemVideoOutput *)videoOutputWithPixelBufferAttributes: + (NSDictionary *)attributes; +@end + +@interface FVPDefaultAVFactory : NSObject +@end \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 4965ab61010e..5c96d2d7ff58 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -10,14 +10,14 @@ #import +#import "FVPAVFactory.h" #import "FVPDisplayLink.h" #import "FVPFrameUpdater.h" -#import "FVPVideoPlayerPlugin_Test.h" /// FVPVideoPlayer is responsible for managing video playback using AVPlayer. /// It provides methods to control playback, adjust volume, handle seeking, and /// notify the Flutter engine about new video frames. -@interface FVPVideoPlayer () +@interface FVPVideoPlayer : NSObject /// The AVPlayerItemVideoOutput associated with this video player. @property(readonly, nonatomic, nonnull) AVPlayerItemVideoOutput *videoOutput; /// The plugin registrar, to obtain view information from. @@ -28,9 +28,13 @@ @property(nonatomic, nonnull) FlutterEventSink eventSink; /// The preferred transform for the video. It can be used to handle the rotation of the video. @property(nonatomic) CGAffineTransform preferredTransform; +/// The AVPlayer instance used for video playback. +@property(readonly, nonatomic, nonnull) AVPlayer *player; /// The layer used to display the video content from the AVPlayer. It's responsible for rendering /// the video output of the associated AVPlayer. @property(nonatomic, nonnull) AVPlayerLayer *playerLayer; +/// The current playback position of the video, in milliseconds. +@property(readonly, nonatomic) int64_t position; /// Indicates whether the video player has been disposed. @property(nonatomic, readonly) BOOL disposed; /// Indicates whether the video player is currently playing. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h index e34ce16eabcd..e045210e68c7 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerPlugin_Test.h @@ -2,21 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FVPVideoPlayerPlugin.h" - -#import - +#import "FVPAVFactory.h" #import "FVPDisplayLink.h" +#import "FVPVideoPlayer.h" +#import "FVPVideoPlayerPlugin.h" #import "messages.g.h" -// Protocol for AVFoundation object instance factory. Used for injecting framework objects in tests. -@protocol FVPAVFactory -@required -- (AVPlayer *)playerWithPlayerItem:(AVPlayerItem *)playerItem; -- (AVPlayerItemVideoOutput *)videoOutputWithPixelBufferAttributes: - (NSDictionary *)attributes; -@end - // Protocol for an AVPlayer instance factory. Used for injecting display links in tests. @protocol FVPDisplayLinkFactory - (FVPDisplayLink *)displayLinkWithRegistrar:(id)registrar @@ -25,22 +16,6 @@ #pragma mark - -// TODO(stuartmorgan): Move this whole class to its own files. -@interface FVPVideoPlayer : NSObject -@property(readonly, nonatomic) AVPlayer *player; -// This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 -// (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some video -// streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). -// An invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams -// for issue #1, and restore the correct width and height for issue #2. -@property(readonly, nonatomic) AVPlayerLayer *playerLayer; -@property(readonly, nonatomic) int64_t position; - -- (void)onTextureUnregistered:(NSObject *)texture; -@end - -#pragma mark - - @interface FVPVideoPlayerPlugin () @property(readonly, strong, nonatomic) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index 6eb43096bf84..e9b11f0e367c 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -9,7 +9,7 @@ /// A subclass of FVPVideoPlayer that adds functionality related to texture-based view as a way of /// displaying the video in the app. It manages the CALayer associated with the Flutter view, /// updates frames, and handles display link callbacks. -@interface FVPVideoPlayerTextureApproach : FVPVideoPlayer +@interface FVPVideoPlayerTextureApproach : FVPVideoPlayer // The CALayer associated with the Flutter view this plugin is associated with, if any. @property(nonatomic, readonly, nullable) CALayer *flutterViewLayer; // The updater that drives callbacks to the engine to indicate that a new frame is ready. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m index 2a9c6285edb9..fe60b2e0f9b4 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPNativeVideoViewFactory.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "../video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h" +#import "../video_player_avfoundation/include/video_player_avfoundation/messages.g.h" #import "./include/FVPNativeVideoView.h" #import "./include/FVPNativeVideoViewFactory.h" From 352f3a1361ddbf572ceb48ce9df99b6e643d1f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 13:23:36 +0100 Subject: [PATCH 37/92] Adjust tests to use FVPVideoPlayerTextureApproach --- .../darwin/RunnerTests/VideoPlayerTests.m | 25 +++++++---- .../FVPVideoPlayer.m | 41 +++++++++++++++---- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index 2ef43ad0be7a..10675f882184 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -276,7 +276,8 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily { // Seeking to a new position should start the display link temporarily. OCMVerify([mockDisplayLink setRunning:YES]); - FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + FVPVideoPlayerTextureApproach *player = + (FVPVideoPlayerTextureApproach *)videoPlayerPlugin.playersByTextureId[textureId]; XCTAssertEqual([player position], 1234); // Simulate a buffer being available. @@ -342,7 +343,8 @@ - (void)testInitStartsDisplayLinkTemporarily { .ignoringNonObjectArgs() .andReturn(fakeBufferRef); // Simulate a callback from the engine to request a new frame. - FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + FVPVideoPlayerTextureApproach *player = + (FVPVideoPlayerTextureApproach *)videoPlayerPlugin.playersByTextureId[textureId]; [player copyPixelBuffer]; // Since a frame was found, and the video is paused, the display link should be paused again. OCMVerify([mockDisplayLink setRunning:NO]); @@ -394,7 +396,8 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink { [self waitForExpectationsWithTimeout:30.0 handler:nil]; OCMVerify([mockDisplayLink setRunning:YES]); - FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + FVPVideoPlayerTextureApproach *player = + (FVPVideoPlayerTextureApproach *)videoPlayerPlugin.playersByTextureId[textureId]; XCTAssertEqual([player position], 1234); // Simulate a buffer being available. @@ -814,7 +817,8 @@ - (void)testHotReloadDoesNotCrash { XCTAssertNil(error); XCTAssertNotNil(textureId); - FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + FVPVideoPlayerTextureApproach *player = + (FVPVideoPlayerTextureApproach *)videoPlayerPlugin.playersByTextureId[textureId]; XCTAssertNotNil(player); weakPlayer = player; @@ -882,11 +886,14 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { [videoPlayerPlugin initialize:&error]; - FVPCreationOptions *create = [FVPCreationOptions makeWithAsset:nil - uri:@"" - packageName:nil - formatHint:nil - httpHeaders:@{}]; + FVPCreationOptions *create = + [FVPCreationOptions makeWithAsset:nil + uri:@"" + packageName:nil + formatHint:nil + httpHeaders:@{} + viewType:[[FVPPlatformVideoViewTypeBox alloc] + initWithValue:FVPPlatformVideoViewTypePlatformView]]; NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; XCTAssertNotNil(player); diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index b9c316d21bc2..3d2cf5c19efc 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -224,13 +224,7 @@ - (void)observeValueForKeyPath:(NSString *)path AVPlayerItem *item = (AVPlayerItem *)object; switch (item.status) { case AVPlayerItemStatusFailed: - if (_eventSink != nil) { - _eventSink([FlutterError - errorWithCode:@"VideoError" - message:[@"Failed to load video: " - stringByAppendingString:[item.error localizedDescription]] - details:nil]); - } + [self sendFailedToLoadVideoEvent]; break; case AVPlayerItemStatusUnknown: break; @@ -282,6 +276,32 @@ - (void)updatePlayingState { } } +- (void)sendFailedToLoadVideoEvent { + if (_eventSink == nil) { + return; + } + // Prefer more detailed error information from tracks loading. + NSError *error; + if ([self.player.currentItem.asset statusOfValueForKey:@"tracks" + error:&error] != AVKeyValueStatusFailed) { + error = self.player.currentItem.error; + } + __block NSMutableOrderedSet *details = + [NSMutableOrderedSet orderedSetWithObject:@"Failed to load video"]; + void (^add)(NSString *) = ^(NSString *detail) { + if (detail != nil) { + [details addObject:detail]; + } + }; + NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey]; + add(error.localizedDescription); + add(error.localizedFailureReason); + add(underlyingError.localizedDescription); + add(underlyingError.localizedFailureReason); + NSString *message = [details.array componentsJoinedByString:@": "]; + _eventSink([FlutterError errorWithCode:@"VideoError" message:message details:nil]); +} + - (void)setupEventSinkIfReadyToPlay { if (_eventSink && !_isInitialized) { AVPlayerItem *currentItem = self.player.currentItem; @@ -415,6 +435,13 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments // This line ensures the 'initialized' event is sent when the event // 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this function // onListenWithArguments is called) + // and also send error in similar case with 'AVPlayerItemStatusFailed' + // https://github.com/flutter/flutter/issues/151475 + // https://github.com/flutter/flutter/issues/147707 + if (self.player.currentItem.status == AVPlayerItemStatusFailed) { + [self sendFailedToLoadVideoEvent]; + return nil; + } [self setupEventSinkIfReadyToPlay]; return nil; } From f84f85a816459bd2541342bf6ab8735fbae944cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 15:27:35 +0100 Subject: [PATCH 38/92] Make video_player platform interface backwards compatible --- .../example/lib/mini_controller.dart | 16 +++-- .../lib/src/avfoundation_video_player.dart | 23 ++++++-- .../lib/video_player_platform_interface.dart | 58 +++++++++++++------ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index e00929610f8c..335836872c82 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -231,27 +231,30 @@ class MiniController extends ValueNotifier { sourceType: DataSourceType.asset, asset: dataSource, package: package, + viewType: viewType, ); case DataSourceType.network: dataSourceDescription = DataSource( sourceType: DataSourceType.network, uri: dataSource, + viewType: viewType, ); case DataSourceType.file: dataSourceDescription = DataSource( sourceType: DataSourceType.file, uri: dataSource, + viewType: viewType, ); case DataSourceType.contentUri: dataSourceDescription = DataSource( sourceType: DataSourceType.contentUri, uri: dataSource, + viewType: viewType, ); } - _textureId = - (await _platform.create(dataSourceDescription, viewType: viewType)) ?? - kUninitializedTextureId; + _textureId = (await _platform.create(dataSourceDescription)) ?? + kUninitializedTextureId; _creatingCompleter!.complete(null); final Completer initializingCompleter = Completer(); @@ -433,7 +436,12 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == MiniController.kUninitializedTextureId ? Container() - : _platform.buildView(_textureId, widget.controller.viewType); + : _platform.buildViewWithOptions( + VideoViewOptions( + playerId: _textureId, + viewType: widget.controller.viewType, + ), + ); } } 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 ad9672aaf821..79f10c91ce46 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 @@ -150,10 +150,25 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } @override - Widget buildView(int textureId, VideoViewType viewType) { - return switch (viewType) { - VideoViewType.textureView => Texture(textureId: textureId), - VideoViewType.platformView => _buildPlatformView(textureId), + Widget buildView(int textureId) { + return buildViewWithOptions( + VideoViewOptions( + playerId: textureId, + // Texture view was the only supported view type before + // buildViewWithOptions was introduced. We pass it here to maintain + // backwards compatibility. + viewType: VideoViewType.textureView, + ), + ); + } + + @override + Widget buildViewWithOptions(VideoViewOptions options) { + final int playerId = options.playerId; + + return switch (options.viewType) { + VideoViewType.textureView => Texture(textureId: playerId), + VideoViewType.platformView => _buildPlatformView(playerId), }; } 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 54325a2598a9..52a1c444de77 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 @@ -48,13 +48,8 @@ abstract class VideoPlayerPlatform extends PlatformInterface { throw UnimplementedError('dispose() has not been implemented.'); } - /// Creates an instance of a video player with and returns its textureId. - /// The [viewType] parameter specifies the type of view to be used for the - /// video player. - Future create( - DataSource dataSource, { - VideoViewType viewType = VideoViewType.textureView, - }) { + /// Creates an instance of a video player and returns its textureId. + Future create(DataSource dataSource) { throw UnimplementedError('create() has not been implemented.'); } @@ -98,12 +93,17 @@ abstract class VideoPlayerPlatform extends PlatformInterface { throw UnimplementedError('getPosition() has not been implemented.'); } - /// Returns a widget displaying the video with a given textureID, using given - /// viewType. - Widget buildView(int textureId, VideoViewType viewType) { + /// Returns a widget displaying the video with a given textureID. + @Deprecated('Use buildViewWithOptions() instead.') + Widget buildView(int textureId) { throw UnimplementedError('buildView() has not been implemented.'); } + /// Returns a widget displaying the video based on given options. + Widget buildViewWithOptions(VideoViewOptions options) { + return buildView(options.playerId); + } + /// Sets the audio mode to mix with other sources Future setMixWithOthers(bool mixWithOthers) { throw UnimplementedError('setMixWithOthers() has not been implemented.'); @@ -117,15 +117,6 @@ abstract class VideoPlayerPlatform extends PlatformInterface { class _PlaceholderImplementation extends VideoPlayerPlatform {} -/// Enum representing the type of video view to be used. -enum VideoViewType { - /// Uses a texture view for rendering video. - textureView, - - /// Uses a platform view for rendering video. - platformView, -} - /// Description of the data source used to create an instance of /// the video player. class DataSource { @@ -149,6 +140,7 @@ class DataSource { this.asset, this.package, this.httpHeaders = const {}, + this.viewType = VideoViewType.textureView, }); /// The way in which the video was originally loaded. @@ -178,6 +170,9 @@ class DataSource { /// The package that the asset was loaded from. Only set for /// [DataSourceType.asset] videos. final String? package; + + /// The type of view to be used for displaying the video player. + final VideoViewType viewType; } /// The way in which the video was originally loaded. @@ -213,6 +208,15 @@ enum VideoFormat { other, } +/// Enum representing the type of video view to be used. +enum VideoViewType { + /// Uses a texture view for rendering video. + textureView, + + /// Uses a platform view for rendering video. + platformView, +} + /// Event emitted from the platform implementation. @immutable class VideoEvent { @@ -491,3 +495,19 @@ class VideoPlayerWebOptionsControls { return controlsList.join(' '); } } + +/// [VideoViewOptions] contains configuration options for a video view. +@immutable +class VideoViewOptions { + /// Constructs an instance of [VideoViewOptions]. + const VideoViewOptions({ + required this.playerId, + required this.viewType, + }); + + /// The identifier of the video player. + final int playerId; + + /// The type of the video view. + final VideoViewType viewType; +} From d6724d9307b95ed726be26b526e874a8294ef417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 15:33:51 +0100 Subject: [PATCH 39/92] Bump iOS package version to 2.7.0 --- packages/video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ packages/video_player/video_player_avfoundation/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 78e816410429..e7883e2fcb5d 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.0 + +* Adds support for platform views as the optional way of displaying a video. + ## 2.6.4 * Extracts `FVPVideoPlayer` and `FVPFrameUpdater` from the `FVPVideoPlayerPlugin.m` file and places them into separate files. diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 233a9b65e86b..21f10e6e75f4 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.6.4 +version: 2.7.0 environment: sdk: ^3.3.0 From 60a17331d912539f139f42d5463a784bdc474587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 15:40:45 +0100 Subject: [PATCH 40/92] Add viewType parameter to VideoPlayerController and use it to display view --- .../video_player/lib/video_player.dart | 45 +++++++++++++------ .../lib/video_player_platform_interface.dart | 2 +- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index b7ba8340fa66..6e161e80375a 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -270,11 +270,13 @@ class VideoPlayerController extends ValueNotifier { /// The name of the asset is given by the [dataSource] argument and must not be /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. - VideoPlayerController.asset(this.dataSource, - {this.package, - Future? closedCaptionFile, - this.videoPlayerOptions}) - : _closedCaptionFileFuture = closedCaptionFile, + VideoPlayerController.asset( + this.dataSource, { + this.package, + Future? closedCaptionFile, + this.videoPlayerOptions, + this.viewType = VideoViewType.textureView, + }) : _closedCaptionFileFuture = closedCaptionFile, dataSourceType = DataSourceType.asset, formatHint = null, httpHeaders = const {}, @@ -296,6 +298,7 @@ class VideoPlayerController extends ValueNotifier { Future? closedCaptionFile, this.videoPlayerOptions, this.httpHeaders = const {}, + this.viewType = VideoViewType.textureView, }) : _closedCaptionFileFuture = closedCaptionFile, dataSourceType = DataSourceType.network, package = null, @@ -316,6 +319,7 @@ class VideoPlayerController extends ValueNotifier { Future? closedCaptionFile, this.videoPlayerOptions, this.httpHeaders = const {}, + this.viewType = VideoViewType.textureView, }) : _closedCaptionFileFuture = closedCaptionFile, dataSource = url.toString(), dataSourceType = DataSourceType.network, @@ -326,11 +330,13 @@ class VideoPlayerController extends ValueNotifier { /// /// 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, - {Future? closedCaptionFile, - this.videoPlayerOptions, - this.httpHeaders = const {}}) - : _closedCaptionFileFuture = closedCaptionFile, + VideoPlayerController.file( + File file, { + Future? closedCaptionFile, + this.videoPlayerOptions, + this.httpHeaders = const {}, + this.viewType = VideoViewType.textureView, + }) : _closedCaptionFileFuture = closedCaptionFile, dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, @@ -341,9 +347,12 @@ class VideoPlayerController extends ValueNotifier { /// /// This will load the video from the input content-URI. /// This is supported on Android only. - VideoPlayerController.contentUri(Uri contentUri, - {Future? closedCaptionFile, this.videoPlayerOptions}) - : assert(defaultTargetPlatform == TargetPlatform.android, + VideoPlayerController.contentUri( + Uri contentUri, { + Future? closedCaptionFile, + this.videoPlayerOptions, + this.viewType = VideoViewType.textureView, + }) : assert(defaultTargetPlatform == TargetPlatform.android, 'VideoPlayerController.contentUri is only supported on Android.'), _closedCaptionFileFuture = closedCaptionFile, dataSource = contentUri.toString(), @@ -376,6 +385,9 @@ class VideoPlayerController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; + /// The type of view used to display the video. + final VideoViewType viewType; + Future? _closedCaptionFileFuture; ClosedCaptionFile? _closedCaptionFile; Timer? _timer; @@ -877,7 +889,12 @@ class _VideoPlayerState extends State { ? Container() : _VideoPlayerWithRotation( rotation: widget.controller.value.rotationCorrection, - child: _videoPlayerPlatform.buildView(_textureId), + child: _videoPlayerPlatform.buildViewWithOptions( + VideoViewOptions( + playerId: _textureId, + viewType: widget.controller.viewType, + ), + ), ); } } 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 52a1c444de77..66fb1b2aec6e 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 @@ -208,7 +208,7 @@ enum VideoFormat { other, } -/// Enum representing the type of video view to be used. +/// The type of video view to be used. enum VideoViewType { /// Uses a texture view for rendering video. textureView, From e0721680ec2a1b32a7e8fe2f9a6a03007e9a77eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 16:13:55 +0100 Subject: [PATCH 41/92] Improve if statements readability --- .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 561bb6f69532..eb0dabb2fbe1 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 @@ -88,9 +88,11 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr } - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater { + BOOL usesTextureApproach = + frameUpdater != nil && [player isKindOfClass:[FVPVideoPlayerTextureApproach class]]; // FIXME Rename textureId to playerId, in all other places as well. int64_t textureId; - if (frameUpdater && [player isKindOfClass:[FVPVideoPlayerTextureApproach class]]) { + if (usesTextureApproach) { textureId = [self.registry registerTexture:(FVPVideoPlayerTextureApproach *)player]; frameUpdater.textureId = textureId; } else { @@ -107,10 +109,9 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater player.eventChannel = eventChannel; self.playersByTextureId[@(textureId)] = player; - if (frameUpdater) { + if (usesTextureApproach) { // Ensure that the first frame is drawn once available, even if the video isn't played, since // the engine is now expecting the texture to be populated. - // We can safely cast to FVPVideoPlayerTextureApproach since frameUpdater is non-nil. [(FVPVideoPlayerTextureApproach *)player expectFrame]; } From 61b0d642b4ee38ee0b979d828e243e9ac6b9faa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 16:18:59 +0100 Subject: [PATCH 42/92] Rename textureId to playerId in onPlayerSetup --- .../FVPVideoPlayerPlugin.m | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) 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 eb0dabb2fbe1..10937edc3d45 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 @@ -90,24 +90,23 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater *)frameUpdater { BOOL usesTextureApproach = frameUpdater != nil && [player isKindOfClass:[FVPVideoPlayerTextureApproach class]]; - // FIXME Rename textureId to playerId, in all other places as well. - int64_t textureId; + int64_t playerId; if (usesTextureApproach) { - textureId = [self.registry registerTexture:(FVPVideoPlayerTextureApproach *)player]; - frameUpdater.textureId = textureId; + playerId = [self.registry registerTexture:(FVPVideoPlayerTextureApproach *)player]; + frameUpdater.textureId = playerId; } else { // FIXME Possibly start with a predefined prefix and then increment it to avoid // collisions withtextureId. - textureId = arc4random(); + playerId = arc4random(); } FlutterEventChannel *eventChannel = [FlutterEventChannel - eventChannelWithName:[NSString stringWithFormat:@"flutter.io/videoPlayer/videoEvents%lld", - textureId] + eventChannelWithName:[NSString + stringWithFormat:@"flutter.io/videoPlayer/videoEvents%lld", playerId] binaryMessenger:_messenger]; [eventChannel setStreamHandler:player]; player.eventChannel = eventChannel; - self.playersByTextureId[@(textureId)] = player; + self.playersByTextureId[@(playerId)] = player; if (usesTextureApproach) { // Ensure that the first frame is drawn once available, even if the video isn't played, since @@ -115,7 +114,7 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater [(FVPVideoPlayerTextureApproach *)player expectFrame]; } - return textureId; + return playerId; } - (void)initialize:(FlutterError *__autoreleasing *)error { From 777cff4350ee603a3d0ed978007ced3d10968a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 17:21:02 +0100 Subject: [PATCH 43/92] Fix overriding create method from platform interface on iOS --- .../lib/src/avfoundation_video_player.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 79f10c91ce46..406208d503eb 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 @@ -32,10 +32,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { } @override - Future create( - DataSource dataSource, { - VideoViewType viewType = VideoViewType.textureView, - }) async { + Future create(DataSource dataSource) async { String? asset; String? packageName; String? uri; @@ -60,7 +57,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { uri: uri, httpHeaders: httpHeaders, formatHint: formatHint, - viewType: _platformVideoViewTypeFromVideoViewType(viewType), + viewType: _platformVideoViewTypeFromVideoViewType(dataSource.viewType), ); return _api.create(options); From 7d9be33cbca4a99cbb9cf003e6f57587d6561e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 26 Nov 2024 17:21:36 +0100 Subject: [PATCH 44/92] Start non-texture player ids from a high number to avoid collisions --- .../video_player_avfoundation/FVPVideoPlayerPlugin.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 10937edc3d45..20f6a5bf21ff 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 @@ -39,6 +39,10 @@ - (FVPDisplayLink *)displayLinkWithRegistrar:(id)registr #pragma mark - +/// The next non-texture player ID, initialized to a high number to avoid collisions with +/// texture IDs (which are generated separately). +static int64_t nextNonTexturePlayerId = 1000000; + @interface FVPVideoPlayerPlugin () @property(readonly, weak, nonatomic) NSObject *registry; @property(readonly, weak, nonatomic) NSObject *messenger; @@ -95,9 +99,9 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater playerId = [self.registry registerTexture:(FVPVideoPlayerTextureApproach *)player]; frameUpdater.textureId = playerId; } else { - // FIXME Possibly start with a predefined prefix and then increment it to avoid - // collisions withtextureId. - playerId = arc4random(); + @synchronized(self) { + playerId = nextNonTexturePlayerId++; + } } FlutterEventChannel *eventChannel = [FlutterEventChannel From 92c5e2ac34d1390eb8231694abab9c05b5aec4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 09:28:14 +0100 Subject: [PATCH 45/92] Update comments --- .../lib/src/avfoundation_video_player.dart | 2 +- .../lib/video_player_platform_interface.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 406208d503eb..8f509cdc6e04 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 @@ -25,7 +25,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return _api.initialize(); } - // FIXME Rename textureId to playerId everywhere. + // TODO(FirentisTFW): Rename textureId to playerId everywhere. @override Future dispose(int textureId) { return _api.dispose(textureId); 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 66fb1b2aec6e..5b2bb0aed1bb 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 @@ -101,6 +101,7 @@ abstract class VideoPlayerPlatform extends PlatformInterface { /// Returns a widget displaying the video based on given options. Widget buildViewWithOptions(VideoViewOptions options) { + // Default implementation for backwards compatibility. return buildView(options.playerId); } From faf17a5e96c97b2f7ae484646a6cd7d9e6a351de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 09:28:27 +0100 Subject: [PATCH 46/92] Add viewType to FakeController in tests --- packages/video_player/video_player/test/video_player_test.dart | 3 +++ 1 file changed, 3 insertions(+) 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 f6eef2448119..c6e6de841aeb 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -46,6 +46,9 @@ class FakeController extends ValueNotifier @override Future get position async => value.position; + @override + VideoViewType get viewType => VideoViewType.textureView; + @override Future seekTo(Duration moment) async {} From 9aa2d234086e1713d429a5ed496964405040638a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 10:42:11 +0100 Subject: [PATCH 47/92] Use correct view type to create data source in the main package --- packages/video_player/video_player/lib/video_player.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 6e161e80375a..fa1f6bcac658 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -423,6 +423,7 @@ class VideoPlayerController extends ValueNotifier { sourceType: DataSourceType.asset, asset: dataSource, package: package, + viewType: viewType, ); case DataSourceType.network: dataSourceDescription = DataSource( @@ -430,17 +431,20 @@ class VideoPlayerController extends ValueNotifier { uri: dataSource, formatHint: formatHint, httpHeaders: httpHeaders, + viewType: viewType, ); case DataSourceType.file: dataSourceDescription = DataSource( sourceType: DataSourceType.file, uri: dataSource, httpHeaders: httpHeaders, + viewType: viewType, ); case DataSourceType.contentUri: dataSourceDescription = DataSource( sourceType: DataSourceType.contentUri, uri: dataSource, + viewType: viewType, ); } From 6619b78715994af922fa3463ce3a0ac267b8abd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 10:42:38 +0100 Subject: [PATCH 48/92] Add unit tests for passing view type correctly --- .../video_player/test/video_player_test.dart | 10 +++++++++ .../test/avfoundation_video_player_test.dart | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+) 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 c6e6de841aeb..900d75ddce5c 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -475,6 +475,16 @@ void main() { await controller.initialize(); expect(controller.value.hasError, equals(false)); }); + + test('uses passed view type', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File('a.avi'), + viewType: VideoViewType.platformView); + await controller.initialize(); + + expect(fakeVideoPlayerPlatform.dataSources[0].viewType, + VideoViewType.platformView); + }); }); test('contentUri', () async { 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 dac0553fbed0..d23c73dd20a8 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 @@ -188,6 +188,28 @@ void main() { expect(textureId, 3); }); + test('create with texture view', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + // Texture view is a default view type. + )); + expect(log.log.last, 'create'); + expect(log.creationOptions?.viewType, PlatformVideoViewType.textureView); + expect(textureId, 3); + }); + + test('create with platform view', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + viewType: VideoViewType.platformView, + )); + expect(log.log.last, 'create'); + expect(log.creationOptions?.viewType, PlatformVideoViewType.platformView); + expect(textureId, 3); + }); + test('setLooping', () async { await player.setLooping(1, true); expect(log.log.last, 'setLooping'); From cf7cdf476cd72e52fce41653c63ec3527e13e045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 11:18:25 +0100 Subject: [PATCH 49/92] Throw an assertion if platform view is used on platform different than iOS and Android --- .../video_player/lib/video_player.dart | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index fa1f6bcac658..47c17226e879 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -280,7 +280,9 @@ class VideoPlayerController extends ValueNotifier { dataSourceType = DataSourceType.asset, formatHint = null, httpHeaders = const {}, - super(const VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)) { + _validateConstructorInput(); + } /// Constructs a [VideoPlayerController] playing a network video. /// @@ -302,7 +304,9 @@ class VideoPlayerController extends ValueNotifier { }) : _closedCaptionFileFuture = closedCaptionFile, dataSourceType = DataSourceType.network, package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)) { + _validateConstructorInput(); + } /// Constructs a [VideoPlayerController] playing a network video. /// @@ -324,7 +328,9 @@ class VideoPlayerController extends ValueNotifier { dataSource = url.toString(), dataSourceType = DataSourceType.network, package = null, - super(const VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)) { + _validateConstructorInput(); + } /// Constructs a [VideoPlayerController] playing a video from a file. /// @@ -341,7 +347,9 @@ class VideoPlayerController extends ValueNotifier { dataSourceType = DataSourceType.file, package = null, formatHint = null, - super(const VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)) { + _validateConstructorInput(); + } /// Constructs a [VideoPlayerController] playing a video from a contentUri. /// @@ -360,7 +368,9 @@ class VideoPlayerController extends ValueNotifier { package = null, formatHint = null, httpHeaders = const {}, - super(const VideoPlayerValue(duration: Duration.zero)); + super(const VideoPlayerValue(duration: Duration.zero)) { + _validateConstructorInput(); + } /// The URI to the video file. This will be in different formats depending on /// the [DataSourceType] of the original video. @@ -406,6 +416,15 @@ class VideoPlayerController extends ValueNotifier { @visibleForTesting int get textureId => _textureId; + /// Validates the values passed in the constructor. + void _validateConstructorInput() { + assert( + viewType != VideoViewType.platformView || + defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS, + 'VideoViewType.platformView is only supported on Android and iOS.'); + } + /// Attempts to open the given [dataSource] and load metadata about the video. Future initialize() async { final bool allowBackgroundPlayback = From 5cc2959917e96e062960dad9e50596ceef5900a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 12:36:34 +0100 Subject: [PATCH 50/92] Adjust example app to allow changing view type on iOS --- .../video_player/example/lib/main.dart | 107 ++++++++++++++++-- .../video_player/lib/video_player.dart | 3 +- 2 files changed, 102 insertions(+), 8 deletions(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 0ce77d603b5a..c3aa5fc7649a 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -8,6 +8,8 @@ /// video. library; +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; @@ -55,18 +57,97 @@ class _App extends StatelessWidget { ), ), body: TabBarView( - children: [ - _BumbleBeeRemoteVideo(), - _ButterFlyAssetVideo(), - _ButterFlyAssetVideoInList(), - ], + children: Platform.isIOS + ? [ + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeRemoteVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _ButterFlyAssetVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _ButterFlyAssetVideoInList(viewType), + ), + ] + // We don't support platform views on other platforms yet. + : const [ + _BumbleBeeRemoteVideo(VideoViewType.textureView), + _ButterFlyAssetVideo(VideoViewType.textureView), + _ButterFlyAssetVideoInList(VideoViewType.textureView), + ], ), ), ); } } +class _ViewTypeTabBar extends StatefulWidget { + const _ViewTypeTabBar({ + required this.builder, + }); + + final Widget Function(VideoViewType) builder; + + @override + State<_ViewTypeTabBar> createState() => _ViewTypeTabBarState(); +} + +class _ViewTypeTabBarState extends State<_ViewTypeTabBar> + with SingleTickerProviderStateMixin { + late final TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TabBar( + controller: _tabController, + isScrollable: true, + tabs: const [ + Tab( + icon: Icon(Icons.texture), + text: 'Texture view', + ), + Tab( + icon: Icon(Icons.construction), + text: 'Platform view', + ), + ], + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + widget.builder(VideoViewType.textureView), + widget.builder(VideoViewType.platformView), + ], + ), + ), + ], + ); + } +} + class _ButterFlyAssetVideoInList extends StatelessWidget { + const _ButterFlyAssetVideoInList(this.viewType); + + final VideoViewType viewType; + @override Widget build(BuildContext context) { return ListView( @@ -90,7 +171,7 @@ class _ButterFlyAssetVideoInList extends StatelessWidget { alignment: FractionalOffset.bottomRight + const FractionalOffset(-0.1, -0.1), children: [ - _ButterFlyAssetVideo(), + _ButterFlyAssetVideo(viewType), Image.asset('assets/flutter-mark-square-64.png'), ]), ], @@ -150,6 +231,10 @@ class _ExampleCard extends StatelessWidget { } class _ButterFlyAssetVideo extends StatefulWidget { + const _ButterFlyAssetVideo(this.viewType); + + final VideoViewType viewType; + @override _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); } @@ -160,7 +245,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { @override void initState() { super.initState(); - _controller = VideoPlayerController.asset('assets/Butterfly-209.mp4'); + _controller = VideoPlayerController.asset( + 'assets/Butterfly-209.mp4', + viewType: widget.viewType, + ); _controller.addListener(() { setState(() {}); @@ -206,6 +294,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { } class _BumbleBeeRemoteVideo extends StatefulWidget { + const _BumbleBeeRemoteVideo(this.viewType); + + final VideoViewType viewType; + @override _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); } @@ -228,6 +320,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), closedCaptionFile: _loadCaptions(), videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), + viewType: widget.viewType, ); _controller.addListener(() { diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 47c17226e879..531f193b219a 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -20,7 +20,8 @@ export 'package:video_player_platform_interface/video_player_platform_interface. VideoFormat, VideoPlayerOptions, VideoPlayerWebOptions, - VideoPlayerWebOptionsControls; + VideoPlayerWebOptionsControls, + VideoViewType; export 'src/closed_caption_file.dart'; From f373efc8bc02ed386dc6fe6489f6eaac2eb7b95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 13:59:39 +0100 Subject: [PATCH 51/92] Add missing doc comments --- .../video_player_avfoundation/FVPVideoPlayer.h | 11 +++++------ .../FVPVideoPlayerTextureApproach.h | 4 ++++ .../include/FVPNativeVideoViewFactory.h | 4 ++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 5c96d2d7ff58..8b06b781029f 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -46,21 +46,20 @@ NS_ASSUME_NONNULL_BEGIN -/// Initializes a new instance of FVPVideoPlayer with the given URL, frame updater, display link, -/// HTTP headers, AV factory, and registrar. +/// Initializes a new instance of FVPVideoPlayer with the given URL, HTTP headers, AV factory, and +/// registrar. - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers avFactory:(id)avFactory registrar:(NSObject *)registrar; -/// Initializes a new instance of FVPVideoPlayer with the given AVPlayerItem, frame updater, display -/// link, AV factory, and registrar. +/// Initializes a new instance of FVPVideoPlayer with the given AVPlayerItem, AV factory, and +/// registrar. - (instancetype)initWithPlayerItem:(AVPlayerItem *)item avFactory:(id)avFactory registrar:(NSObject *)registrar; -/// Initializes a new instance of FVPVideoPlayer with the given asset, frame updater, display link, -/// AV factory, and registrar. +/// Initializes a new instance of FVPVideoPlayer with the given asset, AV factory, and registrar. - (instancetype)initWithAsset:(NSString *)asset avFactory:(id)avFactory registrar:(NSObject *)registrar; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h index e9b11f0e367c..1ecb2b0d7b25 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayerTextureApproach.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN +/// Initializes a new instance of FVPVideoPlayerTextureApproach with the given URL, frame updater, +/// display link, HTTP headers, AV factory, and registrar. - (instancetype)initWithURL:(NSURL *)url frameUpdater:(FVPFrameUpdater *)frameUpdater displayLink:(FVPDisplayLink *)displayLink @@ -30,6 +32,8 @@ NS_ASSUME_NONNULL_BEGIN avFactory:(id)avFactory registrar:(NSObject *)registrar; +/// Initializes a new instance of FVPVideoPlayerTextureApproach with the given asset, frame updater, +/// display link, AV factory, and registrar. - (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FVPFrameUpdater *)frameUpdater displayLink:(FVPDisplayLink *)displayLink diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h index fe04fd2967fb..ac0a94a40c22 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h @@ -6,7 +6,11 @@ #import "FVPVideoPlayer.h" +/// A factory class responsible for creating native video views that can be embedded in a +/// Flutter app. @interface FVPNativeVideoViewFactory : NSObject +/// Initializes a new instance of FVPNativeVideoViewFactory with the given messenger and +/// playersByTextureId dictionary which stores the video players associated with the texture IDs. - (instancetype)initWithMessenger:(NSObject *)messenger playersByTextureId: (NSMutableDictionary *)playersByTextureId; From 29d5f5f861304fdc71cc636041bced3dff41cb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 27 Nov 2024 14:00:05 +0100 Subject: [PATCH 52/92] Remove placeholder files which are not needed anymore --- .../Sources/video_player_avfoundation_ios/include/.gitkeep | 0 .../video_player_avfoundation_ios/include/FVPEmpty.h | 6 ------ 2 files changed, 6 deletions(-) delete mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep delete mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h deleted file mode 100644 index c7b344f7299d..000000000000 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h +++ /dev/null @@ -1,6 +0,0 @@ -// 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. - -// Empty file to perserve include directory in pub-cache. See -// https://github.com/flutter/flutter/issues/148002 for more information. From ea9e1dde59b3d5e4e315d00922ed41d13a93d36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 13:02:24 +0100 Subject: [PATCH 53/92] Add native view factory for Android (dirty code) --- .../flutter/plugins/videoplayer/Messages.java | 226 +++++++++------ .../plugins/videoplayer/NativeView.java | 62 +++++ .../videoplayer/NativeViewFactory.java | 33 +++ .../plugins/videoplayer/VideoPlayer.java | 2 + .../videoplayer/VideoPlayerPlugin.java | 4 + .../lib/src/android_video_player.dart | 47 ++++ .../lib/src/messages.g.dart | 144 +++++----- .../pigeons/messages.dart | 16 ++ .../video_player_android/test/test_api.g.dart | 259 +++++++----------- 9 files changed, 479 insertions(+), 314 deletions(-) create mode 100644 packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java create mode 100644 packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java 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 870045518852..492b9ad7c565 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.5.0), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.videoplayer; @@ -21,6 +21,10 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -37,7 +41,8 @@ public static class FlutterError extends RuntimeException { /** The error details. Must be a datatype supported by the api codec. */ public final Object details; - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) { + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { super(message); this.code = code; this.details = details; @@ -56,7 +61,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -65,6 +70,85 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { @Retention(CLASS) @interface CanIgnoreReturnValue {} + /** Pigeon equivalent of VideoViewType. */ + public enum PlatformVideoViewType { + TEXTURE_VIEW(0), + PLATFORM_VIEW(1); + + final int index; + + PlatformVideoViewType(final int index) { + this.index = index; + } + } + + /** + * Information passed to the platform view creation. + * + * Generated class from Pigeon that represents data sent in messages. + */ + public static final class PlatformVideoViewCreationParams { + private @NonNull Long playerId; + + public @NonNull Long getPlayerId() { + return playerId; + } + + public void setPlayerId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"playerId\" is null."); + } + this.playerId = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformVideoViewCreationParams() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + PlatformVideoViewCreationParams that = (PlatformVideoViewCreationParams) o; + return playerId.equals(that.playerId); + } + + @Override + public int hashCode() { + return Objects.hash(playerId); + } + + public static final class Builder { + + private @Nullable Long playerId; + + @CanIgnoreReturnValue + public @NonNull Builder setPlayerId(@NonNull Long setterArg) { + this.playerId = setterArg; + return this; + } + + public @NonNull PlatformVideoViewCreationParams build() { + PlatformVideoViewCreationParams pigeonReturn = new PlatformVideoViewCreationParams(); + pigeonReturn.setPlayerId(playerId); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(1); + toListResult.add(playerId); + return toListResult; + } + + static @NonNull PlatformVideoViewCreationParams fromList(@NonNull ArrayList pigeonVar_list) { + PlatformVideoViewCreationParams pigeonResult = new PlatformVideoViewCreationParams(); + Object playerId = pigeonVar_list.get(0); + pigeonResult.setPlayerId((Long) playerId); + return pigeonResult; + } + } + /** Generated class from Pigeon that represents data sent in messages. */ public static final class CreateMessage { private @Nullable String asset; @@ -125,18 +209,10 @@ public void setHttpHeaders(@NonNull Map setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } CreateMessage that = (CreateMessage) o; - return Objects.equals(asset, that.asset) - && Objects.equals(uri, that.uri) - && Objects.equals(packageName, that.packageName) - && Objects.equals(formatHint, that.formatHint) - && httpHeaders.equals(that.httpHeaders); + return Objects.equals(asset, that.asset) && Objects.equals(uri, that.uri) && Objects.equals(packageName, that.packageName) && Objects.equals(formatHint, that.formatHint) && httpHeaders.equals(that.httpHeaders); } @Override @@ -232,7 +308,13 @@ private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 129: + case (byte) 129: { + Object value = readValue(buffer); + return value == null ? null : PlatformVideoViewType.values()[((Long) value).intValue()]; + } + case (byte) 130: + return PlatformVideoViewCreationParams.fromList((ArrayList) readValue(buffer)); + case (byte) 131: return CreateMessage.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -241,8 +323,14 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof CreateMessage) { + if (value instanceof PlatformVideoViewType) { stream.write(129); + writeValue(stream, value == null ? null : ((PlatformVideoViewType) value).index); + } else if (value instanceof PlatformVideoViewCreationParams) { + stream.write(130); + writeValue(stream, ((PlatformVideoViewCreationParams) value).toList()); + } else if (value instanceof CreateMessage) { + stream.write(131); writeValue(stream, ((CreateMessage) value).toList()); } else { super.writeValue(stream, value); @@ -255,7 +343,7 @@ public interface AndroidVideoPlayerApi { void initialize(); - @NonNull + @NonNull Long create(@NonNull CreateMessage msg); void dispose(@NonNull Long textureId); @@ -268,7 +356,7 @@ public interface AndroidVideoPlayerApi { void play(@NonNull Long textureId); - @NonNull + @NonNull Long position(@NonNull Long textureId); void seekTo(@NonNull Long textureId, @NonNull Long position); @@ -281,27 +369,16 @@ public interface AndroidVideoPlayerApi { static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } - /** - * Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the - * `binaryMessenger`. - */ - static void setUp( - @NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVideoPlayerApi api) { + /**Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the `binaryMessenger`. */ + static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVideoPlayerApi api) { setUp(binaryMessenger, "", api); } - - static void setUp( - @NonNull BinaryMessenger binaryMessenger, - @NonNull String messageChannelSuffix, - @Nullable AndroidVideoPlayerApi api) { + static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable AndroidVideoPlayerApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -309,7 +386,8 @@ static void setUp( try { api.initialize(); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -321,10 +399,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -334,7 +409,8 @@ static void setUp( try { Long output = api.create(msgArg); wrapped.add(0, output); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -346,10 +422,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -359,7 +432,8 @@ static void setUp( try { api.dispose(textureIdArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -371,10 +445,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -385,7 +456,8 @@ static void setUp( try { api.setLooping(textureIdArg, loopingArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -397,10 +469,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -411,7 +480,8 @@ static void setUp( try { api.setVolume(textureIdArg, volumeArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -423,10 +493,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -437,7 +504,8 @@ static void setUp( try { api.setPlaybackSpeed(textureIdArg, speedArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -449,10 +517,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -462,7 +527,8 @@ static void setUp( try { api.play(textureIdArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -474,10 +540,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -487,7 +550,8 @@ static void setUp( try { Long output = api.position(textureIdArg); wrapped.add(0, output); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -499,10 +563,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -513,7 +574,8 @@ static void setUp( try { api.seekTo(textureIdArg, positionArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -525,10 +587,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -538,7 +597,8 @@ static void setUp( try { api.pause(textureIdArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); @@ -550,10 +610,7 @@ static void setUp( { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -563,7 +620,8 @@ static void setUp( try { api.setMixWithOthers(mixWithOthersArg); wrapped.add(0, null); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java new file mode 100644 index 000000000000..d53de34b8284 --- /dev/null +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -0,0 +1,62 @@ +package io.flutter.plugins.videoplayer; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.media3.exoplayer.ExoPlayer; +import io.flutter.plugin.platform.PlatformView; + +class NativeView implements PlatformView { + @NonNull private final TextureView textureView; + + NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { + textureView = new TextureView(context); + + // Set the Surface for the ExoPlayer + textureView.setSurfaceTextureListener( + new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable( + @NonNull SurfaceTexture surface, int width, int height) { + Surface videoSurface = new Surface(surface); + exoPlayer.setVideoSurface(videoSurface); + exoPlayer.play(); + } + + @Override + public void onSurfaceTextureSizeChanged( + @NonNull SurfaceTexture surface, int width, int height) { + // FIXME Implement or remove + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + exoPlayer.setVideoSurface(null); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // FIXME Implement or remove + } + }); + } + + @NonNull + @Override + public View getView() { + return textureView; + } + + @Override + public void dispose() { + SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); + if (surfaceTexture != null) { + surfaceTexture.release(); + } + textureView.setSurfaceTextureListener(null); + } +} diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java new file mode 100644 index 000000000000..02fb6750ecf8 --- /dev/null +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java @@ -0,0 +1,33 @@ +package io.flutter.plugins.videoplayer; + +import android.content.Context; +import android.util.LongSparseArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.media3.exoplayer.ExoPlayer; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.plugin.platform.PlatformView; +import io.flutter.plugin.platform.PlatformViewFactory; +import java.util.Objects; + +class NativeViewFactory extends PlatformViewFactory { + private final LongSparseArray videoPlayers; + + NativeViewFactory(LongSparseArray videoPlayers) { + super(StandardMessageCodec.INSTANCE); + this.videoPlayers = videoPlayers; + } + + @NonNull + @Override + public PlatformView create(@NonNull Context context, int id, @Nullable Object args) { + final Messages.PlatformVideoViewCreationParams params = + Objects.requireNonNull((Messages.PlatformVideoViewCreationParams) args); + final Long playerId = params.getPlayerId(); + + final VideoPlayer player = videoPlayers.get(playerId); + final ExoPlayer exoPlayer = player.getExoPlayer(); + + return new NativeView(context, id, exoPlayer); + } +} 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 c481df2d7796..0e84fdc1022d 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 @@ -160,6 +160,8 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + ExoPlayer getExoPlayer() { return exoPlayer; } + void dispose() { exoPlayer.release(); surfaceProducer.release(); 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 d248ad2f0be8..9907f32e6971 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 @@ -37,6 +37,10 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { injector.flutterLoader()::getLookupKeyForAsset, binding.getTextureRegistry()); flutterState.startListening(this, binding.getBinaryMessenger()); + binding + .getPlatformViewRegistry() + .registerViewFactory( + "plugins.flutter.dev/video_player_android", new NativeViewFactory(videoPlayers)); } @Override 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 45665631d932..1e86833dbc0a 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 @@ -4,6 +4,9 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -146,6 +149,50 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { return Texture(textureId: textureId); } + @override + Widget buildViewWithOptions(VideoViewOptions options) { + final int playerId = options.playerId; + + return switch (options.viewType) { + VideoViewType.textureView => Texture(textureId: playerId), + VideoViewType.platformView => _buildPlatformView(playerId), + }; + } + + Widget _buildPlatformView(int playerId) { + const String viewType = 'plugins.flutter.dev/video_player_android'; + final PlatformVideoViewCreationParams creationParams = + PlatformVideoViewCreationParams(playerId: playerId); + + // FIXME Check if this setup is complete + + return PlatformViewLink( + viewType: viewType, + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: viewType, + layoutDirection: TextDirection.ltr, + creationParams: creationParams, + creationParamsCodec: AndroidVideoPlayerApi.pigeonChannelCodec, + onFocus: () => params.onFocusChanged(true), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ); + } + @override Future setMixWithOthers(bool mixWithOthers) { return _api.setMixWithOthers(mixWithOthers); 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 e3190b389e4e..32bf2855c2be 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.5.0), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), 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 @@ -18,8 +18,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -29,6 +28,34 @@ List wrapResponse( return [error.code, error.message, error.details]; } +/// Pigeon equivalent of VideoViewType. +enum PlatformVideoViewType { + textureView, + platformView, +} + +/// Information passed to the platform view creation. +class PlatformVideoViewCreationParams { + PlatformVideoViewCreationParams({ + required this.playerId, + }); + + int playerId; + + Object encode() { + return [ + playerId, + ]; + } + + static PlatformVideoViewCreationParams decode(Object result) { + result as List; + return PlatformVideoViewCreationParams( + playerId: result[0]! as int, + ); + } +} + class CreateMessage { CreateMessage({ this.asset, @@ -65,12 +92,12 @@ class CreateMessage { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: - (result[4] as Map?)!.cast(), + httpHeaders: (result[4] as Map?)!.cast(), ); } } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -78,8 +105,14 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is CreateMessage) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is PlatformVideoViewCreationParams) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is CreateMessage) { + buffer.putUint8(131); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -89,7 +122,12 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformVideoViewType.values[value]; + case 130: + return PlatformVideoViewCreationParams.decode(readValue(buffer)!); + case 131: return CreateMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -101,11 +139,9 @@ class AndroidVideoPlayerApi { /// Constructor for [AndroidVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AndroidVideoPlayerApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AndroidVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -113,10 +149,8 @@ class AndroidVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -137,10 +171,8 @@ class AndroidVideoPlayerApi { } Future create(CreateMessage msg) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -166,10 +198,8 @@ class AndroidVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -190,16 +220,14 @@ class AndroidVideoPlayerApi { } Future setLooping(int textureId, bool looping) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([textureId, looping]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([textureId, looping]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -214,16 +242,14 @@ class AndroidVideoPlayerApi { } Future setVolume(int textureId, double volume) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([textureId, volume]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([textureId, volume]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -238,16 +264,14 @@ class AndroidVideoPlayerApi { } Future setPlaybackSpeed(int textureId, double speed) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([textureId, speed]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([textureId, speed]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -262,10 +286,8 @@ class AndroidVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -286,10 +308,8 @@ class AndroidVideoPlayerApi { } Future position(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -315,16 +335,14 @@ class AndroidVideoPlayerApi { } Future seekTo(int textureId, int position) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([textureId, position]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([textureId, position]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -339,10 +357,8 @@ class AndroidVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -363,16 +379,14 @@ class AndroidVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index 37bde3dcda5f..e4f36f51ef3e 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -13,6 +13,22 @@ import 'package:pigeon/pigeon.dart'; ), copyrightHeader: 'pigeons/copyright.txt', )) + +/// Pigeon equivalent of VideoViewType. +enum PlatformVideoViewType { + textureView, + platformView, +} + +/// Information passed to the platform view creation. +class PlatformVideoViewCreationParams { + const PlatformVideoViewCreationParams({ + required this.playerId, + }); + + final int playerId; +} + class CreateMessage { CreateMessage({required this.httpHeaders}); String? asset; 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 838dcfbc5a21..e11ea4d0ef2d 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.5.0), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), 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 @@ -13,6 +13,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_android/src/messages.g.dart'; + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -20,8 +21,14 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is CreateMessage) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is PlatformVideoViewCreationParams) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is CreateMessage) { + buffer.putUint8(131); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -31,7 +38,12 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformVideoViewType.values[value]; + case 130: + return PlatformVideoViewCreationParams.decode(readValue(buffer)!); + case 131: return CreateMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -40,8 +52,7 @@ class _PigeonCodec extends StandardMessageCodec { } abstract class TestHostVideoPlayerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); void initialize(); @@ -66,55 +77,37 @@ abstract class TestHostVideoPlayerApi { void setMixWithOthers(bool mixWithOthers); - static void setUp( - TestHostVideoPlayerApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp(TestHostVideoPlayerApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { try { api.initialize(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create was null.'); final List args = (message as List?)!; final CreateMessage? arg_msg = (args[0] as CreateMessage?); assert(arg_msg != null, @@ -124,29 +117,22 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -156,29 +142,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -191,29 +170,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -226,29 +198,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -261,29 +226,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -293,29 +251,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -325,29 +276,22 @@ abstract class TestHostVideoPlayerApi { return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -360,29 +304,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final int? arg_textureId = (args[0] as int?); assert(arg_textureId != null, @@ -392,29 +329,22 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final bool? arg_mixWithOthers = (args[0] as bool?); assert(arg_mixWithOthers != null, @@ -424,9 +354,8 @@ abstract class TestHostVideoPlayerApi { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } From dfd1441595891f4261e73df44e5f3c1fe7426a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 14:08:51 +0100 Subject: [PATCH 54/92] Pass view type when creating android video player --- .../flutter/plugins/videoplayer/Messages.java | 28 +++++++++++++++++-- .../videoplayer/NativeViewFactory.java | 2 +- .../example/lib/mini_controller.dart | 8 +++++- .../lib/src/android_video_player.dart | 10 +++++++ .../lib/src/messages.g.dart | 5 ++++ .../pigeons/messages.dart | 1 + 6 files changed, 49 insertions(+), 5 deletions(-) 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 492b9ad7c565..075bb321b2e1 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 @@ -204,6 +204,16 @@ public void setHttpHeaders(@NonNull Map setterArg) { this.httpHeaders = setterArg; } + private @Nullable PlatformVideoViewType viewType; + + public @Nullable PlatformVideoViewType getViewType() { + return viewType; + } + + public void setViewType(@Nullable PlatformVideoViewType setterArg) { + this.viewType = setterArg; + } + /** Constructor is non-public to enforce null safety; use Builder. */ CreateMessage() {} @@ -212,12 +222,12 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CreateMessage that = (CreateMessage) o; - return Objects.equals(asset, that.asset) && Objects.equals(uri, that.uri) && Objects.equals(packageName, that.packageName) && Objects.equals(formatHint, that.formatHint) && httpHeaders.equals(that.httpHeaders); + return Objects.equals(asset, that.asset) && Objects.equals(uri, that.uri) && Objects.equals(packageName, that.packageName) && Objects.equals(formatHint, that.formatHint) && httpHeaders.equals(that.httpHeaders) && Objects.equals(viewType, that.viewType); } @Override public int hashCode() { - return Objects.hash(asset, uri, packageName, formatHint, httpHeaders); + return Objects.hash(asset, uri, packageName, formatHint, httpHeaders, viewType); } public static final class Builder { @@ -262,6 +272,14 @@ public static final class Builder { return this; } + private @Nullable PlatformVideoViewType viewType; + + @CanIgnoreReturnValue + public @NonNull Builder setViewType(@Nullable PlatformVideoViewType setterArg) { + this.viewType = setterArg; + return this; + } + public @NonNull CreateMessage build() { CreateMessage pigeonReturn = new CreateMessage(); pigeonReturn.setAsset(asset); @@ -269,18 +287,20 @@ public static final class Builder { pigeonReturn.setPackageName(packageName); pigeonReturn.setFormatHint(formatHint); pigeonReturn.setHttpHeaders(httpHeaders); + pigeonReturn.setViewType(viewType); return pigeonReturn; } } @NonNull ArrayList toList() { - ArrayList toListResult = new ArrayList<>(5); + ArrayList toListResult = new ArrayList<>(6); toListResult.add(asset); toListResult.add(uri); toListResult.add(packageName); toListResult.add(formatHint); toListResult.add(httpHeaders); + toListResult.add(viewType); return toListResult; } @@ -296,6 +316,8 @@ ArrayList toList() { pigeonResult.setFormatHint((String) formatHint); Object httpHeaders = pigeonVar_list.get(4); pigeonResult.setHttpHeaders((Map) httpHeaders); + Object viewType = pigeonVar_list.get(5); + pigeonResult.setViewType((PlatformVideoViewType) viewType); return pigeonResult; } } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java index 02fb6750ecf8..577cb62e8973 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java @@ -14,7 +14,7 @@ class NativeViewFactory extends PlatformViewFactory { private final LongSparseArray videoPlayers; NativeViewFactory(LongSparseArray videoPlayers) { - super(StandardMessageCodec.INSTANCE); + super(Messages.AndroidVideoPlayerApi.getCodec()); this.videoPlayers = videoPlayers; } diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index a38013533d62..bdcaf4a4264b 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -422,7 +422,13 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == MiniController.kUninitializedTextureId ? Container() - : _platform.buildView(_textureId); + : _platform.buildViewWithOptions( + VideoViewOptions( + playerId: _textureId, + // FIXME Do not hardcode this + viewType: VideoViewType.platformView, + ), + ); } } 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 1e86833dbc0a..31ed3f8b5f97 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 @@ -60,6 +60,7 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { uri: uri, httpHeaders: httpHeaders, formatHint: formatHint, + viewType: _platformVideoViewTypeFromVideoViewType(dataSource.viewType), ); return _api.create(message); @@ -218,3 +219,12 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { ); } } + +PlatformVideoViewType _platformVideoViewTypeFromVideoViewType( + VideoViewType viewType, +) { + return switch (viewType) { + VideoViewType.textureView => PlatformVideoViewType.textureView, + VideoViewType.platformView => PlatformVideoViewType.platformView, + }; +} 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 32bf2855c2be..72c4d8b15577 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 @@ -63,6 +63,7 @@ class CreateMessage { this.packageName, this.formatHint, required this.httpHeaders, + this.viewType, }); String? asset; @@ -75,6 +76,8 @@ class CreateMessage { Map httpHeaders; + PlatformVideoViewType? viewType; + Object encode() { return [ asset, @@ -82,6 +85,7 @@ class CreateMessage { packageName, formatHint, httpHeaders, + viewType, ]; } @@ -93,6 +97,7 @@ class CreateMessage { packageName: result[2] as String?, formatHint: result[3] as String?, httpHeaders: (result[4] as Map?)!.cast(), + viewType: result[5] as PlatformVideoViewType?, ); } } diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index e4f36f51ef3e..b060ec6c8467 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -36,6 +36,7 @@ class CreateMessage { String? packageName; String? formatHint; Map httpHeaders; + PlatformVideoViewType? viewType; } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') From 6ea2011ca26c000d3551d81bb85f8640062ebcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 14:15:52 +0100 Subject: [PATCH 55/92] Add IgnorePointer above platform view --- .../lib/src/android_video_player.dart | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) 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 31ed3f8b5f97..664a41d2e373 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 @@ -167,30 +167,33 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { // FIXME Check if this setup is complete - return PlatformViewLink( - viewType: viewType, - surfaceFactory: ( - BuildContext context, - PlatformViewController controller, - ) { - return AndroidViewSurface( - controller: controller as AndroidViewController, - gestureRecognizers: const >{}, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - ); - }, - onCreatePlatformView: (PlatformViewCreationParams params) { - return PlatformViewsService.initSurfaceAndroidView( - id: params.id, - viewType: viewType, - layoutDirection: TextDirection.ltr, - creationParams: creationParams, - creationParamsCodec: AndroidVideoPlayerApi.pigeonChannelCodec, - onFocus: () => params.onFocusChanged(true), - ) - ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) - ..create(); - }, + return IgnorePointer( + // IgnorePointer so that GestureDetector can be used above the platform view. + child: PlatformViewLink( + viewType: viewType, + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: viewType, + layoutDirection: TextDirection.ltr, + creationParams: creationParams, + creationParamsCodec: AndroidVideoPlayerApi.pigeonChannelCodec, + onFocus: () => params.onFocusChanged(true), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ), ); } From eb0f154249f6de3bcac7d587749a9585dfe34086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 14:26:08 +0100 Subject: [PATCH 56/92] Allow choosing between platform view and texture view in example app --- .../example/lib/main.dart | 86 ++++++++++++++++++- .../example/lib/mini_controller.dart | 25 ++++-- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index 79f4963bbfa7..c9966c454518 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -5,6 +5,7 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'mini_controller.dart'; @@ -36,9 +37,17 @@ class _App extends StatelessWidget { ), body: TabBarView( children: [ - _BumbleBeeRemoteVideo(), - _RtspRemoteVideo(), - _ButterFlyAssetVideo(), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _BumbleBeeRemoteVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => _RtspRemoteVideo(viewType), + ), + _ViewTypeTabBar( + builder: (VideoViewType viewType) => + _ButterFlyAssetVideo(viewType), + ), ], ), ), @@ -46,7 +55,70 @@ class _App extends StatelessWidget { } } +class _ViewTypeTabBar extends StatefulWidget { + const _ViewTypeTabBar({ + required this.builder, + }); + + final Widget Function(VideoViewType) builder; + + @override + State<_ViewTypeTabBar> createState() => _ViewTypeTabBarState(); +} + +class _ViewTypeTabBarState extends State<_ViewTypeTabBar> + with SingleTickerProviderStateMixin { + late final TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TabBar( + controller: _tabController, + isScrollable: true, + tabs: const [ + Tab( + icon: Icon(Icons.texture), + text: 'Texture view', + ), + Tab( + icon: Icon(Icons.construction), + text: 'Platform view', + ), + ], + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + widget.builder(VideoViewType.textureView), + widget.builder(VideoViewType.platformView), + ], + ), + ), + ], + ); + } +} + class _ButterFlyAssetVideo extends StatefulWidget { + const _ButterFlyAssetVideo(this.viewType); + + final VideoViewType viewType; + @override _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); } @@ -101,6 +173,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { } class _BumbleBeeRemoteVideo extends StatefulWidget { + const _BumbleBeeRemoteVideo(this.viewType); + + final VideoViewType viewType; + @override _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); } @@ -155,6 +231,10 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { } class _RtspRemoteVideo extends StatefulWidget { + const _RtspRemoteVideo(this.viewType); + + final VideoViewType viewType; + @override _RtspRemoteVideoState createState() => _RtspRemoteVideoState(); } diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index bdcaf4a4264b..d495b739ca0c 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -167,20 +167,27 @@ class MiniController extends ValueNotifier { /// The name of the asset is given by the [dataSource] argument and must not be /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. - MiniController.asset(this.dataSource, {this.package}) - : dataSourceType = DataSourceType.asset, + MiniController.asset( + this.dataSource, { + this.package, + this.viewType = VideoViewType.textureView, + }) : dataSourceType = DataSourceType.asset, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from /// the network. - MiniController.network(this.dataSource) - : dataSourceType = DataSourceType.network, + MiniController.network( + this.dataSource, { + this.viewType = VideoViewType.textureView, + }) : dataSourceType = DataSourceType.network, package = null, super(const VideoPlayerValue(duration: Duration.zero)); /// Constructs a [MiniController] playing a video from obtained from a file. - MiniController.file(File file) - : dataSource = Uri.file(file.absolute.path).toString(), + MiniController.file( + File file, { + this.viewType = VideoViewType.textureView, + }) : dataSource = Uri.file(file.absolute.path).toString(), dataSourceType = DataSourceType.file, package = null, super(const VideoPlayerValue(duration: Duration.zero)); @@ -196,6 +203,9 @@ class MiniController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; + /// The type of view used to display the video. + final VideoViewType viewType; + Timer? _timer; Completer? _creatingCompleter; StreamSubscription? _eventSubscription; @@ -425,8 +435,7 @@ class _VideoPlayerState extends State { : _platform.buildViewWithOptions( VideoViewOptions( playerId: _textureId, - // FIXME Do not hardcode this - viewType: VideoViewType.platformView, + viewType: widget.controller.viewType, ), ); } From 5effae9d31e8207f7d08885547cb2e38f7dbe005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 14:34:34 +0100 Subject: [PATCH 57/92] Add copyright entries to native view files --- .../main/java/io/flutter/plugins/videoplayer/NativeView.java | 4 ++++ .../io/flutter/plugins/videoplayer/NativeViewFactory.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index d53de34b8284..9cfe74d430a6 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -1,3 +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. + package io.flutter.plugins.videoplayer; import android.content.Context; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java index 577cb62e8973..041fc5de44da 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java @@ -1,3 +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. + package io.flutter.plugins.videoplayer; import android.content.Context; From 9e3458aeb6eed2caec07faf0cc0a0330ed561a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 28 Nov 2024 15:57:30 +0100 Subject: [PATCH 58/92] Pass view type to video player correctly in the example app --- .../video_player_android/example/lib/main.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index c9966c454518..e4f87356f7a3 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -129,7 +129,10 @@ class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { @override void initState() { super.initState(); - _controller = MiniController.asset('assets/Butterfly-209.mp4'); + _controller = MiniController.asset( + 'assets/Butterfly-209.mp4', + viewType: widget.viewType, + ); _controller.addListener(() { setState(() {}); @@ -189,6 +192,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { super.initState(); _controller = MiniController.network( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + viewType: widget.viewType, ); _controller.addListener(() { @@ -254,7 +258,10 @@ class _RtspRemoteVideoState extends State<_RtspRemoteVideo> { } setState(() { - _controller = MiniController.network(url); + _controller = MiniController.network( + url, + viewType: widget.viewType, + ); }); _controller!.addListener(() { From 9ba04efd08029621e155c981f522d83254e11e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 29 Nov 2024 10:43:16 +0100 Subject: [PATCH 59/92] Do not play the video automatically in platform view --- .../main/java/io/flutter/plugins/videoplayer/NativeView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 9cfe74d430a6..65fcd09241d5 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -19,15 +19,14 @@ class NativeView implements PlatformView { NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { textureView = new TextureView(context); - // Set the Surface for the ExoPlayer textureView.setSurfaceTextureListener( new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable( @NonNull SurfaceTexture surface, int width, int height) { Surface videoSurface = new Surface(surface); + // FIXME The same thing is in VideoPlayer.java. Maybe it is the same surface? exoPlayer.setVideoSurface(videoSurface); - exoPlayer.play(); } @Override From 7a4c16024f54c3d753839838452f34ab284fc3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 29 Nov 2024 18:04:59 +0100 Subject: [PATCH 60/92] Use SurfaceView instead of TextureView in native view implementation --- .../plugins/videoplayer/NativeView.java | 45 +++---------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 65fcd09241d5..50ebd8d37f99 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -5,61 +5,28 @@ package io.flutter.plugins.videoplayer; import android.content.Context; -import android.graphics.SurfaceTexture; -import android.view.Surface; -import android.view.TextureView; +import android.view.SurfaceView; import android.view.View; import androidx.annotation.NonNull; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugin.platform.PlatformView; class NativeView implements PlatformView { - @NonNull private final TextureView textureView; + @NonNull private final SurfaceView surfaceView; NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { - textureView = new TextureView(context); - - textureView.setSurfaceTextureListener( - new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable( - @NonNull SurfaceTexture surface, int width, int height) { - Surface videoSurface = new Surface(surface); - // FIXME The same thing is in VideoPlayer.java. Maybe it is the same surface? - exoPlayer.setVideoSurface(videoSurface); - } - - @Override - public void onSurfaceTextureSizeChanged( - @NonNull SurfaceTexture surface, int width, int height) { - // FIXME Implement or remove - } - - @Override - public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { - exoPlayer.setVideoSurface(null); - return true; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - // FIXME Implement or remove - } - }); + surfaceView = new SurfaceView(context); + exoPlayer.setVideoSurfaceView(surfaceView); } @NonNull @Override public View getView() { - return textureView; + return surfaceView; } @Override public void dispose() { - SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); - if (surfaceTexture != null) { - surfaceTexture.release(); - } - textureView.setSurfaceTextureListener(null); + surfaceView.getHolder().getSurface().release(); } } From 5bc6c47337f0f6c452c227001ea7bf6353e84b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 29 Nov 2024 18:50:10 +0100 Subject: [PATCH 61/92] Pass view type correctly to the native side in MiniController from example app --- .../video_player_android/example/lib/mini_controller.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index d495b739ca0c..10c346ebb2d9 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -231,21 +231,25 @@ class MiniController extends ValueNotifier { sourceType: DataSourceType.asset, asset: dataSource, package: package, + viewType: viewType, ); case DataSourceType.network: dataSourceDescription = DataSource( sourceType: DataSourceType.network, uri: dataSource, + viewType: viewType, ); case DataSourceType.file: dataSourceDescription = DataSource( sourceType: DataSourceType.file, uri: dataSource, + viewType: viewType, ); case DataSourceType.contentUri: dataSourceDescription = DataSource( sourceType: DataSourceType.contentUri, uri: dataSource, + viewType: viewType, ); } @@ -257,8 +261,10 @@ class MiniController extends ValueNotifier { void eventListener(VideoEvent event) { switch (event.eventType) { case VideoEventType.initialized: + print('LALA initialized'); value = value.copyWith( duration: event.duration, + // FIXME This is important! size: event.size, isInitialized: event.duration != null, ); From 760214b79ddc0ab7fc9c1141187cc7125009f6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 29 Nov 2024 18:51:58 +0100 Subject: [PATCH 62/92] Add separate video players for platform view texture and flutter texture --- .../plugins/videoplayer/VideoPlayer.java | 20 +----- .../videoplayer/VideoPlayerPlugin.java | 50 +++++++++---- .../VideoPlayerTextureApproach.java | 70 +++++++++++++++++++ 3 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java 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 0e84fdc1022d..418651141d2f 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 @@ -17,23 +17,20 @@ import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackParameters; import androidx.media3.exoplayer.ExoPlayer; -import io.flutter.view.TextureRegistry; -final class VideoPlayer implements TextureRegistry.SurfaceProducer.Callback { +class VideoPlayer { @NonNull private final ExoPlayerProvider exoPlayerProvider; @NonNull private final MediaItem mediaItem; - @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; @NonNull private final VideoPlayerCallbacks videoPlayerEvents; @NonNull private final VideoPlayerOptions options; - @NonNull private ExoPlayer exoPlayer; @Nullable private ExoPlayerState savedStateDuring; + @NonNull protected ExoPlayer exoPlayer; /** * Creates a video player. * * @param context application context. * @param events event callbacks. - * @param surfaceProducer produces a texture to render to. * @param asset asset to play. * @param options options for playback. * @return a video player instance. @@ -42,7 +39,6 @@ final class VideoPlayer implements TextureRegistry.SurfaceProducer.Callback { static VideoPlayer create( @NonNull Context context, @NonNull VideoPlayerCallbacks events, - @NonNull TextureRegistry.SurfaceProducer surfaceProducer, @NonNull VideoAsset asset, @NonNull VideoPlayerOptions options) { return new VideoPlayer( @@ -53,7 +49,6 @@ static VideoPlayer create( return builder.build(); }, events, - surfaceProducer, asset.getMediaItem(), options); } @@ -72,16 +67,13 @@ interface ExoPlayerProvider { VideoPlayer( @NonNull ExoPlayerProvider exoPlayerProvider, @NonNull VideoPlayerCallbacks events, - @NonNull TextureRegistry.SurfaceProducer surfaceProducer, @NonNull MediaItem mediaItem, @NonNull VideoPlayerOptions options) { this.exoPlayerProvider = exoPlayerProvider; this.videoPlayerEvents = events; - this.surfaceProducer = surfaceProducer; this.mediaItem = mediaItem; this.options = options; this.exoPlayer = createVideoPlayer(); - surfaceProducer.setCallback(this); } @RestrictTo(RestrictTo.Scope.LIBRARY) @@ -108,8 +100,6 @@ private ExoPlayer createVideoPlayer() { exoPlayer.setMediaItem(mediaItem); exoPlayer.prepare(); - exoPlayer.setVideoSurface(surfaceProducer.getSurface()); - boolean wasInitialized = savedStateDuring != null; exoPlayer.addListener(new ExoPlayerEventListener(exoPlayer, videoPlayerEvents, wasInitialized)); setAudioAttributes(exoPlayer, options.mixWithOthers); @@ -160,14 +150,10 @@ long getPosition() { return exoPlayer.getCurrentPosition(); } + @NonNull ExoPlayer getExoPlayer() { return exoPlayer; } void dispose() { exoPlayer.release(); - surfaceProducer.release(); - - // TODO(matanlurey): Remove when embedder no longer calls-back once released. - // https://github.com/flutter/flutter/issues/156434. - surfaceProducer.setCallback(null); } } 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 9907f32e6971..c3951a1d9876 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 @@ -18,6 +18,10 @@ /** Android platform implementation of the VideoPlayerPlugin. */ public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { + /// The next non-texture player ID, initialized to a high number to avoid collisions with + /// texture IDs (which are generated separately). + static Long nextNonTexturePlayerId = 1000000L; + private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); private FlutterState flutterState; @@ -76,11 +80,6 @@ public void initialize() { @Override public @NonNull Long create(@NonNull CreateMessage arg) { - TextureRegistry.SurfaceProducer handle = flutterState.textureRegistry.createSurfaceProducer(); - EventChannel eventChannel = - new EventChannel( - flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + handle.id()); - final VideoAsset videoAsset; if (arg.getAsset() != null) { String assetLookupKey; @@ -111,16 +110,37 @@ public void initialize() { } videoAsset = VideoAsset.fromRemoteUrl(arg.getUri(), streamingFormat, arg.getHttpHeaders()); } - videoPlayers.put( - handle.id(), - VideoPlayer.create( - flutterState.applicationContext, - VideoPlayerEventCallbacks.bindTo(eventChannel), - handle, - videoAsset, - options)); - - return handle.id(); + + Long id; + VideoPlayer videoPlayer; + if (arg.getViewType() == Messages.PlatformVideoViewType.PLATFORM_VIEW) { + id = VideoPlayerPlugin.nextNonTexturePlayerId++; + EventChannel eventChannel = + new EventChannel(flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + id); + + videoPlayer = + VideoPlayer.create( + flutterState.applicationContext, + VideoPlayerEventCallbacks.bindTo(eventChannel), + videoAsset, + options); + } else { + TextureRegistry.SurfaceProducer handle = flutterState.textureRegistry.createSurfaceProducer(); + id = handle.id(); + EventChannel eventChannel = + new EventChannel(flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + id); + + videoPlayer = + VideoPlayerTextureApproach.create( + flutterState.applicationContext, + VideoPlayerEventCallbacks.bindTo(eventChannel), + handle, + videoAsset, + options); + } + + videoPlayers.put(id, videoPlayer); + return id; } @NonNull diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java new file mode 100644 index 000000000000..1524bf6b112f --- /dev/null +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -0,0 +1,70 @@ +package io.flutter.plugins.videoplayer; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.media3.common.MediaItem; +import androidx.media3.exoplayer.ExoPlayer; + +import io.flutter.view.TextureRegistry; + +final class VideoPlayerTextureApproach extends VideoPlayer implements TextureRegistry.SurfaceProducer.Callback { + @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; + + /** + * Creates a video player. + * + * @param context application context. + * @param events event callbacks. + * @param surfaceProducer produces a texture to render to. + * @param asset asset to play. + * @param options options for playback. + * @return a video player instance. + */ + @NonNull + static VideoPlayerTextureApproach create( + @NonNull Context context, + @NonNull VideoPlayerCallbacks events, + @NonNull TextureRegistry.SurfaceProducer surfaceProducer, + @NonNull VideoAsset asset, + @NonNull VideoPlayerOptions options) { + return new VideoPlayerTextureApproach( + () -> { + ExoPlayer.Builder builder = + new ExoPlayer.Builder(context) + .setMediaSourceFactory(asset.getMediaSourceFactory(context)); + return builder.build(); + }, + events, + surfaceProducer, + asset.getMediaItem(), + options); + } + + @VisibleForTesting + VideoPlayerTextureApproach( + @NonNull ExoPlayerProvider exoPlayerProvider, + @NonNull VideoPlayerCallbacks events, + @NonNull TextureRegistry.SurfaceProducer surfaceProducer, + @NonNull MediaItem mediaItem, + @NonNull VideoPlayerOptions options) { + super(exoPlayerProvider, events, mediaItem, options); + + this.surfaceProducer = surfaceProducer; + surfaceProducer.setCallback(this); + + this.exoPlayer.setVideoSurface(surfaceProducer.getSurface()); + } + + + void dispose() { + surfaceProducer.release(); + // TODO(matanlurey): Remove when embedder no longer calls-back once released. + // https://github.com/flutter/flutter/issues/156434. + surfaceProducer.setCallback(null); + + super.dispose(); + } + +} From b9d1dfb346f4dab82f51ca40952becd07b97a90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 29 Nov 2024 18:54:37 +0100 Subject: [PATCH 63/92] Fix (?) sizing of the video player for platform view approach --- .../videoplayer/ExoPlayerEventListener.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index df6115fd5846..5b1b1342d3a8 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -11,6 +11,7 @@ import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; import androidx.media3.common.VideoSize; +import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.ExoPlayer; import java.util.Objects; @@ -68,6 +69,7 @@ private void setBuffering(boolean buffering) { } } + @OptIn(markerClass = UnstableApi.class) @SuppressWarnings("SuspiciousNameCombination") private void sendInitialized() { if (isInitialized) { @@ -76,8 +78,10 @@ private void sendInitialized() { isInitialized = true; VideoSize videoSize = exoPlayer.getVideoSize(); int rotationCorrection = 0; - int width = videoSize.width; - int height = videoSize.height; + // FIXME This is 0, 0 for platform view, because there is no surface assigned to the player? + // Is this solution okay? + int width = exoPlayer.getVideoFormat().width; + int height = exoPlayer.getVideoFormat().height; if (width != 0 && height != 0) { RotationDegrees reportedRotationCorrection = RotationDegrees.ROTATE_0; @@ -91,7 +95,8 @@ private void sendInitialized() { rotationCorrection = getRotationCorrectionFromUnappliedRotation(reportedRotationCorrection); } catch (IllegalArgumentException e) { - // Unapplied rotation other than 0, 90, 180, 270 reported by VideoSize. Because this is unexpected, + // Unapplied rotation other than 0, 90, 180, 270 reported by VideoSize. Because this is + // unexpected, // we apply no rotation correction. reportedRotationCorrection = RotationDegrees.ROTATE_0; rotationCorrection = 0; @@ -112,7 +117,8 @@ else if (Build.VERSION.SDK_INT < 29) { try { reportedRotationCorrection = RotationDegrees.fromDegrees(rotationCorrection); } catch (IllegalArgumentException e) { - // Rotation correction other than 0, 90, 180, 270 reported by Format. Because this is unexpected, + // Rotation correction other than 0, 90, 180, 270 reported by Format. Because this is + // unexpected, // we apply no rotation correction. reportedRotationCorrection = RotationDegrees.ROTATE_0; rotationCorrection = 0; @@ -178,7 +184,8 @@ public void onPlaybackStateChanged(final int playbackState) { public void onPlayerError(@NonNull final PlaybackException error) { setBuffering(false); if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { - // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window + // See + // https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window exoPlayer.seekToDefaultPosition(); exoPlayer.prepare(); } else { From d3e948b03a921ba93be9a50bc219204f45a5a3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 2 Dec 2024 16:05:45 +0100 Subject: [PATCH 64/92] Move texture registry related code to the class related to texture approach --- .../plugins/videoplayer/VideoPlayer.java | 37 ++++++------------- .../VideoPlayerTextureApproach.java | 32 ++++++++++++++-- 2 files changed, 39 insertions(+), 30 deletions(-) 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 418651141d2f..12de85842ec7 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 @@ -9,8 +9,6 @@ import android.content.Context; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; @@ -18,12 +16,11 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.exoplayer.ExoPlayer; -class VideoPlayer { +class VideoPlayer { @NonNull private final ExoPlayerProvider exoPlayerProvider; @NonNull private final MediaItem mediaItem; @NonNull private final VideoPlayerCallbacks videoPlayerEvents; @NonNull private final VideoPlayerOptions options; - @Nullable private ExoPlayerState savedStateDuring; @NonNull protected ExoPlayer exoPlayer; /** @@ -76,37 +73,23 @@ interface ExoPlayerProvider { this.exoPlayer = createVideoPlayer(); } - @RestrictTo(RestrictTo.Scope.LIBRARY) - // TODO(matanlurey): https://github.com/flutter/flutter/issues/155131. - @SuppressWarnings({"deprecation", "removal"}) - public void onSurfaceCreated() { - if (savedStateDuring != null) { - exoPlayer = createVideoPlayer(); - savedStateDuring.restore(exoPlayer); - savedStateDuring = null; - } - } - - @RestrictTo(RestrictTo.Scope.LIBRARY) - 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(); - } - - private ExoPlayer createVideoPlayer() { + protected ExoPlayer createVideoPlayer() { ExoPlayer exoPlayer = exoPlayerProvider.get(); exoPlayer.setMediaItem(mediaItem); exoPlayer.prepare(); - boolean wasInitialized = savedStateDuring != null; + boolean wasInitialized = wasPlayerInitialized(); exoPlayer.addListener(new ExoPlayerEventListener(exoPlayer, videoPlayerEvents, wasInitialized)); setAudioAttributes(exoPlayer, options.mixWithOthers); return exoPlayer; } + protected boolean wasPlayerInitialized() { + // Can be overridden in subclasses. + return false; + } + void sendBufferingUpdate() { videoPlayerEvents.onBufferingUpdate(exoPlayer.getBufferedPosition()); } @@ -151,7 +134,9 @@ long getPosition() { } @NonNull - ExoPlayer getExoPlayer() { return exoPlayer; } + ExoPlayer getExoPlayer() { + return exoPlayer; + } void dispose() { exoPlayer.release(); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 1524bf6b112f..2a808c51ece4 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -1,16 +1,18 @@ package io.flutter.plugins.videoplayer; import android.content.Context; - import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.media3.common.MediaItem; import androidx.media3.exoplayer.ExoPlayer; - import io.flutter.view.TextureRegistry; -final class VideoPlayerTextureApproach extends VideoPlayer implements TextureRegistry.SurfaceProducer.Callback { +final class VideoPlayerTextureApproach extends VideoPlayer + implements TextureRegistry.SurfaceProducer.Callback { @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; + @Nullable private ExoPlayerState savedStateDuring; /** * Creates a video player. @@ -57,6 +59,29 @@ static VideoPlayerTextureApproach create( this.exoPlayer.setVideoSurface(surfaceProducer.getSurface()); } + @RestrictTo(RestrictTo.Scope.LIBRARY) + // TODO(matanlurey): https://github.com/flutter/flutter/issues/155131. + @SuppressWarnings({"deprecation", "removal"}) + public void onSurfaceCreated() { + if (savedStateDuring != null) { + exoPlayer = createVideoPlayer(); + savedStateDuring.restore(exoPlayer); + savedStateDuring = null; + } + } + + @RestrictTo(RestrictTo.Scope.LIBRARY) + 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(); + } + + @Override + protected boolean wasPlayerInitialized() { + return savedStateDuring != null; + } void dispose() { surfaceProducer.release(); @@ -66,5 +91,4 @@ void dispose() { super.dispose(); } - } From 8676a4267d4e254367fd2a0dca325014ca273f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 2 Dec 2024 16:29:46 +0100 Subject: [PATCH 65/92] Use primitive type --- .../java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c3951a1d9876..9cfa2dc6f773 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 @@ -111,7 +111,7 @@ public void initialize() { videoAsset = VideoAsset.fromRemoteUrl(arg.getUri(), streamingFormat, arg.getHttpHeaders()); } - Long id; + long id; VideoPlayer videoPlayer; if (arg.getViewType() == Messages.PlatformVideoViewType.PLATFORM_VIEW) { id = VideoPlayerPlugin.nextNonTexturePlayerId++; From b6adb911d6e2857e7ce7081a47146804484d7c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 10 Dec 2024 14:28:29 +0100 Subject: [PATCH 66/92] Update doc comments for FVPVideoPlayer constructors --- .../include/video_player_avfoundation/FVPVideoPlayer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index c3912a444d76..a11516433293 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -36,8 +36,8 @@ NS_ASSUME_NONNULL_BEGIN avFactory:(id)avFactory registrar:(NSObject *)registrar; -/// Initializes a new instance of FVPVideoPlayer with the given URL, frame updater, display link, -/// HTTP headers, AV factory, and registrar. +/// Initializes a new instance of FVPVideoPlayer with the given URL, HTTP headers, AV factory, and +/// registrar. - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers avFactory:(id)avFactory From a2f35ec9315990637cf726b2953b8c37d756379e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 10 Dec 2024 14:31:50 +0100 Subject: [PATCH 67/92] Clean up iOS part after merge --- .../video_player_avfoundation/CHANGELOG.md | 118 +++++++++--------- .../darwin/video_player_avfoundation.podspec | 1 - .../FVPDisplayLink.m | 4 +- .../include/.gitkeep | 0 .../include/FVPEmpty.h | 6 + .../include/FVPNativeVideoView.h | 16 --- .../include/FVPNativeVideoViewFactory.h | 17 --- 7 files changed, 67 insertions(+), 95 deletions(-) create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep create mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h delete mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h delete mode 100644 packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index e9ff3fb10c43..425df6e9b914 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,176 +1,176 @@ ## 2.7.0 -- Adds support for platform views as an optional way of displaying a video. +* Adds support for platform views as an optional way of displaying a video. ## 2.6.4 -- Refactors native code structure. +* Refactors native code structure. ## 2.6.3 -- Fixes VideoPlayerController.initialize() future never resolving with invalid video file. -- Adds more details to the error message returned by VideoPlayerController.initialize(). +* Fixes VideoPlayerController.initialize() future never resolving with invalid video file. +* Adds more details to the error message returned by VideoPlayerController.initialize(). ## 2.6.2 -- Updates Pigeon for non-nullable collection type support. -- Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Updates Pigeon for non-nullable collection type support. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 2.6.1 -- Adds files to make include directory permanent. +* Adds files to make include directory permanent. ## 2.6.0 -- Adds Swift Package Manager compatibility. +* Adds Swift Package Manager compatibility. ## 2.5.7 -- Adds frame availability checks on iOS. -- Simplifies internal platform channel interfaces. -- Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. +* Adds frame availability checks on iOS. +* Simplifies internal platform channel interfaces. +* Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. ## 2.5.6 -- Adds privacy manifest. +* Adds privacy manifest. ## 2.5.5 -- Fixes display of initial frame when paused. +* Fixes display of initial frame when paused. ## 2.5.4 -- Fixes new lint warnings. +* Fixes new lint warnings. ## 2.5.3 -- Publishes an instance of the plugin to the registrar on macOS, as on iOS. +* Publishes an instance of the plugin to the registrar on macOS, as on iOS. ## 2.5.2 -- Fixes flickering and seek-while-paused on macOS. +* Fixes flickering and seek-while-paused on macOS. ## 2.5.1 -- Updates to Pigeon 13. +* Updates to Pigeon 13. ## 2.5.0 -- Adds support for macOS. +* Adds support for macOS. ## 2.4.11 -- Updates Pigeon. -- Changes Objective-C class prefixes to avoid future collisions. +* Updates Pigeon. +* Changes Objective-C class prefixes to avoid future collisions. ## 2.4.10 -- Adds pub topics to package metadata. -- Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. ## 2.4.9 -- Fixes the iOS crash when using multiple players on the same screen. +* Fixes the iOS crash when using multiple players on the same screen. See: https://github.com/flutter/flutter/issues/124937 ## 2.4.8 -- Fixes missing `isPlaybackLikelyToKeepUp` check for iOS video player `bufferingEnd` event and `bufferingStart` event. +* Fixes missing `isPlaybackLikelyToKeepUp` check for iOS video player `bufferingEnd` event and `bufferingStart` event. ## 2.4.7 -- Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. -- Adds iOS exception on incorrect asset path +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Adds iOS exception on incorrect asset path ## 2.4.6 -- Fixes hang when seeking to end of video. +* Fixes hang when seeking to end of video. ## 2.4.5 -- Updates functions without a prototype to avoid deprecation warning. +* Updates functions without a prototype to avoid deprecation warning. ## 2.4.4 -- Updates pigeon to fix warnings with clang 15. +* Updates pigeon to fix warnings with clang 15. ## 2.4.3 -- Synchronizes `VideoPlayerValue.isPlaying` with `AVPlayer`. +* Synchronizes `VideoPlayerValue.isPlaying` with `AVPlayer`. ## 2.4.2 -- Makes seekTo async and only complete when AVPlayer.seekTo completes. +* Makes seekTo async and only complete when AVPlayer.seekTo completes. ## 2.4.1 -- Clarifies explanation of endorsement in README. -- Aligns Dart and Flutter SDK constraints. +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. ## 2.4.0 -- Updates minimum Flutter version to 3.3 and iOS 11. +* Updates minimum Flutter version to 3.3 and iOS 11. ## 2.3.9 -- Updates links for the merge of flutter/plugins into flutter/packages. -- Updates minimum Flutter version to 3.0. +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. ## 2.3.8 -- Adds compatibilty with version 6.0 of the platform interface. -- Fixes file URI construction in example. -- Updates code for new analysis options. -- Adds an integration test for a bug where the aspect ratios of some HLS videos are incorrectly inverted. -- Removes an unnecessary override in example code. +* Adds compatibilty with version 6.0 of the platform interface. +* Fixes file URI construction in example. +* Updates code for new analysis options. +* Adds an integration test for a bug where the aspect ratios of some HLS videos are incorrectly inverted. +* Removes an unnecessary override in example code. ## 2.3.7 -- Fixes a bug where the aspect ratio of some HLS videos are incorrectly inverted. -- Updates code for `no_leading_underscores_for_local_identifiers` lint. +* Fixes a bug where the aspect ratio of some HLS videos are incorrectly inverted. +* Updates code for `no_leading_underscores_for_local_identifiers` lint. ## 2.3.6 -- Fixes a bug in iOS 16 where videos from protected live streams are not shown. -- Updates minimum Flutter version to 2.10. -- Fixes violations of new analysis option use_named_constants. -- Fixes avoid_redundant_argument_values lint warnings and minor typos. -- Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Fixes a bug in iOS 16 where videos from protected live streams are not shown. +* Updates minimum Flutter version to 2.10. +* Fixes violations of new analysis option use_named_constants. +* Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 2.3.5 -- Updates references to the obsolete master branch. +* Updates references to the obsolete master branch. ## 2.3.4 -- Removes unnecessary imports. -- Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors +* Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors lint warnings. ## 2.3.3 -- Fix XCUITest based on the new voice over announcement for tooltips. +* Fix XCUITest based on the new voice over announcement for tooltips. See: https://github.com/flutter/flutter/pull/87684 ## 2.3.2 -- Applies the standardized transform for videos with different orientations. +* Applies the standardized transform for videos with different orientations. ## 2.3.1 -- Renames internal method channels to avoid potential confusion with the +* Renames internal method channels to avoid potential confusion with the default implementation's method channel. -- Updates Pigeon to 2.0.1. +* Updates Pigeon to 2.0.1. ## 2.3.0 -- Updates Pigeon to ^1.0.16. +* Updates Pigeon to ^1.0.16. ## 2.2.18 -- Wait to initialize m3u8 videos until size is set, fixing aspect ratio. -- Adjusts test timeouts for network-dependent native tests to avoid flake. +* Wait to initialize m3u8 videos until size is set, fixing aspect ratio. +* Adjusts test timeouts for network-dependent native tests to avoid flake. ## 2.2.17 -- Splits from `video_player` as a federated implementation. +* Splits from `video_player` as a federated implementation. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec index 94281a6dc0d7..81e8d2f8e4ef 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec @@ -18,7 +18,6 @@ Downloaded by pub (not CocoaPods). s.ios.source_files = 'video_player_avfoundation/Sources/video_player_avfoundation_ios/*' s.osx.source_files = 'video_player_avfoundation/Sources/video_player_avfoundation_macos/*' s.public_header_files = 'video_player_avfoundation/Sources/video_player_avfoundation/include/**/*.h' - s.ios.public_header_files = 'video_player_avfoundation/Sources/video_player_avfoundation_ios/include/*.h' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m index c3921496a353..9bdb321ae164 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/FVPDisplayLink.m @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "../video_player_avfoundation/include/video_player_avfoundation/FVPDisplayLink.h" + #import #import -#import "FVPDisplayLink.h" - /// A proxy object to act as a CADisplayLink target, to avoid retain loops, since FVPDisplayLink /// owns its CADisplayLink, but CADisplayLink retains its target. @interface FVPDisplayLinkTarget : NSObject diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h new file mode 100644 index 000000000000..c7b344f7299d --- /dev/null +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPEmpty.h @@ -0,0 +1,6 @@ +// 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. + +// Empty file to perserve include directory in pub-cache. See +// https://github.com/flutter/flutter/issues/148002 for more information. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h deleted file mode 100644 index ed5f4d6d157d..000000000000 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoView.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -#import - -/// A class used to create a native video view that can be embedded in a Flutter app. -/// This class wraps an AVPlayer instance and displays its video content. -@interface FVPNativeVideoView : NSObject -/// Initializes a new instance of a native view. -/// It creates a video view instance and sets the provided AVPlayer instance to it. -- (instancetype)initWithPlayer:(AVPlayer *)player; - -/// Returns the native UIView that displays the video content. -- (UIView *)view; -@end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h deleted file mode 100644 index ac0a94a40c22..000000000000 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation_ios/include/FVPNativeVideoViewFactory.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -#import - -#import "FVPVideoPlayer.h" - -/// A factory class responsible for creating native video views that can be embedded in a -/// Flutter app. -@interface FVPNativeVideoViewFactory : NSObject -/// Initializes a new instance of FVPNativeVideoViewFactory with the given messenger and -/// playersByTextureId dictionary which stores the video players associated with the texture IDs. -- (instancetype)initWithMessenger:(NSObject *)messenger - playersByTextureId: - (NSMutableDictionary *)playersByTextureId; -@end From 649ecf1edcd0d057d92627c0cd260c90c4fe9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 10 Dec 2024 17:24:51 +0100 Subject: [PATCH 68/92] Send correct (or working the same way as on main branch) video dimensions in onInitialized callback --- .../videoplayer/ExoPlayerEventListener.java | 31 ++++++++++++++----- .../plugins/videoplayer/VideoPlayer.java | 7 ++++- .../VideoPlayerTextureApproach.java | 5 +++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index 5b1b1342d3a8..a981889336f8 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -20,6 +20,7 @@ final class ExoPlayerEventListener implements Player.Listener { private final VideoPlayerCallbacks events; private boolean isBuffering = false; private boolean isInitialized; + private Messages.PlatformVideoViewType viewType; private enum RotationDegrees { ROTATE_0(0), @@ -48,13 +49,18 @@ public int getDegrees() { } ExoPlayerEventListener(ExoPlayer exoPlayer, VideoPlayerCallbacks events) { - this(exoPlayer, events, false); + this(exoPlayer, events, false, Messages.PlatformVideoViewType.TEXTURE_VIEW); } - ExoPlayerEventListener(ExoPlayer exoPlayer, VideoPlayerCallbacks events, boolean initialized) { + ExoPlayerEventListener( + ExoPlayer exoPlayer, + VideoPlayerCallbacks events, + boolean initialized, + Messages.PlatformVideoViewType viewType) { this.exoPlayer = exoPlayer; this.events = events; this.isInitialized = initialized; + this.viewType = viewType; } private void setBuffering(boolean buffering) { @@ -77,11 +83,18 @@ private void sendInitialized() { } isInitialized = true; VideoSize videoSize = exoPlayer.getVideoSize(); + Format videoFormat = exoPlayer.getVideoFormat(); int rotationCorrection = 0; - // FIXME This is 0, 0 for platform view, because there is no surface assigned to the player? - // Is this solution okay? - int width = exoPlayer.getVideoFormat().width; - int height = exoPlayer.getVideoFormat().height; + // FIXME Is this solution okay? + int width = + (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW) + ? videoSize.width + : videoFormat.width; + int height = + (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW) + ? videoSize.height + : videoFormat.height; + if (width != 0 && height != 0) { RotationDegrees reportedRotationCorrection = RotationDegrees.ROTATE_0; @@ -127,8 +140,10 @@ else if (Build.VERSION.SDK_INT < 29) { // Switch the width/height if video was taken in portrait mode and a rotation // correction was detected. - if (reportedRotationCorrection == RotationDegrees.ROTATE_90 - || reportedRotationCorrection == RotationDegrees.ROTATE_270) { + // FIXME Describe why needed only for texture view + if (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW + && (reportedRotationCorrection == RotationDegrees.ROTATE_90 + || reportedRotationCorrection == RotationDegrees.ROTATE_270)) { width = videoSize.height; height = videoSize.width; } 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 12de85842ec7..dada8a32ef9a 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 @@ -79,7 +79,8 @@ protected ExoPlayer createVideoPlayer() { exoPlayer.prepare(); boolean wasInitialized = wasPlayerInitialized(); - exoPlayer.addListener(new ExoPlayerEventListener(exoPlayer, videoPlayerEvents, wasInitialized)); + exoPlayer.addListener( + new ExoPlayerEventListener(exoPlayer, videoPlayerEvents, wasInitialized, getViewType())); setAudioAttributes(exoPlayer, options.mixWithOthers); return exoPlayer; @@ -90,6 +91,10 @@ protected boolean wasPlayerInitialized() { return false; } + protected Messages.PlatformVideoViewType getViewType() { + return Messages.PlatformVideoViewType.PLATFORM_VIEW; + } + void sendBufferingUpdate() { videoPlayerEvents.onBufferingUpdate(exoPlayer.getBufferedPosition()); } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 2a808c51ece4..a0d84e83c206 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -83,6 +83,11 @@ protected boolean wasPlayerInitialized() { return savedStateDuring != null; } + @Override + protected Messages.PlatformVideoViewType getViewType() { + return Messages.PlatformVideoViewType.TEXTURE_VIEW; + } + void dispose() { surfaceProducer.release(); // TODO(matanlurey): Remove when embedder no longer calls-back once released. From 49150f8dd253f4da26458f8e50fbcc779eaba019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 12 Dec 2024 14:31:19 +0100 Subject: [PATCH 69/92] Dispose player before texture surface --- .../plugins/videoplayer/VideoPlayerTextureApproach.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index a0d84e83c206..75ad2db76794 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -89,11 +89,12 @@ protected Messages.PlatformVideoViewType getViewType() { } void dispose() { + // Super must be called first to ensure the player is released first. + super.dispose(); + surfaceProducer.release(); // TODO(matanlurey): Remove when embedder no longer calls-back once released. // https://github.com/flutter/flutter/issues/156434. surfaceProducer.setCallback(null); - - super.dispose(); } } From 283c390b59376a44ba3471540ceb7c75f00a9cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 12 Dec 2024 14:31:47 +0100 Subject: [PATCH 70/92] Use texture approach in tests by default --- .../java/io/flutter/plugins/videoplayer/VideoPlayerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index 4912803ce1d4..bd99ecc43419 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -65,7 +65,7 @@ private VideoPlayer createVideoPlayer() { } private VideoPlayer createVideoPlayer(VideoPlayerOptions options) { - return new VideoPlayer( + return new VideoPlayerTextureApproach( () -> mockExoPlayer, mockEvents, mockProducer, fakeVideoAsset.getMediaItem(), options); } From 2617cfa76d074f963df574002c14828c94937267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 12 Dec 2024 14:47:39 +0100 Subject: [PATCH 71/92] Format code with google-java-format --- .../plugins/videoplayer/ExoPlayerEventListener.java | 11 +++++------ .../io/flutter/plugins/videoplayer/VideoPlayer.java | 1 + .../videoplayer/VideoPlayerTextureApproach.java | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index a981889336f8..eea448cd5b4a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -5,6 +5,7 @@ package io.flutter.plugins.videoplayer; import android.os.Build; + import androidx.annotation.NonNull; import androidx.annotation.OptIn; import androidx.media3.common.Format; @@ -13,6 +14,7 @@ import androidx.media3.common.VideoSize; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.ExoPlayer; + import java.util.Objects; final class ExoPlayerEventListener implements Player.Listener { @@ -109,8 +111,7 @@ private void sendInitialized() { getRotationCorrectionFromUnappliedRotation(reportedRotationCorrection); } catch (IllegalArgumentException e) { // Unapplied rotation other than 0, 90, 180, 270 reported by VideoSize. Because this is - // unexpected, - // we apply no rotation correction. + // unexpected, we apply no rotation correction. reportedRotationCorrection = RotationDegrees.ROTATE_0; rotationCorrection = 0; } @@ -131,8 +132,7 @@ else if (Build.VERSION.SDK_INT < 29) { reportedRotationCorrection = RotationDegrees.fromDegrees(rotationCorrection); } catch (IllegalArgumentException e) { // Rotation correction other than 0, 90, 180, 270 reported by Format. Because this is - // unexpected, - // we apply no rotation correction. + // unexpected, we apply no rotation correction. reportedRotationCorrection = RotationDegrees.ROTATE_0; rotationCorrection = 0; } @@ -199,8 +199,7 @@ public void onPlaybackStateChanged(final int playbackState) { public void onPlayerError(@NonNull final PlaybackException error) { setBuffering(false); if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { - // See - // https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window + // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window exoPlayer.seekToDefaultPosition(); exoPlayer.prepare(); } else { 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 dada8a32ef9a..6f16e8a87210 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 @@ -8,6 +8,7 @@ import static androidx.media3.common.Player.REPEAT_MODE_OFF; import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.media3.common.AudioAttributes; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 75ad2db76794..bb129e40a15d 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -1,12 +1,14 @@ package io.flutter.plugins.videoplayer; import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.media3.common.MediaItem; import androidx.media3.exoplayer.ExoPlayer; + import io.flutter.view.TextureRegistry; final class VideoPlayerTextureApproach extends VideoPlayer From 32d430b7d907239a58ed36168ae4babda522a07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 12 Dec 2024 15:11:40 +0100 Subject: [PATCH 72/92] Add copyright information to a new file, fix imports --- .../java/io/flutter/plugins/videoplayer/VideoPlayer.java | 1 - .../plugins/videoplayer/VideoPlayerTextureApproach.java | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 6f16e8a87210..dada8a32ef9a 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 @@ -8,7 +8,6 @@ import static androidx.media3.common.Player.REPEAT_MODE_OFF; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.media3.common.AudioAttributes; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index bb129e40a15d..706eb0b687d4 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -1,14 +1,16 @@ +// 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. + package io.flutter.plugins.videoplayer; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.media3.common.MediaItem; import androidx.media3.exoplayer.ExoPlayer; - import io.flutter.view.TextureRegistry; final class VideoPlayerTextureApproach extends VideoPlayer From 04e567efa03be9cba0266c3be70fa790c3ac1cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 12 Dec 2024 15:49:08 +0100 Subject: [PATCH 73/92] Clean up code --- .../plugins/videoplayer/ExoPlayerEventListener.java | 8 +++++--- .../java/io/flutter/plugins/videoplayer/VideoPlayer.java | 4 ++-- .../plugins/videoplayer/VideoPlayerTextureApproach.java | 2 +- .../plugins/videoplayer/ExoPlayerEventListenerTest.java | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index eea448cd5b4a..996a2f4d3b3c 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -50,8 +50,9 @@ public int getDegrees() { } } - ExoPlayerEventListener(ExoPlayer exoPlayer, VideoPlayerCallbacks events) { - this(exoPlayer, events, false, Messages.PlatformVideoViewType.TEXTURE_VIEW); + ExoPlayerEventListener( + ExoPlayer exoPlayer, VideoPlayerCallbacks events, Messages.PlatformVideoViewType viewType) { + this(exoPlayer, events, false, viewType); } ExoPlayerEventListener( @@ -199,7 +200,8 @@ public void onPlaybackStateChanged(final int playbackState) { public void onPlayerError(@NonNull final PlaybackException error) { setBuffering(false); if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { - // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window + // See + // https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window exoPlayer.seekToDefaultPosition(); exoPlayer.prepare(); } else { 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 dada8a32ef9a..bd9e03a6e1cb 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 @@ -78,9 +78,9 @@ protected ExoPlayer createVideoPlayer() { exoPlayer.setMediaItem(mediaItem); exoPlayer.prepare(); - boolean wasInitialized = wasPlayerInitialized(); exoPlayer.addListener( - new ExoPlayerEventListener(exoPlayer, videoPlayerEvents, wasInitialized, getViewType())); + new ExoPlayerEventListener( + exoPlayer, videoPlayerEvents, wasPlayerInitialized(), getViewType())); setAudioAttributes(exoPlayer, options.mixWithOthers); return exoPlayer; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 706eb0b687d4..00cd7274510e 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -93,7 +93,7 @@ protected Messages.PlatformVideoViewType getViewType() { } void dispose() { - // Super must be called first to ensure the player is released first. + // Super must be called first to ensure the player is released before the surface. super.dispose(); surfaceProducer.release(); diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java index 65dfb311c31c..d3896e4b774f 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java @@ -44,7 +44,7 @@ public final class ExoPlayerEventListenerTest { @Before public void setUp() { - eventListener = new ExoPlayerEventListener(mockExoPlayer, mockCallbacks); + eventListener = new ExoPlayerEventListener(mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.TEXTURE_VIEW); } @Test From ea7e586faf405c70270054c881e774aac4848d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 13 Dec 2024 15:31:04 +0100 Subject: [PATCH 74/92] Use TextureView instead of SurfaceView --- .../plugins/videoplayer/NativeView.java | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 50ebd8d37f99..23f2071ebf0d 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -5,28 +5,64 @@ package io.flutter.plugins.videoplayer; import android.content.Context; -import android.view.SurfaceView; +import android.graphics.SurfaceTexture; +import android.view.Surface; +import android.view.TextureView; import android.view.View; +import android.widget.FrameLayout; import androidx.annotation.NonNull; +import androidx.media3.common.VideoSize; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugin.platform.PlatformView; class NativeView implements PlatformView { - @NonNull private final SurfaceView surfaceView; + @NonNull private final TextureView textureView; + private Surface videoSurface; NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { - surfaceView = new SurfaceView(context); - exoPlayer.setVideoSurfaceView(surfaceView); + textureView = new TextureView(context); + textureView.setSurfaceTextureListener( + new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable( + @NonNull SurfaceTexture surface, int width, int height) { + videoSurface = new Surface(surface); + exoPlayer.setVideoSurface(videoSurface); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // No implementation needed. + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + exoPlayer.setVideoSurface(null); + if (videoSurface != null) { + videoSurface.release(); + videoSurface = null; + } + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // No implementation needed. + } + }); } @NonNull @Override public View getView() { - return surfaceView; + return textureView; } @Override public void dispose() { - surfaceView.getHolder().getSurface().release(); + if (videoSurface != null) { + videoSurface.release(); + videoSurface = null; + } } } From d25617c94004cbe68666a15d0c268c54a03414c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 13 Dec 2024 15:32:09 +0100 Subject: [PATCH 75/92] Rely on video format for obtaining video width and height when using platform view --- .../videoplayer/ExoPlayerEventListener.java | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index 996a2f4d3b3c..e08e83df3806 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -78,25 +78,44 @@ private void setBuffering(boolean buffering) { } } - @OptIn(markerClass = UnstableApi.class) @SuppressWarnings("SuspiciousNameCombination") private void sendInitialized() { if (isInitialized) { return; } isInitialized = true; - VideoSize videoSize = exoPlayer.getVideoSize(); + + // FIXME Comment why this is needed + if (viewType == Messages.PlatformVideoViewType.PLATFORM_VIEW) { + sendInitializedPlatformViewApproach(); + } else { + sendInitializedTextureApproach(); + } + } + + @OptIn(markerClass = UnstableApi.class) + private void sendInitializedPlatformViewApproach() { Format videoFormat = exoPlayer.getVideoFormat(); + RotationDegrees rotationCorrection = RotationDegrees.fromDegrees(videoFormat.rotationDegrees); + int width = videoFormat.width; + int height = videoFormat.height; + + if (rotationCorrection == RotationDegrees.ROTATE_90 + || rotationCorrection == RotationDegrees.ROTATE_270) { + width = videoFormat.height; + height = videoFormat.width; + + rotationCorrection = RotationDegrees.fromDegrees(0); + } + + events.onInitialized(width, height, exoPlayer.getDuration(), rotationCorrection.degrees); + } + + private void sendInitializedTextureApproach() { + VideoSize videoSize = exoPlayer.getVideoSize(); int rotationCorrection = 0; - // FIXME Is this solution okay? - int width = - (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW) - ? videoSize.width - : videoFormat.width; - int height = - (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW) - ? videoSize.height - : videoFormat.height; + int width = videoSize.width; + int height = videoSize.height; if (width != 0 && height != 0) { RotationDegrees reportedRotationCorrection = RotationDegrees.ROTATE_0; @@ -142,9 +161,8 @@ else if (Build.VERSION.SDK_INT < 29) { // Switch the width/height if video was taken in portrait mode and a rotation // correction was detected. // FIXME Describe why needed only for texture view - if (viewType == Messages.PlatformVideoViewType.TEXTURE_VIEW - && (reportedRotationCorrection == RotationDegrees.ROTATE_90 - || reportedRotationCorrection == RotationDegrees.ROTATE_270)) { + if (reportedRotationCorrection == RotationDegrees.ROTATE_90 + || reportedRotationCorrection == RotationDegrees.ROTATE_270) { width = videoSize.height; height = videoSize.width; } From 52fa6aa21c63d3d66cf4e9b22c1dd9f8bcac5a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 16 Dec 2024 13:27:19 +0100 Subject: [PATCH 76/92] Revert "Use TextureView instead of SurfaceView" This reverts commit ea7e586faf405c70270054c881e774aac4848d09. --- .../plugins/videoplayer/NativeView.java | 48 +++---------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 23f2071ebf0d..50ebd8d37f99 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -5,64 +5,28 @@ package io.flutter.plugins.videoplayer; import android.content.Context; -import android.graphics.SurfaceTexture; -import android.view.Surface; -import android.view.TextureView; +import android.view.SurfaceView; import android.view.View; -import android.widget.FrameLayout; import androidx.annotation.NonNull; -import androidx.media3.common.VideoSize; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugin.platform.PlatformView; class NativeView implements PlatformView { - @NonNull private final TextureView textureView; - private Surface videoSurface; + @NonNull private final SurfaceView surfaceView; NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { - textureView = new TextureView(context); - textureView.setSurfaceTextureListener( - new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable( - @NonNull SurfaceTexture surface, int width, int height) { - videoSurface = new Surface(surface); - exoPlayer.setVideoSurface(videoSurface); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - // No implementation needed. - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - exoPlayer.setVideoSurface(null); - if (videoSurface != null) { - videoSurface.release(); - videoSurface = null; - } - return true; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - // No implementation needed. - } - }); + surfaceView = new SurfaceView(context); + exoPlayer.setVideoSurfaceView(surfaceView); } @NonNull @Override public View getView() { - return textureView; + return surfaceView; } @Override public void dispose() { - if (videoSurface != null) { - videoSurface.release(); - videoSurface = null; - } + surfaceView.getHolder().getSurface().release(); } } From 8ef85adf7b209f6e04f3facae9490fbf10865551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 16 Dec 2024 15:33:38 +0100 Subject: [PATCH 77/92] Fix native view on Android 7.1 - call setZOrderMediaOverlay method --- .../main/java/io/flutter/plugins/videoplayer/NativeView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 50ebd8d37f99..98cba887e8fe 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -16,6 +16,8 @@ class NativeView implements PlatformView { NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { surfaceView = new SurfaceView(context); + // The line below is needed to display the video correctly on older Android versions. + surfaceView.setZOrderMediaOverlay(true); exoPlayer.setVideoSurfaceView(surfaceView); } From ed0baf1de36e7436231dd49ed03b59a3ff03d30c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 16 Dec 2024 15:56:32 +0100 Subject: [PATCH 78/92] Remove redundant parameter --- .../main/java/io/flutter/plugins/videoplayer/NativeView.java | 2 +- .../java/io/flutter/plugins/videoplayer/NativeViewFactory.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index 98cba887e8fe..a5dd9d5224a2 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -14,7 +14,7 @@ class NativeView implements PlatformView { @NonNull private final SurfaceView surfaceView; - NativeView(@NonNull Context context, int id, @NonNull ExoPlayer exoPlayer) { + NativeView(@NonNull Context context, @NonNull ExoPlayer exoPlayer) { surfaceView = new SurfaceView(context); // The line below is needed to display the video correctly on older Android versions. surfaceView.setZOrderMediaOverlay(true); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java index 041fc5de44da..e7e9c4e74619 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java @@ -32,6 +32,6 @@ public PlatformView create(@NonNull Context context, int id, @Nullable Object ar final VideoPlayer player = videoPlayers.get(playerId); final ExoPlayer exoPlayer = player.getExoPlayer(); - return new NativeView(context, id, exoPlayer); + return new NativeView(context, exoPlayer); } } From cc4b94928aa76ba32ef4c21e3ab2678a87bf5340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Mon, 16 Dec 2024 17:11:23 +0100 Subject: [PATCH 79/92] Add unit tests for onInitialized event when using platform view --- .../ExoPlayerEventListenerTest.java | 69 ++++++++++++++++--- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java index d3896e4b774f..aaafe86547c7 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/ExoPlayerEventListenerTest.java @@ -44,12 +44,14 @@ public final class ExoPlayerEventListenerTest { @Before public void setUp() { - eventListener = new ExoPlayerEventListener(mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.TEXTURE_VIEW); + eventListener = + new ExoPlayerEventListener( + mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.TEXTURE_VIEW); } @Test @Config(maxSdk = 28) - public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { + public void onPlaybackStateChangedReadySendInitialized_belowAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 0, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -61,7 +63,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(minSdk = 29) public void - onPlaybackStateChangedReadySendInitializedWithRotationCorrectionAndWidthAndHeightSwap_aboveAndroid29() { + onPlaybackStateChangedReadySendInitializedWithRotationCorrectionAndWidthAndHeightSwap_aboveAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 90; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -77,7 +79,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(maxSdk = 21) public void - onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_belowAndroid21() { + onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_belowAndroid21_textureApproach() { VideoSize size = new VideoSize(800, 400, 90, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -89,7 +91,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(minSdk = 22, maxSdk = 28) public void - onPlaybackStateChangedReadyInPortraitMode90DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29() { + onPlaybackStateChangedReadyInPortraitMode90DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 90, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); @@ -102,7 +104,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(minSdk = 29) public void - onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_aboveAndroid29() { + onPlaybackStateChangedReadyInPortraitMode90DegreesSwapWidthAndHeight_aboveAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 90; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -118,7 +120,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(maxSdk = 21) public void - onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_belowAndroid21() { + onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_belowAndroid21_textureApproach() { VideoSize size = new VideoSize(800, 400, 270, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -130,7 +132,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(minSdk = 22, maxSdk = 28) public void - onPlaybackStateChangedReadyInPortraitMode270DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29() { + onPlaybackStateChangedReadyInPortraitMode270DegreesDoesNotSwapWidthAndHeight_aboveAndroid21belowAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 270, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -142,7 +144,7 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(minSdk = 29) public void - onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_aboveAndroid29() { + onPlaybackStateChangedReadyInPortraitMode270DegreesSwapWidthAndHeight_aboveAndroid29_textureApproach() { VideoSize size = new VideoSize(800, 400, 0, 0); int rotationCorrection = 270; Format videoFormat = new Format.Builder().setRotationDegrees(rotationCorrection).build(); @@ -157,7 +159,8 @@ public void onPlaybackStateChangedReadySendInitialized_belowAndroid29() { @Test @Config(maxSdk = 21) - public void onPlaybackStateChangedReadyFlipped180DegreesInformEventHandler_belowAndroid21() { + public void + onPlaybackStateChangedReadyFlipped180DegreesInformEventHandler_belowAndroid21_textureApproach() { VideoSize size = new VideoSize(800, 400, 180, 0); when(mockExoPlayer.getVideoSize()).thenReturn(size); when(mockExoPlayer.getDuration()).thenReturn(10L); @@ -166,6 +169,52 @@ public void onPlaybackStateChangedReadyFlipped180DegreesInformEventHandler_below verify(mockCallbacks).onInitialized(800, 400, 10L, 180); } + @Test + public void onPlaybackStateChangedReadySendInitialized_platformViewApproach() { + eventListener = + new ExoPlayerEventListener( + mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.PLATFORM_VIEW); + + Format format = new Format.Builder().setWidth(800).setHeight(400).build(); + when(mockExoPlayer.getVideoFormat()).thenReturn(format); + when(mockExoPlayer.getDuration()).thenReturn(10L); + + eventListener.onPlaybackStateChanged(Player.STATE_READY); + verify(mockCallbacks).onInitialized(800, 400, 10L, 0); + } + + @Test + public void + onPlaybackStateChangedReadyInPortraitMode90DegreesSwapsWidthAndHeight_platformViewApproach() { + eventListener = + new ExoPlayerEventListener( + mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.PLATFORM_VIEW); + + Format format = + new Format.Builder().setWidth(800).setHeight(400).setRotationDegrees(90).build(); + when(mockExoPlayer.getVideoFormat()).thenReturn(format); + when(mockExoPlayer.getDuration()).thenReturn(10L); + + eventListener.onPlaybackStateChanged(Player.STATE_READY); + verify(mockCallbacks).onInitialized(400, 800, 10L, 0); + } + + @Test + public void + onPlaybackStateChangedReadyInPortraitMode270DegreesSwapsWidthAndHeight_platformViewApproach() { + eventListener = + new ExoPlayerEventListener( + mockExoPlayer, mockCallbacks, Messages.PlatformVideoViewType.PLATFORM_VIEW); + + Format format = + new Format.Builder().setWidth(800).setHeight(400).setRotationDegrees(270).build(); + when(mockExoPlayer.getVideoFormat()).thenReturn(format); + when(mockExoPlayer.getDuration()).thenReturn(10L); + + eventListener.onPlaybackStateChanged(Player.STATE_READY); + verify(mockCallbacks).onInitialized(400, 800, 10L, 0); + } + @Test public void onPlaybackStateChangedBufferingSendsBufferingStartAndUpdates() { when(mockExoPlayer.getBufferedPosition()).thenReturn(10L); From 8b203fc8694d4475abbb86e259823b176a87e278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 17 Dec 2024 13:14:26 +0100 Subject: [PATCH 80/92] Split tests for VideoPlayer and VideoPlayerTextureApproach to different files --- .../videoplayer/ExoPlayerEventListener.java | 1 - .../VideoPlayerTextureApproach.java | 2 + .../plugins/videoplayer/VideoPlayerTest.java | 127 +--------- .../VideoPlayerTextureApproachTest.java | 225 ++++++++++++++++++ 4 files changed, 234 insertions(+), 121 deletions(-) create mode 100644 packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproachTest.java diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index e08e83df3806..87c7633584f8 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -160,7 +160,6 @@ else if (Build.VERSION.SDK_INT < 29) { // Switch the width/height if video was taken in portrait mode and a rotation // correction was detected. - // FIXME Describe why needed only for texture view if (reportedRotationCorrection == RotationDegrees.ROTATE_90 || reportedRotationCorrection == RotationDegrees.ROTATE_270) { width = videoSize.height; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 00cd7274510e..5937f69afd90 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -18,6 +18,8 @@ final class VideoPlayerTextureApproach extends VideoPlayer @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; @Nullable private ExoPlayerState savedStateDuring; + private static final Messages.PlatformVideoViewType viewType = + Messages.PlatformVideoViewType.TEXTURE_VIEW; /** * Creates a video player. * diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index bd99ecc43419..d593abd5a6e6 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -5,15 +5,13 @@ package io.flutter.plugins.videoplayer; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import android.view.Surface; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; +import androidx.media3.common.Format; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; -import androidx.media3.common.VideoSize; import androidx.media3.exoplayer.ExoPlayer; import io.flutter.view.TextureRegistry; import org.junit.Before; @@ -22,7 +20,6 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -46,10 +43,8 @@ public final class VideoPlayerTest { private FakeVideoAsset fakeVideoAsset; @Mock private VideoPlayerCallbacks mockEvents; - @Mock private TextureRegistry.SurfaceProducer mockProducer; @Mock private ExoPlayer mockExoPlayer; @Captor private ArgumentCaptor attributesCaptor; - @Captor private ArgumentCaptor callbackCaptor; @Captor private ArgumentCaptor listenerCaptor; @Rule public MockitoRule initRule = MockitoJUnit.rule(); @@ -57,7 +52,6 @@ public final class VideoPlayerTest { @Before public void setUp() { fakeVideoAsset = new FakeVideoAsset(FAKE_ASSET_URL); - when(mockProducer.getSurface()).thenReturn(mock(Surface.class)); } private VideoPlayer createVideoPlayer() { @@ -65,8 +59,7 @@ private VideoPlayer createVideoPlayer() { } private VideoPlayer createVideoPlayer(VideoPlayerOptions options) { - return new VideoPlayerTextureApproach( - () -> mockExoPlayer, mockEvents, mockProducer, fakeVideoAsset.getMediaItem(), options); + return new VideoPlayer(() -> mockExoPlayer, mockEvents, fakeVideoAsset.getMediaItem(), options); } @Test @@ -75,8 +68,6 @@ public void loadsAndPreparesProvidedMediaEnablesAudioFocusByDefault() { verify(mockExoPlayer).setMediaItem(fakeVideoAsset.getMediaItem()); verify(mockExoPlayer).prepare(); - verify(mockProducer).getSurface(); - verify(mockExoPlayer).setVideoSurface(any()); verify(mockExoPlayer).setAudioAttributes(attributesCaptor.capture(), eq(true)); assertEquals(attributesCaptor.getValue().contentType, C.AUDIO_CONTENT_TYPE_MOVIE); @@ -171,66 +162,14 @@ public void seekAndGetPosition() { assertEquals(20L, videoPlayer.getPosition()); } - @Test - public void onSurfaceProducerDestroyedAndRecreatedReleasesAndThenRecreatesAndResumesPlayer() { - VideoPlayer videoPlayer = createVideoPlayer(); - - verify(mockProducer).setCallback(callbackCaptor.capture()); - verify(mockExoPlayer, never()).release(); - - when(mockExoPlayer.getCurrentPosition()).thenReturn(10L); - when(mockExoPlayer.getRepeatMode()).thenReturn(Player.REPEAT_MODE_ALL); - when(mockExoPlayer.getVolume()).thenReturn(0.5f); - when(mockExoPlayer.getPlaybackParameters()).thenReturn(new PlaybackParameters(2.5f)); - - TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); - producerLifecycle.onSurfaceDestroyed(); - - verify(mockExoPlayer).release(); - - // Create a new mock exo player so that we get a new instance. - mockExoPlayer = mock(ExoPlayer.class); - simulateSurfaceCreation(producerLifecycle); - - verify(mockExoPlayer).seekTo(10L); - verify(mockExoPlayer).setRepeatMode(Player.REPEAT_MODE_ALL); - verify(mockExoPlayer).setVolume(0.5f); - verify(mockExoPlayer).setPlaybackParameters(new PlaybackParameters(2.5f)); - - videoPlayer.dispose(); - } - - @Test - public void onSurfaceProducerDestroyedDoesNotStopOrPauseVideo() { - VideoPlayer videoPlayer = createVideoPlayer(); - - verify(mockProducer).setCallback(callbackCaptor.capture()); - TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); - producerLifecycle.onSurfaceDestroyed(); - - verify(mockExoPlayer, never()).stop(); - verify(mockExoPlayer, never()).pause(); - verify(mockExoPlayer, never()).setPlayWhenReady(anyBoolean()); - - videoPlayer.dispose(); - } - - @Test - public void onDisposeSurfaceProducerCallbackIsDisconnected() { - // Regression test for https://github.com/flutter/flutter/issues/156158. - VideoPlayer videoPlayer = createVideoPlayer(); - verify(mockProducer).setCallback(any()); - - videoPlayer.dispose(); - verify(mockProducer).setCallback(null); - } - @Test public void onInitializedCalledWhenVideoPlayerInitiallyCreated() { VideoPlayer videoPlayer = createVideoPlayer(); // Pretend we have a video, and capture the registered event listener. - when(mockExoPlayer.getVideoSize()).thenReturn(new VideoSize(300, 200)); + when(mockExoPlayer.getVideoFormat()) + .thenReturn( + new Format.Builder().setWidth(300).setHeight(200).setRotationDegrees(0).build()); verify(mockExoPlayer).addListener(listenerCaptor.capture()); Player.Listener listener = listenerCaptor.getValue(); @@ -242,64 +181,12 @@ public void onInitializedCalledWhenVideoPlayerInitiallyCreated() { } @Test - public void onSurfaceCreatedDoesNotSendInitializeEventAgain() { - // The VideoPlayer contract assumes that the event "initialized" is sent exactly once - // (duplicate events cause an error to be thrown at the shared Dart layer). This test verifies - // that the onInitialized event is sent exactly once per player. - // - // Regression test for https://github.com/flutter/flutter/issues/154602. - VideoPlayer videoPlayer = createVideoPlayer(); - when(mockExoPlayer.getVideoSize()).thenReturn(new VideoSize(300, 200)); - - // Capture the lifecycle events so we can simulate onSurfaceCreated/Destroyed. - verify(mockProducer).setCallback(callbackCaptor.capture()); - TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); - - // Trigger destroyed/created. - producerLifecycle.onSurfaceDestroyed(); - simulateSurfaceCreation(producerLifecycle); - - // Initial listener, and the new one from the resume. - verify(mockExoPlayer, times(2)).addListener(listenerCaptor.capture()); - Player.Listener listener = listenerCaptor.getValue(); - - // Now trigger that same event, which would happen in the case of a background/resume. - listener.onPlaybackStateChanged(Player.STATE_READY); - - // Was not called because it was a result of a background/resume. - verify(mockEvents, never()).onInitialized(anyInt(), anyInt(), anyLong(), anyInt()); - - videoPlayer.dispose(); - } - - @Test - public void onSurfaceCreatedWithoutDestroyDoesNotRecreate() { - // Initially create the video player, which creates the initial surface. + public void disposeReleasesExoPlayer() { VideoPlayer videoPlayer = createVideoPlayer(); - verify(mockProducer).getSurface(); - - // Capture the lifecycle events so we can simulate onSurfaceCreated/Destroyed. - verify(mockProducer).setCallback(callbackCaptor.capture()); - TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); - - // Calling onSurfaceCreated does not do anything, since the surface was never destroyed. - simulateSurfaceCreation(producerLifecycle); - verifyNoMoreInteractions(mockProducer); videoPlayer.dispose(); - } - @Test - public void disposeReleasesExoPlayerBeforeTexture() { - VideoPlayer videoPlayer = createVideoPlayer(); - - videoPlayer.dispose(); - - // Regression test for https://github.com/flutter/flutter/issues/156158. - // The player must be destroyed before the surface it is writing to. - InOrder inOrder = inOrder(mockExoPlayer, mockProducer); - inOrder.verify(mockExoPlayer).release(); - inOrder.verify(mockProducer).release(); + verify(mockExoPlayer).release(); } // TODO(matanlurey): Replace with inline calls to onSurfaceAvailable once diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproachTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproachTest.java new file mode 100644 index 000000000000..400ee33571bf --- /dev/null +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproachTest.java @@ -0,0 +1,225 @@ +// 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. + +package io.flutter.plugins.videoplayer; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import android.view.Surface; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; +import androidx.media3.common.VideoSize; +import androidx.media3.exoplayer.ExoPlayer; +import io.flutter.view.TextureRegistry; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** + * Unit tests for {@link VideoPlayerTextureApproach}. + * + *

This test suite narrowly verifies that {@link VideoPlayerTextureApproach} interfaces + * with the {@link ExoPlayer} interface exactly as it did when the test suite was created. + * That is, if the behavior changes, this test will need to change. However, this suite should catch + * bugs related to "this is a safe refactor with no behavior changes". + * + *

It's hypothetically possible to write better tests using {@link + * androidx.media3.test.utils.FakeMediaSource}, but you really need a PhD in the Android media APIs + * in order to figure out how to set everything up so the player "works". + */ +@RunWith(RobolectricTestRunner.class) +public final class VideoPlayerTextureApproachTest { + private static final String FAKE_ASSET_URL = "https://flutter.dev/movie.mp4"; + private FakeVideoAsset fakeVideoAsset; + + @Mock private VideoPlayerCallbacks mockEvents; + @Mock private TextureRegistry.SurfaceProducer mockProducer; + @Mock private ExoPlayer mockExoPlayer; + @Captor private ArgumentCaptor attributesCaptor; + @Captor private ArgumentCaptor callbackCaptor; + @Captor private ArgumentCaptor listenerCaptor; + + @Rule public MockitoRule initRule = MockitoJUnit.rule(); + + @Before + public void setUp() { + fakeVideoAsset = new FakeVideoAsset(FAKE_ASSET_URL); + when(mockProducer.getSurface()).thenReturn(mock(Surface.class)); + } + + private VideoPlayer createVideoPlayer() { + return createVideoPlayer(new VideoPlayerOptions()); + } + + private VideoPlayerTextureApproach createVideoPlayer(VideoPlayerOptions options) { + return new VideoPlayerTextureApproach( + () -> mockExoPlayer, mockEvents, mockProducer, fakeVideoAsset.getMediaItem(), options); + } + + @Test + public void loadsAndPreparesProvidedMediaEnablesAudioFocusByDefault() { + VideoPlayer videoPlayer = createVideoPlayer(); + + verify(mockExoPlayer).setMediaItem(fakeVideoAsset.getMediaItem()); + verify(mockExoPlayer).prepare(); + verify(mockProducer).getSurface(); + verify(mockExoPlayer).setVideoSurface(any()); + + verify(mockExoPlayer).setAudioAttributes(attributesCaptor.capture(), eq(true)); + assertEquals(attributesCaptor.getValue().contentType, C.AUDIO_CONTENT_TYPE_MOVIE); + + videoPlayer.dispose(); + } + + @Test + public void onSurfaceProducerDestroyedAndRecreatedReleasesAndThenRecreatesAndResumesPlayer() { + VideoPlayer videoPlayer = createVideoPlayer(); + + verify(mockProducer).setCallback(callbackCaptor.capture()); + verify(mockExoPlayer, never()).release(); + + when(mockExoPlayer.getCurrentPosition()).thenReturn(10L); + when(mockExoPlayer.getRepeatMode()).thenReturn(Player.REPEAT_MODE_ALL); + when(mockExoPlayer.getVolume()).thenReturn(0.5f); + when(mockExoPlayer.getPlaybackParameters()).thenReturn(new PlaybackParameters(2.5f)); + + TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); + producerLifecycle.onSurfaceDestroyed(); + + verify(mockExoPlayer).release(); + + // Create a new mock exo player so that we get a new instance. + mockExoPlayer = mock(ExoPlayer.class); + simulateSurfaceCreation(producerLifecycle); + + verify(mockExoPlayer).seekTo(10L); + verify(mockExoPlayer).setRepeatMode(Player.REPEAT_MODE_ALL); + verify(mockExoPlayer).setVolume(0.5f); + verify(mockExoPlayer).setPlaybackParameters(new PlaybackParameters(2.5f)); + + videoPlayer.dispose(); + } + + @Test + public void onSurfaceProducerDestroyedDoesNotStopOrPauseVideo() { + VideoPlayer videoPlayer = createVideoPlayer(); + + verify(mockProducer).setCallback(callbackCaptor.capture()); + TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); + producerLifecycle.onSurfaceDestroyed(); + + verify(mockExoPlayer, never()).stop(); + verify(mockExoPlayer, never()).pause(); + verify(mockExoPlayer, never()).setPlayWhenReady(anyBoolean()); + + videoPlayer.dispose(); + } + + @Test + public void onDisposeSurfaceProducerCallbackIsDisconnected() { + // Regression test for https://github.com/flutter/flutter/issues/156158. + VideoPlayer videoPlayer = createVideoPlayer(); + verify(mockProducer).setCallback(any()); + + videoPlayer.dispose(); + verify(mockProducer).setCallback(null); + } + + @Test + public void onInitializedCalledWhenVideoPlayerInitiallyCreated() { + VideoPlayer videoPlayer = createVideoPlayer(); + + // Pretend we have a video, and capture the registered event listener. + when(mockExoPlayer.getVideoSize()).thenReturn(new VideoSize(300, 200)); + verify(mockExoPlayer).addListener(listenerCaptor.capture()); + Player.Listener listener = listenerCaptor.getValue(); + + // Trigger an event that would trigger onInitialized. + listener.onPlaybackStateChanged(Player.STATE_READY); + verify(mockEvents).onInitialized(anyInt(), anyInt(), anyLong(), anyInt()); + + videoPlayer.dispose(); + } + + @Test + public void onSurfaceCreatedDoesNotSendInitializeEventAgain() { + // The VideoPlayer contract assumes that the event "initialized" is sent exactly once + // (duplicate events cause an error to be thrown at the shared Dart layer). This test verifies + // that the onInitialized event is sent exactly once per player. + // + // Regression test for https://github.com/flutter/flutter/issues/154602. + VideoPlayer videoPlayer = createVideoPlayer(); + when(mockExoPlayer.getVideoSize()).thenReturn(new VideoSize(300, 200)); + + // Capture the lifecycle events so we can simulate onSurfaceCreated/Destroyed. + verify(mockProducer).setCallback(callbackCaptor.capture()); + TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); + + // Trigger destroyed/created. + producerLifecycle.onSurfaceDestroyed(); + simulateSurfaceCreation(producerLifecycle); + + // Initial listener, and the new one from the resume. + verify(mockExoPlayer, times(2)).addListener(listenerCaptor.capture()); + Player.Listener listener = listenerCaptor.getValue(); + + // Now trigger that same event, which would happen in the case of a background/resume. + listener.onPlaybackStateChanged(Player.STATE_READY); + + // Was not called because it was a result of a background/resume. + verify(mockEvents, never()).onInitialized(anyInt(), anyInt(), anyLong(), anyInt()); + + videoPlayer.dispose(); + } + + @Test + public void onSurfaceCreatedWithoutDestroyDoesNotRecreate() { + // Initially create the video player, which creates the initial surface. + VideoPlayer videoPlayer = createVideoPlayer(); + verify(mockProducer).getSurface(); + + // Capture the lifecycle events so we can simulate onSurfaceCreated/Destroyed. + verify(mockProducer).setCallback(callbackCaptor.capture()); + TextureRegistry.SurfaceProducer.Callback producerLifecycle = callbackCaptor.getValue(); + + // Calling onSurfaceCreated does not do anything, since the surface was never destroyed. + simulateSurfaceCreation(producerLifecycle); + verifyNoMoreInteractions(mockProducer); + + videoPlayer.dispose(); + } + + @Test + public void disposeReleasesExoPlayerBeforeTexture() { + VideoPlayer videoPlayer = createVideoPlayer(); + + videoPlayer.dispose(); + + // Regression test for https://github.com/flutter/flutter/issues/156158. + // The player must be destroyed before the surface it is writing to. + InOrder inOrder = inOrder(mockExoPlayer, mockProducer); + inOrder.verify(mockExoPlayer).release(); + inOrder.verify(mockProducer).release(); + } + + // TODO(matanlurey): Replace with inline calls to onSurfaceAvailable once + // available on stable; see https://github.com/flutter/flutter/issues/155131. + // This separate method only exists to scope the suppression. + @SuppressWarnings({"deprecation", "removal"}) + void simulateSurfaceCreation(TextureRegistry.SurfaceProducer.Callback producerLifecycle) { + producerLifecycle.onSurfaceCreated(); + } +} From f3590a82ded29ec541d5de180e7942ac6c973b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 17 Dec 2024 15:31:11 +0100 Subject: [PATCH 81/92] Clea up Android native code --- .../videoplayer/ExoPlayerEventListener.java | 30 +++++++++---------- .../plugins/videoplayer/NativeView.java | 3 +- .../videoplayer/NativeViewFactory.java | 1 - .../plugins/videoplayer/VideoPlayer.java | 2 +- .../videoplayer/VideoPlayerPlugin.java | 24 ++++++++------- .../VideoPlayerTextureApproach.java | 2 -- .../plugins/videoplayer/VideoPlayerTest.java | 9 ------ 7 files changed, 30 insertions(+), 41 deletions(-) diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java index 87c7633584f8..e4b368443b4b 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/ExoPlayerEventListener.java @@ -5,7 +5,6 @@ package io.flutter.plugins.videoplayer; import android.os.Build; - import androidx.annotation.NonNull; import androidx.annotation.OptIn; import androidx.media3.common.Format; @@ -14,15 +13,14 @@ import androidx.media3.common.VideoSize; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.ExoPlayer; - import java.util.Objects; final class ExoPlayerEventListener implements Player.Listener { private final ExoPlayer exoPlayer; private final VideoPlayerCallbacks events; + private final Messages.PlatformVideoViewType viewType; private boolean isBuffering = false; private boolean isInitialized; - private Messages.PlatformVideoViewType viewType; private enum RotationDegrees { ROTATE_0(0), @@ -52,18 +50,18 @@ public int getDegrees() { ExoPlayerEventListener( ExoPlayer exoPlayer, VideoPlayerCallbacks events, Messages.PlatformVideoViewType viewType) { - this(exoPlayer, events, false, viewType); + this(exoPlayer, events, viewType, false); } ExoPlayerEventListener( ExoPlayer exoPlayer, VideoPlayerCallbacks events, - boolean initialized, - Messages.PlatformVideoViewType viewType) { + Messages.PlatformVideoViewType viewType, + boolean initialized) { this.exoPlayer = exoPlayer; this.events = events; - this.isInitialized = initialized; this.viewType = viewType; + this.isInitialized = initialized; } private void setBuffering(boolean buffering) { @@ -85,18 +83,20 @@ private void sendInitialized() { } isInitialized = true; - // FIXME Comment why this is needed if (viewType == Messages.PlatformVideoViewType.PLATFORM_VIEW) { - sendInitializedPlatformViewApproach(); + sendInitializedForPlatformViewApproach(); } else { - sendInitializedTextureApproach(); + sendInitializedForTextureApproach(); } } @OptIn(markerClass = UnstableApi.class) - private void sendInitializedPlatformViewApproach() { + private void sendInitializedForPlatformViewApproach() { + // We can't rely on VideoSize here, because at this point it is not available - the platform + // view was not created yet. We use the video format instead. Format videoFormat = exoPlayer.getVideoFormat(); - RotationDegrees rotationCorrection = RotationDegrees.fromDegrees(videoFormat.rotationDegrees); + RotationDegrees rotationCorrection = + RotationDegrees.fromDegrees(Objects.requireNonNull(videoFormat).rotationDegrees); int width = videoFormat.width; int height = videoFormat.height; @@ -111,12 +111,11 @@ private void sendInitializedPlatformViewApproach() { events.onInitialized(width, height, exoPlayer.getDuration(), rotationCorrection.degrees); } - private void sendInitializedTextureApproach() { + private void sendInitializedForTextureApproach() { VideoSize videoSize = exoPlayer.getVideoSize(); int rotationCorrection = 0; int width = videoSize.width; int height = videoSize.height; - if (width != 0 && height != 0) { RotationDegrees reportedRotationCorrection = RotationDegrees.ROTATE_0; @@ -217,8 +216,7 @@ public void onPlaybackStateChanged(final int playbackState) { public void onPlayerError(@NonNull final PlaybackException error) { setBuffering(false); if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { - // See - // https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window + // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window exoPlayer.seekToDefaultPosition(); exoPlayer.prepare(); } else { diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java index a5dd9d5224a2..586a3d698709 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeView.java @@ -16,7 +16,8 @@ class NativeView implements PlatformView { NativeView(@NonNull Context context, @NonNull ExoPlayer exoPlayer) { surfaceView = new SurfaceView(context); - // The line below is needed to display the video correctly on older Android versions. + // The line below is needed to display the video correctly on older Android versions (blank + // space instead of a video). surfaceView.setZOrderMediaOverlay(true); exoPlayer.setVideoSurfaceView(surfaceView); } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java index e7e9c4e74619..f9bf9d27077c 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/NativeViewFactory.java @@ -9,7 +9,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.media3.exoplayer.ExoPlayer; -import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.plugin.platform.PlatformView; import io.flutter.plugin.platform.PlatformViewFactory; import java.util.Objects; 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 bd9e03a6e1cb..7db25fc47072 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 @@ -80,7 +80,7 @@ protected ExoPlayer createVideoPlayer() { exoPlayer.addListener( new ExoPlayerEventListener( - exoPlayer, videoPlayerEvents, wasPlayerInitialized(), getViewType())); + exoPlayer, videoPlayerEvents, getViewType(), wasPlayerInitialized())); setAudioAttributes(exoPlayer, options.mixWithOthers); return exoPlayer; 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 9cfa2dc6f773..1f86048bd145 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 @@ -18,9 +18,11 @@ /** Android platform implementation of the VideoPlayerPlugin. */ public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { - /// The next non-texture player ID, initialized to a high number to avoid collisions with - /// texture IDs (which are generated separately). - static Long nextNonTexturePlayerId = 1000000L; + /** + * The next non-texture player ID, initialized to a high number to avoid collisions with texture + * IDs (which are generated separately). + */ + private static Long nextNonTexturePlayerId = 1000000L; private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); @@ -115,25 +117,19 @@ public void initialize() { VideoPlayer videoPlayer; if (arg.getViewType() == Messages.PlatformVideoViewType.PLATFORM_VIEW) { id = VideoPlayerPlugin.nextNonTexturePlayerId++; - EventChannel eventChannel = - new EventChannel(flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + id); - videoPlayer = VideoPlayer.create( flutterState.applicationContext, - VideoPlayerEventCallbacks.bindTo(eventChannel), + VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), videoAsset, options); } else { TextureRegistry.SurfaceProducer handle = flutterState.textureRegistry.createSurfaceProducer(); id = handle.id(); - EventChannel eventChannel = - new EventChannel(flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + id); - videoPlayer = VideoPlayerTextureApproach.create( flutterState.applicationContext, - VideoPlayerEventCallbacks.bindTo(eventChannel), + VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), handle, videoAsset, options); @@ -143,6 +139,12 @@ public void initialize() { return id; } + @NonNull + private EventChannel createEventChannel(long id) { + return new EventChannel( + flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + id); + } + @NonNull private VideoPlayer getPlayer(long textureId) { VideoPlayer player = videoPlayers.get(textureId); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java index 5937f69afd90..00cd7274510e 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerTextureApproach.java @@ -18,8 +18,6 @@ final class VideoPlayerTextureApproach extends VideoPlayer @NonNull private final TextureRegistry.SurfaceProducer surfaceProducer; @Nullable private ExoPlayerState savedStateDuring; - private static final Messages.PlatformVideoViewType viewType = - Messages.PlatformVideoViewType.TEXTURE_VIEW; /** * Creates a video player. * diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index d593abd5a6e6..8a0c7d41df18 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -13,7 +13,6 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.exoplayer.ExoPlayer; -import io.flutter.view.TextureRegistry; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -188,12 +187,4 @@ public void disposeReleasesExoPlayer() { verify(mockExoPlayer).release(); } - - // TODO(matanlurey): Replace with inline calls to onSurfaceAvailable once - // available on stable; see https://github.com/flutter/flutter/issues/155131. - // This separate method only exists to scope the suppression. - @SuppressWarnings({"deprecation", "removal"}) - void simulateSurfaceCreation(TextureRegistry.SurfaceProducer.Callback producerLifecycle) { - producerLifecycle.onSurfaceCreated(); - } } From 53b35dcd21d56b73c68f6915c4f2b5ae7a451c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 17 Dec 2024 16:28:28 +0100 Subject: [PATCH 82/92] Rotate video according to rotation correction in Android example app --- .../example/lib/mini_controller.dart | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 10c346ebb2d9..8cb65218bf2e 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -39,6 +40,7 @@ class VideoPlayerValue { this.isBuffering = false, this.playbackSpeed = 1.0, this.errorDescription, + this.rotationCorrection = 0, }); /// Returns an instance for a video that hasn't been loaded. @@ -83,6 +85,9 @@ class VideoPlayerValue { /// Indicates whether or not the video has been loaded and is ready to play. final bool isInitialized; + /// Degrees to rotate the video (clockwise) so it is displayed correctly. + final int rotationCorrection; + /// Indicates whether or not the video is in an error state. If this is true /// [errorDescription] should have information about the problem. bool get hasError => errorDescription != null; @@ -116,6 +121,7 @@ class VideoPlayerValue { bool? isBuffering, double? playbackSpeed, String? errorDescription, + int? rotationCorrection, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -127,6 +133,7 @@ class VideoPlayerValue { isBuffering: isBuffering ?? this.isBuffering, playbackSpeed: playbackSpeed ?? this.playbackSpeed, errorDescription: errorDescription ?? this.errorDescription, + rotationCorrection: rotationCorrection ?? this.rotationCorrection, ); } @@ -143,7 +150,8 @@ class VideoPlayerValue { playbackSpeed == other.playbackSpeed && errorDescription == other.errorDescription && size == other.size && - isInitialized == other.isInitialized; + isInitialized == other.isInitialized && + rotationCorrection == other.rotationCorrection; @override int get hashCode => Object.hash( @@ -156,6 +164,7 @@ class VideoPlayerValue { errorDescription, size, isInitialized, + rotationCorrection, ); } @@ -261,10 +270,9 @@ class MiniController extends ValueNotifier { void eventListener(VideoEvent event) { switch (event.eventType) { case VideoEventType.initialized: - print('LALA initialized'); value = value.copyWith( duration: event.duration, - // FIXME This is important! + rotationCorrection: event.rotationCorrection, size: event.size, isInitialized: event.duration != null, ); @@ -438,15 +446,38 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == MiniController.kUninitializedTextureId ? Container() - : _platform.buildViewWithOptions( - VideoViewOptions( - playerId: _textureId, - viewType: widget.controller.viewType, + : _VideoPlayerWithRotation( + rotation: widget.controller.value.rotationCorrection, + child: _platform.buildViewWithOptions( + VideoViewOptions( + playerId: _textureId, + viewType: widget.controller.viewType, + ), ), ); } } +class _VideoPlayerWithRotation extends StatelessWidget { + const _VideoPlayerWithRotation({ + required this.rotation, + required this.child, + }); + + final int rotation; + final Widget child; + + @override + Widget build(BuildContext context) { + return rotation == 0 + ? child + : Transform.rotate( + angle: rotation * pi / 180, + child: child, + ); + } +} + class _VideoScrubber extends StatefulWidget { const _VideoScrubber({ required this.child, From 7b246adbb4407aeb1c1093d7052b11ab01c47a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 17 Dec 2024 17:12:11 +0100 Subject: [PATCH 83/92] Use TextDirectio from context for layoutDirection when creating a surface view --- .../lib/src/android_video_player.dart | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) 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 664a41d2e373..d68a354dbc4d 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 @@ -167,32 +167,36 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { // FIXME Check if this setup is complete - return IgnorePointer( - // IgnorePointer so that GestureDetector can be used above the platform view. - child: PlatformViewLink( - viewType: viewType, - surfaceFactory: ( - BuildContext context, - PlatformViewController controller, - ) { - return AndroidViewSurface( - controller: controller as AndroidViewController, - gestureRecognizers: const >{}, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, - ); - }, - onCreatePlatformView: (PlatformViewCreationParams params) { - return PlatformViewsService.initSurfaceAndroidView( - id: params.id, - viewType: viewType, - layoutDirection: TextDirection.ltr, - creationParams: creationParams, - creationParamsCodec: AndroidVideoPlayerApi.pigeonChannelCodec, - onFocus: () => params.onFocusChanged(true), - ) - ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) - ..create(); - }, + return Builder( + builder: (BuildContext context) => IgnorePointer( + // IgnorePointer so that GestureDetector can be used above the platform view. + child: PlatformViewLink( + viewType: viewType, + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: viewType, + layoutDirection: + Directionality.maybeOf(context) ?? TextDirection.ltr, + creationParams: creationParams, + creationParamsCodec: AndroidVideoPlayerApi.pigeonChannelCodec, + onFocus: () => params.onFocusChanged(true), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ), ), ); } From f41ff9a55e6ab64e8caeada74ac867519341e4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Tue, 17 Dec 2024 17:14:05 +0100 Subject: [PATCH 84/92] Format messages.g.dart file --- .../lib/src/android_video_player.dart | 2 - .../lib/src/messages.g.dart | 111 +++++++++++------- 2 files changed, 68 insertions(+), 45 deletions(-) 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 d68a354dbc4d..66289434e183 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 @@ -165,8 +165,6 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { final PlatformVideoViewCreationParams creationParams = PlatformVideoViewCreationParams(playerId: playerId); - // FIXME Check if this setup is complete - return Builder( builder: (BuildContext context) => IgnorePointer( // IgnorePointer so that GestureDetector can be used above the platform view. 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 72c4d8b15577..89b8ed0283c4 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 @@ -18,7 +18,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -96,13 +97,13 @@ class CreateMessage { uri: result[1] as String?, packageName: result[2] as String?, formatHint: result[3] as String?, - httpHeaders: (result[4] as Map?)!.cast(), + httpHeaders: + (result[4] as Map?)!.cast(), viewType: result[5] as PlatformVideoViewType?, ); } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -110,13 +111,13 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { + } else if (value is PlatformVideoViewType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is PlatformVideoViewCreationParams) { + } else if (value is PlatformVideoViewCreationParams) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is CreateMessage) { + } else if (value is CreateMessage) { buffer.putUint8(131); writeValue(buffer, value.encode()); } else { @@ -127,12 +128,12 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoViewType.values[value]; - case 130: + case 130: return PlatformVideoViewCreationParams.decode(readValue(buffer)!); - case 131: + case 131: return CreateMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -144,9 +145,11 @@ class AndroidVideoPlayerApi { /// Constructor for [AndroidVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AndroidVideoPlayerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + AndroidVideoPlayerApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -154,8 +157,10 @@ class AndroidVideoPlayerApi { final String pigeonVar_messageChannelSuffix; Future initialize() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -176,8 +181,10 @@ class AndroidVideoPlayerApi { } Future create(CreateMessage msg) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -203,8 +210,10 @@ class AndroidVideoPlayerApi { } Future dispose(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -225,14 +234,16 @@ class AndroidVideoPlayerApi { } Future setLooping(int textureId, bool looping) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([textureId, looping]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([textureId, looping]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -247,14 +258,16 @@ class AndroidVideoPlayerApi { } Future setVolume(int textureId, double volume) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([textureId, volume]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([textureId, volume]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -269,14 +282,16 @@ class AndroidVideoPlayerApi { } Future setPlaybackSpeed(int textureId, double speed) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([textureId, speed]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([textureId, speed]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -291,8 +306,10 @@ class AndroidVideoPlayerApi { } Future play(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -313,8 +330,10 @@ class AndroidVideoPlayerApi { } Future position(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -340,14 +359,16 @@ class AndroidVideoPlayerApi { } Future seekTo(int textureId, int position) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([textureId, position]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([textureId, position]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -362,8 +383,10 @@ class AndroidVideoPlayerApi { } Future pause(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -384,14 +407,16 @@ class AndroidVideoPlayerApi { } Future setMixWithOthers(bool mixWithOthers) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([mixWithOthers]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([mixWithOthers]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { From 7e297f3206af2b5893f03bbe264c987628466576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 18 Dec 2024 11:07:46 +0100 Subject: [PATCH 85/92] Add Dart unit tests for create method on Android --- .../test/android_video_player_test.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) 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 2c99cb934d02..e6f38bd0cb1b 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 @@ -188,6 +188,31 @@ void main() { {'Authorization': 'Bearer token'}); expect(textureId, 3); }); + + test('create with texture view', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + // Texture view is a default view type. + )); + expect(log.log.last, 'create'); + expect( + log.passedCreateMessage?.viewType, PlatformVideoViewType.textureView); + expect(textureId, 3); + }); + + test('create with platform view', () async { + final int? playerId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + viewType: VideoViewType.platformView, + )); + expect(log.log.last, 'create'); + expect(log.passedCreateMessage?.viewType, + PlatformVideoViewType.platformView); + expect(playerId, 3); + }); + test('setLooping', () async { await player.setLooping(1, true); expect(log.log.last, 'setLooping'); From 00f66e6d4ac0cb3950c1095de539530de6326e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Wed, 18 Dec 2024 11:08:04 +0100 Subject: [PATCH 86/92] Add spacing between test cases --- packages/video_player/video_player/test/video_player_test.dart | 2 ++ 1 file changed, 2 insertions(+) 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 900d75ddce5c..38304868b71f 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -312,6 +312,7 @@ void main() { ); }); }); + group('initialize', () { test('started app lifecycle observing', () async { final VideoPlayerController controller = @@ -449,6 +450,7 @@ void main() { {'Authorization': 'Bearer token'}, ); }, skip: kIsWeb /* Web does not support file assets. */); + test('successful initialize on controller with error clears error', () async { final VideoPlayerController controller = VideoPlayerController.network( From 0212f0933f35dd0f089ecbfd4ab3581c58e499e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Thu, 19 Dec 2024 16:40:45 +0100 Subject: [PATCH 87/92] Add integration test for Android (using Espresso) --- .../example/android/app/build.gradle | 4 ++ .../videoplayerexample/VideoPlayerUITest.java | 72 +++++++++++++++++++ .../android/app/src/debug/AndroidManifest.xml | 20 ++++++ .../DriverExtensionActivity.java | 10 +++ .../main/res/xml/network_security_config.xml | 2 + .../video_player_android_test.dart | 19 +++++ .../example/lib/main.dart | 1 + .../video_player_android/example/pubspec.yaml | 3 + 8 files changed, 131 insertions(+) create mode 100644 packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/VideoPlayerUITest.java create mode 100644 packages/video_player/video_player_android/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/java/io/flutter/plugins/videoplayerexample/DriverExtensionActivity.java create mode 100644 packages/video_player/video_player_android/example/integration_test/video_player_android_test.dart diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 50b1ecdaa4ae..6cea0c7b6b12 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -57,9 +57,13 @@ flutter { } dependencies { + testImplementation 'androidx.test.ext:junit:1.2.1' + testImplementation "com.google.truth:truth:1.1.3" testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'org.mockito:mockito-core:5.0.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + implementation project(':espresso') + api 'androidx.test:core:1.2.0' } diff --git a/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/VideoPlayerUITest.java b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/VideoPlayerUITest.java new file mode 100644 index 000000000000..e4628fbf59d3 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/VideoPlayerUITest.java @@ -0,0 +1,72 @@ +// 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. + +package io.flutter.plugins.videoplayerexample; + +import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget; +import static androidx.test.espresso.flutter.EspressoFlutter.WidgetInteraction; +import static androidx.test.espresso.flutter.action.FlutterActions.click; +import static androidx.test.espresso.flutter.assertion.FlutterAssertions.matches; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.isExisting; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withText; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withValueKey; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class VideoPlayerUITest { + + @Rule + public ActivityScenarioRule activityRule = + new ActivityScenarioRule<>(DriverExtensionActivity.class); + + @Test + public void playVideo() { + WidgetInteraction remoteTab = onFlutterWidget(withText("Remote")); + remoteTab.check(matches(isExisting())); + + for (String tabName : new String[] {"Platform view", "Texture view"}) { + WidgetInteraction viewTypeTab = onFlutterWidget(withText(tabName)); + viewTypeTab.check(matches(isExisting())); + viewTypeTab.perform(click()); + + WidgetInteraction playButton = onFlutterWidget(withValueKey("Play")); + playButton.check(matches(isExisting())); + playButton.perform(click()); + + WidgetInteraction playbackSpeed1x = onFlutterWidget(withText("1.0x")); + playbackSpeed1x.check(matches(isExisting())); + playbackSpeed1x.perform(click()); + + WidgetInteraction playbackSpeed5xButton = onFlutterWidget(withText("5.0x")); + playbackSpeed5xButton.check(matches(isExisting())); + playbackSpeed5xButton.perform(click()); + + WidgetInteraction playbackSpeed5x = onFlutterWidget(withText("5.0x")); + playbackSpeed5x.check(matches(isExisting())); + } + + for (String[] tabData : + new String[][] {{"Asset", "With assets mp4"}, {"Remote", "With remote mp4"}}) { + String tabName = tabData[0]; + String videoDescription = tabData[1]; + WidgetInteraction tab = onFlutterWidget(withText(tabName)); + WidgetInteraction tabDescription = onFlutterWidget(withText(videoDescription)); + tab.check(matches(isExisting())); + + // TODO(FirentisTFW): Assert that testDescription is not visible before we tap on tab. + // This should be done once the Espresso API allows us to perform such an assertion. See + // https://github.com/flutter/flutter/issues/160599 + + tab.perform(click()); + + tab.check(matches(isExisting())); + tabDescription.check(matches(isExisting())); + } + } +} diff --git a/packages/video_player/video_player_android/example/android/app/src/debug/AndroidManifest.xml b/packages/video_player/video_player_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..03eb1a7bf7a5 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/packages/video_player/video_player_android/example/android/app/src/main/java/io/flutter/plugins/videoplayerexample/DriverExtensionActivity.java b/packages/video_player/video_player_android/example/android/app/src/main/java/io/flutter/plugins/videoplayerexample/DriverExtensionActivity.java new file mode 100644 index 000000000000..98fadc7f4e8a --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/main/java/io/flutter/plugins/videoplayerexample/DriverExtensionActivity.java @@ -0,0 +1,10 @@ +// 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. + +package io.flutter.plugins.videoplayerexample; + +import io.flutter.embedding.android.FlutterActivity; + +/** Test Activity that sets the name of the Dart method entrypoint in the manifest. */ +public class DriverExtensionActivity extends FlutterActivity {} diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml b/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml index 043e5ce55a2b..37ad2d39041d 100644 --- a/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml +++ b/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml @@ -3,5 +3,7 @@ www.sample-videos.com 184.72.239.149 + + 127.0.0.1 \ No newline at end of file diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_android_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_android_test.dart new file mode 100644 index 000000000000..aba59a1ef6c7 --- /dev/null +++ b/packages/video_player/video_player_android/example/integration_test/video_player_android_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_driver/driver_extension.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:video_player_example/main.dart' as app; + +@pragma('vm:entry-point') +void integrationTestMain() { + enableFlutterDriverExtension(); + + app.main(); +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Since this test is lacking integration tests, this test ensures the example + // app can be launched on an emulator/device. + testWidgets('Launch Test', (WidgetTester tester) async {}); +} diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index e4f87356f7a3..2e638b2fb0d4 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -354,6 +354,7 @@ class _ControlsOverlay extends StatelessWidget { color: Colors.black26, child: Center( child: Icon( + key: ValueKey('Play'), Icons.play_arrow, color: Colors.white, size: 100.0, diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml index 7b6f3bbfba7b..349eee14ca52 100644 --- a/packages/video_player/video_player_android/example/pubspec.yaml +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_driver: + sdk: flutter video_player_android: # When depending on this package from a real application you should use: # video_player_android: ^x.y.z @@ -19,6 +21,7 @@ dependencies: video_player_platform_interface: ">=6.1.0 <7.0.0" dev_dependencies: + espresso: ^0.4.0 flutter_test: sdk: flutter integration_test: From cecd6d03fcfdf52df42dfb44ba72dfb4c15ffc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 20 Dec 2024 10:59:55 +0100 Subject: [PATCH 88/92] Override video_player_android dependency in video_player --- packages/video_player/video_player/example/pubspec.yaml | 2 ++ packages/video_player/video_player/pubspec.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index e3a1073ee63e..c1adf428f8f4 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -40,6 +40,8 @@ flutter: # 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: diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 903c611c626f..c408fc817dd1 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -42,6 +42,8 @@ topics: # 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: From 5dfa83653713e45fe83cadfe74fbd5222b1ec4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 20 Dec 2024 11:00:46 +0100 Subject: [PATCH 89/92] Use buildViewWithOptions in buildView in Android platform interface implementation --- .../lib/src/android_video_player.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 66289434e183..82bb42fd5579 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 @@ -147,7 +147,15 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { @override Widget buildView(int textureId) { - return Texture(textureId: textureId); + return buildViewWithOptions( + VideoViewOptions( + playerId: textureId, + // Texture view was the only supported view type before + // buildViewWithOptions was introduced. We pass it here to maintain + // backwards compatibility. + viewType: VideoViewType.textureView, + ), + ); } @override From 737f43fd569216833496a5a414819ff0a2e688fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 20 Dec 2024 11:01:19 +0100 Subject: [PATCH 90/92] Allow choosing between texture view and platform view on Android in video_player example app --- packages/video_player/video_player/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index c3aa5fc7649a..296fbb1f256e 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -57,7 +57,7 @@ class _App extends StatelessWidget { ), ), body: TabBarView( - children: Platform.isIOS + children: Platform.isIOS || Platform.isAndroid ? [ _ViewTypeTabBar( builder: (VideoViewType viewType) => From 9ac0637a338f36c428c568e5ed25f490c7648ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 20 Dec 2024 12:15:36 +0100 Subject: [PATCH 91/92] Bump video_player_android version and update changelog --- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ packages/video_player/video_player_android/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index ef0db8cc5913..5d76ea6c7174 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +* Adds support for platform views as an optional way of displaying a video. + ## 2.7.16 * Updates internal Pigeon API to use newer features. diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index faabc30e7a20..24a9d4d0bc6f 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.7.16 +version: 2.8.0 environment: sdk: ^3.5.0 From 635970f6b2d3973336c3a268e2db312e6ce45df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jakubowski?= Date: Fri, 20 Dec 2024 12:22:56 +0100 Subject: [PATCH 92/92] Bump video_player version and update changelog --- packages/video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 5e448be001b3..4947c4b0a199 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.10.0 + +* Adds support for platform views as an optional way of displaying a video on Android and iOS. + ## 2.9.2 * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index c408fc817dd1..b6ea1b2431d1 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.2 +version: 2.10.0 environment: sdk: ^3.3.0