From 0e4a03d16d641bf6574d86236b93af27c59d37d4 Mon Sep 17 00:00:00 2001 From: abhinav Date: Mon, 30 Jan 2023 12:21:53 +0530 Subject: [PATCH 01/24] print statements --- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Podfile | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 25 +- ios/Classes/BetterPlayer.m | 1296 ++++++++++-------- ios/Classes/BetterPlayerPlugin.m | 897 ++++++------ ios/Classes/CacheManager.swift | 1 + lib/src/core/better_player_controller.dart | 1 + 7 files changed, 1228 insertions(+), 996 deletions(-) diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f2872cf47..4f8d4d245 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 1e8c3c90a..88359b225 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 6e6bf2d5a..5199b2ba7 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ "${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework", "${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework", "${BUILT_PRODUCTS_DIR}/better_player/better_player.framework", - "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -281,7 +281,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINCache.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINOperation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/better_player.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -364,7 +364,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -390,7 +390,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -451,7 +454,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -500,7 +503,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -527,7 +530,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -559,7 +565,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 2837ef651..4a82bc209 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -5,178 +5,200 @@ #import "BetterPlayer.h" #import -static void* timeRangeContext = &timeRangeContext; -static void* statusContext = &statusContext; -static void* playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; -static void* playbackBufferEmptyContext = &playbackBufferEmptyContext; -static void* playbackBufferFullContext = &playbackBufferFullContext; -static void* presentationSizeContext = &presentationSizeContext; - +static void *timeRangeContext = &timeRangeContext; +static void *statusContext = &statusContext; +static void *playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; +static void *playbackBufferEmptyContext = &playbackBufferEmptyContext; +static void *playbackBufferFullContext = &playbackBufferFullContext; +static void *presentationSizeContext = &presentationSizeContext; #if TARGET_OS_IOS -void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)(BOOL); +void (^__strong _Nonnull _restoreUserInterfaceForPIPStopCompletionHandler)( + BOOL); API_AVAILABLE(ios(9.0)) AVPictureInPictureController *_pipController; #endif @implementation BetterPlayer - (instancetype)initWithFrame:(CGRect)frame { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _isInitialized = false; - _isPlaying = false; - _disposed = false; - _player = [[AVPlayer alloc] init]; - _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - ///Fix for loading large videos - if (@available(iOS 10.0, *)) { - _player.automaticallyWaitsToMinimizeStalling = false; - } - self._observersAdded = false; - return self; + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + NSLog(@"initWithFrame =======>"); + _isInitialized = false; + _isPlaying = false; + _disposed = false; + _player = [[AVPlayer alloc] init]; + _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; + /// Fix for loading large videos + if (@available(iOS 10.0, *)) { + _player.automaticallyWaitsToMinimizeStalling = false; + } + self._observersAdded = false; + return self; } - (nonnull UIView *)view { - BetterPlayerView *playerView = [[BetterPlayerView alloc] initWithFrame:CGRectZero]; - playerView.player = _player; - return playerView; -} - -- (void)addObservers:(AVPlayerItem*)item { - if (!self._observersAdded){ - [_player addObserver:self forKeyPath:@"rate" options:0 context:nil]; - [item addObserver:self forKeyPath:@"loadedTimeRanges" options:0 context:timeRangeContext]; - [item addObserver:self forKeyPath:@"status" options:0 context:statusContext]; - [item addObserver:self forKeyPath:@"presentationSize" options:0 context:presentationSizeContext]; - [item addObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - options:0 - context:playbackLikelyToKeepUpContext]; - [item addObserver:self - forKeyPath:@"playbackBufferEmpty" - options:0 - context:playbackBufferEmptyContext]; - [item addObserver:self - forKeyPath:@"playbackBufferFull" - options:0 - context:playbackBufferFullContext]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(itemDidPlayToEndTime:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:item]; - self._observersAdded = true; - } + NSLog(@"UIView view =======>"); + BetterPlayerView *playerView = + [[BetterPlayerView alloc] initWithFrame:CGRectZero]; + playerView.player = _player; + return playerView; +} + +- (void)addObservers:(AVPlayerItem *)item { + NSLog(@"addObservers =======>"); + if (!self._observersAdded) { + [_player addObserver:self forKeyPath:@"rate" options:0 context:nil]; + [item addObserver:self + forKeyPath:@"loadedTimeRanges" + options:0 + context:timeRangeContext]; + [item addObserver:self + forKeyPath:@"status" + options:0 + context:statusContext]; + [item addObserver:self + forKeyPath:@"presentationSize" + options:0 + context:presentationSizeContext]; + [item addObserver:self + forKeyPath:@"playbackLikelyToKeepUp" + options:0 + context:playbackLikelyToKeepUpContext]; + [item addObserver:self + forKeyPath:@"playbackBufferEmpty" + options:0 + context:playbackBufferEmptyContext]; + [item addObserver:self + forKeyPath:@"playbackBufferFull" + options:0 + context:playbackBufferFullContext]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(itemDidPlayToEndTime:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:item]; + self._observersAdded = true; + } } - (void)clear { - _isInitialized = false; - _isPlaying = false; - _disposed = false; - _failedCount = 0; - _key = nil; - if (_player.currentItem == nil) { - return; - } - - if (_player.currentItem == nil) { - return; - } + NSLog(@"clear =======>"); + _isInitialized = false; + _isPlaying = false; + _disposed = false; + _failedCount = 0; + _key = nil; + if (_player.currentItem == nil) { + return; + } - [self removeObservers]; - AVAsset* asset = [_player.currentItem asset]; - [asset cancelLoading]; -} - -- (void) removeObservers{ - if (self._observersAdded){ - [_player removeObserver:self forKeyPath:@"rate" context:nil]; - [[_player currentItem] removeObserver:self forKeyPath:@"status" context:statusContext]; - [[_player currentItem] removeObserver:self forKeyPath:@"presentationSize" context:presentationSizeContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"loadedTimeRanges" - context:timeRangeContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - context:playbackLikelyToKeepUpContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackBufferEmpty" - context:playbackBufferEmptyContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackBufferFull" - context:playbackBufferFullContext]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - self._observersAdded = false; - } + [self removeObservers]; + AVAsset *asset = [_player.currentItem asset]; + [asset cancelLoading]; +} + +- (void)removeObservers { + NSLog(@"removeObservers =======>"); + if (self._observersAdded) { + [_player removeObserver:self forKeyPath:@"rate" context:nil]; + [[_player currentItem] removeObserver:self + forKeyPath:@"status" + context:statusContext]; + [[_player currentItem] removeObserver:self + forKeyPath:@"presentationSize" + context:presentationSizeContext]; + [[_player currentItem] removeObserver:self + forKeyPath:@"loadedTimeRanges" + context:timeRangeContext]; + [[_player currentItem] removeObserver:self + forKeyPath:@"playbackLikelyToKeepUp" + context:playbackLikelyToKeepUpContext]; + [[_player currentItem] removeObserver:self + forKeyPath:@"playbackBufferEmpty" + context:playbackBufferEmptyContext]; + [[_player currentItem] removeObserver:self + forKeyPath:@"playbackBufferFull" + context:playbackBufferFullContext]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + self._observersAdded = false; + } } -- (void)itemDidPlayToEndTime:(NSNotification*)notification { - if (_isLooping) { - AVPlayerItem* p = [notification object]; - [p seekToTime:kCMTimeZero completionHandler:nil]; - } else { - if (_eventSink) { - _eventSink(@{@"event" : @"completed", @"key" : _key}); - [ self removeObservers]; - - } +- (void)itemDidPlayToEndTime:(NSNotification *)notification { + NSLog(@"removeObservers =======>"); + if (_isLooping) { + AVPlayerItem *p = [notification object]; + [p seekToTime:kCMTimeZero completionHandler:nil]; + } else { + if (_eventSink) { + _eventSink(@{@"event" : @"completed", @"key" : _key}); + [self removeObservers]; } + } } - static inline CGFloat radiansToDegrees(CGFloat radians) { - // Input range [-pi, pi] or [-180, 180] - CGFloat degrees = GLKMathRadiansToDegrees((float)radians); - if (degrees < 0) { - // Convert -90 to 270 and -180 to 180 - return degrees + 360; - } - // Output degrees in between [0, 360[ - return degrees; + // Input range [-pi, pi] or [-180, 180] + CGFloat degrees = GLKMathRadiansToDegrees((float)radians); + if (degrees < 0) { + // Convert -90 to 270 and -180 to 180 + return degrees + 360; + } + // Output degrees in between [0, 360[ + return degrees; }; -- (AVMutableVideoComposition*)getVideoCompositionWithTransform:(CGAffineTransform)transform - withAsset:(AVAsset*)asset - withVideoTrack:(AVAssetTrack*)videoTrack { - AVMutableVideoCompositionInstruction* instruction = - [AVMutableVideoCompositionInstruction videoCompositionInstruction]; - instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [asset duration]); - AVMutableVideoCompositionLayerInstruction* layerInstruction = - [AVMutableVideoCompositionLayerInstruction - videoCompositionLayerInstructionWithAssetTrack:videoTrack]; - [layerInstruction setTransform:_preferredTransform atTime:kCMTimeZero]; - - AVMutableVideoComposition* videoComposition = [AVMutableVideoComposition videoComposition]; - instruction.layerInstructions = @[ layerInstruction ]; - videoComposition.instructions = @[ instruction ]; - - // If in portrait mode, switch the width and height of the video - CGFloat width = videoTrack.naturalSize.width; - CGFloat height = videoTrack.naturalSize.height; - NSInteger rotationDegrees = - (NSInteger)round(radiansToDegrees(atan2(_preferredTransform.b, _preferredTransform.a))); - if (rotationDegrees == 90 || rotationDegrees == 270) { - width = videoTrack.naturalSize.height; - height = videoTrack.naturalSize.width; - } - videoComposition.renderSize = CGSizeMake(width, height); +- (AVMutableVideoComposition *) + getVideoCompositionWithTransform:(CGAffineTransform)transform + withAsset:(AVAsset *)asset + withVideoTrack:(AVAssetTrack *)videoTrack { + NSLog(@"getVideoCompositionWithTransform =======>"); + AVMutableVideoCompositionInstruction *instruction = + [AVMutableVideoCompositionInstruction videoCompositionInstruction]; + instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [asset duration]); + AVMutableVideoCompositionLayerInstruction *layerInstruction = + [AVMutableVideoCompositionLayerInstruction + videoCompositionLayerInstructionWithAssetTrack:videoTrack]; + [layerInstruction setTransform:_preferredTransform atTime:kCMTimeZero]; + + AVMutableVideoComposition *videoComposition = + [AVMutableVideoComposition videoComposition]; + instruction.layerInstructions = @[ layerInstruction ]; + videoComposition.instructions = @[ instruction ]; + + // If in portrait mode, switch the width and height of the video + CGFloat width = videoTrack.naturalSize.width; + CGFloat height = videoTrack.naturalSize.height; + NSInteger rotationDegrees = (NSInteger)round( + radiansToDegrees(atan2(_preferredTransform.b, _preferredTransform.a))); + if (rotationDegrees == 90 || rotationDegrees == 270) { + width = videoTrack.naturalSize.height; + height = videoTrack.naturalSize.width; + } + videoComposition.renderSize = CGSizeMake(width, height); - float nominalFrameRate = videoTrack.nominalFrameRate; - int fps = 30; - if (nominalFrameRate > 0) { - fps = (int) ceil(nominalFrameRate); - } - videoComposition.frameDuration = CMTimeMake(1, fps); - - return videoComposition; + float nominalFrameRate = videoTrack.nominalFrameRate; + int fps = 30; + if (nominalFrameRate > 0) { + fps = (int)ceil(nominalFrameRate); + } + videoComposition.frameDuration = CMTimeMake(1, fps); + + return videoComposition; } -- (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack { +- (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { + NSLog(@"fixTransform =======>"); CGAffineTransform transform = videoTrack.preferredTransform; - // TODO(@recastrodiaz): why do we need to do this? Why is the preferredTransform incorrect? - // At least 2 user videos show a black screen when in portrait mode if we directly use the - // videoTrack.preferredTransform Setting tx to the height of the video instead of 0, properly - // displays the video https://github.com/flutter/flutter/issues/17606#issuecomment-413473181 - NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a))); + // TODO(@recastrodiaz): why do we need to do this? Why is the + // preferredTransform incorrect? At least 2 user videos show a black screen + // when in portrait mode if we directly use the videoTrack.preferredTransform + // Setting tx to the height of the video instead of 0, properly displays the + // video + // https://github.com/flutter/flutter/issues/17606#issuecomment-413473181 + NSInteger rotationDegrees = + (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a))); if (rotationDegrees == 90) { transform.tx = videoTrack.naturalSize.height; transform.ty = 0; @@ -190,575 +212,681 @@ - (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack { return transform; } -- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration{ - NSString* path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; - return [self setDataSourceURL:[NSURL fileURLWithPath:path] withKey:key withCertificateUrl:certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders: @{} withCache: false cacheKey:cacheKey cacheManager:cacheManager overriddenDuration:overriddenDuration videoExtension: nil]; -} +- (void)setDataSourceAsset:(NSString *)asset + withKey:(NSString *)key + withCertificateUrl:(NSString *)certificateUrl + withLicenseUrl:(NSString *)licenseUrl + cacheKey:(NSString *)cacheKey + cacheManager:(CacheManager *)cacheManager + overriddenDuration:(int)overriddenDuration { + NSLog(@"setDataSourceAsset =======>"); + NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; + return [self setDataSourceURL:[NSURL fileURLWithPath:path] + withKey:key + withCertificateUrl:certificateUrl + withLicenseUrl:(NSString *)licenseUrl + withHeaders:@{} + withCache:false + cacheKey:cacheKey + cacheManager:cacheManager + overriddenDuration:overriddenDuration + videoExtension:nil]; +} + +- (void)setDataSourceURL:(NSURL *)url + withKey:(NSString *)key + withCertificateUrl:(NSString *)certificateUrl + withLicenseUrl:(NSString *)licenseUrl + withHeaders:(NSDictionary *)headers + withCache:(BOOL)useCache + cacheKey:(NSString *)cacheKey + cacheManager:(CacheManager *)cacheManager + overriddenDuration:(int)overriddenDuration + videoExtension:(NSString *)videoExtension { + NSLog(@"setDataSourceURL =======>"); + _overriddenDuration = 0; + if (headers == [NSNull null] || headers == NULL) { + headers = @{}; + } -- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension: (NSString*) videoExtension{ - _overriddenDuration = 0; - if (headers == [NSNull null] || headers == NULL){ - headers = @{}; + AVPlayerItem *item; + if (useCache) { + if (cacheKey == [NSNull null]) { + cacheKey = nil; } - - AVPlayerItem* item; - if (useCache){ - if (cacheKey == [NSNull null]){ - cacheKey = nil; - } - if (videoExtension == [NSNull null]){ - videoExtension = nil; - } - - item = [cacheManager getCachingPlayerItemForNormalPlayback:url cacheKey:cacheKey videoExtension: videoExtension headers:headers]; - } else { - AVURLAsset* asset = [AVURLAsset URLAssetWithURL:url - options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}]; - if (certificateUrl && certificateUrl != [NSNull null] && [certificateUrl length] > 0) { - NSURL * certificateNSURL = [[NSURL alloc] initWithString: certificateUrl]; - NSURL * licenseNSURL = [[NSURL alloc] initWithString: licenseUrl]; - _loaderDelegate = [[BetterPlayerEzDrmAssetsLoaderDelegate alloc] init:certificateNSURL withLicenseURL:licenseNSURL]; - dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1); - dispatch_queue_t streamQueue = dispatch_queue_create("streamQueue", qos); - [asset.resourceLoader setDelegate:_loaderDelegate queue:streamQueue]; - } - item = [AVPlayerItem playerItemWithAsset:asset]; + if (videoExtension == [NSNull null]) { + videoExtension = nil; } - if (@available(iOS 10.0, *) && overriddenDuration > 0) { - _overriddenDuration = overriddenDuration; - } - return [self setDataSourcePlayerItem:item withKey:key]; -} + item = [cacheManager getCachingPlayerItemForNormalPlayback:url + cacheKey:cacheKey + videoExtension:videoExtension + headers:headers]; + } else { + AVURLAsset *asset = [AVURLAsset + URLAssetWithURL:url + options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}]; + if (certificateUrl && certificateUrl != [NSNull null] && + [certificateUrl length] > 0) { + NSURL *certificateNSURL = [[NSURL alloc] initWithString:certificateUrl]; + NSURL *licenseNSURL = [[NSURL alloc] initWithString:licenseUrl]; + _loaderDelegate = + [[BetterPlayerEzDrmAssetsLoaderDelegate alloc] init:certificateNSURL + withLicenseURL:licenseNSURL]; + dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class( + DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1); + dispatch_queue_t streamQueue = dispatch_queue_create("streamQueue", qos); + [asset.resourceLoader setDelegate:_loaderDelegate queue:streamQueue]; + } + item = [AVPlayerItem playerItemWithAsset:asset]; + } -- (void)setDataSourcePlayerItem:(AVPlayerItem*)item withKey:(NSString*)key{ - _key = key; - _stalledCount = 0; - _isStalledCheckStarted = false; - _playerRate = 1; - [_player replaceCurrentItemWithPlayerItem:item]; - - AVAsset* asset = [item asset]; - void (^assetCompletionHandler)(void) = ^{ - if ([asset statusOfValueForKey:@"tracks" error:nil] == AVKeyValueStatusLoaded) { - NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count] > 0) { - AVAssetTrack* videoTrack = tracks[0]; - void (^trackCompletionHandler)(void) = ^{ - if (self->_disposed) return; - if ([videoTrack statusOfValueForKey:@"preferredTransform" - error:nil] == AVKeyValueStatusLoaded) { - // Rotate the video by using a videoComposition and the preferredTransform - self->_preferredTransform = [self fixTransform:videoTrack]; - // Note: - // https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition - // Video composition can only be used with file-based media and is not supported for - // use with media served using HTTP Live Streaming. - AVMutableVideoComposition* videoComposition = - [self getVideoCompositionWithTransform:self->_preferredTransform - withAsset:asset - withVideoTrack:videoTrack]; - item.videoComposition = videoComposition; - } - }; - [videoTrack loadValuesAsynchronouslyForKeys:@[ @"preferredTransform" ] - completionHandler:trackCompletionHandler]; - } - } - }; + if (@available(iOS 10.0, *) && overriddenDuration > 0) { + _overriddenDuration = overriddenDuration; + } + return [self setDataSourcePlayerItem:item withKey:key]; +} + +- (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key { + NSLog(@"setDataSourcePlayerItem =======>"); + _key = key; + _stalledCount = 0; + _isStalledCheckStarted = false; + _playerRate = 1; + [_player replaceCurrentItemWithPlayerItem:item]; + + AVAsset *asset = [item asset]; + void (^assetCompletionHandler)(void) = ^{ + if ([asset statusOfValueForKey:@"tracks" + error:nil] == AVKeyValueStatusLoaded) { + NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; + if ([tracks count] > 0) { + AVAssetTrack *videoTrack = tracks[0]; + void (^trackCompletionHandler)(void) = ^{ + if (self->_disposed) + return; + if ([videoTrack statusOfValueForKey:@"preferredTransform" + error:nil] == AVKeyValueStatusLoaded) { + // Rotate the video by using a videoComposition and the + // preferredTransform + self->_preferredTransform = [self fixTransform:videoTrack]; + // Note: + // https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition + // Video composition can only be used with file-based media and is + // not supported for use with media served using HTTP Live + // Streaming. + AVMutableVideoComposition *videoComposition = + [self getVideoCompositionWithTransform:self->_preferredTransform + withAsset:asset + withVideoTrack:videoTrack]; + item.videoComposition = videoComposition; + } + }; + [videoTrack loadValuesAsynchronouslyForKeys:@[ @"preferredTransform" ] + completionHandler:trackCompletionHandler]; + } + } + }; + + [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] + completionHandler:assetCompletionHandler]; + [self addObservers:item]; +} + +- (void)handleStalled { + NSLog(@"handleStalled =======>"); + if (_isStalledCheckStarted) { + return; + } + _isStalledCheckStarted = true; + [self startStalledCheck]; +} - [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] completionHandler:assetCompletionHandler]; - [self addObservers:item]; +- (void)startStalledCheck { + NSLog(@"startStalledCheck =======>"); + if (_player.currentItem.playbackLikelyToKeepUp || + [self availableDuration] - + CMTimeGetSeconds(_player.currentItem.currentTime) > + 10.0) { + [self play]; + } else { + _stalledCount++; + if (_stalledCount > 60) { + if (_eventSink != nil) { + _eventSink([FlutterError + errorWithCode:@"VideoError" + message:@"Failed to load video: playback stalled" + details:nil]); + } + return; + } + [self performSelector:@selector(startStalledCheck) + withObject:nil + afterDelay:1]; + } } --(void)handleStalled { - if (_isStalledCheckStarted){ - return; - } - _isStalledCheckStarted = true; - [self startStalledCheck]; +- (NSTimeInterval)availableDuration { + NSLog(@"availableDuration =======>"); + NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; + NSLog(@"loadedTimeRanges availableduration =======> %@", loadedTimeRanges); + if (loadedTimeRanges.count > 0) { + CMTimeRange timeRange = + [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; + Float64 startSeconds = CMTimeGetSeconds(timeRange.start); + Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); + NSTimeInterval result = startSeconds + durationSeconds; + return result; + } else { + return 0; + } } --(void)startStalledCheck{ - if (_player.currentItem.playbackLikelyToKeepUp || - [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > 10.0) { - [self play]; - } else { - _stalledCount++; - if (_stalledCount > 60){ - if (_eventSink != nil) { - _eventSink([FlutterError - errorWithCode:@"VideoError" - message:@"Failed to load video: playback stalled" - details:nil]); - } - return; +- (void)observeValueForKeyPath:(NSString *)path + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + NSLog(@"observeValueForKeyPath =======>"); + if ([path isEqualToString:@"rate"]) { + NSLog(@"path isEqualToString =======> %@", path); + if (@available(iOS 10.0, *)) { + if (_pipController.pictureInPictureActive == true) { + if (_lastAvPlayerTimeControlStatus != [NSNull null] && + _lastAvPlayerTimeControlStatus == _player.timeControlStatus) { + return; } - [self performSelector:@selector(startStalledCheck) withObject:nil afterDelay:1]; + if (_player.timeControlStatus == AVPlayerTimeControlStatusPaused) { + _lastAvPlayerTimeControlStatus = _player.timeControlStatus; + if (_eventSink != nil) { + _eventSink(@{@"event" : @"pause"}); + } + return; + } + if (_player.timeControlStatus == AVPlayerTimeControlStatusPlaying) { + _lastAvPlayerTimeControlStatus = _player.timeControlStatus; + if (_eventSink != nil) { + _eventSink(@{@"event" : @"play"}); + } + } + } } -} -- (NSTimeInterval) availableDuration -{ - NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; - if (loadedTimeRanges.count > 0){ - CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; - Float64 startSeconds = CMTimeGetSeconds(timeRange.start); - Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); - NSTimeInterval result = startSeconds + durationSeconds; - return result; - } else { - return 0; + if (_player.rate == 0 && // if player rate dropped to 0 + CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, + kCMTimeZero) && // if video was started + CMTIME_COMPARE_INLINE( + _player.currentItem.currentTime, <, + _player.currentItem.duration) && // but not yet finished + _isPlaying) { // instance variable to handle overall state (changed to + // YES when user triggers playback) + [self handleStalled]; } + } -} - -- (void)observeValueForKeyPath:(NSString*)path - ofObject:(id)object - change:(NSDictionary*)change - context:(void*)context { - - if ([path isEqualToString:@"rate"]) { - if (@available(iOS 10.0, *)) { - if (_pipController.pictureInPictureActive == true){ - if (_lastAvPlayerTimeControlStatus != [NSNull null] && _lastAvPlayerTimeControlStatus == _player.timeControlStatus){ - return; - } - - if (_player.timeControlStatus == AVPlayerTimeControlStatusPaused){ - _lastAvPlayerTimeControlStatus = _player.timeControlStatus; - if (_eventSink != nil) { - _eventSink(@{@"event" : @"pause"}); - } - return; - - } - if (_player.timeControlStatus == AVPlayerTimeControlStatusPlaying){ - _lastAvPlayerTimeControlStatus = _player.timeControlStatus; - if (_eventSink != nil) { - _eventSink(@{@"event" : @"play"}); - } - } - } + if (context == timeRangeContext) { + NSLog(@"context =======> %@", context); + if (_eventSink != nil) { + NSMutableArray *> *values = + [[NSMutableArray alloc] init]; + for (NSValue *rangeValue in [object loadedTimeRanges]) { + CMTimeRange range = [rangeValue CMTimeRangeValue]; + int64_t start = [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.start)]; + int64_t end = + start + [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.duration)]; + if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { + int64_t endTime = [BetterPlayerTimeUtils + FLTCMTimeToMillis:(_player.currentItem.forwardPlaybackEndTime)]; + if (end > endTime) { + end = endTime; + } } - if (_player.rate == 0 && //if player rate dropped to 0 - CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, kCMTimeZero) && //if video was started - CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, <, _player.currentItem.duration) && //but not yet finished - _isPlaying) { //instance variable to handle overall state (changed to YES when user triggers playback) - [self handleStalled]; - } + [values addObject:@[ @(start), @(end) ]]; + } + _eventSink( + @{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); } + } else if (context == presentationSizeContext) { + [self onReadyToPlay]; + } - if (context == timeRangeContext) { - if (_eventSink != nil) { - NSMutableArray*>* values = [[NSMutableArray alloc] init]; - for (NSValue* rangeValue in [object loadedTimeRanges]) { - CMTimeRange range = [rangeValue CMTimeRangeValue]; - int64_t start = [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.start)]; - int64_t end = start + [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.duration)]; - if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { - int64_t endTime = [BetterPlayerTimeUtils FLTCMTimeToMillis:(_player.currentItem.forwardPlaybackEndTime)]; - if (end > endTime){ - end = endTime; - } - } - - [values addObject:@[ @(start), @(end) ]]; - } - _eventSink(@{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); - } - } - else if (context == presentationSizeContext){ - [self onReadyToPlay]; + else if (context == statusContext) { + AVPlayerItem *item = (AVPlayerItem *)object; + switch (item.status) { + case AVPlayerItemStatusFailed: + NSLog(@"Failed to load video:"); + NSLog(item.error.debugDescription); + + if (_eventSink != nil) { + _eventSink([FlutterError + errorWithCode:@"VideoError" + message:[@"Failed to load video: " + stringByAppendingString: + [item.error localizedDescription]] + details:nil]); + } + break; + case AVPlayerItemStatusUnknown: + break; + case AVPlayerItemStatusReadyToPlay: + [self onReadyToPlay]; + break; + } + } else if (context == playbackLikelyToKeepUpContext) { + if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { + [self updatePlayingState]; + if (_eventSink != nil) { + _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); + } + } + } else if (context == playbackBufferEmptyContext) { + if (_eventSink != nil) { + _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); } - - else if (context == statusContext) { - AVPlayerItem* item = (AVPlayerItem*)object; - switch (item.status) { - case AVPlayerItemStatusFailed: - NSLog(@"Failed to load video:"); - NSLog(item.error.debugDescription); - - if (_eventSink != nil) { - _eventSink([FlutterError - errorWithCode:@"VideoError" - message:[@"Failed to load video: " - stringByAppendingString:[item.error localizedDescription]] - details:nil]); - } - break; - case AVPlayerItemStatusUnknown: - break; - case AVPlayerItemStatusReadyToPlay: - [self onReadyToPlay]; - break; - } - } else if (context == playbackLikelyToKeepUpContext) { - if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { - [self updatePlayingState]; - if (_eventSink != nil) { - _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); - } - } - } else if (context == playbackBufferEmptyContext) { - if (_eventSink != nil) { - _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); - } - } else if (context == playbackBufferFullContext) { - if (_eventSink != nil) { - _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); - } + } else if (context == playbackBufferFullContext) { + if (_eventSink != nil) { + _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } + } } - (void)updatePlayingState { - if (!_isInitialized || !_key) { - return; - } - if (!self._observersAdded){ - [self addObservers:[_player currentItem]]; - } + NSLog(@"updatePlayingState =======>"); + if (!_isInitialized || !_key) { + return; + } + if (!self._observersAdded) { + [self addObservers:[_player currentItem]]; + } - if (_isPlaying) { - if (@available(iOS 10.0, *)) { - [_player playImmediatelyAtRate:1.0]; - _player.rate = _playerRate; - } else { - [_player play]; - _player.rate = _playerRate; - } + if (_isPlaying) { + if (@available(iOS 10.0, *)) { + [_player playImmediatelyAtRate:1.0]; + _player.rate = _playerRate; } else { - [_player pause]; + [_player play]; + _player.rate = _playerRate; } + } else { + [_player pause]; + } } - (void)onReadyToPlay { - if (_eventSink && !_isInitialized && _key) { - if (!_player.currentItem) { - return; - } - if (_player.status != AVPlayerStatusReadyToPlay) { - return; - } - - CGSize size = [_player currentItem].presentationSize; - CGFloat width = size.width; - CGFloat height = size.height; - + NSLog(@"onReadyToPlay =======>"); + if (_eventSink && !_isInitialized && _key) { + if (!_player.currentItem) { + return; + } + if (_player.status != AVPlayerStatusReadyToPlay) { + return; + } - AVAsset *asset = _player.currentItem.asset; - bool onlyAudio = [[asset tracksWithMediaType:AVMediaTypeVideo] count] == 0; + CGSize size = [_player currentItem].presentationSize; + CGFloat width = size.width; + CGFloat height = size.height; - // The player has not yet initialized. - if (!onlyAudio && height == CGSizeZero.height && width == CGSizeZero.width) { - return; - } - const BOOL isLive = CMTIME_IS_INDEFINITE([_player currentItem].duration); - // The player may be initialized but still needs to determine the duration. - if (isLive == false && [self duration] == 0) { - return; - } + AVAsset *asset = _player.currentItem.asset; + bool onlyAudio = [[asset tracksWithMediaType:AVMediaTypeVideo] count] == 0; - //Fix from https://github.com/flutter/flutter/issues/66413 - AVPlayerItemTrack *track = [self.player currentItem].tracks.firstObject; - CGSize naturalSize = track.assetTrack.naturalSize; - CGAffineTransform prefTrans = track.assetTrack.preferredTransform; - CGSize realSize = CGSizeApplyAffineTransform(naturalSize, prefTrans); + // The player has not yet initialized. + if (!onlyAudio && height == CGSizeZero.height && + width == CGSizeZero.width) { + return; + } + const BOOL isLive = CMTIME_IS_INDEFINITE([_player currentItem].duration); + // The player may be initialized but still needs to determine the duration. + if (isLive == false && [self duration] == 0) { + return; + } - int64_t duration = [BetterPlayerTimeUtils FLTCMTimeToMillis:(_player.currentItem.asset.duration)]; - if (_overriddenDuration > 0 && duration > _overriddenDuration){ - _player.currentItem.forwardPlaybackEndTime = CMTimeMake(_overriddenDuration/1000, 1); - } + // Fix from https://github.com/flutter/flutter/issues/66413 + AVPlayerItemTrack *track = [self.player currentItem].tracks.firstObject; + CGSize naturalSize = track.assetTrack.naturalSize; + CGAffineTransform prefTrans = track.assetTrack.preferredTransform; + CGSize realSize = CGSizeApplyAffineTransform(naturalSize, prefTrans); - _isInitialized = true; - [self updatePlayingState]; - _eventSink(@{ - @"event" : @"initialized", - @"duration" : @([self duration]), - @"width" : @(fabs(realSize.width) ? : width), - @"height" : @(fabs(realSize.height) ? : height), - @"key" : _key - }); + int64_t duration = [BetterPlayerTimeUtils + FLTCMTimeToMillis:(_player.currentItem.asset.duration)]; + if (_overriddenDuration > 0 && duration > _overriddenDuration) { + _player.currentItem.forwardPlaybackEndTime = + CMTimeMake(_overriddenDuration / 1000, 1); + NSLog(@"_player.currentItem.forwardPlaybackEndTime :::: %@ =======> ", + _player.currentItem.forwardPlaybackEndTime); } + + _isInitialized = true; + [self updatePlayingState]; + _eventSink(@{ + @"event" : @"initialized", + @"duration" : @([self duration]), + @"width" : @(fabs(realSize.width) ?: width), + @"height" : @(fabs(realSize.height) ?: height), + @"key" : _key + }); + } } - (void)play { - _stalledCount = 0; - _isStalledCheckStarted = false; - _isPlaying = true; - [self updatePlayingState]; + NSLog(@"play =======>"); + _stalledCount = 0; + _isStalledCheckStarted = false; + _isPlaying = true; + [self updatePlayingState]; } - (void)pause { - _isPlaying = false; - [self updatePlayingState]; + NSLog(@"pause =======>"); + _isPlaying = false; + [self updatePlayingState]; } - (int64_t)position { - return [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]; + NSLog(@"position =======>"); + return [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]; } - (int64_t)absolutePosition { - return [BetterPlayerTimeUtils FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] timeIntervalSince1970])]; + NSLog(@"absolutePosition =======>"); + return [BetterPlayerTimeUtils + FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] + timeIntervalSince1970])]; } - (int64_t)duration { - CMTime time; - if (@available(iOS 13, *)) { - time = [[_player currentItem] duration]; - } else { - time = [[[_player currentItem] asset] duration]; - } - if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { - time = [[_player currentItem] forwardPlaybackEndTime]; - } + NSLog(@"duration =======>"); + CMTime time; + if (@available(iOS 13, *)) { + time = [[_player currentItem] duration]; + } else { + time = [[[_player currentItem] asset] duration]; + } + if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { + time = [[_player currentItem] forwardPlaybackEndTime]; + } - return [BetterPlayerTimeUtils FLTCMTimeToMillis:(time)]; + return [BetterPlayerTimeUtils FLTCMTimeToMillis:(time)]; } - (void)seekTo:(int)location { - ///When player is playing, pause video, seek to new position and start again. This will prevent issues with seekbar jumps. - bool wasPlaying = _isPlaying; - if (wasPlaying){ - [_player pause]; - } + NSLog(@"seekTo =======>"); + /// When player is playing, pause video, seek to new position and start again. + /// This will prevent issues with seekbar jumps. + bool wasPlaying = _isPlaying; + if (wasPlaying) { + [_player pause]; + } - [_player seekToTime:CMTimeMake(location, 1000) + [_player seekToTime:CMTimeMake(location, 1000) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero - completionHandler:^(BOOL finished){ - if (wasPlaying){ - _player.rate = _playerRate; + completionHandler:^(BOOL finished) { + if (wasPlaying) { + _player.rate = _playerRate; } - }]; + }]; } - (void)setIsLooping:(bool)isLooping { - _isLooping = isLooping; + _isLooping = isLooping; } - (void)setVolume:(double)volume { - _player.volume = (float)((volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume)); + NSLog(@"setVolume =======>"); + _player.volume = + (float)((volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume)); } - (void)setSpeed:(double)speed result:(FlutterResult)result { - if (speed == 1.0 || speed == 0.0) { - _playerRate = 1; - result(nil); - } else if (speed < 0 || speed > 2.0) { - result([FlutterError errorWithCode:@"unsupported_speed" - message:@"Speed must be >= 0.0 and <= 2.0" - details:nil]); - } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || - (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { - _playerRate = speed; - result(nil); + NSLog(@"setSpeed =======>"); + if (speed == 1.0 || speed == 0.0) { + _playerRate = 1; + result(nil); + } else if (speed < 0 || speed > 2.0) { + result([FlutterError errorWithCode:@"unsupported_speed" + message:@"Speed must be >= 0.0 and <= 2.0" + details:nil]); + } else if ((speed > 1.0 && _player.currentItem.canPlayFastForward) || + (speed < 1.0 && _player.currentItem.canPlaySlowForward)) { + _playerRate = speed; + result(nil); + } else { + if (speed > 1.0) { + result([FlutterError + errorWithCode:@"unsupported_fast_forward" + message:@"This video cannot be played fast forward" + details:nil]); } else { - if (speed > 1.0) { - result([FlutterError errorWithCode:@"unsupported_fast_forward" - message:@"This video cannot be played fast forward" - details:nil]); - } else { - result([FlutterError errorWithCode:@"unsupported_slow_forward" - message:@"This video cannot be played slow forward" - details:nil]); - } + result([FlutterError + errorWithCode:@"unsupported_slow_forward" + message:@"This video cannot be played slow forward" + details:nil]); } + } - if (_isPlaying){ - _player.rate = _playerRate; - } + if (_isPlaying) { + _player.rate = _playerRate; + } } - -- (void)setTrackParameters:(int) width: (int) height: (int)bitrate { - _player.currentItem.preferredPeakBitRate = bitrate; - if (@available(iOS 11.0, *)) { - if (width == 0 && height == 0){ - _player.currentItem.preferredMaximumResolution = CGSizeZero; - } else { - _player.currentItem.preferredMaximumResolution = CGSizeMake(width, height); - } +- (void)setTrackParameters:(int)width:(int)height:(int)bitrate { + NSLog(@"setTrackParameters =======>"); + _player.currentItem.preferredPeakBitRate = bitrate; + if (@available(iOS 11.0, *)) { + if (width == 0 && height == 0) { + _player.currentItem.preferredMaximumResolution = CGSizeZero; + } else { + _player.currentItem.preferredMaximumResolution = + CGSizeMake(width, height); } + } } -- (void)setPictureInPicture:(BOOL)pictureInPicture -{ - self._pictureInPicture = pictureInPicture; - if (@available(iOS 9.0, *)) { - if (_pipController && self._pictureInPicture && ![_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController startPictureInPicture]; - }); - } else if (_pipController && !self._pictureInPicture && [_pipController isPictureInPictureActive]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_pipController stopPictureInPicture]; - }); - } else { - // Fallback on earlier versions - } } +- (void)setPictureInPicture:(BOOL)pictureInPicture { + NSLog(@"setPictureInPicture =======>"); + self._pictureInPicture = pictureInPicture; + if (@available(iOS 9.0, *)) { + if (_pipController && self._pictureInPicture && + ![_pipController isPictureInPictureActive]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [_pipController startPictureInPicture]; + }); + } else if (_pipController && !self._pictureInPicture && + [_pipController isPictureInPictureActive]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [_pipController stopPictureInPicture]; + }); + } else { + // Fallback on earlier versions + } + } } #if TARGET_OS_IOS -- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore -{ - if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) { - _restoreUserInterfaceForPIPStopCompletionHandler(restore); - _restoreUserInterfaceForPIPStopCompletionHandler = NULL; - } +- (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore { + NSLog(@"setRestoreUserInterfaceForPIPStopCompletionHandler =======>"); + if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) { + _restoreUserInterfaceForPIPStopCompletionHandler(restore); + _restoreUserInterfaceForPIPStopCompletionHandler = NULL; + } } - (void)setupPipController { - if (@available(iOS 9.0, *)) { - [[AVAudioSession sharedInstance] setActive: YES error: nil]; - [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; - if (!_pipController && self._playerLayer && [AVPictureInPictureController isPictureInPictureSupported]) { - _pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:self._playerLayer]; - _pipController.delegate = self; - } - } else { - // Fallback on earlier versions + NSLog(@"setupPipController =======>"); + if (@available(iOS 9.0, *)) { + [[AVAudioSession sharedInstance] setActive:YES error:nil]; + [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; + if (!_pipController && self._playerLayer && + [AVPictureInPictureController isPictureInPictureSupported]) { + _pipController = [[AVPictureInPictureController alloc] + initWithPlayerLayer:self._playerLayer]; + _pipController.delegate = self; } + } else { + // Fallback on earlier versions + } } -- (void) enablePictureInPicture: (CGRect) frame{ - [self disablePictureInPicture]; - [self usePlayerLayer:frame]; -} - -- (void)usePlayerLayer: (CGRect) frame -{ - if( _player ) - { - // Create new controller passing reference to the AVPlayerLayer - self._playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; - UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController]; - self._playerLayer.frame = frame; - self._playerLayer.needsDisplayOnBoundsChange = YES; - // [self._playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil]; - [vc.view.layer addSublayer:self._playerLayer]; - vc.view.layer.needsDisplayOnBoundsChange = YES; - if (@available(iOS 9.0, *)) { - _pipController = NULL; - } - [self setupPipController]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - [self setPictureInPicture:true]; - }); +- (void)enablePictureInPicture:(CGRect)frame { + NSLog(@"enablePictureInPicture =======>"); + [self disablePictureInPicture]; + [self usePlayerLayer:frame]; +} + +- (void)usePlayerLayer:(CGRect)frame { + NSLog(@"usePlayerLayer =======>"); + if (_player) { + // Create new controller passing reference to the AVPlayerLayer + self._playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; + UIViewController *vc = + [[[UIApplication sharedApplication] keyWindow] rootViewController]; + self._playerLayer.frame = frame; + self._playerLayer.needsDisplayOnBoundsChange = YES; + // [self._playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath + // options:NSKeyValueObservingOptionNew context:nil]; + [vc.view.layer addSublayer:self._playerLayer]; + vc.view.layer.needsDisplayOnBoundsChange = YES; + if (@available(iOS 9.0, *)) { + _pipController = NULL; } + [self setupPipController]; + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [self setPictureInPicture:true]; + }); + } } -- (void)disablePictureInPicture -{ - [self setPictureInPicture:true]; - if (__playerLayer){ - [self._playerLayer removeFromSuperlayer]; - self._playerLayer = nil; - if (_eventSink != nil) { - _eventSink(@{@"event" : @"pipStop"}); - } +- (void)disablePictureInPicture { + NSLog(@"disablePictureInPicture =======>"); + [self setPictureInPicture:true]; + if (__playerLayer) { + [self._playerLayer removeFromSuperlayer]; + self._playerLayer = nil; + if (_eventSink != nil) { + _eventSink(@{@"event" : @"pipStop"}); } + } } #endif #if TARGET_OS_IOS -- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController API_AVAILABLE(ios(9.0)){ - [self disablePictureInPicture]; +- (void)pictureInPictureControllerDidStopPictureInPicture: + (AVPictureInPictureController *)pictureInPictureController + API_AVAILABLE(ios(9.0)) { + [self disablePictureInPicture]; } -- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController API_AVAILABLE(ios(9.0)){ - if (_eventSink != nil) { - _eventSink(@{@"event" : @"pipStart"}); - } +- (void)pictureInPictureControllerDidStartPictureInPicture: + (AVPictureInPictureController *)pictureInPictureController + API_AVAILABLE(ios(9.0)) { + if (_eventSink != nil) { + _eventSink(@{@"event" : @"pipStart"}); + } } -- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController API_AVAILABLE(ios(9.0)){ - +- (void)pictureInPictureControllerWillStopPictureInPicture: + (AVPictureInPictureController *)pictureInPictureController + API_AVAILABLE(ios(9.0)) { } -- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - +- (void)pictureInPictureControllerWillStartPictureInPicture: + (AVPictureInPictureController *)pictureInPictureController { } -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error { - +- (void)pictureInPictureController: + (AVPictureInPictureController *)pictureInPictureController + failedToStartPictureInPictureWithError:(NSError *)error { } -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL))completionHandler { - [self setRestoreUserInterfaceForPIPStopCompletionHandler: true]; +- (void)pictureInPictureController: + (AVPictureInPictureController *)pictureInPictureController + restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: + (void (^)(BOOL))completionHandler { + [self setRestoreUserInterfaceForPIPStopCompletionHandler:true]; } -- (void) setAudioTrack:(NSString*) name index:(int) index{ - AVMediaSelectionGroup *audioSelectionGroup = [[[_player currentItem] asset] mediaSelectionGroupForMediaCharacteristic: AVMediaCharacteristicAudible]; - NSArray* options = audioSelectionGroup.options; - - - for (int audioTrackIndex = 0; audioTrackIndex < [options count]; audioTrackIndex++) { - AVMediaSelectionOption* option = [options objectAtIndex:audioTrackIndex]; - NSArray *metaDatas = [AVMetadataItem metadataItemsFromArray:option.commonMetadata withKey:@"title" keySpace:@"comn"]; - if (metaDatas.count > 0) { - NSString *title = ((AVMetadataItem*)[metaDatas objectAtIndex:0]).stringValue; - if ([name compare:title] == NSOrderedSame && audioTrackIndex == index ){ - [[_player currentItem] selectMediaOption:option inMediaSelectionGroup: audioSelectionGroup]; - } - } +- (void)setAudioTrack:(NSString *)name index:(int)index { + NSLog(@"setAudioTrack =======>"); + AVMediaSelectionGroup *audioSelectionGroup = [[[_player currentItem] asset] + mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; + NSArray *options = audioSelectionGroup.options; + for (int audioTrackIndex = 0; audioTrackIndex < [options count]; + audioTrackIndex++) { + AVMediaSelectionOption *option = [options objectAtIndex:audioTrackIndex]; + NSArray *metaDatas = + [AVMetadataItem metadataItemsFromArray:option.commonMetadata + withKey:@"title" + keySpace:@"comn"]; + if (metaDatas.count > 0) { + NSString *title = + ((AVMetadataItem *)[metaDatas objectAtIndex:0]).stringValue; + if ([name compare:title] == NSOrderedSame && audioTrackIndex == index) { + [[_player currentItem] selectMediaOption:option + inMediaSelectionGroup:audioSelectionGroup]; + } } - + } } - (void)setMixWithOthers:(bool)mixWithOthers { + NSLog(@"setMixWithOthers =======>"); if (mixWithOthers) { + [[AVAudioSession sharedInstance] + setCategory:AVAudioSessionCategoryPlayback + withOptions:AVAudioSessionCategoryOptionMixWithOthers + error:nil]; + } else { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback - withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil]; - } else { - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; } } - #endif -- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { - _eventSink = nil; - return nil; +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + NSLog(@"onCancelWithArguments =======>"); + _eventSink = nil; + return nil; } -- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments - eventSink:(nonnull FlutterEventSink)events { - _eventSink = events; - // TODO(@recastrodiaz): remove the line below when the race condition is resolved: - // https://github.com/flutter/flutter/issues/21483 - // This line ensures the 'initialized' event is sent when the event - // 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this function - // onListenWithArguments is called) - [self onReadyToPlay]; - return nil; +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink: + (nonnull FlutterEventSink)events { + NSLog(@"onListenWithArguments =======>"); + _eventSink = events; + // TODO(@recastrodiaz): remove the line below when the race condition is + // resolved: https://github.com/flutter/flutter/issues/21483 This line ensures + // the 'initialized' event is sent when the event + // 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this + // function onListenWithArguments is called) + [self onReadyToPlay]; + 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 { - @try{ - [self clear]; - } - @catch(NSException *exception) { - NSLog(exception.debugDescription); - } + NSLog(@"disposeSansEventChannel =======>"); + @try { + [self clear]; + } @catch (NSException *exception) { + NSLog(exception.debugDescription); + } } - (void)dispose { - [self pause]; - [self disposeSansEventChannel]; - [_eventChannel setStreamHandler:nil]; - [self disablePictureInPicture]; - [self setPictureInPicture:false]; - _disposed = true; + NSLog(@"dispose =======>"); + [self pause]; + [self disposeSansEventChannel]; + [_eventChannel setStreamHandler:nil]; + [self disablePictureInPicture]; + [self setPictureInPicture:false]; + _disposed = true; } @end diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index ed50403fa..35ccc44f7 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -9,466 +9,559 @@ #error Code Requires ARC. #endif - @implementation BetterPlayerPlugin -NSMutableDictionary* _dataSourceDict; -NSMutableDictionary* _timeObserverIdDict; -NSMutableDictionary* _artworkImageDict; -CacheManager* _cacheManager; +NSMutableDictionary *_dataSourceDict; +NSMutableDictionary *_timeObserverIdDict; +NSMutableDictionary *_artworkImageDict; +CacheManager *_cacheManager; int texturesCount = -1; -BetterPlayer* _notificationPlayer; +BetterPlayer *_notificationPlayer; bool _remoteCommandsInitialized = false; - #pragma mark - FlutterPlugin protocol -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = - [FlutterMethodChannel methodChannelWithName:@"better_player_channel" - binaryMessenger:[registrar messenger]]; - BetterPlayerPlugin* instance = [[BetterPlayerPlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:instance channel:channel]; - //[registrar publish:instance]; - [registrar registerViewFactory:instance withId:@"com.jhomlala/better_player"]; ++ (void)registerWithRegistrar:(NSObject *)registrar { + NSLog(@"registerWithRegistrar =======>"); + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:@"better_player_channel" + binaryMessenger:[registrar messenger]]; + BetterPlayerPlugin *instance = + [[BetterPlayerPlugin alloc] initWithRegistrar:registrar]; + [registrar addMethodCallDelegate:instance channel:channel]; + //[registrar publish:instance]; + [registrar registerViewFactory:instance withId:@"com.jhomlala/better_player"]; } -- (instancetype)initWithRegistrar:(NSObject*)registrar { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _messenger = [registrar messenger]; - _registrar = registrar; - _players = [NSMutableDictionary dictionaryWithCapacity:1]; - _timeObserverIdDict = [NSMutableDictionary dictionary]; - _artworkImageDict = [NSMutableDictionary dictionary]; - _dataSourceDict = [NSMutableDictionary dictionary]; - _cacheManager = [[CacheManager alloc] init]; - [_cacheManager setup]; - return self; +- (instancetype)initWithRegistrar: + (NSObject *)registrar { + self = [super init]; + NSLog(@"initWithRegistrar =======>"); + NSAssert(self, @"super init cannot be nil"); + _messenger = [registrar messenger]; + _registrar = registrar; + _players = [NSMutableDictionary dictionaryWithCapacity:1]; + _timeObserverIdDict = [NSMutableDictionary dictionary]; + _artworkImageDict = [NSMutableDictionary dictionary]; + _dataSourceDict = [NSMutableDictionary dictionary]; + _cacheManager = [[CacheManager alloc] init]; + [_cacheManager setup]; + NSLog(@"_dataSourceDict =======> %@", _dataSourceDict); + NSLog(@"_players =======> %@", _players); + NSLog(@"_messenger =======> %@", _messenger); + return self; } -- (void)detachFromEngineForRegistrar:(NSObject*)registrar { - for (NSNumber* textureId in _players.allKeys) { - BetterPlayer* player = _players[textureId]; - [player disposeSansEventChannel]; - } - [_players removeAllObjects]; +- (void)detachFromEngineForRegistrar: + (NSObject *)registrar { + NSLog(@"detachFromEngineForRegistrar =======>"); + for (NSNumber *textureId in _players.allKeys) { + BetterPlayer *player = _players[textureId]; + [player disposeSansEventChannel]; + } + [_players removeAllObjects]; } #pragma mark - FlutterPlatformViewFactory protocol -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - NSNumber* textureId = [args objectForKey:@"textureId"]; - BetterPlayerView* player = [_players objectForKey:@(textureId.intValue)]; - return player; +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + NSLog(@"createWithFrame =======>"); + NSNumber *textureId = [args objectForKey:@"textureId"]; + BetterPlayerView *player = [_players objectForKey:@(textureId.intValue)]; + return player; } -- (NSObject*)createArgsCodec { - return [FlutterStandardMessageCodec sharedInstance]; +- (NSObject *)createArgsCodec { + NSLog(@"createArgsCodec =======>"); + return [FlutterStandardMessageCodec sharedInstance]; } #pragma mark - BetterPlayerPlugin class - (int)newTextureId { - texturesCount += 1; - return texturesCount; + NSLog(@"newTextureId =======>"); + texturesCount += 1; + return texturesCount; } -- (void)onPlayerSetup:(BetterPlayer*)player - result:(FlutterResult)result { - int64_t textureId = [self newTextureId]; - FlutterEventChannel* eventChannel = [FlutterEventChannel - eventChannelWithName:[NSString stringWithFormat:@"better_player_channel/videoEvents%lld", - textureId] - binaryMessenger:_messenger]; - [player setMixWithOthers:false]; - [eventChannel setStreamHandler:player]; - player.eventChannel = eventChannel; - _players[@(textureId)] = player; - result(@{@"textureId" : @(textureId)}); +- (void)onPlayerSetup:(BetterPlayer *)player result:(FlutterResult)result { + NSLog(@"onPlayerSetup =======>"); + int64_t textureId = [self newTextureId]; + NSLog(@"better_player_channel/videoEvents%lld", textureId); + FlutterEventChannel *eventChannel = [FlutterEventChannel + eventChannelWithName: + [NSString stringWithFormat:@"better_player_channel/videoEvents%lld", + textureId] + binaryMessenger:_messenger]; + [player setMixWithOthers:false]; + [eventChannel setStreamHandler:player]; + player.eventChannel = eventChannel; + _players[@(textureId)] = player; + result(@{@"textureId" : @(textureId)}); } -- (void) setupRemoteNotification :(BetterPlayer*) player{ - _notificationPlayer = player; - [self stopOtherUpdateListener:player]; - NSDictionary* dataSource = [_dataSourceDict objectForKey:[self getTextureId:player]]; - BOOL showNotification = false; - id showNotificationObject = [dataSource objectForKey:@"showNotification"]; - if (showNotificationObject != [NSNull null]) { - showNotification = [[dataSource objectForKey:@"showNotification"] boolValue]; - } - NSString* title = dataSource[@"title"]; - NSString* author = dataSource[@"author"]; - NSString* imageUrl = dataSource[@"imageUrl"]; - - if (showNotification){ - [self setRemoteCommandsNotificationActive]; - [self setupRemoteCommands: player]; - [self setupRemoteCommandNotification: player, title, author, imageUrl]; - [self setupUpdateListener: player, title, author, imageUrl]; - } +- (void)setupRemoteNotification:(BetterPlayer *)player { + NSLog(@"setupRemoteNotification =======>"); + _notificationPlayer = player; + [self stopOtherUpdateListener:player]; + NSDictionary *dataSource = + [_dataSourceDict objectForKey:[self getTextureId:player]]; + BOOL showNotification = false; + id showNotificationObject = [dataSource objectForKey:@"showNotification"]; + if (showNotificationObject != [NSNull null]) { + showNotification = + [[dataSource objectForKey:@"showNotification"] boolValue]; + } + NSString *title = dataSource[@"title"]; + NSString *author = dataSource[@"author"]; + NSString *imageUrl = dataSource[@"imageUrl"]; + + if (showNotification) { + [self setRemoteCommandsNotificationActive]; + [self setupRemoteCommands:player]; + [self setupRemoteCommandNotification:player, title, author, imageUrl]; + [self setupUpdateListener:player, title, author, imageUrl]; + } } -- (void) setRemoteCommandsNotificationActive{ - [[AVAudioSession sharedInstance] setActive:true error:nil]; - [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; +- (void)setRemoteCommandsNotificationActive { + NSLog(@"setRemoteCommandsNotificationActive =======>"); + [[AVAudioSession sharedInstance] setActive:true error:nil]; + [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } -- (void) setRemoteCommandsNotificationNotActive{ - if ([_players count] == 0) { - [[AVAudioSession sharedInstance] setActive:false error:nil]; - } +- (void)setRemoteCommandsNotificationNotActive { + NSLog(@"setRemoteCommandsNotificationNotActive =======>"); + if ([_players count] == 0) { + [[AVAudioSession sharedInstance] setActive:false error:nil]; + } - [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; + [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; } - -- (void) setupRemoteCommands:(BetterPlayer*)player { - if (_remoteCommandsInitialized){ - return; - } - MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; - [commandCenter.togglePlayPauseCommand setEnabled:YES]; - [commandCenter.playCommand setEnabled:YES]; - [commandCenter.pauseCommand setEnabled:YES]; - [commandCenter.nextTrackCommand setEnabled:NO]; - [commandCenter.previousTrackCommand setEnabled:NO]; - if (@available(iOS 9.1, *)) { - [commandCenter.changePlaybackPositionCommand setEnabled:YES]; - } - - [commandCenter.togglePlayPauseCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { - if (_notificationPlayer != [NSNull null]){ - if (_notificationPlayer.isPlaying){ - _notificationPlayer.eventSink(@{@"event" : @"play"}); - } else { - _notificationPlayer.eventSink(@{@"event" : @"pause"}); - } - } - return MPRemoteCommandHandlerStatusSuccess; - }]; - - [commandCenter.playCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { - if (_notificationPlayer != [NSNull null]){ +- (void)setupRemoteCommands:(BetterPlayer *)player { + NSLog(@"setupRemoteCommands =======>"); + if (_remoteCommandsInitialized) { + return; + } + MPRemoteCommandCenter *commandCenter = + [MPRemoteCommandCenter sharedCommandCenter]; + [commandCenter.togglePlayPauseCommand setEnabled:YES]; + [commandCenter.playCommand setEnabled:YES]; + [commandCenter.pauseCommand setEnabled:YES]; + [commandCenter.nextTrackCommand setEnabled:NO]; + [commandCenter.previousTrackCommand setEnabled:NO]; + if (@available(iOS 9.1, *)) { + [commandCenter.changePlaybackPositionCommand setEnabled:YES]; + } + + [commandCenter.togglePlayPauseCommand + addTargetWithHandler:^MPRemoteCommandHandlerStatus( + MPRemoteCommandEvent *_Nonnull event) { + if (_notificationPlayer != [NSNull null]) { + if (_notificationPlayer.isPlaying) { _notificationPlayer.eventSink(@{@"event" : @"play"}); + } else { + _notificationPlayer.eventSink(@{@"event" : @"pause"}); + } } return MPRemoteCommandHandlerStatusSuccess; - }]; + }]; - [commandCenter.pauseCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { - if (_notificationPlayer != [NSNull null]){ - _notificationPlayer.eventSink(@{@"event" : @"pause"}); + [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus( + MPRemoteCommandEvent *_Nonnull event) { + if (_notificationPlayer != [NSNull null]) { + _notificationPlayer.eventSink(@{@"event" : @"play"}); + } + return MPRemoteCommandHandlerStatusSuccess; + }]; + + [commandCenter.pauseCommand + addTargetWithHandler:^MPRemoteCommandHandlerStatus( + MPRemoteCommandEvent *_Nonnull event) { + if (_notificationPlayer != [NSNull null]) { + _notificationPlayer.eventSink(@{@"event" : @"pause"}); } return MPRemoteCommandHandlerStatusSuccess; - }]; - - - - if (@available(iOS 9.1, *)) { - [commandCenter.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { - if (_notificationPlayer != [NSNull null]){ - MPChangePlaybackPositionCommandEvent * playbackEvent = (MPChangePlaybackRateCommandEvent * ) event; - CMTime time = CMTimeMake(playbackEvent.positionTime, 1); - int64_t millis = [BetterPlayerTimeUtils FLTCMTimeToMillis:(time)]; - [_notificationPlayer seekTo: millis]; - _notificationPlayer.eventSink(@{@"event" : @"seek", @"position": @(millis)}); - } - return MPRemoteCommandHandlerStatusSuccess; + }]; + + if (@available(iOS 9.1, *)) { + [commandCenter.changePlaybackPositionCommand + addTargetWithHandler:^MPRemoteCommandHandlerStatus( + MPRemoteCommandEvent *_Nonnull event) { + if (_notificationPlayer != [NSNull null]) { + MPChangePlaybackPositionCommandEvent *playbackEvent = + (MPChangePlaybackRateCommandEvent *)event; + CMTime time = CMTimeMake(playbackEvent.positionTime, 1); + int64_t millis = [BetterPlayerTimeUtils FLTCMTimeToMillis:(time)]; + [_notificationPlayer seekTo:millis]; + _notificationPlayer.eventSink( + @{@"event" : @"seek", + @"position" : @(millis)}); + } + return MPRemoteCommandHandlerStatusSuccess; }]; - } - _remoteCommandsInitialized = true; + } + _remoteCommandsInitialized = true; } -- (void) setupRemoteCommandNotification:(BetterPlayer*)player, NSString* title, NSString* author , NSString* imageUrl{ - float positionInSeconds = player.position /1000; - float durationInSeconds = player.duration/ 1000; - - - NSMutableDictionary * nowPlayingInfoDict = [@{MPMediaItemPropertyArtist: author, - MPMediaItemPropertyTitle: title, - MPNowPlayingInfoPropertyElapsedPlaybackTime: [ NSNumber numberWithFloat : positionInSeconds], - MPMediaItemPropertyPlaybackDuration: [NSNumber numberWithFloat:durationInSeconds], - MPNowPlayingInfoPropertyPlaybackRate: @1, - } mutableCopy]; - - if (imageUrl != [NSNull null]){ - NSString* key = [self getTextureId:player]; - MPMediaItemArtwork* artworkImage = [_artworkImageDict objectForKey:key]; - - if (key != [NSNull null]){ - if (artworkImage){ - [nowPlayingInfoDict setObject:artworkImage forKey:MPMediaItemPropertyArtwork]; - [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfoDict; - +- (void)setupRemoteCommandNotification:(BetterPlayer *)player, NSString *title, + NSString *author, NSString *imageUrl { + NSLog(@"setupRemoteCommandNotification =======>"); + float positionInSeconds = player.position / 1000; + float durationInSeconds = player.duration / 1000; + + NSMutableDictionary *nowPlayingInfoDict = [@{ + MPMediaItemPropertyArtist : author, + MPMediaItemPropertyTitle : title, + MPNowPlayingInfoPropertyElapsedPlaybackTime : + [NSNumber numberWithFloat:positionInSeconds], + MPMediaItemPropertyPlaybackDuration : + [NSNumber numberWithFloat:durationInSeconds], + MPNowPlayingInfoPropertyPlaybackRate : @1, + } mutableCopy]; + + if (imageUrl != [NSNull null]) { + NSString *key = [self getTextureId:player]; + MPMediaItemArtwork *artworkImage = [_artworkImageDict objectForKey:key]; + + if (key != [NSNull null]) { + if (artworkImage) { + [nowPlayingInfoDict setObject:artworkImage + forKey:MPMediaItemPropertyArtwork]; + [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = + nowPlayingInfoDict; + + } else { + dispatch_queue_t queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(queue, ^{ + @try { + UIImage *tempArtworkImage = nil; + if ([imageUrl rangeOfString:@"http"].location == NSNotFound) { + tempArtworkImage = [UIImage imageWithContentsOfFile:imageUrl]; } else { - dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - dispatch_async(queue, ^{ - @try{ - UIImage * tempArtworkImage = nil; - if ([imageUrl rangeOfString:@"http"].location == NSNotFound){ - tempArtworkImage = [UIImage imageWithContentsOfFile:imageUrl]; - } else { - NSURL *nsImageUrl =[NSURL URLWithString:imageUrl]; - tempArtworkImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:nsImageUrl]]; - } - if(tempArtworkImage) - { - MPMediaItemArtwork* artworkImage = [[MPMediaItemArtwork alloc] initWithImage: tempArtworkImage]; - [_artworkImageDict setObject:artworkImage forKey:key]; - [nowPlayingInfoDict setObject:artworkImage forKey:MPMediaItemPropertyArtwork]; - } - [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfoDict; - } - @catch(NSException *exception) { - - } - }); + NSURL *nsImageUrl = [NSURL URLWithString:imageUrl]; + tempArtworkImage = [UIImage + imageWithData:[NSData dataWithContentsOfURL:nsImageUrl]]; } - } - } else { - [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfoDict; + if (tempArtworkImage) { + MPMediaItemArtwork *artworkImage = + [[MPMediaItemArtwork alloc] initWithImage:tempArtworkImage]; + [_artworkImageDict setObject:artworkImage forKey:key]; + [nowPlayingInfoDict setObject:artworkImage + forKey:MPMediaItemPropertyArtwork]; + } + [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = + nowPlayingInfoDict; + } @catch (NSException *exception) { + } + }); + } } + } else { + [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfoDict; + } } - - -- (NSString*) getTextureId: (BetterPlayer*) player{ - NSArray* temp = [_players allKeysForObject: player]; - NSString* key = [temp lastObject]; - return key; +- (NSString *)getTextureId:(BetterPlayer *)player { + NSLog(@"getTextureId =======>"); + NSArray *temp = [_players allKeysForObject:player]; + NSString *key = [temp lastObject]; + return key; } -- (void) setupUpdateListener:(BetterPlayer*)player,NSString* title, NSString* author,NSString* imageUrl { - id _timeObserverId = [player.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time){ - [self setupRemoteCommandNotification:player, title, author, imageUrl]; - }]; - - NSString* key = [self getTextureId:player]; - [ _timeObserverIdDict setObject:_timeObserverId forKey: key]; +- (void)setupUpdateListener:(BetterPlayer *)player, NSString *title, + NSString *author, NSString *imageUrl { + NSLog(@"setupUpdateListener =======>"); + id _timeObserverId = [player.player + addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) + queue:NULL + usingBlock:^(CMTime time) { + [self setupRemoteCommandNotification:player, + title, + author, + imageUrl]; + }]; + + NSString *key = [self getTextureId:player]; + [_timeObserverIdDict setObject:_timeObserverId forKey:key]; } - -- (void) disposeNotificationData: (BetterPlayer*)player{ - if (player == _notificationPlayer){ - _notificationPlayer = NULL; - _remoteCommandsInitialized = false; - } - NSString* key = [self getTextureId:player]; - id _timeObserverId = _timeObserverIdDict[key]; - [_timeObserverIdDict removeObjectForKey: key]; - [_artworkImageDict removeObjectForKey:key]; - if (_timeObserverId){ - [player.player removeTimeObserver:_timeObserverId]; - _timeObserverId = nil; - } - [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{}; +- (void)disposeNotificationData:(BetterPlayer *)player { + NSLog(@"disposeNotificationData =======>"); + if (player == _notificationPlayer) { + _notificationPlayer = NULL; + _remoteCommandsInitialized = false; + } + NSString *key = [self getTextureId:player]; + id _timeObserverId = _timeObserverIdDict[key]; + [_timeObserverIdDict removeObjectForKey:key]; + [_artworkImageDict removeObjectForKey:key]; + if (_timeObserverId) { + [player.player removeTimeObserver:_timeObserverId]; + _timeObserverId = nil; + } + [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{}; } -- (void) stopOtherUpdateListener: (BetterPlayer*) player{ - NSString* currentPlayerTextureId = [self getTextureId:player]; - for (NSString* textureId in _timeObserverIdDict.allKeys) { - if (currentPlayerTextureId == textureId){ - continue; - } - - id timeObserverId = [_timeObserverIdDict objectForKey:textureId]; - BetterPlayer* playerToRemoveListener = [_players objectForKey:textureId]; - [playerToRemoveListener.player removeTimeObserver: timeObserverId]; +- (void)stopOtherUpdateListener:(BetterPlayer *)player { + NSLog(@"stopOtherUpdateListener =======>"); + NSString *currentPlayerTextureId = [self getTextureId:player]; + for (NSString *textureId in _timeObserverIdDict.allKeys) { + if (currentPlayerTextureId == textureId) { + continue; } - [_timeObserverIdDict removeAllObjects]; + id timeObserverId = [_timeObserverIdDict objectForKey:textureId]; + BetterPlayer *playerToRemoveListener = [_players objectForKey:textureId]; + [playerToRemoveListener.player removeTimeObserver:timeObserverId]; + } + [_timeObserverIdDict removeAllObjects]; } +- (void)handleMethodCall:(FlutterMethodCall *)call + result:(FlutterResult)result { + NSLog(@"handleMethodCall objective C =======>"); + if ([@"init" isEqualToString:call.method]) { + NSLog(@"init method called =======>"); + // Allow audio playback when the Ring/Silent switch is set to silent + for (NSNumber *textureId in _players) { + [_players[textureId] dispose]; + NSLog(@"player =======> %@", _players); + } -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - - - if ([@"init" isEqualToString:call.method]) { - // Allow audio playback when the Ring/Silent switch is set to silent - for (NSNumber* textureId in _players) { - [_players[textureId] dispose]; + [_players removeAllObjects]; + result(nil); + } else if ([@"create" isEqualToString:call.method]) { + NSLog(@"create method called =======>"); + BetterPlayer *player = [[BetterPlayer alloc] initWithFrame:CGRectZero]; + [self onPlayerSetup:player result:result]; + } else { + NSDictionary *argsMap = call.arguments; + int64_t textureId = + ((NSNumber *)argsMap[@"textureId"]).unsignedIntegerValue; + BetterPlayer *player = _players[@(textureId)]; + if ([@"setDataSource" isEqualToString:call.method]) { + NSLog(@"setDataSource method called =======>"); + [player clear]; + // This call will clear cached frame because we will return transparent + // frame + + NSDictionary *dataSource = argsMap[@"dataSource"]; + NSLog(@"dataSource ========> %@", dataSource); + [_dataSourceDict setObject:dataSource forKey:[self getTextureId:player]]; + NSString *assetArg = dataSource[@"asset"]; + NSString *uriArg = dataSource[@"uri"]; + NSString *key = dataSource[@"key"]; + NSString *certificateUrl = dataSource[@"certificateUrl"]; + NSString *licenseUrl = dataSource[@"licenseUrl"]; + NSDictionary *headers = dataSource[@"headers"]; + NSString *cacheKey = dataSource[@"cacheKey"]; + NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; + NSString *videoExtension = dataSource[@"videoExtension"]; + int overriddenDuration = 0; + if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]) { + overriddenDuration = [dataSource[@"overriddenDuration"] intValue]; + } + NSLog(@"overriddenDuration ========> %@", overriddenDuration); + BOOL useCache = false; + id useCacheObject = [dataSource objectForKey:@"useCache"]; + NSLog(@"useCacheObject ========> %@", useCacheObject); + if (useCacheObject != [NSNull null]) { + useCache = [[dataSource objectForKey:@"useCache"] boolValue]; + if (useCache) { + [_cacheManager setMaxCacheSize:maxCacheSize]; } - - [_players removeAllObjects]; - result(nil); - } else if ([@"create" isEqualToString:call.method]) { - BetterPlayer* player = [[BetterPlayer alloc] initWithFrame:CGRectZero]; - [self onPlayerSetup:player result:result]; - } else { - NSDictionary* argsMap = call.arguments; - int64_t textureId = ((NSNumber*)argsMap[@"textureId"]).unsignedIntegerValue; - BetterPlayer* player = _players[@(textureId)]; - if ([@"setDataSource" isEqualToString:call.method]) { - [player clear]; - // This call will clear cached frame because we will return transparent frame - - NSDictionary* dataSource = argsMap[@"dataSource"]; - [_dataSourceDict setObject:dataSource forKey:[self getTextureId:player]]; - NSString* assetArg = dataSource[@"asset"]; - NSString* uriArg = dataSource[@"uri"]; - NSString* key = dataSource[@"key"]; - NSString* certificateUrl = dataSource[@"certificateUrl"]; - NSString* licenseUrl = dataSource[@"licenseUrl"]; - NSDictionary* headers = dataSource[@"headers"]; - NSString* cacheKey = dataSource[@"cacheKey"]; - NSNumber* maxCacheSize = dataSource[@"maxCacheSize"]; - NSString* videoExtension = dataSource[@"videoExtension"]; - - int overriddenDuration = 0; - if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]){ - overriddenDuration = [dataSource[@"overriddenDuration"] intValue]; - } - - BOOL useCache = false; - id useCacheObject = [dataSource objectForKey:@"useCache"]; - if (useCacheObject != [NSNull null]) { - useCache = [[dataSource objectForKey:@"useCache"] boolValue]; - if (useCache){ - [_cacheManager setMaxCacheSize:maxCacheSize]; - } - } - - if (headers == [NSNull null] || headers == NULL){ - headers = @{}; - } - - if (assetArg) { - NSString* assetPath; - NSString* package = dataSource[@"package"]; - if (![package isEqual:[NSNull null]]) { - assetPath = [_registrar lookupKeyForAsset:assetArg fromPackage:package]; - } else { - assetPath = [_registrar lookupKeyForAsset:assetArg]; - } - [player setDataSourceAsset:assetPath withKey:key withCertificateUrl:certificateUrl withLicenseUrl: licenseUrl cacheKey:cacheKey cacheManager:_cacheManager overriddenDuration:overriddenDuration]; - } else if (uriArg) { - [player setDataSourceURL:[NSURL URLWithString:uriArg] withKey:key withCertificateUrl:certificateUrl withLicenseUrl: licenseUrl withHeaders:headers withCache: useCache cacheKey:cacheKey cacheManager:_cacheManager overriddenDuration:overriddenDuration videoExtension: videoExtension]; - } else { - result(FlutterMethodNotImplemented); - } - result(nil); - } else if ([@"dispose" isEqualToString:call.method]) { - [player clear]; - [self disposeNotificationData:player]; - [self setRemoteCommandsNotificationNotActive]; - [_players removeObjectForKey:@(textureId)]; - // If the Flutter contains https://github.com/flutter/engine/pull/12695, - // the `player` is disposed via `onTextureUnregistered` at the right time. - // Without https://github.com/flutter/engine/pull/12695, there is no guarantee that the - // texture has completed the un-reregistration. It may leads a crash if we dispose the - // `player` before the texture is unregistered. We add a dispatch_after hack to make sure the - // texture is unregistered before we dispose the `player`. - // - // TODO(cyanglaz): Remove this dispatch block when - // https://github.com/flutter/flutter/commit/8159a9906095efc9af8b223f5e232cb63542ad0b is in - // stable And update the min flutter version of the plugin to the stable version. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - if (!player.disposed) { - [player dispose]; - } - }); - if ([_players count] == 0) { - [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; - } - result(nil); - } else if ([@"setLooping" isEqualToString:call.method]) { - [player setIsLooping:[argsMap[@"looping"] boolValue]]; - result(nil); - } else if ([@"setVolume" isEqualToString:call.method]) { - [player setVolume:[argsMap[@"volume"] doubleValue]]; - result(nil); - } else if ([@"play" isEqualToString:call.method]) { - [self setupRemoteNotification:player]; - [player play]; - result(nil); - } else if ([@"position" isEqualToString:call.method]) { - result(@([player position])); - } else if ([@"absolutePosition" isEqualToString:call.method]) { - result(@([player absolutePosition])); - } else if ([@"seekTo" isEqualToString:call.method]) { - [player seekTo:[argsMap[@"location"] intValue]]; - result(nil); - } else if ([@"pause" isEqualToString:call.method]) { - [player pause]; - result(nil); - } else if ([@"setSpeed" isEqualToString:call.method]) { - [player setSpeed:[[argsMap objectForKey:@"speed"] doubleValue] result:result]; - }else if ([@"setTrackParameters" isEqualToString:call.method]) { - int width = [argsMap[@"width"] intValue]; - int height = [argsMap[@"height"] intValue]; - int bitrate = [argsMap[@"bitrate"] intValue]; - - [player setTrackParameters:width: height : bitrate]; - result(nil); - } else if ([@"enablePictureInPicture" isEqualToString:call.method]){ - double left = [argsMap[@"left"] doubleValue]; - double top = [argsMap[@"top"] doubleValue]; - double width = [argsMap[@"width"] doubleValue]; - double height = [argsMap[@"height"] doubleValue]; - [player enablePictureInPicture:CGRectMake(left, top, width, height)]; - } else if ([@"isPictureInPictureSupported" isEqualToString:call.method]){ - if (@available(iOS 9.0, *)){ - if ([AVPictureInPictureController isPictureInPictureSupported]){ - result([NSNumber numberWithBool:true]); - return; - } - } - - result([NSNumber numberWithBool:false]); - } else if ([@"disablePictureInPicture" isEqualToString:call.method]){ - [player disablePictureInPicture]; - [player setPictureInPicture:false]; - } else if ([@"setAudioTrack" isEqualToString:call.method]){ - NSString* name = argsMap[@"name"]; - int index = [argsMap[@"index"] intValue]; - [player setAudioTrack:name index: index]; - } else if ([@"setMixWithOthers" isEqualToString:call.method]){ - [player setMixWithOthers:[argsMap[@"mixWithOthers"] boolValue]]; - } else if ([@"preCache" isEqualToString:call.method]){ - NSDictionary* dataSource = argsMap[@"dataSource"]; - NSString* urlArg = dataSource[@"uri"]; - NSString* cacheKey = dataSource[@"cacheKey"]; - NSDictionary* headers = dataSource[@"headers"]; - NSNumber* maxCacheSize = dataSource[@"maxCacheSize"]; - NSString* videoExtension = dataSource[@"videoExtension"]; - - if (headers == [ NSNull null ]){ - headers = @{}; - } - if (videoExtension == [NSNull null]){ - videoExtension = nil; - } - - if (urlArg != [NSNull null]){ - NSURL* url = [NSURL URLWithString:urlArg]; - if ([_cacheManager isPreCacheSupportedWithUrl:url videoExtension:videoExtension]){ - [_cacheManager setMaxCacheSize:maxCacheSize]; - [_cacheManager preCacheURL:url cacheKey:cacheKey videoExtension:videoExtension withHeaders:headers completionHandler:^(BOOL success){ - }]; - } else { - NSLog(@"Pre cache is not supported for given data source."); - } + } + + if (headers == [NSNull null] || headers == NULL) { + headers = @{}; + } + NSLog(@"assetArg ========> %@", assetArg); + if (assetArg) { + NSString *assetPath; + NSString *package = dataSource[@"package"]; + if (![package isEqual:[NSNull null]]) { + assetPath = [_registrar lookupKeyForAsset:assetArg + fromPackage:package]; + } else { + assetPath = [_registrar lookupKeyForAsset:assetArg]; + } + [player setDataSourceAsset:assetPath + withKey:key + withCertificateUrl:certificateUrl + withLicenseUrl:licenseUrl + cacheKey:cacheKey + cacheManager:_cacheManager + overriddenDuration:overriddenDuration]; + } else if (uriArg) { + [player setDataSourceURL:[NSURL URLWithString:uriArg] + withKey:key + withCertificateUrl:certificateUrl + withLicenseUrl:licenseUrl + withHeaders:headers + withCache:useCache + cacheKey:cacheKey + cacheManager:_cacheManager + overriddenDuration:overriddenDuration + videoExtension:videoExtension]; + } else { + result(FlutterMethodNotImplemented); + } + result(nil); + } else if ([@"dispose" isEqualToString:call.method]) { + NSLog(@"dispose method called =======>"); + [player clear]; + [self disposeNotificationData:player]; + [self setRemoteCommandsNotificationNotActive]; + [_players removeObjectForKey:@(textureId)]; + // If the Flutter contains https://github.com/flutter/engine/pull/12695, + // the `player` is disposed via `onTextureUnregistered` at the right time. + // Without https://github.com/flutter/engine/pull/12695, there is no + // guarantee that the texture has completed the un-reregistration. It may + // leads a crash if we dispose the `player` before the texture is + // unregistered. We add a dispatch_after hack to make sure the texture is + // unregistered before we dispose the `player`. + // + // TODO(cyanglaz): Remove this dispatch block when + // https://github.com/flutter/flutter/commit/8159a9906095efc9af8b223f5e232cb63542ad0b + // is in stable And update the min flutter version of the plugin to the + // stable version. + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (!player.disposed) { + [player dispose]; } - result(nil); - } else if ([@"clearCache" isEqualToString:call.method]){ - [_cacheManager clearCache]; - result(nil); - } else if ([@"stopPreCache" isEqualToString:call.method]){ - NSString* urlArg = argsMap[@"url"]; - NSString* cacheKey = argsMap[@"cacheKey"]; - NSString* videoExtension = argsMap[@"videoExtension"]; - if (urlArg != [NSNull null]){ - NSURL* url = [NSURL URLWithString:urlArg]; - if ([_cacheManager isPreCacheSupportedWithUrl:url videoExtension:videoExtension]){ - [_cacheManager stopPreCache:url cacheKey:cacheKey - completionHandler:^(BOOL success){ + }); + if ([_players count] == 0) { + [[AVAudioSession sharedInstance] + setActive:NO + withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation + error:nil]; + } + result(nil); + } else if ([@"setLooping" isEqualToString:call.method]) { + NSLog(@"setLooping method called =======>"); + [player setIsLooping:[argsMap[@"looping"] boolValue]]; + result(nil); + } else if ([@"setVolume" isEqualToString:call.method]) { + NSLog(@"setVolume method called =======>"); + [player setVolume:[argsMap[@"volume"] doubleValue]]; + result(nil); + } else if ([@"play" isEqualToString:call.method]) { + NSLog(@"play method called =======>"); + [self setupRemoteNotification:player]; + [player play]; + result(nil); + } else if ([@"position" isEqualToString:call.method]) { + NSLog(@"position method called =======>"); + result(@([player position])); + } else if ([@"absolutePosition" isEqualToString:call.method]) { + NSLog(@"absolutePosition method called =======>"); + result(@([player absolutePosition])); + } else if ([@"seekTo" isEqualToString:call.method]) { + NSLog(@"seekTo method called =======>"); + [player seekTo:[argsMap[@"location"] intValue]]; + result(nil); + } else if ([@"pause" isEqualToString:call.method]) { + NSLog(@"pause method called =======>"); + [player pause]; + result(nil); + } else if ([@"setSpeed" isEqualToString:call.method]) { + [player setSpeed:[[argsMap objectForKey:@"speed"] doubleValue] + result:result]; + } else if ([@"setTrackParameters" isEqualToString:call.method]) { + int width = [argsMap[@"width"] intValue]; + int height = [argsMap[@"height"] intValue]; + int bitrate = [argsMap[@"bitrate"] intValue]; + + [player setTrackParameters:width:height:bitrate]; + result(nil); + } else if ([@"enablePictureInPicture" isEqualToString:call.method]) { + NSLog(@"enablePictureInPicture method called =======>"); + double left = [argsMap[@"left"] doubleValue]; + double top = [argsMap[@"top"] doubleValue]; + double width = [argsMap[@"width"] doubleValue]; + double height = [argsMap[@"height"] doubleValue]; + [player enablePictureInPicture:CGRectMake(left, top, width, height)]; + } else if ([@"isPictureInPictureSupported" isEqualToString:call.method]) { + NSLog(@"isPictureInPictureSupported method called =======>"); + if (@available(iOS 9.0, *)) { + if ([AVPictureInPictureController isPictureInPictureSupported]) { + result([NSNumber numberWithBool:true]); + return; + } + } + + result([NSNumber numberWithBool:false]); + } else if ([@"disablePictureInPicture" isEqualToString:call.method]) { + NSLog(@"disablePictureInPicture method called =======>"); + [player disablePictureInPicture]; + [player setPictureInPicture:false]; + } else if ([@"setAudioTrack" isEqualToString:call.method]) { + NSLog(@"setAudioTrack method called =======>"); + NSString *name = argsMap[@"name"]; + int index = [argsMap[@"index"] intValue]; + [player setAudioTrack:name index:index]; + } else if ([@"setMixWithOthers" isEqualToString:call.method]) { + NSLog(@"setMixWithOthers method called =======>"); + [player setMixWithOthers:[argsMap[@"mixWithOthers"] boolValue]]; + } else if ([@"preCache" isEqualToString:call.method]) { + NSLog(@"preCache method called =======>"); + NSDictionary *dataSource = argsMap[@"dataSource"]; + NSString *urlArg = dataSource[@"uri"]; + NSString *cacheKey = dataSource[@"cacheKey"]; + NSDictionary *headers = dataSource[@"headers"]; + NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; + NSString *videoExtension = dataSource[@"videoExtension"]; + + if (headers == [NSNull null]) { + headers = @{}; + } + if (videoExtension == [NSNull null]) { + videoExtension = nil; + } + + if (urlArg != [NSNull null]) { + NSURL *url = [NSURL URLWithString:urlArg]; + if ([_cacheManager isPreCacheSupportedWithUrl:url + videoExtension:videoExtension]) { + [_cacheManager setMaxCacheSize:maxCacheSize]; + [_cacheManager preCacheURL:url + cacheKey:cacheKey + videoExtension:videoExtension + withHeaders:headers + completionHandler:^(BOOL success){ + }]; + } else { + NSLog(@"Pre cache is not supported for given data source."); + } + } + result(nil); + } else if ([@"clearCache" isEqualToString:call.method]) { + NSLog(@"clearCache method called =======>"); + [_cacheManager clearCache]; + result(nil); + } else if ([@"stopPreCache" isEqualToString:call.method]) { + NSLog(@"stopPreCache method called =======>"); + NSString *urlArg = argsMap[@"url"]; + NSString *cacheKey = argsMap[@"cacheKey"]; + NSString *videoExtension = argsMap[@"videoExtension"]; + if (urlArg != [NSNull null]) { + NSURL *url = [NSURL URLWithString:urlArg]; + if ([_cacheManager isPreCacheSupportedWithUrl:url + videoExtension:videoExtension]) { + [_cacheManager stopPreCache:url + cacheKey:cacheKey + completionHandler:^(BOOL success){ }]; - } else { - NSLog(@"Stop pre cache is not supported for given data source."); - } - } - result(nil); } else { - result(FlutterMethodNotImplemented); + NSLog(@"Stop pre cache is not supported for given data source."); } + } + result(nil); + } else { + result(FlutterMethodNotImplemented); } + } } @end diff --git a/ios/Classes/CacheManager.swift b/ios/Classes/CacheManager.swift index f6fafff96..c69697900 100644 --- a/ios/Classes/CacheManager.swift +++ b/ios/Classes/CacheManager.swift @@ -37,6 +37,7 @@ import PINCache ///Setups cache server for HLS streams @objc public func setup(){ + print("Cache Setup Swift Ran ==========>"); GCDWebServer.setLogLevel(4) let webServer = GCDWebServer() let cache = PINCache.shared diff --git a/lib/src/core/better_player_controller.dart b/lib/src/core/better_player_controller.dart index cd01523c3..dd26cd64a 100644 --- a/lib/src/core/better_player_controller.dart +++ b/lib/src/core/better_player_controller.dart @@ -1240,6 +1240,7 @@ class BetterPlayerController { ///currently not supported on iOS. On iOS, the video format must be in this ///list: https://github.com/sendyhalim/Swime/blob/master/Sources/MimeType.swift Future preCache(BetterPlayerDataSource betterPlayerDataSource) async { + print("====> prechache better player"); final cacheConfig = betterPlayerDataSource.cacheConfiguration ?? const BetterPlayerCacheConfiguration(useCache: true); From 286a8344406acfb495e0589e94d7edf6378c3fa6 Mon Sep 17 00:00:00 2001 From: abhinav Date: Mon, 30 Jan 2023 20:49:16 +0530 Subject: [PATCH 02/24] changes to preferredForwardBufferDuration --- example/lib/constants.dart | 4 +- example/lib/pages/hls_audio_page.dart | 12 ++++-- ios/Classes/BetterPlayer.m | 37 +++++++++++++------ .../method_channel_video_player.dart | 1 - test/better_player_test_utils.dart | 2 +- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/example/lib/constants.dart b/example/lib/constants.dart index ec49d02af..ce1276903 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -33,8 +33,10 @@ class Constants { static String logo = "logo.png"; static String placeholderUrl = "https://imgix.bustle.com/uploads/image/2020/8/5/23905b9c-6b8c-47fa-bc0f-434de1d7e9bf-avengers-5.jpg"; + // static String elephantDreamStreamUrl = + // "http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"; static String elephantDreamStreamUrl = - "http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"; + "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"; static String tokenEncodedHlsUrl = "https://amssamples.streaming.mediaservices.windows.net/830584f8-f0c8-4e41-968b-6538b9380aa5/TearsOfSteelTeaser.ism/manifest(format=m3u8-aapl)"; static String tokenEncodedHlsToken = diff --git a/example/lib/pages/hls_audio_page.dart b/example/lib/pages/hls_audio_page.dart index 116223d04..2bd814633 100644 --- a/example/lib/pages/hls_audio_page.dart +++ b/example/lib/pages/hls_audio_page.dart @@ -12,10 +12,12 @@ class _HlsAudioPageState extends State { @override void initState() { + print("_HlsAudioPageState"); BetterPlayerConfiguration betterPlayerConfiguration = BetterPlayerConfiguration( aspectRatio: 16 / 9, fit: BoxFit.contain, + autoPlay: true, ); BetterPlayerDataSource dataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, @@ -44,10 +46,12 @@ class _HlsAudioPageState extends State { style: TextStyle(fontSize: 16), ), ), - AspectRatio( - aspectRatio: 16 / 9, - child: BetterPlayer(controller: _betterPlayerController), - ), + _betterPlayerController.isVideoInitialized() ?? false + ? AspectRatio( + aspectRatio: 16 / 9, + child: BetterPlayer(controller: _betterPlayerController), + ) + : Container(), ], ), ); diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 4a82bc209..43d535d29 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -23,7 +23,7 @@ @implementation BetterPlayer - (instancetype)initWithFrame:(CGRect)frame { self = [super init]; NSAssert(self, @"super init cannot be nil"); - NSLog(@"initWithFrame =======>"); + // NSLog(@"initWithFrame =======>"); _isInitialized = false; _isPlaying = false; _disposed = false; @@ -38,7 +38,7 @@ - (instancetype)initWithFrame:(CGRect)frame { } - (nonnull UIView *)view { - NSLog(@"UIView view =======>"); + // NSLog(@"UIView view =======>"); BetterPlayerView *playerView = [[BetterPlayerView alloc] initWithFrame:CGRectZero]; playerView.player = _player; @@ -83,7 +83,7 @@ - (void)addObservers:(AVPlayerItem *)item { } - (void)clear { - NSLog(@"clear =======>"); + // NSLog(@"clear =======>"); _isInitialized = false; _isPlaying = false; _disposed = false; @@ -99,7 +99,7 @@ - (void)clear { } - (void)removeObservers { - NSLog(@"removeObservers =======>"); + // NSLog(@"removeObservers =======>"); if (self._observersAdded) { [_player removeObserver:self forKeyPath:@"rate" context:nil]; [[_player currentItem] removeObserver:self @@ -126,7 +126,7 @@ - (void)removeObservers { } - (void)itemDidPlayToEndTime:(NSNotification *)notification { - NSLog(@"removeObservers =======>"); + NSLog(@"itemDidPlayToEndTime =======>"); if (_isLooping) { AVPlayerItem *p = [notification object]; [p seekToTime:kCMTimeZero completionHandler:nil]; @@ -189,7 +189,7 @@ static inline CGFloat radiansToDegrees(CGFloat radians) { } - (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { - NSLog(@"fixTransform =======>"); + // NSLog(@"fixTransform =======>"); CGAffineTransform transform = videoTrack.preferredTransform; // TODO(@recastrodiaz): why do we need to do this? Why is the // preferredTransform incorrect? At least 2 user videos show a black screen @@ -294,7 +294,7 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key { _isStalledCheckStarted = false; _playerRate = 1; [_player replaceCurrentItemWithPlayerItem:item]; - + _player.currentItem.preferredForwardBufferDuration = 1; AVAsset *asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ if ([asset statusOfValueForKey:@"tracks" @@ -334,7 +334,7 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key { } - (void)handleStalled { - NSLog(@"handleStalled =======>"); + // NSLog(@"handleStalled =======>"); if (_isStalledCheckStarted) { return; } @@ -343,7 +343,7 @@ - (void)handleStalled { } - (void)startStalledCheck { - NSLog(@"startStalledCheck =======>"); + // NSLog(@"startStalledCheck =======>"); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > @@ -382,14 +382,20 @@ - (NSTimeInterval)availableDuration { } } +// waitingToPlayAtSpecifiedRate time cotrol status= 1 +// pause time cotrol status = 0 +// pause time cotrol status = 2 + - (void)observeValueForKeyPath:(NSString *)path ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"observeValueForKeyPath =======>"); + NSLog(@"change Dictionary =======> %@", change); + // NSLog(@"context =======> %@", context); if ([path isEqualToString:@"rate"]) { - NSLog(@"path isEqualToString =======> %@", path); if (@available(iOS 10.0, *)) { + NSLog(@"@available(iOS 10.0, *) =======>%ld", @available(iOS 10.0, *)); if (_pipController.pictureInPictureActive == true) { if (_lastAvPlayerTimeControlStatus != [NSNull null] && _lastAvPlayerTimeControlStatus == _player.timeControlStatus) { @@ -425,7 +431,8 @@ - (void)observeValueForKeyPath:(NSString *)path } if (context == timeRangeContext) { - NSLog(@"context =======> %@", context); + NSLog(@"timeRangeContext observer called =======>"); + // NSLog(@"context =======> %@", context); if (_eventSink != nil) { NSMutableArray *> *values = [[NSMutableArray alloc] init]; @@ -434,6 +441,8 @@ - (void)observeValueForKeyPath:(NSString *)path int64_t start = [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.start)]; int64_t end = start + [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.duration)]; + NSLog(@"start =======> %lld", start); + NSLog(@"end =======> %lld", end); if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { int64_t endTime = [BetterPlayerTimeUtils FLTCMTimeToMillis:(_player.currentItem.forwardPlaybackEndTime)]; @@ -448,10 +457,12 @@ - (void)observeValueForKeyPath:(NSString *)path @{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); } } else if (context == presentationSizeContext) { + NSLog(@"presentationSizeContext observer called =======>"); [self onReadyToPlay]; } else if (context == statusContext) { + NSLog(@"statusContext observer called =======>"); AVPlayerItem *item = (AVPlayerItem *)object; switch (item.status) { case AVPlayerItemStatusFailed: @@ -475,16 +486,19 @@ - (void)observeValueForKeyPath:(NSString *)path } } else if (context == playbackLikelyToKeepUpContext) { if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { + NSLog(@"isPlaybackLikelyToKeepUp called =======>"); [self updatePlayingState]; if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } } } else if (context == playbackBufferEmptyContext) { + NSLog(@"playbackBufferEmptyContext observer called =======>"); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); } } else if (context == playbackBufferFullContext) { + NSLog(@"playbackBufferFullContext observer called =======>"); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } @@ -493,6 +507,7 @@ - (void)observeValueForKeyPath:(NSString *)path - (void)updatePlayingState { NSLog(@"updatePlayingState =======>"); + NSLog(@"_player.rate ::: %f", _player.rate); if (!_isInitialized || !_key) { return; } diff --git a/lib/src/video_player/method_channel_video_player.dart b/lib/src/video_player/method_channel_video_player.dart index a4066d5f0..bac001a5b 100644 --- a/lib/src/video_player/method_channel_video_player.dart +++ b/lib/src/video_player/method_channel_video_player.dart @@ -361,7 +361,6 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { ); case 'bufferingUpdate': final List values = map['values'] as List; - return VideoEvent( eventType: VideoEventType.bufferingUpdate, key: key, diff --git a/test/better_player_test_utils.dart b/test/better_player_test_utils.dart index c3678433f..7e8cd4998 100644 --- a/test/better_player_test_utils.dart +++ b/test/better_player_test_utils.dart @@ -10,7 +10,7 @@ class BetterPlayerTestUtils { static const String forBiggerBlazesUrl = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"; static const String elephantDreamStreamUrl = - "http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"; + "https://storage.googleapis.com/ps_videos/videos/05zrn7pv/05zrn7pv.m3u8"; static BetterPlayerMockController setupBetterPlayerMockController( {VideoPlayerController? controller}) { From c31306f5a38a6116d33d50aea2855287a1cef87a Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:08:03 +0530 Subject: [PATCH 03/24] all changes for library to use buffer completed --- ios/Classes/BetterPlayer.m | 96 +++++-------------- ios/Classes/BetterPlayerPlugin.m | 69 +++---------- .../better_player_data_source.dart | 84 ++++++++-------- lib/src/core/better_player_controller.dart | 1 + .../method_channel_video_player.dart | 7 +- lib/src/video_player/video_player.dart | 2 + .../video_player_platform_interface.dart | 6 ++ test/mock_video_player_controller.dart | 1 + 8 files changed, 99 insertions(+), 167 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 43d535d29..afcc14544 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -23,7 +23,6 @@ @implementation BetterPlayer - (instancetype)initWithFrame:(CGRect)frame { self = [super init]; NSAssert(self, @"super init cannot be nil"); - // NSLog(@"initWithFrame =======>"); _isInitialized = false; _isPlaying = false; _disposed = false; @@ -38,7 +37,6 @@ - (instancetype)initWithFrame:(CGRect)frame { } - (nonnull UIView *)view { - // NSLog(@"UIView view =======>"); BetterPlayerView *playerView = [[BetterPlayerView alloc] initWithFrame:CGRectZero]; playerView.player = _player; @@ -46,7 +44,6 @@ - (nonnull UIView *)view { } - (void)addObservers:(AVPlayerItem *)item { - NSLog(@"addObservers =======>"); if (!self._observersAdded) { [_player addObserver:self forKeyPath:@"rate" options:0 context:nil]; [item addObserver:self @@ -83,7 +80,6 @@ - (void)addObservers:(AVPlayerItem *)item { } - (void)clear { - // NSLog(@"clear =======>"); _isInitialized = false; _isPlaying = false; _disposed = false; @@ -99,7 +95,6 @@ - (void)clear { } - (void)removeObservers { - // NSLog(@"removeObservers =======>"); if (self._observersAdded) { [_player removeObserver:self forKeyPath:@"rate" context:nil]; [[_player currentItem] removeObserver:self @@ -126,7 +121,6 @@ - (void)removeObservers { } - (void)itemDidPlayToEndTime:(NSNotification *)notification { - NSLog(@"itemDidPlayToEndTime =======>"); if (_isLooping) { AVPlayerItem *p = [notification object]; [p seekToTime:kCMTimeZero completionHandler:nil]; @@ -153,7 +147,6 @@ static inline CGFloat radiansToDegrees(CGFloat radians) { getVideoCompositionWithTransform:(CGAffineTransform)transform withAsset:(AVAsset *)asset withVideoTrack:(AVAssetTrack *)videoTrack { - NSLog(@"getVideoCompositionWithTransform =======>"); AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [asset duration]); @@ -189,7 +182,6 @@ static inline CGFloat radiansToDegrees(CGFloat radians) { } - (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { - // NSLog(@"fixTransform =======>"); CGAffineTransform transform = videoTrack.preferredTransform; // TODO(@recastrodiaz): why do we need to do this? Why is the // preferredTransform incorrect? At least 2 user videos show a black screen @@ -218,8 +210,8 @@ - (void)setDataSourceAsset:(NSString *)asset withLicenseUrl:(NSString *)licenseUrl cacheKey:(NSString *)cacheKey cacheManager:(CacheManager *)cacheManager - overriddenDuration:(int)overriddenDuration { - NSLog(@"setDataSourceAsset =======>"); + overriddenDuration:(int)overriddenDuration + downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; return [self setDataSourceURL:[NSURL fileURLWithPath:path] withKey:key @@ -230,20 +222,21 @@ - (void)setDataSourceAsset:(NSString *)asset cacheKey:cacheKey cacheManager:cacheManager overriddenDuration:overriddenDuration + downloadFullVideoOnIos:downloadFullVideoOnIos videoExtension:nil]; } - (void)setDataSourceURL:(NSURL *)url - withKey:(NSString *)key - withCertificateUrl:(NSString *)certificateUrl - withLicenseUrl:(NSString *)licenseUrl - withHeaders:(NSDictionary *)headers - withCache:(BOOL)useCache - cacheKey:(NSString *)cacheKey - cacheManager:(CacheManager *)cacheManager - overriddenDuration:(int)overriddenDuration - videoExtension:(NSString *)videoExtension { - NSLog(@"setDataSourceURL =======>"); + withKey:(NSString *)key + withCertificateUrl:(NSString *)certificateUrl + withLicenseUrl:(NSString *)licenseUrl + withHeaders:(NSDictionary *)headers + withCache:(BOOL)useCache + cacheKey:(NSString *)cacheKey + cacheManager:(CacheManager *)cacheManager + overriddenDuration:(int)overriddenDuration + videoExtension:(NSString *)videoExtension + downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { _overriddenDuration = 0; if (headers == [NSNull null] || headers == NULL) { headers = @{}; @@ -284,17 +277,24 @@ - (void)setDataSourceURL:(NSURL *)url if (@available(iOS 10.0, *) && overriddenDuration > 0) { _overriddenDuration = overriddenDuration; } - return [self setDataSourcePlayerItem:item withKey:key]; + return [self setDataSourcePlayerItem:item + withKey:key + downloadFullVideoOnIos:downloadFullVideoOnIos]; } -- (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key { - NSLog(@"setDataSourcePlayerItem =======>"); +- (void)setDataSourcePlayerItem:(AVPlayerItem *)item + withKey:(NSString *)key + downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { _key = key; _stalledCount = 0; _isStalledCheckStarted = false; _playerRate = 1; [_player replaceCurrentItemWithPlayerItem:item]; - _player.currentItem.preferredForwardBufferDuration = 1; + // downloadFullVideoOnIos if set to 1 will only download the buffer duration + // for video + if (downloadFullVideoOnIos == 1) { + _player.currentItem.preferredForwardBufferDuration = 1; + } AVAsset *asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ if ([asset statusOfValueForKey:@"tracks" @@ -334,7 +334,6 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key { } - (void)handleStalled { - // NSLog(@"handleStalled =======>"); if (_isStalledCheckStarted) { return; } @@ -343,7 +342,6 @@ - (void)handleStalled { } - (void)startStalledCheck { - // NSLog(@"startStalledCheck =======>"); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > @@ -367,9 +365,7 @@ - (void)startStalledCheck { } - (NSTimeInterval)availableDuration { - NSLog(@"availableDuration =======>"); NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; - NSLog(@"loadedTimeRanges availableduration =======> %@", loadedTimeRanges); if (loadedTimeRanges.count > 0) { CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; @@ -390,12 +386,8 @@ - (void)observeValueForKeyPath:(NSString *)path ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - NSLog(@"observeValueForKeyPath =======>"); - NSLog(@"change Dictionary =======> %@", change); - // NSLog(@"context =======> %@", context); if ([path isEqualToString:@"rate"]) { if (@available(iOS 10.0, *)) { - NSLog(@"@available(iOS 10.0, *) =======>%ld", @available(iOS 10.0, *)); if (_pipController.pictureInPictureActive == true) { if (_lastAvPlayerTimeControlStatus != [NSNull null] && _lastAvPlayerTimeControlStatus == _player.timeControlStatus) { @@ -431,8 +423,6 @@ - (void)observeValueForKeyPath:(NSString *)path } if (context == timeRangeContext) { - NSLog(@"timeRangeContext observer called =======>"); - // NSLog(@"context =======> %@", context); if (_eventSink != nil) { NSMutableArray *> *values = [[NSMutableArray alloc] init]; @@ -441,8 +431,6 @@ - (void)observeValueForKeyPath:(NSString *)path int64_t start = [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.start)]; int64_t end = start + [BetterPlayerTimeUtils FLTCMTimeToMillis:(range.duration)]; - NSLog(@"start =======> %lld", start); - NSLog(@"end =======> %lld", end); if (!CMTIME_IS_INVALID(_player.currentItem.forwardPlaybackEndTime)) { int64_t endTime = [BetterPlayerTimeUtils FLTCMTimeToMillis:(_player.currentItem.forwardPlaybackEndTime)]; @@ -457,18 +445,13 @@ - (void)observeValueForKeyPath:(NSString *)path @{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); } } else if (context == presentationSizeContext) { - NSLog(@"presentationSizeContext observer called =======>"); [self onReadyToPlay]; } else if (context == statusContext) { - NSLog(@"statusContext observer called =======>"); AVPlayerItem *item = (AVPlayerItem *)object; switch (item.status) { case AVPlayerItemStatusFailed: - NSLog(@"Failed to load video:"); - NSLog(item.error.debugDescription); - if (_eventSink != nil) { _eventSink([FlutterError errorWithCode:@"VideoError" @@ -486,19 +469,16 @@ - (void)observeValueForKeyPath:(NSString *)path } } else if (context == playbackLikelyToKeepUpContext) { if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { - NSLog(@"isPlaybackLikelyToKeepUp called =======>"); [self updatePlayingState]; if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } } } else if (context == playbackBufferEmptyContext) { - NSLog(@"playbackBufferEmptyContext observer called =======>"); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); } } else if (context == playbackBufferFullContext) { - NSLog(@"playbackBufferFullContext observer called =======>"); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } @@ -506,8 +486,6 @@ - (void)observeValueForKeyPath:(NSString *)path } - (void)updatePlayingState { - NSLog(@"updatePlayingState =======>"); - NSLog(@"_player.rate ::: %f", _player.rate); if (!_isInitialized || !_key) { return; } @@ -529,7 +507,6 @@ - (void)updatePlayingState { } - (void)onReadyToPlay { - NSLog(@"onReadyToPlay =======>"); if (_eventSink && !_isInitialized && _key) { if (!_player.currentItem) { return; @@ -567,8 +544,6 @@ - (void)onReadyToPlay { if (_overriddenDuration > 0 && duration > _overriddenDuration) { _player.currentItem.forwardPlaybackEndTime = CMTimeMake(_overriddenDuration / 1000, 1); - NSLog(@"_player.currentItem.forwardPlaybackEndTime :::: %@ =======> ", - _player.currentItem.forwardPlaybackEndTime); } _isInitialized = true; @@ -584,7 +559,6 @@ - (void)onReadyToPlay { } - (void)play { - NSLog(@"play =======>"); _stalledCount = 0; _isStalledCheckStarted = false; _isPlaying = true; @@ -592,25 +566,23 @@ - (void)play { } - (void)pause { - NSLog(@"pause =======>"); + _isPlaying = false; [self updatePlayingState]; } - (int64_t)position { - NSLog(@"position =======>"); + return [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]; } - (int64_t)absolutePosition { - NSLog(@"absolutePosition =======>"); return [BetterPlayerTimeUtils FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] timeIntervalSince1970])]; } - (int64_t)duration { - NSLog(@"duration =======>"); CMTime time; if (@available(iOS 13, *)) { time = [[_player currentItem] duration]; @@ -625,7 +597,6 @@ - (int64_t)duration { } - (void)seekTo:(int)location { - NSLog(@"seekTo =======>"); /// When player is playing, pause video, seek to new position and start again. /// This will prevent issues with seekbar jumps. bool wasPlaying = _isPlaying; @@ -648,13 +619,11 @@ - (void)setIsLooping:(bool)isLooping { } - (void)setVolume:(double)volume { - NSLog(@"setVolume =======>"); _player.volume = (float)((volume < 0.0) ? 0.0 : ((volume > 1.0) ? 1.0 : volume)); } - (void)setSpeed:(double)speed result:(FlutterResult)result { - NSLog(@"setSpeed =======>"); if (speed == 1.0 || speed == 0.0) { _playerRate = 1; result(nil); @@ -686,7 +655,6 @@ - (void)setSpeed:(double)speed result:(FlutterResult)result { } - (void)setTrackParameters:(int)width:(int)height:(int)bitrate { - NSLog(@"setTrackParameters =======>"); _player.currentItem.preferredPeakBitRate = bitrate; if (@available(iOS 11.0, *)) { if (width == 0 && height == 0) { @@ -699,7 +667,6 @@ - (void)setTrackParameters:(int)width:(int)height:(int)bitrate { } - (void)setPictureInPicture:(BOOL)pictureInPicture { - NSLog(@"setPictureInPicture =======>"); self._pictureInPicture = pictureInPicture; if (@available(iOS 9.0, *)) { if (_pipController && self._pictureInPicture && @@ -720,7 +687,6 @@ - (void)setPictureInPicture:(BOOL)pictureInPicture { #if TARGET_OS_IOS - (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore { - NSLog(@"setRestoreUserInterfaceForPIPStopCompletionHandler =======>"); if (_restoreUserInterfaceForPIPStopCompletionHandler != NULL) { _restoreUserInterfaceForPIPStopCompletionHandler(restore); _restoreUserInterfaceForPIPStopCompletionHandler = NULL; @@ -728,7 +694,6 @@ - (void)setRestoreUserInterfaceForPIPStopCompletionHandler:(BOOL)restore { } - (void)setupPipController { - NSLog(@"setupPipController =======>"); if (@available(iOS 9.0, *)) { [[AVAudioSession sharedInstance] setActive:YES error:nil]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; @@ -744,13 +709,11 @@ - (void)setupPipController { } - (void)enablePictureInPicture:(CGRect)frame { - NSLog(@"enablePictureInPicture =======>"); [self disablePictureInPicture]; [self usePlayerLayer:frame]; } - (void)usePlayerLayer:(CGRect)frame { - NSLog(@"usePlayerLayer =======>"); if (_player) { // Create new controller passing reference to the AVPlayerLayer self._playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; @@ -775,7 +738,6 @@ - (void)usePlayerLayer:(CGRect)frame { } - (void)disablePictureInPicture { - NSLog(@"disablePictureInPicture =======>"); [self setPictureInPicture:true]; if (__playerLayer) { [self._playerLayer removeFromSuperlayer]; @@ -824,7 +786,6 @@ - (void)pictureInPictureController: } - (void)setAudioTrack:(NSString *)name index:(int)index { - NSLog(@"setAudioTrack =======>"); AVMediaSelectionGroup *audioSelectionGroup = [[[_player currentItem] asset] mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; NSArray *options = audioSelectionGroup.options; @@ -848,7 +809,6 @@ - (void)setAudioTrack:(NSString *)name index:(int)index { } - (void)setMixWithOthers:(bool)mixWithOthers { - NSLog(@"setMixWithOthers =======>"); if (mixWithOthers) { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback @@ -863,7 +823,6 @@ - (void)setMixWithOthers:(bool)mixWithOthers { #endif - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - NSLog(@"onCancelWithArguments =======>"); _eventSink = nil; return nil; } @@ -871,7 +830,6 @@ - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments eventSink: (nonnull FlutterEventSink)events { - NSLog(@"onListenWithArguments =======>"); _eventSink = events; // TODO(@recastrodiaz): remove the line below when the race condition is // resolved: https://github.com/flutter/flutter/issues/21483 This line ensures @@ -886,7 +844,6 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments /// 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 { - NSLog(@"disposeSansEventChannel =======>"); @try { [self clear]; } @catch (NSException *exception) { @@ -895,7 +852,6 @@ - (void)disposeSansEventChannel { } - (void)dispose { - NSLog(@"dispose =======>"); [self pause]; [self disposeSansEventChannel]; [_eventChannel setStreamHandler:nil]; diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index 35ccc44f7..0c92e67ea 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -20,7 +20,6 @@ @implementation BetterPlayerPlugin #pragma mark - FlutterPlugin protocol + (void)registerWithRegistrar:(NSObject *)registrar { - NSLog(@"registerWithRegistrar =======>"); FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"better_player_channel" binaryMessenger:[registrar messenger]]; @@ -34,7 +33,6 @@ + (void)registerWithRegistrar:(NSObject *)registrar { - (instancetype)initWithRegistrar: (NSObject *)registrar { self = [super init]; - NSLog(@"initWithRegistrar =======>"); NSAssert(self, @"super init cannot be nil"); _messenger = [registrar messenger]; _registrar = registrar; @@ -44,15 +42,11 @@ - (instancetype)initWithRegistrar: _dataSourceDict = [NSMutableDictionary dictionary]; _cacheManager = [[CacheManager alloc] init]; [_cacheManager setup]; - NSLog(@"_dataSourceDict =======> %@", _dataSourceDict); - NSLog(@"_players =======> %@", _players); - NSLog(@"_messenger =======> %@", _messenger); return self; } - (void)detachFromEngineForRegistrar: (NSObject *)registrar { - NSLog(@"detachFromEngineForRegistrar =======>"); for (NSNumber *textureId in _players.allKeys) { BetterPlayer *player = _players[textureId]; [player disposeSansEventChannel]; @@ -64,27 +58,22 @@ - (void)detachFromEngineForRegistrar: - (NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args { - NSLog(@"createWithFrame =======>"); NSNumber *textureId = [args objectForKey:@"textureId"]; BetterPlayerView *player = [_players objectForKey:@(textureId.intValue)]; return player; } - (NSObject *)createArgsCodec { - NSLog(@"createArgsCodec =======>"); return [FlutterStandardMessageCodec sharedInstance]; } #pragma mark - BetterPlayerPlugin class - (int)newTextureId { - NSLog(@"newTextureId =======>"); texturesCount += 1; return texturesCount; } - (void)onPlayerSetup:(BetterPlayer *)player result:(FlutterResult)result { - NSLog(@"onPlayerSetup =======>"); int64_t textureId = [self newTextureId]; - NSLog(@"better_player_channel/videoEvents%lld", textureId); FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName: [NSString stringWithFormat:@"better_player_channel/videoEvents%lld", @@ -98,7 +87,6 @@ - (void)onPlayerSetup:(BetterPlayer *)player result:(FlutterResult)result { } - (void)setupRemoteNotification:(BetterPlayer *)player { - NSLog(@"setupRemoteNotification =======>"); _notificationPlayer = player; [self stopOtherUpdateListener:player]; NSDictionary *dataSource = @@ -122,13 +110,11 @@ - (void)setupRemoteNotification:(BetterPlayer *)player { } - (void)setRemoteCommandsNotificationActive { - NSLog(@"setRemoteCommandsNotificationActive =======>"); [[AVAudioSession sharedInstance] setActive:true error:nil]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; } - (void)setRemoteCommandsNotificationNotActive { - NSLog(@"setRemoteCommandsNotificationNotActive =======>"); if ([_players count] == 0) { [[AVAudioSession sharedInstance] setActive:false error:nil]; } @@ -137,7 +123,6 @@ - (void)setRemoteCommandsNotificationNotActive { } - (void)setupRemoteCommands:(BetterPlayer *)player { - NSLog(@"setupRemoteCommands =======>"); if (_remoteCommandsInitialized) { return; } @@ -204,7 +189,6 @@ - (void)setupRemoteCommands:(BetterPlayer *)player { - (void)setupRemoteCommandNotification:(BetterPlayer *)player, NSString *title, NSString *author, NSString *imageUrl { - NSLog(@"setupRemoteCommandNotification =======>"); float positionInSeconds = player.position / 1000; float durationInSeconds = player.duration / 1000; @@ -262,7 +246,6 @@ - (void)setupRemoteCommandNotification:(BetterPlayer *)player, NSString *title, } - (NSString *)getTextureId:(BetterPlayer *)player { - NSLog(@"getTextureId =======>"); NSArray *temp = [_players allKeysForObject:player]; NSString *key = [temp lastObject]; return key; @@ -270,7 +253,6 @@ - (NSString *)getTextureId:(BetterPlayer *)player { - (void)setupUpdateListener:(BetterPlayer *)player, NSString *title, NSString *author, NSString *imageUrl { - NSLog(@"setupUpdateListener =======>"); id _timeObserverId = [player.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL @@ -286,7 +268,6 @@ - (void)setupUpdateListener:(BetterPlayer *)player, NSString *title, } - (void)disposeNotificationData:(BetterPlayer *)player { - NSLog(@"disposeNotificationData =======>"); if (player == _notificationPlayer) { _notificationPlayer = NULL; _remoteCommandsInitialized = false; @@ -303,7 +284,6 @@ - (void)disposeNotificationData:(BetterPlayer *)player { } - (void)stopOtherUpdateListener:(BetterPlayer *)player { - NSLog(@"stopOtherUpdateListener =======>"); NSString *currentPlayerTextureId = [self getTextureId:player]; for (NSString *textureId in _timeObserverIdDict.allKeys) { if (currentPlayerTextureId == textureId) { @@ -319,19 +299,15 @@ - (void)stopOtherUpdateListener:(BetterPlayer *)player { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - NSLog(@"handleMethodCall objective C =======>"); if ([@"init" isEqualToString:call.method]) { - NSLog(@"init method called =======>"); // Allow audio playback when the Ring/Silent switch is set to silent for (NSNumber *textureId in _players) { [_players[textureId] dispose]; - NSLog(@"player =======> %@", _players); } [_players removeAllObjects]; result(nil); } else if ([@"create" isEqualToString:call.method]) { - NSLog(@"create method called =======>"); BetterPlayer *player = [[BetterPlayer alloc] initWithFrame:CGRectZero]; [self onPlayerSetup:player result:result]; } else { @@ -340,13 +316,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call ((NSNumber *)argsMap[@"textureId"]).unsignedIntegerValue; BetterPlayer *player = _players[@(textureId)]; if ([@"setDataSource" isEqualToString:call.method]) { - NSLog(@"setDataSource method called =======>"); [player clear]; // This call will clear cached frame because we will return transparent // frame NSDictionary *dataSource = argsMap[@"dataSource"]; - NSLog(@"dataSource ========> %@", dataSource); [_dataSourceDict setObject:dataSource forKey:[self getTextureId:player]]; NSString *assetArg = dataSource[@"asset"]; NSString *uriArg = dataSource[@"uri"]; @@ -357,14 +331,14 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *cacheKey = dataSource[@"cacheKey"]; NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; NSString *videoExtension = dataSource[@"videoExtension"]; + NSInteger *downloadFullVideoOnIos = dataSource[@"downloadFullVideoOnIos"]; + int overriddenDuration = 0; if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]) { overriddenDuration = [dataSource[@"overriddenDuration"] intValue]; } - NSLog(@"overriddenDuration ========> %@", overriddenDuration); BOOL useCache = false; id useCacheObject = [dataSource objectForKey:@"useCache"]; - NSLog(@"useCacheObject ========> %@", useCacheObject); if (useCacheObject != [NSNull null]) { useCache = [[dataSource objectForKey:@"useCache"] boolValue]; if (useCache) { @@ -375,7 +349,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call if (headers == [NSNull null] || headers == NULL) { headers = @{}; } - NSLog(@"assetArg ========> %@", assetArg); if (assetArg) { NSString *assetPath; NSString *package = dataSource[@"package"]; @@ -391,24 +364,25 @@ - (void)handleMethodCall:(FlutterMethodCall *)call withLicenseUrl:licenseUrl cacheKey:cacheKey cacheManager:_cacheManager - overriddenDuration:overriddenDuration]; + overriddenDuration:overriddenDuration + downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos]; } else if (uriArg) { [player setDataSourceURL:[NSURL URLWithString:uriArg] - withKey:key - withCertificateUrl:certificateUrl - withLicenseUrl:licenseUrl - withHeaders:headers - withCache:useCache - cacheKey:cacheKey - cacheManager:_cacheManager - overriddenDuration:overriddenDuration - videoExtension:videoExtension]; + withKey:key + withCertificateUrl:certificateUrl + withLicenseUrl:licenseUrl + withHeaders:headers + withCache:useCache + cacheKey:cacheKey + cacheManager:_cacheManager + overriddenDuration:overriddenDuration + videoExtension:videoExtension + downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos]; } else { result(FlutterMethodNotImplemented); } result(nil); } else if ([@"dispose" isEqualToString:call.method]) { - NSLog(@"dispose method called =======>"); [player clear]; [self disposeNotificationData:player]; [self setRemoteCommandsNotificationNotActive]; @@ -440,30 +414,23 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } result(nil); } else if ([@"setLooping" isEqualToString:call.method]) { - NSLog(@"setLooping method called =======>"); [player setIsLooping:[argsMap[@"looping"] boolValue]]; result(nil); } else if ([@"setVolume" isEqualToString:call.method]) { - NSLog(@"setVolume method called =======>"); [player setVolume:[argsMap[@"volume"] doubleValue]]; result(nil); } else if ([@"play" isEqualToString:call.method]) { - NSLog(@"play method called =======>"); [self setupRemoteNotification:player]; [player play]; result(nil); } else if ([@"position" isEqualToString:call.method]) { - NSLog(@"position method called =======>"); result(@([player position])); } else if ([@"absolutePosition" isEqualToString:call.method]) { - NSLog(@"absolutePosition method called =======>"); result(@([player absolutePosition])); } else if ([@"seekTo" isEqualToString:call.method]) { - NSLog(@"seekTo method called =======>"); [player seekTo:[argsMap[@"location"] intValue]]; result(nil); } else if ([@"pause" isEqualToString:call.method]) { - NSLog(@"pause method called =======>"); [player pause]; result(nil); } else if ([@"setSpeed" isEqualToString:call.method]) { @@ -477,14 +444,12 @@ - (void)handleMethodCall:(FlutterMethodCall *)call [player setTrackParameters:width:height:bitrate]; result(nil); } else if ([@"enablePictureInPicture" isEqualToString:call.method]) { - NSLog(@"enablePictureInPicture method called =======>"); double left = [argsMap[@"left"] doubleValue]; double top = [argsMap[@"top"] doubleValue]; double width = [argsMap[@"width"] doubleValue]; double height = [argsMap[@"height"] doubleValue]; [player enablePictureInPicture:CGRectMake(left, top, width, height)]; } else if ([@"isPictureInPictureSupported" isEqualToString:call.method]) { - NSLog(@"isPictureInPictureSupported method called =======>"); if (@available(iOS 9.0, *)) { if ([AVPictureInPictureController isPictureInPictureSupported]) { result([NSNumber numberWithBool:true]); @@ -494,19 +459,15 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result([NSNumber numberWithBool:false]); } else if ([@"disablePictureInPicture" isEqualToString:call.method]) { - NSLog(@"disablePictureInPicture method called =======>"); [player disablePictureInPicture]; [player setPictureInPicture:false]; } else if ([@"setAudioTrack" isEqualToString:call.method]) { - NSLog(@"setAudioTrack method called =======>"); NSString *name = argsMap[@"name"]; int index = [argsMap[@"index"] intValue]; [player setAudioTrack:name index:index]; } else if ([@"setMixWithOthers" isEqualToString:call.method]) { - NSLog(@"setMixWithOthers method called =======>"); [player setMixWithOthers:[argsMap[@"mixWithOthers"] boolValue]]; } else if ([@"preCache" isEqualToString:call.method]) { - NSLog(@"preCache method called =======>"); NSDictionary *dataSource = argsMap[@"dataSource"]; NSString *urlArg = dataSource[@"uri"]; NSString *cacheKey = dataSource[@"cacheKey"]; @@ -538,11 +499,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } result(nil); } else if ([@"clearCache" isEqualToString:call.method]) { - NSLog(@"clearCache method called =======>"); [_cacheManager clearCache]; result(nil); } else if ([@"stopPreCache" isEqualToString:call.method]) { - NSLog(@"stopPreCache method called =======>"); NSString *urlArg = argsMap[@"url"]; NSString *cacheKey = argsMap[@"cacheKey"]; NSString *videoExtension = argsMap[@"videoExtension"]; diff --git a/lib/src/configuration/better_player_data_source.dart b/lib/src/configuration/better_player_data_source.dart index bccdfa4d7..45a28c2f6 100644 --- a/lib/src/configuration/better_player_data_source.dart +++ b/lib/src/configuration/better_player_data_source.dart @@ -39,6 +39,11 @@ class BetterPlayerDataSource { ///If empty, then better player will choose name based on track parameters final List? asmsTrackNames; + /// the download of video IOS is dependednt on this value , + /// if set to 0 IOS will device itself how much to buffer + /// if set to 1 will take the buffering configurations well + int? useBufferForIos = 0; + ///Optional, alternative resolutions for non-hls/dash video. Used to setup ///different qualities for video. ///Data should be in given format: @@ -99,6 +104,7 @@ class BetterPlayerDataSource { this.drmConfiguration, this.placeholder, this.bufferingConfiguration = const BetterPlayerBufferingConfiguration(), + this.useBufferForIos, }) : assert( (type == BetterPlayerDataSourceType.network || type == BetterPlayerDataSourceType.file) || @@ -126,25 +132,24 @@ class BetterPlayerDataSource { Widget? placeholder, BetterPlayerBufferingConfiguration bufferingConfiguration = const BetterPlayerBufferingConfiguration(), + int? useBufferForIos, }) { - return BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - url, - subtitles: subtitles, - liveStream: liveStream, - headers: headers, - useAsmsSubtitles: useAsmsSubtitles, - useAsmsTracks: useAsmsTracks, - useAsmsAudioTracks: useAsmsAudioTracks, - resolutions: qualities, - cacheConfiguration: cacheConfiguration, - notificationConfiguration: notificationConfiguration, - overriddenDuration: overriddenDuration, - videoFormat: videoFormat, - drmConfiguration: drmConfiguration, - placeholder: placeholder, - bufferingConfiguration: bufferingConfiguration, - ); + return BetterPlayerDataSource(BetterPlayerDataSourceType.network, url, + subtitles: subtitles, + liveStream: liveStream, + headers: headers, + useAsmsSubtitles: useAsmsSubtitles, + useAsmsTracks: useAsmsTracks, + useAsmsAudioTracks: useAsmsAudioTracks, + resolutions: qualities, + cacheConfiguration: cacheConfiguration, + notificationConfiguration: notificationConfiguration, + overriddenDuration: overriddenDuration, + videoFormat: videoFormat, + drmConfiguration: drmConfiguration, + placeholder: placeholder, + bufferingConfiguration: bufferingConfiguration, + useBufferForIos: useBufferForIos); } ///Factory method to build file data source which uses url as data source. @@ -227,28 +232,27 @@ class BetterPlayerDataSource { Widget? placeholder, BetterPlayerBufferingConfiguration? bufferingConfiguration = const BetterPlayerBufferingConfiguration(), + int? useBufferForIos, }) { - return BetterPlayerDataSource( - type ?? this.type, - url ?? this.url, - bytes: bytes ?? this.bytes, - subtitles: subtitles ?? this.subtitles, - liveStream: liveStream ?? this.liveStream, - headers: headers ?? this.headers, - useAsmsSubtitles: useAsmsSubtitles ?? this.useAsmsSubtitles, - useAsmsTracks: useAsmsTracks ?? this.useAsmsTracks, - useAsmsAudioTracks: useAsmsAudioTracks ?? this.useAsmsAudioTracks, - resolutions: resolutions ?? this.resolutions, - cacheConfiguration: cacheConfiguration ?? this.cacheConfiguration, - notificationConfiguration: - notificationConfiguration ?? this.notificationConfiguration, - overriddenDuration: overriddenDuration ?? this.overriddenDuration, - videoFormat: videoFormat ?? this.videoFormat, - videoExtension: videoExtension ?? this.videoExtension, - drmConfiguration: drmConfiguration ?? this.drmConfiguration, - placeholder: placeholder ?? this.placeholder, - bufferingConfiguration: - bufferingConfiguration ?? this.bufferingConfiguration, - ); + return BetterPlayerDataSource(type ?? this.type, url ?? this.url, + bytes: bytes ?? this.bytes, + subtitles: subtitles ?? this.subtitles, + liveStream: liveStream ?? this.liveStream, + headers: headers ?? this.headers, + useAsmsSubtitles: useAsmsSubtitles ?? this.useAsmsSubtitles, + useAsmsTracks: useAsmsTracks ?? this.useAsmsTracks, + useAsmsAudioTracks: useAsmsAudioTracks ?? this.useAsmsAudioTracks, + resolutions: resolutions ?? this.resolutions, + cacheConfiguration: cacheConfiguration ?? this.cacheConfiguration, + notificationConfiguration: + notificationConfiguration ?? this.notificationConfiguration, + overriddenDuration: overriddenDuration ?? this.overriddenDuration, + videoFormat: videoFormat ?? this.videoFormat, + videoExtension: videoExtension ?? this.videoExtension, + drmConfiguration: drmConfiguration ?? this.drmConfiguration, + placeholder: placeholder ?? this.placeholder, + bufferingConfiguration: + bufferingConfiguration ?? this.bufferingConfiguration, + useBufferForIos: useBufferForIos ?? this.useBufferForIos); } } diff --git a/lib/src/core/better_player_controller.dart b/lib/src/core/better_player_controller.dart index dd26cd64a..dc70287e9 100644 --- a/lib/src/core/better_player_controller.dart +++ b/lib/src/core/better_player_controller.dart @@ -471,6 +471,7 @@ class BetterPlayerController { _betterPlayerDataSource?.notificationConfiguration?.activityName, clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey, videoExtension: _betterPlayerDataSource!.videoExtension, + useBufferForIos: _betterPlayerDataSource!.useBufferForIos, ); break; diff --git a/lib/src/video_player/method_channel_video_player.dart b/lib/src/video_player/method_channel_video_player.dart index bac001a5b..e5b7f9c60 100644 --- a/lib/src/video_player/method_channel_video_player.dart +++ b/lib/src/video_player/method_channel_video_player.dart @@ -70,7 +70,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'imageUrl': dataSource.imageUrl, 'notificationChannelName': dataSource.notificationChannelName, 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, - 'activityName': dataSource.activityName + 'activityName': dataSource.activityName, + 'downloadFullVideoOnIos': dataSource.useBufferForIos, }; break; case DataSourceType.network: @@ -95,6 +96,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'activityName': dataSource.activityName, 'clearKey': dataSource.clearKey, 'videoExtension': dataSource.videoExtension, + 'downloadFullVideoOnIos': dataSource.useBufferForIos, }; break; case DataSourceType.file: @@ -111,7 +113,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'notificationChannelName': dataSource.notificationChannelName, 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, 'activityName': dataSource.activityName, - 'clearKey': dataSource.clearKey + 'clearKey': dataSource.clearKey, + 'downloadFullVideoOnIos': dataSource.useBufferForIos, }; break; } diff --git a/lib/src/video_player/video_player.dart b/lib/src/video_player/video_player.dart index 7a8453f13..4119e5465 100644 --- a/lib/src/video_player/video_player.dart +++ b/lib/src/video_player/video_player.dart @@ -335,6 +335,7 @@ class VideoPlayerController extends ValueNotifier { String? activityName, String? clearKey, String? videoExtension, + int? useBufferForIos, }) { return _setDataSource( DataSource( @@ -358,6 +359,7 @@ class VideoPlayerController extends ValueNotifier { activityName: activityName, clearKey: clearKey, videoExtension: videoExtension, + useBufferForIos: useBufferForIos, ), ); } diff --git a/lib/src/video_player/video_player_platform_interface.dart b/lib/src/video_player/video_player_platform_interface.dart index 336f54b04..af8a02c2c 100644 --- a/lib/src/video_player/video_player_platform_interface.dart +++ b/lib/src/video_player/video_player_platform_interface.dart @@ -227,6 +227,7 @@ class DataSource { this.activityName, this.clearKey, this.videoExtension, + this.useBufferForIos = 0, }) : assert(uri == null || asset == null); /// Describes the type of data source this [VideoPlayerController] @@ -238,6 +239,11 @@ class DataSource { /// from which the video is fetched from. final DataSourceType sourceType; + /// the download of video IOS is dependednt on this value , + /// if set to 0 IOS will device itself how much to buffer + /// if set to 1 will take the buffering configurations well + int? useBufferForIos; + /// The URI to the video file. /// /// This will be in different formats depending on the [DataSourceType] of diff --git a/test/mock_video_player_controller.dart b/test/mock_video_player_controller.dart index b9bceebab..94e03f534 100644 --- a/test/mock_video_player_controller.dart +++ b/test/mock_video_player_controller.dart @@ -74,5 +74,6 @@ class MockVideoPlayerController extends VideoPlayerController { String? activityName, String? clearKey, String? videoExtension, + int? useBufferForIos, }) async {} } From 2b30a43cef3f272d27b4c8c674a07d6bb05d7a9d Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:33:16 +0530 Subject: [PATCH 04/24] added parameter to abstract class --- ios/Classes/BetterPlayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Classes/BetterPlayer.h b/ios/Classes/BetterPlayer.h index 3e721a32e..3b1981a84 100644 --- a/ios/Classes/BetterPlayer.h +++ b/ios/Classes/BetterPlayer.h @@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFrame:(CGRect)frame; - (void)setMixWithOthers:(bool)mixWithOthers; - (void)seekTo:(int)location; -- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration; +- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration downloadFullVideoOnIos:(int) downloadFullVideoOnIos; - (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension: (NSString*) videoExtension; - (void)setVolume:(double)volume; - (void)setSpeed:(double)speed result:(FlutterResult)result; From f48dac65595b172d5f64f0c5c03d9b5ff48c3923 Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:39:12 +0530 Subject: [PATCH 05/24] added parameter to anstract class url method --- ios/Classes/BetterPlayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Classes/BetterPlayer.h b/ios/Classes/BetterPlayer.h index 3b1981a84..23c3e6650 100644 --- a/ios/Classes/BetterPlayer.h +++ b/ios/Classes/BetterPlayer.h @@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setMixWithOthers:(bool)mixWithOthers; - (void)seekTo:(int)location; - (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration downloadFullVideoOnIos:(int) downloadFullVideoOnIos; -- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension: (NSString*) videoExtension; +- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension: (NSString*) videoExtension downloadFullVideoOnIos:(int) downloadFullVideoOnIos; - (void)setVolume:(double)volume; - (void)setSpeed:(double)speed result:(FlutterResult)result; - (void) setAudioTrack:(NSString*) name index:(int) index; From cb3cc9e3c3f47dff3cef62bea3070454029616c1 Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:43:34 +0530 Subject: [PATCH 06/24] removed space --- ios/Classes/BetterPlayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Classes/BetterPlayer.h b/ios/Classes/BetterPlayer.h index 23c3e6650..d96a89c6d 100644 --- a/ios/Classes/BetterPlayer.h +++ b/ios/Classes/BetterPlayer.h @@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setMixWithOthers:(bool)mixWithOthers; - (void)seekTo:(int)location; - (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration downloadFullVideoOnIos:(int) downloadFullVideoOnIos; -- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension: (NSString*) videoExtension downloadFullVideoOnIos:(int) downloadFullVideoOnIos; +- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension:(NSString*) videoExtension downloadFullVideoOnIos:(int) downloadFullVideoOnIos; - (void)setVolume:(double)volume; - (void)setSpeed:(double)speed result:(FlutterResult)result; - (void) setAudioTrack:(NSString*) name index:(int) index; From c57611b7471448b771b06960e95bbcf53098d004 Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:50:57 +0530 Subject: [PATCH 07/24] called parameters as per the hierarchy --- ios/Classes/BetterPlayer.h | 4 ++-- ios/Classes/BetterPlayer.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Classes/BetterPlayer.h b/ios/Classes/BetterPlayer.h index d96a89c6d..c91d971ce 100644 --- a/ios/Classes/BetterPlayer.h +++ b/ios/Classes/BetterPlayer.h @@ -45,8 +45,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFrame:(CGRect)frame; - (void)setMixWithOthers:(bool)mixWithOthers; - (void)seekTo:(int)location; -- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration downloadFullVideoOnIos:(int) downloadFullVideoOnIos; -- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int) overriddenDuration videoExtension:(NSString*) videoExtension downloadFullVideoOnIos:(int) downloadFullVideoOnIos; +- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration downloadFullVideoOnIos:(int)downloadFullVideoOnIos; +- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration videoExtension:(NSString*)videoExtension downloadFullVideoOnIos:(int)downloadFullVideoOnIos; - (void)setVolume:(double)volume; - (void)setSpeed:(double)speed result:(FlutterResult)result; - (void) setAudioTrack:(NSString*) name index:(int) index; diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index afcc14544..53db62123 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -222,8 +222,8 @@ - (void)setDataSourceAsset:(NSString *)asset cacheKey:cacheKey cacheManager:cacheManager overriddenDuration:overriddenDuration - downloadFullVideoOnIos:downloadFullVideoOnIos - videoExtension:nil]; + videoExtension:nil + downloadFullVideoOnIos:downloadFullVideoOnIos]; } - (void)setDataSourceURL:(NSURL *)url From 1334849006ec12030b7be06f8fd733765b2a19bd Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 13:56:09 +0530 Subject: [PATCH 08/24] removed redundant code for method call --- ios/Classes/BetterPlayerPlugin.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index 0c92e67ea..7fbe86e24 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -365,7 +365,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call cacheKey:cacheKey cacheManager:_cacheManager overriddenDuration:overriddenDuration - downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos]; + downloadFullVideoOnIos:downloadFullVideoOnIos]; } else if (uriArg) { [player setDataSourceURL:[NSURL URLWithString:uriArg] withKey:key @@ -377,7 +377,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call cacheManager:_cacheManager overriddenDuration:overriddenDuration videoExtension:videoExtension - downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos]; + downloadFullVideoOnIos:downloadFullVideoOnIos]; } else { result(FlutterMethodNotImplemented); } From 24d4e1e2cd7cec1f8e3aa1ec92c02a9206bd3467 Mon Sep 17 00:00:00 2001 From: abhinav Date: Tue, 31 Jan 2023 14:02:07 +0530 Subject: [PATCH 09/24] number declaration synatax --- ios/Classes/BetterPlayerPlugin.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index 7fbe86e24..4d260e4dd 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -331,7 +331,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *cacheKey = dataSource[@"cacheKey"]; NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; NSString *videoExtension = dataSource[@"videoExtension"]; - NSInteger *downloadFullVideoOnIos = dataSource[@"downloadFullVideoOnIos"]; + NSNumber *downloadFullVideoOnIos = + [NSNumber numberWithInteger:dataSource[@"downloadFullVideoOnIos"]]; int overriddenDuration = 0; if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]) { From a284f7e9519d583f6597ecb8c253648f65e9534b Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 1 Feb 2023 11:50:53 +0530 Subject: [PATCH 10/24] done changes to pass parameter --- example/lib/pages/normal_player_page.dart | 1 + ios/Classes/BetterPlayer.m | 6 +++--- ios/Classes/BetterPlayerPlugin.m | 8 +++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index 076107f0d..269dc79a4 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -28,6 +28,7 @@ class _NormalPlayerPageState extends State { _betterPlayerDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl, + useBufferForIos: 1, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(_betterPlayerDataSource); diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 53db62123..52a13083d 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -211,7 +211,7 @@ - (void)setDataSourceAsset:(NSString *)asset cacheKey:(NSString *)cacheKey cacheManager:(CacheManager *)cacheManager overriddenDuration:(int)overriddenDuration - downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { + downloadFullVideoOnIos:(int)downloadFullVideoOnIos { NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; return [self setDataSourceURL:[NSURL fileURLWithPath:path] withKey:key @@ -236,7 +236,7 @@ - (void)setDataSourceURL:(NSURL *)url cacheManager:(CacheManager *)cacheManager overriddenDuration:(int)overriddenDuration videoExtension:(NSString *)videoExtension - downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { + downloadFullVideoOnIos:(int)downloadFullVideoOnIos { _overriddenDuration = 0; if (headers == [NSNull null] || headers == NULL) { headers = @{}; @@ -284,7 +284,7 @@ - (void)setDataSourceURL:(NSURL *)url - (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key - downloadFullVideoOnIos:(NSInteger *)downloadFullVideoOnIos { + downloadFullVideoOnIos:(int)downloadFullVideoOnIos { _key = key; _stalledCount = 0; _isStalledCheckStarted = false; diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index 4d260e4dd..ec4646105 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -331,9 +331,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *cacheKey = dataSource[@"cacheKey"]; NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; NSString *videoExtension = dataSource[@"videoExtension"]; - NSNumber *downloadFullVideoOnIos = - [NSNumber numberWithInteger:dataSource[@"downloadFullVideoOnIos"]]; - + int useBufferIOS = [dataSource[@"downloadFullVideoOnIos"] intValue]; int overriddenDuration = 0; if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]) { overriddenDuration = [dataSource[@"overriddenDuration"] intValue]; @@ -366,7 +364,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call cacheKey:cacheKey cacheManager:_cacheManager overriddenDuration:overriddenDuration - downloadFullVideoOnIos:downloadFullVideoOnIos]; + downloadFullVideoOnIos:useBufferIOS]; } else if (uriArg) { [player setDataSourceURL:[NSURL URLWithString:uriArg] withKey:key @@ -378,7 +376,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call cacheManager:_cacheManager overriddenDuration:overriddenDuration videoExtension:videoExtension - downloadFullVideoOnIos:downloadFullVideoOnIos]; + downloadFullVideoOnIos:useBufferIOS]; } else { result(FlutterMethodNotImplemented); } From 51c5a98d0666f77906f1a289fadbcd9243aba21e Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 1 Feb 2023 13:07:18 +0530 Subject: [PATCH 11/24] removed print statement --- ios/Classes/CacheManager.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ios/Classes/CacheManager.swift b/ios/Classes/CacheManager.swift index c69697900..f6fafff96 100644 --- a/ios/Classes/CacheManager.swift +++ b/ios/Classes/CacheManager.swift @@ -37,7 +37,6 @@ import PINCache ///Setups cache server for HLS streams @objc public func setup(){ - print("Cache Setup Swift Ran ==========>"); GCDWebServer.setLogLevel(4) let webServer = GCDWebServer() let cache = PINCache.shared From 7cdb7b2e488fdbb0c35b7e82e18d64727d6b15df Mon Sep 17 00:00:00 2001 From: abhinav Date: Sun, 26 Feb 2023 11:10:48 +0530 Subject: [PATCH 12/24] changes --- example/lib/constants.dart | 3 +- example/lib/pages/normal_player_page.dart | 6 ++++ ios/Classes/BetterPlayer.m | 35 ++++++++++++++++++++--- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/example/lib/constants.dart b/example/lib/constants.dart index ce1276903..cf1f59573 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -2,7 +2,8 @@ class Constants { static const String bugBuckBunnyVideoUrl = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; static const String forBiggerBlazesUrl = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"; + "https://storage.googleapis.com/ps_videos/enterprise/YTrLL3LEIj2jM/YTrLL3LEIj2jM.m3u8"; + // "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"; static const String fileTestVideoUrl = "testvideo.mp4"; static const String fileTestVideoEncryptUrl = "testvideo_encrypt.mp4"; static const String networkTestVideoEncryptUrl = diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index 269dc79a4..652ecdad0 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -29,6 +29,12 @@ class _NormalPlayerPageState extends State { BetterPlayerDataSourceType.network, Constants.forBiggerBlazesUrl, useBufferForIos: 1, + bufferingConfiguration: const BetterPlayerBufferingConfiguration( + minBufferMs: 5000, + bufferForPlaybackMs: 250, + bufferForPlaybackAfterRebufferMs: 500, + maxBufferMs: 5000, + ), ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(_betterPlayerDataSource); diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 52a13083d..f83da1e6d 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -289,12 +289,15 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item _stalledCount = 0; _isStalledCheckStarted = false; _playerRate = 1; - [_player replaceCurrentItemWithPlayerItem:item]; // downloadFullVideoOnIos if set to 1 will only download the buffer duration // for video + if (downloadFullVideoOnIos == 1) { - _player.currentItem.preferredForwardBufferDuration = 1; + item.preferredForwardBufferDuration = 1; } + + [_player replaceCurrentItemWithPlayerItem:item]; + AVAsset *asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ if ([asset statusOfValueForKey:@"tracks" @@ -342,12 +345,24 @@ - (void)handleStalled { } - (void)startStalledCheck { + NSLog(@"Abhinav >>>>>>>> startStalledCheck"); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > 10.0) { + NSLog(@"==============>"); + NSLog(@"Abhinav >>>>>>>> play video"); + NSLog(@"Abhinav >>>>>>>> Available Duration %f", [self availableDuration]); + NSLog(@"Abhinav >>>>>>>> Current Time %f", + CMTimeGetSeconds(_player.currentItem.currentTime)); [self play]; } else { + NSLog(@"==============>"); + NSLog(@"Abhinav >>>>>>>> Video available duration less than 10 secs"); + NSLog(@"Abhinav >>>>>>>> Available Duration %f", [self availableDuration]); + NSLog(@"Abhinav >>>>>>>> Current Time %f", + CMTimeGetSeconds(_player.currentItem.currentTime)); + _stalledCount++; if (_stalledCount > 60) { if (_eventSink != nil) { @@ -365,12 +380,20 @@ - (void)startStalledCheck { } - (NSTimeInterval)availableDuration { + // NSValue *range = [[_player currentItem] loadedTimeRanges][0]; + // if (range != nil) { + // return CMTimeRangeGetEnd(range.CMTimeRangeValue); + // } + // return kCMTimeZero; + NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; - if (loadedTimeRanges.count > 0) { + if (loadedTimeRanges != nil) { CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; Float64 startSeconds = CMTimeGetSeconds(timeRange.start); Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); + NSLog(@"Abhinav ::::: startSeconds :: %f", startSeconds); + NSLog(@"Abhinav ::::: durationSeconds :: %f", durationSeconds); NSTimeInterval result = startSeconds + durationSeconds; return result; } else { @@ -387,6 +410,7 @@ - (void)observeValueForKeyPath:(NSString *)path change:(NSDictionary *)change context:(void *)context { if ([path isEqualToString:@"rate"]) { + NSLog(@"Abhinav >>>>>>>>>>>>> rate ::::: %f", _player.rate); if (@available(iOS 10.0, *)) { if (_pipController.pictureInPictureActive == true) { if (_lastAvPlayerTimeControlStatus != [NSNull null] && @@ -395,6 +419,7 @@ - (void)observeValueForKeyPath:(NSString *)path } if (_player.timeControlStatus == AVPlayerTimeControlStatusPaused) { + NSLog(@"Abhinav ::::: AVPlayerTimeControlStatus. Paused"); _lastAvPlayerTimeControlStatus = _player.timeControlStatus; if (_eventSink != nil) { _eventSink(@{@"event" : @"pause"}); @@ -402,6 +427,7 @@ - (void)observeValueForKeyPath:(NSString *)path return; } if (_player.timeControlStatus == AVPlayerTimeControlStatusPlaying) { + NSLog(@"Abhinav ::::: AVPlayerTimeControlStatus. Playing"); _lastAvPlayerTimeControlStatus = _player.timeControlStatus; if (_eventSink != nil) { _eventSink(@{@"event" : @"play"}); @@ -417,7 +443,8 @@ - (void)observeValueForKeyPath:(NSString *)path _player.currentItem.currentTime, <, _player.currentItem.duration) && // but not yet finished _isPlaying) { // instance variable to handle overall state (changed to - // YES when user triggers playback) + // YES when user triggers playback) + NSLog(@"Abhinav ::::: ggggggggggggggggg"); [self handleStalled]; } } From fb427be8124f63eeb381db20860f604070879b25 Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 1 Mar 2023 14:21:58 +0530 Subject: [PATCH 13/24] given buffer value after item initiali --- ios/Classes/BetterPlayer.m | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index f83da1e6d..0483297db 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -295,7 +295,6 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item if (downloadFullVideoOnIos == 1) { item.preferredForwardBufferDuration = 1; } - [_player replaceCurrentItemWithPlayerItem:item]; AVAsset *asset = [item asset]; @@ -345,23 +344,12 @@ - (void)handleStalled { } - (void)startStalledCheck { - NSLog(@"Abhinav >>>>>>>> startStalledCheck"); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > 10.0) { - NSLog(@"==============>"); - NSLog(@"Abhinav >>>>>>>> play video"); - NSLog(@"Abhinav >>>>>>>> Available Duration %f", [self availableDuration]); - NSLog(@"Abhinav >>>>>>>> Current Time %f", - CMTimeGetSeconds(_player.currentItem.currentTime)); [self play]; } else { - NSLog(@"==============>"); - NSLog(@"Abhinav >>>>>>>> Video available duration less than 10 secs"); - NSLog(@"Abhinav >>>>>>>> Available Duration %f", [self availableDuration]); - NSLog(@"Abhinav >>>>>>>> Current Time %f", - CMTimeGetSeconds(_player.currentItem.currentTime)); _stalledCount++; if (_stalledCount > 60) { @@ -392,8 +380,6 @@ - (NSTimeInterval)availableDuration { [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; Float64 startSeconds = CMTimeGetSeconds(timeRange.start); Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); - NSLog(@"Abhinav ::::: startSeconds :: %f", startSeconds); - NSLog(@"Abhinav ::::: durationSeconds :: %f", durationSeconds); NSTimeInterval result = startSeconds + durationSeconds; return result; } else { @@ -410,7 +396,6 @@ - (void)observeValueForKeyPath:(NSString *)path change:(NSDictionary *)change context:(void *)context { if ([path isEqualToString:@"rate"]) { - NSLog(@"Abhinav >>>>>>>>>>>>> rate ::::: %f", _player.rate); if (@available(iOS 10.0, *)) { if (_pipController.pictureInPictureActive == true) { if (_lastAvPlayerTimeControlStatus != [NSNull null] && @@ -419,7 +404,6 @@ - (void)observeValueForKeyPath:(NSString *)path } if (_player.timeControlStatus == AVPlayerTimeControlStatusPaused) { - NSLog(@"Abhinav ::::: AVPlayerTimeControlStatus. Paused"); _lastAvPlayerTimeControlStatus = _player.timeControlStatus; if (_eventSink != nil) { _eventSink(@{@"event" : @"pause"}); @@ -427,7 +411,6 @@ - (void)observeValueForKeyPath:(NSString *)path return; } if (_player.timeControlStatus == AVPlayerTimeControlStatusPlaying) { - NSLog(@"Abhinav ::::: AVPlayerTimeControlStatus. Playing"); _lastAvPlayerTimeControlStatus = _player.timeControlStatus; if (_eventSink != nil) { _eventSink(@{@"event" : @"play"}); @@ -444,7 +427,6 @@ - (void)observeValueForKeyPath:(NSString *)path _player.currentItem.duration) && // but not yet finished _isPlaying) { // instance variable to handle overall state (changed to // YES when user triggers playback) - NSLog(@"Abhinav ::::: ggggggggggggggggg"); [self handleStalled]; } } From 9e54006b0bf12e24470081e266ba04d3de18eaef Mon Sep 17 00:00:00 2001 From: abhinav Date: Thu, 2 Mar 2023 09:47:31 +0530 Subject: [PATCH 14/24] removed stall check in rate observer --- ios/Classes/BetterPlayer.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 0483297db..b9fbd4891 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -419,8 +419,7 @@ - (void)observeValueForKeyPath:(NSString *)path } } - if (_player.rate == 0 && // if player rate dropped to 0 - CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, + if (CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, kCMTimeZero) && // if video was started CMTIME_COMPARE_INLINE( _player.currentItem.currentTime, <, From 8d97448c8d775da6a4f1529353feccf6f836154b Mon Sep 17 00:00:00 2001 From: abhinav Date: Fri, 3 Mar 2023 09:50:02 +0530 Subject: [PATCH 15/24] removed own code with original --- ios/Classes/BetterPlayer.m | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index b9fbd4891..695fb7a8f 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -368,14 +368,8 @@ - (void)startStalledCheck { } - (NSTimeInterval)availableDuration { - // NSValue *range = [[_player currentItem] loadedTimeRanges][0]; - // if (range != nil) { - // return CMTimeRangeGetEnd(range.CMTimeRangeValue); - // } - // return kCMTimeZero; - NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; - if (loadedTimeRanges != nil) { + if (loadedTimeRanges.count > 0) { CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; Float64 startSeconds = CMTimeGetSeconds(timeRange.start); @@ -419,7 +413,8 @@ - (void)observeValueForKeyPath:(NSString *)path } } - if (CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, + if (_player.rate == 0.0 && // if player rate dropped to 0 + CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, kCMTimeZero) && // if video was started CMTIME_COMPARE_INLINE( _player.currentItem.currentTime, <, From d3ca9850b646066ca3c389b4dab6adf02402c7df Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Mon, 27 Mar 2023 17:59:32 +0530 Subject: [PATCH 16/24] preferred buffer to 5 --- ios/Classes/BetterPlayer.m | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 695fb7a8f..16154fc16 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -237,6 +237,7 @@ - (void)setDataSourceURL:(NSURL *)url overriddenDuration:(int)overriddenDuration videoExtension:(NSString *)videoExtension downloadFullVideoOnIos:(int)downloadFullVideoOnIos { + // NSLog(@"setDataSourceURL"); _overriddenDuration = 0; if (headers == [NSNull null] || headers == NULL) { headers = @{}; @@ -275,6 +276,7 @@ - (void)setDataSourceURL:(NSURL *)url } if (@available(iOS 10.0, *) && overriddenDuration > 0) { + // NSLog(@"overriddenDuration BetterPlayer.m :::: %f", overriddenDuration); _overriddenDuration = overriddenDuration; } return [self setDataSourcePlayerItem:item @@ -293,7 +295,7 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item // for video if (downloadFullVideoOnIos == 1) { - item.preferredForwardBufferDuration = 1; + item.preferredForwardBufferDuration = 5; } [_player replaceCurrentItemWithPlayerItem:item]; @@ -336,6 +338,7 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item } - (void)handleStalled { + // NSLog(@"handleStalled. %d", _isStalledCheckStarted); if (_isStalledCheckStarted) { return; } @@ -344,6 +347,11 @@ - (void)handleStalled { } - (void)startStalledCheck { + // NSLog(@"available - current =====> %f", + // [self availableDuration] - + // CMTimeGetSeconds(_player.currentItem.currentTime)); + // NSLog(@"playbackLikelyToKeepUp ====> %d", + // _player.currentItem.playbackLikelyToKeepUp); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > @@ -369,11 +377,14 @@ - (void)startStalledCheck { - (NSTimeInterval)availableDuration { NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges]; + if (loadedTimeRanges.count > 0) { CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; Float64 startSeconds = CMTimeGetSeconds(timeRange.start); Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); + NSLog(@"startSeconds %f", startSeconds); + NSLog(@"durationSeconds %f", startSeconds); NSTimeInterval result = startSeconds + durationSeconds; return result; } else { @@ -412,6 +423,12 @@ - (void)observeValueForKeyPath:(NSString *)path } } } + // if (_player.rate != 0.0) { + // NSLog(@"rate not 0 and availableDuration ===> %f", + // [self availableDuration]); + // NSLog(@"currentTime :::: %f", + // CMTimeGetSeconds(_player.currentItem.currentTime)); + // } if (_player.rate == 0.0 && // if player rate dropped to 0 CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, @@ -421,11 +438,17 @@ - (void)observeValueForKeyPath:(NSString *)path _player.currentItem.duration) && // but not yet finished _isPlaying) { // instance variable to handle overall state (changed to // YES when user triggers playback) + // NSLog(@"player rate dropped to 0"); [self handleStalled]; } } if (context == timeRangeContext) { + // NSLog(@"timeRangeContext :::: %@", [object loadedTimeRanges]); + // NSLog(@"currentTime :::: %f", + // CMTimeGetSeconds(_player.currentItem.currentTime)); + // NSLog(@"duration :::: %f", + // CMTimeGetSeconds(_player.currentItem.duration)); if (_eventSink != nil) { NSMutableArray *> *values = [[NSMutableArray alloc] init]; @@ -444,6 +467,8 @@ - (void)observeValueForKeyPath:(NSString *)path [values addObject:@[ @(start), @(end) ]]; } + // NSLog(@"bufferingUpdate start :::: %@", values); + _eventSink( @{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); } @@ -453,6 +478,7 @@ - (void)observeValueForKeyPath:(NSString *)path else if (context == statusContext) { AVPlayerItem *item = (AVPlayerItem *)object; + // NSLog(@"statusContext :::: %ld", item.status); switch (item.status) { case AVPlayerItemStatusFailed: if (_eventSink != nil) { @@ -471,6 +497,7 @@ - (void)observeValueForKeyPath:(NSString *)path break; } } else if (context == playbackLikelyToKeepUpContext) { + // NSLog(@"playbackLikelyToKeepUpContext :::::: "); if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { [self updatePlayingState]; if (_eventSink != nil) { @@ -478,10 +505,12 @@ - (void)observeValueForKeyPath:(NSString *)path } } } else if (context == playbackBufferEmptyContext) { + // NSLog(@"playbackBufferEmptyContext :::::::: "); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); } } else if (context == playbackBufferFullContext) { + // NSLog(@"playbackBufferFullContext :::::::: "); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } @@ -575,11 +604,16 @@ - (void)pause { } - (int64_t)position { - + NSLog(@"position method ::::: %f", + [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]); return [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]; } - (int64_t)absolutePosition { + NSLog(@"absolutePosition method ::::: %f", + [BetterPlayerTimeUtils + FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] + timeIntervalSince1970])]); return [BetterPlayerTimeUtils FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] timeIntervalSince1970])]; From e43ec5a9746855438660c30f6a27115d15884d09 Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Fri, 28 Apr 2023 15:22:24 +0530 Subject: [PATCH 17/24] done changes for empty player --- example/lib/pages/normal_player_page.dart | 51 ++++++++++++++++------- example/lib/pages/player_page.dart | 39 +++++++++++++++++ example/lib/pages/welcome_page.dart | 3 +- ios/Classes/BetterPlayer.m | 2 +- 4 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 example/lib/pages/player_page.dart diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index 652ecdad0..4216391d7 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -1,9 +1,11 @@ import 'package:better_player/better_player.dart'; -import 'package:better_player_example/constants.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:path/path.dart' as path; class NormalPlayerPage extends StatefulWidget { + final String videoLink; + + const NormalPlayerPage({Key? key, required this.videoLink}) : super(key: key); @override _NormalPlayerPageState createState() => _NormalPlayerPageState(); } @@ -15,26 +17,47 @@ class _NormalPlayerPageState extends State { @override void initState() { BetterPlayerConfiguration betterPlayerConfiguration = - BetterPlayerConfiguration( - aspectRatio: 16 / 9, - fit: BoxFit.contain, - autoPlay: true, + const BetterPlayerConfiguration( + autoPlay: false, + autoDetectFullscreenAspectRatio: true, looping: true, - deviceOrientationsAfterFullScreen: [ - DeviceOrientation.portraitDown, - DeviceOrientation.portraitUp - ], + handleLifecycle: true, + autoDispose: false, + controlsConfiguration: BetterPlayerControlsConfiguration( + showControls: false, + enableFullscreen: false, + enableMute: false, + enableProgressText: false, + enableProgressBar: false, + enableProgressBarDrag: false, + enablePlayPause: false, + enableSkips: false, + enableAudioTracks: false, + enableRetry: false, + enableSubtitles: false, + showControlsOnInitialize: false, + ), ); + final String extension = path.extension(widget.videoLink); _betterPlayerDataSource = BetterPlayerDataSource( BetterPlayerDataSourceType.network, - Constants.forBiggerBlazesUrl, - useBufferForIos: 1, + widget.videoLink, + videoExtension: extension == ".m3u8" ? "m3u8" : extension.substring(1), + videoFormat: extension == ".m3u8" ? BetterPlayerVideoFormat.hls : null, + liveStream: extension == ".m3u8" ? true : null, bufferingConfiguration: const BetterPlayerBufferingConfiguration( - minBufferMs: 5000, + minBufferMs: 3000, bufferForPlaybackMs: 250, bufferForPlaybackAfterRebufferMs: 500, - maxBufferMs: 5000, + maxBufferMs: 3000, ), + cacheConfiguration: extension != ".m3u8" + ? null + : BetterPlayerCacheConfiguration( + useCache: true, + key: widget.videoLink, + ), + useBufferForIos: 1, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(_betterPlayerDataSource); diff --git a/example/lib/pages/player_page.dart b/example/lib/pages/player_page.dart new file mode 100644 index 000000000..755fb9e99 --- /dev/null +++ b/example/lib/pages/player_page.dart @@ -0,0 +1,39 @@ +import 'package:better_player_example/pages/normal_player_page.dart'; +import 'package:flutter/material.dart'; + +class PlayerPageWidget extends StatefulWidget { + const PlayerPageWidget({Key? key}) : super(key: key); + + @override + State createState() => _PlayerPageWidgetState(); +} + +class _PlayerPageWidgetState extends State { + TextEditingController textEditingController = TextEditingController(); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Column( + children: [ + TextField( + controller: textEditingController, + decoration: InputDecoration(hintText: "Enter m3u8 video link"), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => NormalPlayerPage( + videoLink: textEditingController.text, + ), + ), + ); + }, + child: Text("Submit"), + ), + ], + ), + ); + } +} diff --git a/example/lib/pages/welcome_page.dart b/example/lib/pages/welcome_page.dart index bad138ec2..073720f77 100644 --- a/example/lib/pages/welcome_page.dart +++ b/example/lib/pages/welcome_page.dart @@ -22,6 +22,7 @@ import 'package:better_player_example/pages/notification_player_page.dart'; import 'package:better_player_example/pages/overridden_aspect_ratio_page.dart'; import 'package:better_player_example/pages/overriden_duration_page.dart'; import 'package:better_player_example/pages/placeholder_until_play_page.dart'; +import 'package:better_player_example/pages/player_page.dart'; import 'package:better_player_example/pages/playlist_page.dart'; import 'package:better_player_example/pages/resolutions_page.dart'; import 'package:better_player_example/pages/reusable_video_list/reusable_video_list_page.dart'; @@ -82,7 +83,7 @@ class _WelcomePageState extends State { _navigateToPage(BasicPlayerPage()); }), _buildExampleElementWidget("Normal player", () { - _navigateToPage(NormalPlayerPage()); + _navigateToPage(PlayerPageWidget()); }), _buildExampleElementWidget("Controls configuration", () { _navigateToPage(ControlsConfigurationPage()); diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 16154fc16..d4329a24f 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -295,7 +295,7 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item // for video if (downloadFullVideoOnIos == 1) { - item.preferredForwardBufferDuration = 5; + item.preferredForwardBufferDuration = 3; } [_player replaceCurrentItemWithPlayerItem:item]; From 9b8ae55ece1693e2a94b09286736554695d97a6d Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Fri, 28 Apr 2023 15:27:08 +0530 Subject: [PATCH 18/24] done changes --- example/lib/pages/normal_player_page.dart | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index 4216391d7..cf79343bb 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -18,24 +18,15 @@ class _NormalPlayerPageState extends State { void initState() { BetterPlayerConfiguration betterPlayerConfiguration = const BetterPlayerConfiguration( - autoPlay: false, + autoPlay: true, autoDetectFullscreenAspectRatio: true, looping: true, handleLifecycle: true, autoDispose: false, controlsConfiguration: BetterPlayerControlsConfiguration( - showControls: false, - enableFullscreen: false, - enableMute: false, - enableProgressText: false, - enableProgressBar: false, - enableProgressBarDrag: false, - enablePlayPause: false, - enableSkips: false, - enableAudioTracks: false, - enableRetry: false, - enableSubtitles: false, - showControlsOnInitialize: false, + showControls: true, + showControlsOnInitialize: true, + enableProgressBar: true, ), ); final String extension = path.extension(widget.videoLink); @@ -64,6 +55,12 @@ class _NormalPlayerPageState extends State { super.initState(); } + @override + void dispose() { + _betterPlayerController.dispose(forceDispose: true); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( From 59576e941a0b7ad7b1e245369210159c22cdcbe0 Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Fri, 28 Apr 2023 15:28:58 +0530 Subject: [PATCH 19/24] changed main --- example/lib/main.dart | 3 ++- example/lib/pages/player_page.dart | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 5cd5edd1f..b5727dc5f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:better_player_example/pages/player_page.dart'; import 'package:better_player_example/pages/welcome_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -25,7 +26,7 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.green, ), - home: WelcomePage(), + home: PlayerPageWidget(), )); } } diff --git a/example/lib/pages/player_page.dart b/example/lib/pages/player_page.dart index 755fb9e99..b61294b0c 100644 --- a/example/lib/pages/player_page.dart +++ b/example/lib/pages/player_page.dart @@ -13,12 +13,17 @@ class _PlayerPageWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(), + appBar: AppBar( + title: Text("Btter player testing"), + ), body: Column( children: [ - TextField( - controller: textEditingController, - decoration: InputDecoration(hintText: "Enter m3u8 video link"), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + controller: textEditingController, + decoration: InputDecoration(hintText: "Enter m3u8 video link"), + ), ), ElevatedButton( onPressed: () { From d6ad4693edcecdc4981e7811ea21c000aaeb21ad Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Tue, 8 Aug 2023 22:57:12 +0530 Subject: [PATCH 20/24] added stalled check event --- .../jhomlala/better_player/BetterPlayer.kt | 7 +++++ ios/Classes/BetterPlayer.h | 4 +-- ios/Classes/BetterPlayer.m | 29 ++++++++----------- .../better_player_data_source.dart | 13 +++++---- .../better_player_event_type.dart | 1 + lib/src/core/better_player_controller.dart | 9 +++++- .../method_channel_video_player.dart | 22 +++++++++++--- lib/src/video_player/video_player.dart | 6 ++-- .../video_player_platform_interface.dart | 9 ++++-- test/mock_video_player_controller.dart | 2 +- 10 files changed, 67 insertions(+), 35 deletions(-) diff --git a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt index edc7a6ad8..8f4ad68d9 100644 --- a/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt +++ b/android/src/main/kotlin/com/jhomlala/better_player/BetterPlayer.kt @@ -92,6 +92,7 @@ internal class BetterPlayer( private val customDefaultLoadControl: CustomDefaultLoadControl = customDefaultLoadControl ?: CustomDefaultLoadControl() private var lastSendBufferedPosition = 0L + private var playerErrorStatus: Boolean = false init { val loadBuilder = DefaultLoadControl.Builder() @@ -485,11 +486,17 @@ internal class BetterPlayer( } Player.STATE_IDLE -> { //no-op + if (playerErrorStatus) { + val event: MutableMap = HashMap() + event["event"] = "stalledCheck" + eventSink.success(event) + } } } } override fun onPlayerError(error: PlaybackException) { + playerErrorStatus = true eventSink.error("VideoError", "Video player had error $error", "") } }) diff --git a/ios/Classes/BetterPlayer.h b/ios/Classes/BetterPlayer.h index c91d971ce..3b7a1be3d 100644 --- a/ios/Classes/BetterPlayer.h +++ b/ios/Classes/BetterPlayer.h @@ -45,8 +45,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFrame:(CGRect)frame; - (void)setMixWithOthers:(bool)mixWithOthers; - (void)seekTo:(int)location; -- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration downloadFullVideoOnIos:(int)downloadFullVideoOnIos; -- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration videoExtension:(NSString*)videoExtension downloadFullVideoOnIos:(int)downloadFullVideoOnIos; +- (void)setDataSourceAsset:(NSString*)asset withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration; +- (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withCertificateUrl:(NSString*)certificateUrl withLicenseUrl:(NSString*)licenseUrl withHeaders:(NSDictionary*)headers withCache:(BOOL)useCache cacheKey:(NSString*)cacheKey cacheManager:(CacheManager*)cacheManager overriddenDuration:(int)overriddenDuration videoExtension:(NSString*)videoExtension preferredForwardBufferDurationIos:(int)preferredForwardBufferDurationIos; - (void)setVolume:(double)volume; - (void)setSpeed:(double)speed result:(FlutterResult)result; - (void) setAudioTrack:(NSString*) name index:(int) index; diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index d4329a24f..14d096d3c 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -210,8 +210,7 @@ - (void)setDataSourceAsset:(NSString *)asset withLicenseUrl:(NSString *)licenseUrl cacheKey:(NSString *)cacheKey cacheManager:(CacheManager *)cacheManager - overriddenDuration:(int)overriddenDuration - downloadFullVideoOnIos:(int)downloadFullVideoOnIos { + overriddenDuration:(int)overriddenDuration{ NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; return [self setDataSourceURL:[NSURL fileURLWithPath:path] withKey:key @@ -222,8 +221,7 @@ - (void)setDataSourceAsset:(NSString *)asset cacheKey:cacheKey cacheManager:cacheManager overriddenDuration:overriddenDuration - videoExtension:nil - downloadFullVideoOnIos:downloadFullVideoOnIos]; + videoExtension:nil]; } - (void)setDataSourceURL:(NSURL *)url @@ -236,8 +234,7 @@ - (void)setDataSourceURL:(NSURL *)url cacheManager:(CacheManager *)cacheManager overriddenDuration:(int)overriddenDuration videoExtension:(NSString *)videoExtension - downloadFullVideoOnIos:(int)downloadFullVideoOnIos { - // NSLog(@"setDataSourceURL"); + preferredForwardBufferDurationIos:(int)preferredForwardBufferDurationIos { _overriddenDuration = 0; if (headers == [NSNull null] || headers == NULL) { headers = @{}; @@ -281,21 +278,21 @@ - (void)setDataSourceURL:(NSURL *)url } return [self setDataSourcePlayerItem:item withKey:key - downloadFullVideoOnIos:downloadFullVideoOnIos]; + preferredForwardBufferDurationIos:preferredForwardBufferDurationIos]; } - (void)setDataSourcePlayerItem:(AVPlayerItem *)item withKey:(NSString *)key - downloadFullVideoOnIos:(int)downloadFullVideoOnIos { + preferredForwardBufferDurationIos:(int)preferredForwardBufferDurationIos { _key = key; _stalledCount = 0; _isStalledCheckStarted = false; _playerRate = 1; - // downloadFullVideoOnIos if set to 1 will only download the buffer duration + // preferredForwardBufferDurationIos if set to 1 will only download the buffer duration // for video - if (downloadFullVideoOnIos == 1) { - item.preferredForwardBufferDuration = 3; + if (preferredForwardBufferDurationIos > 0) { + item.preferredForwardBufferDuration = preferredForwardBufferDurationIos; } [_player replaceCurrentItemWithPlayerItem:item]; @@ -338,7 +335,6 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem *)item } - (void)handleStalled { - // NSLog(@"handleStalled. %d", _isStalledCheckStarted); if (_isStalledCheckStarted) { return; } @@ -347,16 +343,15 @@ - (void)handleStalled { } - (void)startStalledCheck { - // NSLog(@"available - current =====> %f", - // [self availableDuration] - - // CMTimeGetSeconds(_player.currentItem.currentTime)); - // NSLog(@"playbackLikelyToKeepUp ====> %d", - // _player.currentItem.playbackLikelyToKeepUp); if (_player.currentItem.playbackLikelyToKeepUp || [self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > 10.0) { [self play]; + // event added for when video plays again after stalled condition + if (_eventSink != nil) { + _eventSink(@{@"event" : @"stalledCheck"}); + } } else { _stalledCount++; diff --git a/lib/src/configuration/better_player_data_source.dart b/lib/src/configuration/better_player_data_source.dart index 45a28c2f6..0b970478a 100644 --- a/lib/src/configuration/better_player_data_source.dart +++ b/lib/src/configuration/better_player_data_source.dart @@ -42,7 +42,7 @@ class BetterPlayerDataSource { /// the download of video IOS is dependednt on this value , /// if set to 0 IOS will device itself how much to buffer /// if set to 1 will take the buffering configurations well - int? useBufferForIos = 0; + int? preferredForwardBufferDurationIos = 0; ///Optional, alternative resolutions for non-hls/dash video. Used to setup ///different qualities for video. @@ -104,7 +104,7 @@ class BetterPlayerDataSource { this.drmConfiguration, this.placeholder, this.bufferingConfiguration = const BetterPlayerBufferingConfiguration(), - this.useBufferForIos, + this.preferredForwardBufferDurationIos, }) : assert( (type == BetterPlayerDataSourceType.network || type == BetterPlayerDataSourceType.file) || @@ -132,7 +132,7 @@ class BetterPlayerDataSource { Widget? placeholder, BetterPlayerBufferingConfiguration bufferingConfiguration = const BetterPlayerBufferingConfiguration(), - int? useBufferForIos, + int? preferredForwardBufferDurationIos, }) { return BetterPlayerDataSource(BetterPlayerDataSourceType.network, url, subtitles: subtitles, @@ -149,7 +149,7 @@ class BetterPlayerDataSource { drmConfiguration: drmConfiguration, placeholder: placeholder, bufferingConfiguration: bufferingConfiguration, - useBufferForIos: useBufferForIos); + preferredForwardBufferDurationIos: preferredForwardBufferDurationIos); } ///Factory method to build file data source which uses url as data source. @@ -232,7 +232,7 @@ class BetterPlayerDataSource { Widget? placeholder, BetterPlayerBufferingConfiguration? bufferingConfiguration = const BetterPlayerBufferingConfiguration(), - int? useBufferForIos, + int? preferredForwardBufferDurationIos, }) { return BetterPlayerDataSource(type ?? this.type, url ?? this.url, bytes: bytes ?? this.bytes, @@ -253,6 +253,7 @@ class BetterPlayerDataSource { placeholder: placeholder ?? this.placeholder, bufferingConfiguration: bufferingConfiguration ?? this.bufferingConfiguration, - useBufferForIos: useBufferForIos ?? this.useBufferForIos); + preferredForwardBufferDurationIos: preferredForwardBufferDurationIos ?? + this.preferredForwardBufferDurationIos); } } diff --git a/lib/src/configuration/better_player_event_type.dart b/lib/src/configuration/better_player_event_type.dart index 20f3a8f88..392183d8e 100644 --- a/lib/src/configuration/better_player_event_type.dart +++ b/lib/src/configuration/better_player_event_type.dart @@ -23,6 +23,7 @@ enum BetterPlayerEventType { setupDataSource, bufferingStart, bufferingUpdate, + stalledCheck, bufferingEnd, changedPlaylistItem, } diff --git a/lib/src/core/better_player_controller.dart b/lib/src/core/better_player_controller.dart index dc70287e9..ea3a6ddf4 100644 --- a/lib/src/core/better_player_controller.dart +++ b/lib/src/core/better_player_controller.dart @@ -471,7 +471,8 @@ class BetterPlayerController { _betterPlayerDataSource?.notificationConfiguration?.activityName, clearKey: _betterPlayerDataSource?.drmConfiguration?.clearKey, videoExtension: _betterPlayerDataSource!.videoExtension, - useBufferForIos: _betterPlayerDataSource!.useBufferForIos, + preferredForwardBufferDurationIos: + _betterPlayerDataSource!.preferredForwardBufferDurationIos, ); break; @@ -1163,6 +1164,12 @@ class BetterPlayerController { _bufferedParameter: event.buffered, })); break; + case VideoEventType.stalledCheck: + _postEvent(BetterPlayerEvent(BetterPlayerEventType.stalledCheck, + parameters: { + _bufferedParameter: event.buffered, + })); + break; case VideoEventType.bufferingEnd: _postEvent(BetterPlayerEvent(BetterPlayerEventType.bufferingEnd)); break; diff --git a/lib/src/video_player/method_channel_video_player.dart b/lib/src/video_player/method_channel_video_player.dart index e5b7f9c60..fd5ee38a7 100644 --- a/lib/src/video_player/method_channel_video_player.dart +++ b/lib/src/video_player/method_channel_video_player.dart @@ -71,7 +71,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'notificationChannelName': dataSource.notificationChannelName, 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, 'activityName': dataSource.activityName, - 'downloadFullVideoOnIos': dataSource.useBufferForIos, + 'preferredForwardBufferDurationIos': + dataSource.preferredForwardBufferDurationIos, }; break; case DataSourceType.network: @@ -96,7 +97,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'activityName': dataSource.activityName, 'clearKey': dataSource.clearKey, 'videoExtension': dataSource.videoExtension, - 'downloadFullVideoOnIos': dataSource.useBufferForIos, + 'preferredForwardBufferDurationIos': + dataSource.preferredForwardBufferDurationIos, }; break; case DataSourceType.file: @@ -114,7 +116,8 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { 'overriddenDuration': dataSource.overriddenDuration?.inMilliseconds, 'activityName': dataSource.activityName, 'clearKey': dataSource.clearKey, - 'downloadFullVideoOnIos': dataSource.useBufferForIos, + 'preferredForwardBufferDurationIos': + dataSource.preferredForwardBufferDurationIos, }; break; } @@ -155,6 +158,13 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { ); } + Future stalledCheckCalled(int? textureId) { + return _channel.invokeMethod( + 'stalledCheck', + {'textureId': textureId}, + ); + } + @override Future setVolume(int? textureId, double volume) { return _channel.invokeMethod( @@ -404,7 +414,11 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { eventType: VideoEventType.pipStart, key: key, ); - + case 'stalledCheck': + return VideoEvent( + eventType: VideoEventType.stalledCheck, + key: key, + ); case 'pipStop': return VideoEvent( eventType: VideoEventType.pipStop, diff --git a/lib/src/video_player/video_player.dart b/lib/src/video_player/video_player.dart index 4119e5465..f6518d013 100644 --- a/lib/src/video_player/video_player.dart +++ b/lib/src/video_player/video_player.dart @@ -254,6 +254,8 @@ class VideoPlayerController extends ValueNotifier { break; case VideoEventType.unknown: break; + case VideoEventType.stalledCheck: + break; } } @@ -335,7 +337,7 @@ class VideoPlayerController extends ValueNotifier { String? activityName, String? clearKey, String? videoExtension, - int? useBufferForIos, + int? preferredForwardBufferDurationIos, }) { return _setDataSource( DataSource( @@ -359,7 +361,7 @@ class VideoPlayerController extends ValueNotifier { activityName: activityName, clearKey: clearKey, videoExtension: videoExtension, - useBufferForIos: useBufferForIos, + preferredForwardBufferDurationIos: preferredForwardBufferDurationIos, ), ); } diff --git a/lib/src/video_player/video_player_platform_interface.dart b/lib/src/video_player/video_player_platform_interface.dart index af8a02c2c..81589be74 100644 --- a/lib/src/video_player/video_player_platform_interface.dart +++ b/lib/src/video_player/video_player_platform_interface.dart @@ -227,7 +227,7 @@ class DataSource { this.activityName, this.clearKey, this.videoExtension, - this.useBufferForIos = 0, + this.preferredForwardBufferDurationIos = 0, }) : assert(uri == null || asset == null); /// Describes the type of data source this [VideoPlayerController] @@ -242,7 +242,7 @@ class DataSource { /// the download of video IOS is dependednt on this value , /// if set to 0 IOS will device itself how much to buffer /// if set to 1 will take the buffering configurations well - int? useBufferForIos; + int? preferredForwardBufferDurationIos; /// The URI to the video file. /// @@ -470,6 +470,11 @@ enum VideoEventType { /// An unknown event has been received. unknown, + + /// Event received when video plays again after stall condition + /// Stall condition happens for HLS videos when there is no buffer left. + /// when the next biffer arrives and video is played this event is got. + stalledCheck, } /// Describes a discrete segment of time within a video using a [start] and diff --git a/test/mock_video_player_controller.dart b/test/mock_video_player_controller.dart index 94e03f534..1db534dda 100644 --- a/test/mock_video_player_controller.dart +++ b/test/mock_video_player_controller.dart @@ -74,6 +74,6 @@ class MockVideoPlayerController extends VideoPlayerController { String? activityName, String? clearKey, String? videoExtension, - int? useBufferForIos, + int? preferredForwardBufferDurationIos, }) async {} } From 14f49cf00316276eb45600ba3e6bc397c50a7d6d Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Tue, 8 Aug 2023 23:08:46 +0530 Subject: [PATCH 21/24] done error resolving --- example/lib/pages/normal_player_page.dart | 2 +- ios/Classes/BetterPlayer.m | 5 +++-- ios/Classes/BetterPlayerPlugin.m | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/lib/pages/normal_player_page.dart b/example/lib/pages/normal_player_page.dart index cf79343bb..543ad9b35 100644 --- a/example/lib/pages/normal_player_page.dart +++ b/example/lib/pages/normal_player_page.dart @@ -48,7 +48,7 @@ class _NormalPlayerPageState extends State { useCache: true, key: widget.videoLink, ), - useBufferForIos: 1, + preferredForwardBufferDurationIos: 3, ); _betterPlayerController = BetterPlayerController(betterPlayerConfiguration); _betterPlayerController.setupDataSource(_betterPlayerDataSource); diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 14d096d3c..3e4e4f96b 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -210,7 +210,7 @@ - (void)setDataSourceAsset:(NSString *)asset withLicenseUrl:(NSString *)licenseUrl cacheKey:(NSString *)cacheKey cacheManager:(CacheManager *)cacheManager - overriddenDuration:(int)overriddenDuration{ + overriddenDuration:(int)overriddenDuration { NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; return [self setDataSourceURL:[NSURL fileURLWithPath:path] withKey:key @@ -221,7 +221,8 @@ - (void)setDataSourceAsset:(NSString *)asset cacheKey:cacheKey cacheManager:cacheManager overriddenDuration:overriddenDuration - videoExtension:nil]; + videoExtension:nil + preferredForwardBufferDurationIos:nil]; } - (void)setDataSourceURL:(NSURL *)url diff --git a/ios/Classes/BetterPlayerPlugin.m b/ios/Classes/BetterPlayerPlugin.m index ec4646105..e04518f8b 100644 --- a/ios/Classes/BetterPlayerPlugin.m +++ b/ios/Classes/BetterPlayerPlugin.m @@ -331,7 +331,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *cacheKey = dataSource[@"cacheKey"]; NSNumber *maxCacheSize = dataSource[@"maxCacheSize"]; NSString *videoExtension = dataSource[@"videoExtension"]; - int useBufferIOS = [dataSource[@"downloadFullVideoOnIos"] intValue]; + int preferredForwardBufferDurationIos = [dataSource[@"preferredForwardBufferDurationIos"] intValue]; int overriddenDuration = 0; if ([dataSource objectForKey:@"overriddenDuration"] != [NSNull null]) { overriddenDuration = [dataSource[@"overriddenDuration"] intValue]; @@ -363,8 +363,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call withLicenseUrl:licenseUrl cacheKey:cacheKey cacheManager:_cacheManager - overriddenDuration:overriddenDuration - downloadFullVideoOnIos:useBufferIOS]; + overriddenDuration:overriddenDuration]; } else if (uriArg) { [player setDataSourceURL:[NSURL URLWithString:uriArg] withKey:key @@ -376,7 +375,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call cacheManager:_cacheManager overriddenDuration:overriddenDuration videoExtension:videoExtension - downloadFullVideoOnIos:useBufferIOS]; + preferredForwardBufferDurationIos:preferredForwardBufferDurationIos]; } else { result(FlutterMethodNotImplemented); } From e5968bbcf8f6322d5fe3ac7aebe3ab872bcd10aa Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Tue, 8 Aug 2023 23:12:34 +0530 Subject: [PATCH 22/24] removed commented code --- ios/Classes/BetterPlayer.m | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/ios/Classes/BetterPlayer.m b/ios/Classes/BetterPlayer.m index 3e4e4f96b..600ea24a9 100644 --- a/ios/Classes/BetterPlayer.m +++ b/ios/Classes/BetterPlayer.m @@ -274,7 +274,6 @@ - (void)setDataSourceURL:(NSURL *)url } if (@available(iOS 10.0, *) && overriddenDuration > 0) { - // NSLog(@"overriddenDuration BetterPlayer.m :::: %f", overriddenDuration); _overriddenDuration = overriddenDuration; } return [self setDataSourcePlayerItem:item @@ -379,8 +378,6 @@ - (NSTimeInterval)availableDuration { [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue]; Float64 startSeconds = CMTimeGetSeconds(timeRange.start); Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration); - NSLog(@"startSeconds %f", startSeconds); - NSLog(@"durationSeconds %f", startSeconds); NSTimeInterval result = startSeconds + durationSeconds; return result; } else { @@ -419,12 +416,6 @@ - (void)observeValueForKeyPath:(NSString *)path } } } - // if (_player.rate != 0.0) { - // NSLog(@"rate not 0 and availableDuration ===> %f", - // [self availableDuration]); - // NSLog(@"currentTime :::: %f", - // CMTimeGetSeconds(_player.currentItem.currentTime)); - // } if (_player.rate == 0.0 && // if player rate dropped to 0 CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, @@ -434,17 +425,11 @@ - (void)observeValueForKeyPath:(NSString *)path _player.currentItem.duration) && // but not yet finished _isPlaying) { // instance variable to handle overall state (changed to // YES when user triggers playback) - // NSLog(@"player rate dropped to 0"); [self handleStalled]; } } if (context == timeRangeContext) { - // NSLog(@"timeRangeContext :::: %@", [object loadedTimeRanges]); - // NSLog(@"currentTime :::: %f", - // CMTimeGetSeconds(_player.currentItem.currentTime)); - // NSLog(@"duration :::: %f", - // CMTimeGetSeconds(_player.currentItem.duration)); if (_eventSink != nil) { NSMutableArray *> *values = [[NSMutableArray alloc] init]; @@ -463,7 +448,6 @@ - (void)observeValueForKeyPath:(NSString *)path [values addObject:@[ @(start), @(end) ]]; } - // NSLog(@"bufferingUpdate start :::: %@", values); _eventSink( @{@"event" : @"bufferingUpdate", @"values" : values, @"key" : _key}); @@ -474,7 +458,6 @@ - (void)observeValueForKeyPath:(NSString *)path else if (context == statusContext) { AVPlayerItem *item = (AVPlayerItem *)object; - // NSLog(@"statusContext :::: %ld", item.status); switch (item.status) { case AVPlayerItemStatusFailed: if (_eventSink != nil) { @@ -493,7 +476,6 @@ - (void)observeValueForKeyPath:(NSString *)path break; } } else if (context == playbackLikelyToKeepUpContext) { - // NSLog(@"playbackLikelyToKeepUpContext :::::: "); if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { [self updatePlayingState]; if (_eventSink != nil) { @@ -501,12 +483,10 @@ - (void)observeValueForKeyPath:(NSString *)path } } } else if (context == playbackBufferEmptyContext) { - // NSLog(@"playbackBufferEmptyContext :::::::: "); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingStart", @"key" : _key}); } } else if (context == playbackBufferFullContext) { - // NSLog(@"playbackBufferFullContext :::::::: "); if (_eventSink != nil) { _eventSink(@{@"event" : @"bufferingEnd", @"key" : _key}); } @@ -600,16 +580,10 @@ - (void)pause { } - (int64_t)position { - NSLog(@"position method ::::: %f", - [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]); return [BetterPlayerTimeUtils FLTCMTimeToMillis:([_player currentTime])]; } - (int64_t)absolutePosition { - NSLog(@"absolutePosition method ::::: %f", - [BetterPlayerTimeUtils - FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] - timeIntervalSince1970])]); return [BetterPlayerTimeUtils FLTNSTimeIntervalToMillis:([[[_player currentItem] currentDate] timeIntervalSince1970])]; From 8158f7aa890a31c884b4c47e47f48f70fca3df5c Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Tue, 8 Aug 2023 23:15:51 +0530 Subject: [PATCH 23/24] update libraries --- pubspec.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index bf83280a0..20c538d31 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ homepage: https://github.com/jhomlala/betterplayer documentation: https://jhomlala.github.io/betterplayer/ environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=2.12.0 <3.0.0" flutter: ">=2.2.3" dependencies: @@ -17,8 +17,8 @@ dependencies: cupertino_icons: ^1.0.5 wakelock: ^0.6.2 meta: ^1.8.0 - flutter_widget_from_html_core: ^0.8.5+3 - visibility_detector: ^0.3.3 + flutter_widget_from_html_core: ^0.10.3 + visibility_detector: ^0.4.0+2 path_provider: ^2.0.11 collection: ^1.16.0 xml: ^6.1.0 @@ -37,4 +37,4 @@ flutter: package: com.jhomlala.better_player pluginClass: BetterPlayerPlugin ios: - pluginClass: BetterPlayerPlugin \ No newline at end of file + pluginClass: BetterPlayerPlugin From 83bc2f6420dc9c819ee4b121fb89f1d131a24696 Mon Sep 17 00:00:00 2001 From: abhinav503 Date: Tue, 8 Aug 2023 23:35:11 +0530 Subject: [PATCH 24/24] added changelog and --- CHANGELOG.md | 890 +++++++++++++++++++--------------- example/ios/Runner/Info.plist | 2 + pubspec.yaml | 6 +- 3 files changed, 494 insertions(+), 404 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86864b975..0bafca810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,569 +1,657 @@ +## 0.0.84 + +- Added stalledCheck event for IOS and Android +- Added option for setting preferredForwardBufferDuration for IOS. + ## 0.0.83 -* Updated dependencies -* Fixed Flutter 3.0 issues + +- Updated dependencies +- Fixed Flutter 3.0 issues ## 0.0.82 -* Updated ExoPlayer version to 2.17.1. -* Updated dependencies. -* Android native code refactor. + +- Updated ExoPlayer version to 2.17.1. +- Updated dependencies. +- Android native code refactor. ## 0.0.81 -* Fixed full screen button padding in material controls. -* Added `setBetterPlayerControlsConfiguration` in `BetterPlayerController`. -* Added `setOverriddenFit` in `BetterPlayerController`. + +- Fixed full screen button padding in material controls. +- Added `setBetterPlayerControlsConfiguration` in `BetterPlayerController`. +- Added `setOverriddenFit` in `BetterPlayerController`. ## 0.0.80 -* Removed pedantic dependency. -* Updated dependencies. -* Fixed controls render issue for small player (by https://github.com/admarwick) -* Fixed deprecated jCenter. Replaced jCenter with mavenCentral (by https://github.com/petoknm) -* Fixed iOS GCDWebServer and PINCache import issue (by https://github.com/twogood) -* Added is mounted check in player controls (by https://github.com/masoudk2000) -* Updated installation documentation page. + +- Removed pedantic dependency. +- Updated dependencies. +- Fixed controls render issue for small player (by https://github.com/admarwick) +- Fixed deprecated jCenter. Replaced jCenter with mavenCentral (by https://github.com/petoknm) +- Fixed iOS GCDWebServer and PINCache import issue (by https://github.com/twogood) +- Added is mounted check in player controls (by https://github.com/masoudk2000) +- Updated installation documentation page. ## 0.0.79 -* Fixed kotlin version issue. + +- Fixed kotlin version issue. ## 0.0.78 -* [BREAKING_CHANGE] Split controlsHidden into controlsHiddenStart and controlsHiddenEnd. -* [BREAKING_CHANGE] Added to Function(bool) onPlayerVisibilityChanged to customControlsBuilder in [BetterPlayerConfiguration]. -* Migrated android native code to Kotlin. -* Updated ExoPlayer version to 2.15.1. -* Updated screenshots. -* Added onTapDown handle for material and cupertino progress bar to handle show and hide of controls. -* Fixed crash related to Android 12. -* Fixed issue with full url of subtitle for HLS data source. -* Fixed install page from docs. -* Fixed one of the showcase images. -* Fixed video in list example. + +- [BREAKING_CHANGE] Split controlsHidden into controlsHiddenStart and controlsHiddenEnd. +- [BREAKING_CHANGE] Added to Function(bool) onPlayerVisibilityChanged to customControlsBuilder in [BetterPlayerConfiguration]. +- Migrated android native code to Kotlin. +- Updated ExoPlayer version to 2.15.1. +- Updated screenshots. +- Added onTapDown handle for material and cupertino progress bar to handle show and hide of controls. +- Fixed crash related to Android 12. +- Fixed issue with full url of subtitle for HLS data source. +- Fixed install page from docs. +- Fixed one of the showcase images. +- Fixed video in list example. ## 0.0.77 -* Fixed full screen safe area issue in cupertino controls. -* Fixed subtitles duplication after changing data source. -* Fixed progress bar issues when changing position of the video. -* [BREAKING_CHANGE] Changed min. Flutter version to 2.2.3. -* Changed log level in ExoPlayer to Error. -* Added url parameter for changedResolution event. -* Added [videoExtension] support for network data source for scenario where video source has no extension and cache manager requires it. -* Added parameters to [changedTrack] event. -* Added [changedPlaylistItem] event. -* Added [autoDetectFullscreenAspectRatio] parameter in [BetterPlayerConfiguration] (by https://github.com/Brazol) -* Updated license. -* Updated screenshots. + +- Fixed full screen safe area issue in cupertino controls. +- Fixed subtitles duplication after changing data source. +- Fixed progress bar issues when changing position of the video. +- [BREAKING_CHANGE] Changed min. Flutter version to 2.2.3. +- Changed log level in ExoPlayer to Error. +- Added url parameter for changedResolution event. +- Added [videoExtension] support for network data source for scenario where video source has no extension and cache manager requires it. +- Added parameters to [changedTrack] event. +- Added [changedPlaylistItem] event. +- Added [autoDetectFullscreenAspectRatio] parameter in [BetterPlayerConfiguration] (by https://github.com/Brazol) +- Updated license. +- Updated screenshots. ## 0.0.76 -* Fixed iOS build issue. -* [BREAKING_CHANGE] Changed min required iOS version to 11. -* Updated `BetterPlayerConfiguration` `copyWith` method. -* Added `useRootNavigator` option to `BetterPlayerConfiguration`. + +- Fixed iOS build issue. +- [BREAKING_CHANGE] Changed min required iOS version to 11. +- Updated `BetterPlayerConfiguration` `copyWith` method. +- Added `useRootNavigator` option to `BetterPlayerConfiguration`. ## 0.0.75 -* Fixed iOS build issue. + +- Fixed iOS build issue. ## 0.0.74 -* [BREAKING_CHANGE] `nextVideoTimeStreamController` is now marked as private. Please use `nextVideoTimeStream` to access stream. -* [BREAKING_CHANGE] Removed BackdropFilter from cupertino theme. -* [BREAKING_CHANGE] Removed `sigmaX` and `sigmaY` parameters from BetterPlayerControlsConfiguration. -* Added `expandToFill` in `BetterPlayerConfiguration`. -* Added `BetterPlayerControlsConfiguration.theme` factory for `BetterPlayerControlsConfiguration`. -* Added null checks in seek commands in `BetterPlayerControlsState`. -* Added tests. -* Added iOS HLS caching based on HLSCachingReverseProxyServer. -* Added default subtitle support for ASMS HLS data source (by https://github.com/siloebb). -* Fixed issue with live stream where player controls were always visible. -* Fixed iOS seek issue. -* Fixed getting started button link in documentation. -* Changed iOS non-HLS caching implementation based on https://github.com/neekeetab/CachingPlayerItem (by https://github.com/themadmrj). -* Fixed hardcoded 30 FPS on iOS (by https://github.com/antonkrasov). -* Enabled `preCache` and `stopPreCache` for iOS. -* Updated dependencies. + +- [BREAKING_CHANGE] `nextVideoTimeStreamController` is now marked as private. Please use `nextVideoTimeStream` to access stream. +- [BREAKING_CHANGE] Removed BackdropFilter from cupertino theme. +- [BREAKING_CHANGE] Removed `sigmaX` and `sigmaY` parameters from BetterPlayerControlsConfiguration. +- Added `expandToFill` in `BetterPlayerConfiguration`. +- Added `BetterPlayerControlsConfiguration.theme` factory for `BetterPlayerControlsConfiguration`. +- Added null checks in seek commands in `BetterPlayerControlsState`. +- Added tests. +- Added iOS HLS caching based on HLSCachingReverseProxyServer. +- Added default subtitle support for ASMS HLS data source (by https://github.com/siloebb). +- Fixed issue with live stream where player controls were always visible. +- Fixed iOS seek issue. +- Fixed getting started button link in documentation. +- Changed iOS non-HLS caching implementation based on https://github.com/neekeetab/CachingPlayerItem (by https://github.com/themadmrj). +- Fixed hardcoded 30 FPS on iOS (by https://github.com/antonkrasov). +- Enabled `preCache` and `stopPreCache` for iOS. +- Updated dependencies. ## 0.0.73 -* Added `licenseUrl` support for iOS DRM. -* Fixed RTL text direction issue in player controls. -* Added `renderedSubtitle` in `BetterPlayerController`. -* Added additional check in `postControllerEvent` to handle scenario where event stream is closed. -* Updated ExoPlayer version -* Fixed `bufferingUpdate` event triggered too often. -* Updated video list example with bufering configuration. -* Updated video list documentation. -* Added `setMixWithOthers` method in `BetterPlayerListVideoPlayerController`. -* Fixed broken link in cover page of documentation. -* Fixed progress bar issue where position could be set above video duration. -* Fixed iOS remote notification command issue. -* Removed duplicated page in example app (by https://github.com/pinguluk) -* Added support for clear key DRM (by https://github.com/tinusneethling) -* Refreshed look and feel of the player UI (by https://github.com/creativeblaq) -* Added `sigmaX` and `sigmaY` parameters in BetterPlayerControlsConfiguration to control blur of cupertino controls (original idea by: https://github.com/YeFei572) + +- Added `licenseUrl` support for iOS DRM. +- Fixed RTL text direction issue in player controls. +- Added `renderedSubtitle` in `BetterPlayerController`. +- Added additional check in `postControllerEvent` to handle scenario where event stream is closed. +- Updated ExoPlayer version +- Fixed `bufferingUpdate` event triggered too often. +- Updated video list example with bufering configuration. +- Updated video list documentation. +- Added `setMixWithOthers` method in `BetterPlayerListVideoPlayerController`. +- Fixed broken link in cover page of documentation. +- Fixed progress bar issue where position could be set above video duration. +- Fixed iOS remote notification command issue. +- Removed duplicated page in example app (by https://github.com/pinguluk) +- Added support for clear key DRM (by https://github.com/tinusneethling) +- Refreshed look and feel of the player UI (by https://github.com/creativeblaq) +- Added `sigmaX` and `sigmaY` parameters in BetterPlayerControlsConfiguration to control blur of cupertino controls (original idea by: https://github.com/YeFei572) ## 0.0.72 -* Updated ExoPlayer version + +- Updated ExoPlayer version ## 0.0.71 -* Fixed play after seeking issue on iOS. -* Fixed audio track selection issue on iOS/Android. -* Fixed issue where speed which couldn't be applied on iOS was saved in player state. -* Added support for D-pad navigation using a Android TV remote control (by https://github.com/danielz-nenda) -* Added `BetterPlayerMultipleGestureDetector` to handle problems with gesture detection -* Expose getter for `eventListeners` in `BetterPlayerController` (by https://github.com/Letalus) -* Updated documentation -* Updated dependencies + +- Fixed play after seeking issue on iOS. +- Fixed audio track selection issue on iOS/Android. +- Fixed issue where speed which couldn't be applied on iOS was saved in player state. +- Added support for D-pad navigation using a Android TV remote control (by https://github.com/danielz-nenda) +- Added `BetterPlayerMultipleGestureDetector` to handle problems with gesture detection +- Expose getter for `eventListeners` in `BetterPlayerController` (by https://github.com/Letalus) +- Updated documentation +- Updated dependencies ## 0.0.70 -* Fixed file data source exception. Right now user will be only warned. -* Fixed playback speed after seek in iOS. -* Fixed `overriddenDuration` behavior in iOS when passed `overriddenDuration` is longer than video duration. -* Fixed issue where controls were not updated after video finish. -* Fixed auto full screen orientation not enabled in iOS. -* Exposed ASMS classes. -* Exposed BetterPlayerControlsState to provide ways to build custom controls with additional menus. -* Added error handling for CacheWorker to prevent unexpected crashes. -* Added support for FairPlay EZDRM (by https://github.com/adrianByv and https://github.com/koldo92) -* Added `certificateUrl` parameter in BetterPlayerDrmConfiguration. -* Added support for custom buffering configuration in Android (by https://github.com/Letalus) -* Added `bufferingConfiguration` parameter in BetterPlayerConfiguration which contains buffering settings. +- Fixed file data source exception. Right now user will be only warned. +- Fixed playback speed after seek in iOS. +- Fixed `overriddenDuration` behavior in iOS when passed `overriddenDuration` is longer than video duration. +- Fixed issue where controls were not updated after video finish. +- Fixed auto full screen orientation not enabled in iOS. +- Exposed ASMS classes. +- Exposed BetterPlayerControlsState to provide ways to build custom controls with additional menus. +- Added error handling for CacheWorker to prevent unexpected crashes. +- Added support for FairPlay EZDRM (by https://github.com/adrianByv and https://github.com/koldo92) +- Added `certificateUrl` parameter in BetterPlayerDrmConfiguration. +- Added support for custom buffering configuration in Android (by https://github.com/Letalus) +- Added `bufferingConfiguration` parameter in BetterPlayerConfiguration which contains buffering settings. ## 0.0.69 -* Fixed cache clear on Android. -* Added file check for file data source. -* Fixed issue with black screen for some videos on iOS (by https://github.com/themadmrj) -* Fixed iOS eventSink issues. (by https://github.com/alextekartik) -* Added key parameter in BetterPlayerCacheConfiguration to provide way to re-use same video between app sessions. + +- Fixed cache clear on Android. +- Added file check for file data source. +- Fixed issue with black screen for some videos on iOS (by https://github.com/themadmrj) +- Fixed iOS eventSink issues. (by https://github.com/alextekartik) +- Added key parameter in BetterPlayerCacheConfiguration to provide way to re-use same video between app sessions. ## 0.0.68 -* Added support for segmented subtitles. -* Added new fields in in BetterPlayerSubtitlesSource: `asmsIsSegmented`, `asmsSegmentsTime` and ` asmsSegments`. These fields shouldn't be configured -manually. -* Fixed parsing VTT subtitle timestamps with no hour component (by https://github.com/trms-alex). -* Fixed parsing VTT subtitles when there's no subtitles in the file (by https://github.com/trms-alex). -* Added ES translations (by https://github.com/koldo92). -* Fixed iOS Picture in Picture play/pause state. -* Updated dependencies. -* Updated iOS example configuration. + +- Added support for segmented subtitles. +- Added new fields in in BetterPlayerSubtitlesSource: `asmsIsSegmented`, `asmsSegmentsTime` and ` asmsSegments`. These fields shouldn't be configured + manually. +- Fixed parsing VTT subtitle timestamps with no hour component (by https://github.com/trms-alex). +- Fixed parsing VTT subtitles when there's no subtitles in the file (by https://github.com/trms-alex). +- Added ES translations (by https://github.com/koldo92). +- Fixed iOS Picture in Picture play/pause state. +- Updated dependencies. +- Updated iOS example configuration. ## 0.0.67 -* Added support for DASH adaptive stream subtitles, audio tracks, tracks (by https://github.com/adrianByv) -* [BREAKING_CHANGE] Changed useHlsSubtitles, useHlsTracks, useHlsAudio to useAsmsSubtitles, useAsmsTracks, useAsmsAudio. -* Added DASH example. -* Fixed progress bar jumps when seeking video. -* Fixed end of video looping final second, and video stutter during AudioSession deactivation (by https://github.com/NicholasNagy) + +- Added support for DASH adaptive stream subtitles, audio tracks, tracks (by https://github.com/adrianByv) +- [BREAKING_CHANGE] Changed useHlsSubtitles, useHlsTracks, useHlsAudio to useAsmsSubtitles, useAsmsTracks, useAsmsAudio. +- Added DASH example. +- Fixed progress bar jumps when seeking video. +- Fixed end of video looping final second, and video stutter during AudioSession deactivation (by https://github.com/NicholasNagy) ## 0.0.66 -* Added check in seek method to handle scenario when video wasn't ready to play. -* Added setupDataSourceList in BetterPlayerPlaylistController. -* Fixed playback stalled issue in iOS. -* Added pause on iOS dispose call. -* Added bufferedStart, bufferedUpdate, bufferedEnd events. -* Fixed full screen dismissed when new data source loaded. -* Added forget option for VisibilityDetectorController (by https://github.com/ChopinDavid). -* Added vietnamese translations (by https://github.com/thanhvn-57). + +- Added check in seek method to handle scenario when video wasn't ready to play. +- Added setupDataSourceList in BetterPlayerPlaylistController. +- Fixed playback stalled issue in iOS. +- Added pause on iOS dispose call. +- Added bufferedStart, bufferedUpdate, bufferedEnd events. +- Fixed full screen dismissed when new data source loaded. +- Added forget option for VisibilityDetectorController (by https://github.com/ChopinDavid). +- Added vietnamese translations (by https://github.com/thanhvn-57). ## 0.0.65 -* Refactored Android notification image selection. -* Added headers parameter in BetterPlayerSubtitlesSource. Headers is an optional parameter. -* Added activityName to BetterPlayerNotificationConfiguration. -* Android notification will open back application (by https://github.com/shashikantdurge). -* Fixed playing audio-only resources in iOS. -* Updated Exo Player version. -* Fixed notification not updating correctly for playlists in Android. -* [BREAKING_CHANGE] Removed deprecated Android code. Better Player supports now only v2 embedding. + +- Refactored Android notification image selection. +- Added headers parameter in BetterPlayerSubtitlesSource. Headers is an optional parameter. +- Added activityName to BetterPlayerNotificationConfiguration. +- Android notification will open back application (by https://github.com/shashikantdurge). +- Fixed playing audio-only resources in iOS. +- Updated Exo Player version. +- Fixed notification not updating correctly for playlists in Android. +- [BREAKING_CHANGE] Removed deprecated Android code. Better Player supports now only v2 embedding. ## 0.0.64 -* Added Turkish translations (by https://github.com/smurat). -* Video fit fixes (by https://github.com/themadmrj). -* Fixed speed iOS issue. -* Fixed Android's notification image OOM issue. -* Fixed 0 second delay issue in playlist. -* Fixed drmHeaders to be sent in headers rather than request body (by https://github.com/FlutterSu) -* Added preCache, stopPreCache method in BetterPlayerController (coauthored with: https://github.com/themadmrj) -* [BREAKING_CHANGE] clearCache method doesn't require to setup data source in order to use. + +- Added Turkish translations (by https://github.com/smurat). +- Video fit fixes (by https://github.com/themadmrj). +- Fixed speed iOS issue. +- Fixed Android's notification image OOM issue. +- Fixed 0 second delay issue in playlist. +- Fixed drmHeaders to be sent in headers rather than request body (by https://github.com/FlutterSu) +- Added preCache, stopPreCache method in BetterPlayerController (coauthored with: https://github.com/themadmrj) +- [BREAKING_CHANGE] clearCache method doesn't require to setup data source in order to use. ## 0.0.63 -* Fixed pause method in dispose. -* Added clearCache method in BetterPlayerController. -* Fixed reusable video player example issues. + +- Fixed pause method in dispose. +- Added clearCache method in BetterPlayerController. +- Fixed reusable video player example issues. ## 0.0.62 -* Refactored internal event handling. -* [BREAKING_CHANGE] Migrated to null safety. -* [BREAKING_CHANGE] Updated dart min version to 2.12.0. -* Fixed issue where player controls were immediately hidden. -* Removed cancelFullScreenDismiss parameter. -* Added initialization check for VideoPlayerController. -* Changed default value of enableProgressText to true in BetterPlayerControlsConfiguration. -* Setup first selected HLS Audio as default one. -* General bug fixes. + +- Refactored internal event handling. +- [BREAKING_CHANGE] Migrated to null safety. +- [BREAKING_CHANGE] Updated dart min version to 2.12.0. +- Fixed issue where player controls were immediately hidden. +- Removed cancelFullScreenDismiss parameter. +- Added initialization check for VideoPlayerController. +- Changed default value of enableProgressText to true in BetterPlayerControlsConfiguration. +- Setup first selected HLS Audio as default one. +- General bug fixes. ## 0.0.61 -* Fixed fullscreenByDefault issue. -* Updated documentation. + +- Fixed fullscreenByDefault issue. +- Updated documentation. ## 0.0.60 -* Updated documentation. -* Added null checking for videoPlayerController inside BetterPlayerController. -* Added setMixWithOthers method to BetterPlayerController. -* Added initialStartIndex in BetterPlayerPlaylistConfiguration. -* Fixed issue where player did not disposed properly on app quit. -* Added placeholder parameter in BetterPlayerDataSource. -* Fixed custom material full screen icons (by https://github.com/FelipeFernandesLeandro) + +- Updated documentation. +- Added null checking for videoPlayerController inside BetterPlayerController. +- Added setMixWithOthers method to BetterPlayerController. +- Added initialStartIndex in BetterPlayerPlaylistConfiguration. +- Fixed issue where player did not disposed properly on app quit. +- Added placeholder parameter in BetterPlayerDataSource. +- Fixed custom material full screen icons (by https://github.com/FelipeFernandesLeandro) ## 0.0.59 -* Fixed WEBVTT subtitles parsing. -* Updated ExoPlayer version. -* Refactored ExoPlayer code. -* Added missing controller dispose from BetterPlayer widget dispose. -* Added fix for iOS aspect ratio issue. -* Fixed auto play issue where player starts video after load initialization process and player is not visible. -* Updated texts in examples. -* Added missing widevine DRM parameters (by https://github.com/FlutterSu). + +- Fixed WEBVTT subtitles parsing. +- Updated ExoPlayer version. +- Refactored ExoPlayer code. +- Added missing controller dispose from BetterPlayer widget dispose. +- Added fix for iOS aspect ratio issue. +- Fixed auto play issue where player starts video after load initialization process and player is not visible. +- Updated texts in examples. +- Added missing widevine DRM parameters (by https://github.com/FlutterSu). ## 0.0.58 -* Added overflowModalColor and overflowModalTextColor in BetterPlayerControlsConfiguration. -* Disabled picture in picture in fullscreen mode. -* Fixed enabled parameter for skip back and forward. -* Fixed notification configuration null issue (by https://github.com/bounty1342) -* Added token based and widevine DRM support. -* Updated documentation. + +- Added overflowModalColor and overflowModalTextColor in BetterPlayerControlsConfiguration. +- Disabled picture in picture in fullscreen mode. +- Fixed enabled parameter for skip back and forward. +- Fixed notification configuration null issue (by https://github.com/bounty1342) +- Added token based and widevine DRM support. +- Updated documentation. ## 0.0.57 -* Fixed iOS HLS initialization issue. -* Fixed issue where video plays after resume even if it's not visible. -* Updated User-Agent picking for Android. -* Added auto option for quality selection. + +- Fixed iOS HLS initialization issue. +- Fixed issue where video plays after resume even if it's not visible. +- Updated User-Agent picking for Android. +- Added auto option for quality selection. ## 0.0.56 -* Fixed empty data source notification issue. -* Fixed WebVTT subtitles parsing issue. -* Fixed memory data source issue on iOS. -* Added videoExtension parameter for memory data source (works only with memory data source). -* Added videoFormat parameter to network data source. -* Fixed controls visible all time on live stream. -* Fixed potential iOS notification crash. + +- Fixed empty data source notification issue. +- Fixed WebVTT subtitles parsing issue. +- Fixed memory data source issue on iOS. +- Added videoExtension parameter for memory data source (works only with memory data source). +- Added videoFormat parameter to network data source. +- Fixed controls visible all time on live stream. +- Fixed potential iOS notification crash. ## 0.0.55 -* Dart analysis fix + +- Dart analysis fix ## 0.0.54 -* Refactored BetterPlayerPlaylist feature. -* Added new BetterPlayerPlaylistController which is accessible from BetterPlayerPlaylist's current -state. Playlist video can be changed with setupDataSource method and current video index can be -accessed with currentDataSourceIndex getter. -* Fixed iOS availableDuration index issue. -* Added arabic translations (by https://github.com/mohamed-Etman). -* Added headers to HLS data request (by https://github.com/mohamed-Etman). -* Added fullScreenAspectRatio to copyWith method in BetterPlayerConfiguration (by https://github.com/njlawton) + +- Refactored BetterPlayerPlaylist feature. +- Added new BetterPlayerPlaylistController which is accessible from BetterPlayerPlaylist's current + state. Playlist video can be changed with setupDataSource method and current video index can be + accessed with currentDataSourceIndex getter. +- Fixed iOS availableDuration index issue. +- Added arabic translations (by https://github.com/mohamed-Etman). +- Added headers to HLS data request (by https://github.com/mohamed-Etman). +- Added fullScreenAspectRatio to copyWith method in BetterPlayerConfiguration (by https://github.com/njlawton) ## 0.0.53 -* Fixed fullscreen issue. -* Fixed HLS tracks selection. -* Removed HLS parser package and included HLS parser package in Better Player. -* Removed unused player observer in iOS. -* Fixed cache issue in Android where multiple Better Player instances uses same directory. -* Fixed HLS parsing issue. -* Added HLS Audio example. + +- Fixed fullscreen issue. +- Fixed HLS tracks selection. +- Removed HLS parser package and included HLS parser package in Better Player. +- Removed unused player observer in iOS. +- Fixed cache issue in Android where multiple Better Player instances uses same directory. +- Fixed HLS parsing issue. +- Added HLS Audio example. ## 0.0.52 -* Fixed unregister listener issue in iOS. -* Updated documentation. -* [BREAKING_CHANGE] BetterPlayerState visibility changed to private. -* Fixed HLS audio tracks playlist selection issue. -* Added enableProgressBarDrag in BetterPlayerControlsConfiguration. -* Fixed audio track picking in ExoPlayer (Android). -* Changed default loadingColor. + +- Fixed unregister listener issue in iOS. +- Updated documentation. +- [BREAKING_CHANGE] BetterPlayerState visibility changed to private. +- Fixed HLS audio tracks playlist selection issue. +- Added enableProgressBarDrag in BetterPlayerControlsConfiguration. +- Fixed audio track picking in ExoPlayer (Android). +- Changed default loadingColor. ## 0.0.51 -* Fixed lint issues. -* Fixed subtitles setup issue. + +- Fixed lint issues. +- Fixed subtitles setup issue. ## 0.0.50 -* Fixed deprecated resizeToAvoidBottomPadding -* Fixed playing large videos in iOS. -* [BREAKING_CHANGE] Removed autoPlay and errorBuilder from BetterPlayerController. These can be accessed via betterPlayerController. -* Added HLS Audio track support. -* Added setAudioTrack method in BetterPlayerController. -* Added useHlsAudioTrack parameter in BetterPlayerDataSource. -* Added enableAudioTracks and audioTracksIcon, backgroundColor in BetterPlayerControlsConfiguration. -* Fixed HLS loading speed. -* Fixed finished event creation. -* Fixed player pause issue when player notification is displayed. -* Fixed player not pausing/resuming automatically correctly. + +- Fixed deprecated resizeToAvoidBottomPadding +- Fixed playing large videos in iOS. +- [BREAKING_CHANGE] Removed autoPlay and errorBuilder from BetterPlayerController. These can be accessed via betterPlayerController. +- Added HLS Audio track support. +- Added setAudioTrack method in BetterPlayerController. +- Added useHlsAudioTrack parameter in BetterPlayerDataSource. +- Added enableAudioTracks and audioTracksIcon, backgroundColor in BetterPlayerControlsConfiguration. +- Fixed HLS loading speed. +- Fixed finished event creation. +- Fixed player pause issue when player notification is displayed. +- Fixed player not pausing/resuming automatically correctly. ## 0.0.49 -* Fixed fullscreen dispose issue. -* Added videoFormat parameter in BetterPlayerDataSource (should be used when data source url has no extension). -* Added retry feature after video failed to load. -* Added enableRetry in BetterPlayerControlsConfiguration. -* Changed BetterPlayerEventType.openFullscreen and BetterPlayerEventType.hideFullscreen events behavior (now events trigger after route change). -* Removed closed caption support from original video_player codebase. -* Fixed chinese translation typo (fixed by https://github.com/Big7lion) + +- Fixed fullscreen dispose issue. +- Added videoFormat parameter in BetterPlayerDataSource (should be used when data source url has no extension). +- Added retry feature after video failed to load. +- Added enableRetry in BetterPlayerControlsConfiguration. +- Changed BetterPlayerEventType.openFullscreen and BetterPlayerEventType.hideFullscreen events behavior (now events trigger after route change). +- Removed closed caption support from original video_player codebase. +- Fixed chinese translation typo (fixed by https://github.com/Big7lion) ## 0.0.48 -* Fixed loading large videos in iOS. -* Fixed partly progress bar jumping when seek issue in iOS. -* Added forceDispose parameter to dispose method in BetterPlayerController. -* Fixed Android notification vibration issue (fixed by https://github.com/marcusforsberg). + +- Fixed loading large videos in iOS. +- Fixed partly progress bar jumping when seek issue in iOS. +- Added forceDispose parameter to dispose method in BetterPlayerController. +- Fixed Android notification vibration issue (fixed by https://github.com/marcusforsberg). ## 0.0.47 -* Fixed Android loading indicator issue. -* Added setControlsAlwaysVisible in BetterPlayerController. -* Added absolutePosition feature (added by https://github.com/FlutterSu) + +- Fixed Android loading indicator issue. +- Added setControlsAlwaysVisible in BetterPlayerController. +- Added absolutePosition feature (added by https://github.com/FlutterSu) ## 0.0.46 -* Fixed iOS AVPlayer observer issue. -* Fixed iOS headers not applied issue. + +- Fixed iOS AVPlayer observer issue. +- Fixed iOS headers not applied issue. ## 0.0.45 -* Added Picture in Picture support. -* Added new parameters in BetterPlayerControlsConfiguration: pipMenuIcon and enablePip. -* Added new methods in BetterPlayerController: enablePictureInPicture, disablePictureInPicture, isPictureInPictureSupported, -setBetterPlayerGlobalKey. -* Added Picture in Picture icon in player controls. -* Added Picture in Picture example. -* Updated ExoPlayer version. -* Added pipStart and pipStop events. -* [BREAKING_CHANGE] Removed skipsTimeInMilliseconds. Added forwardSkipTimeInMilliseconds and backwardSkipTimeInMilliseconds. -* Updated notification service in android example. -* Fixed event play/pause event not triggered when controlling video with PiP or remote notification. -* Fixed playerTheme not set correctly. -* Fixed progress bar able to drag over other buttons. -* Fixed iOS player last second issue (player did not complete on last second of resource). + +- Added Picture in Picture support. +- Added new parameters in BetterPlayerControlsConfiguration: pipMenuIcon and enablePip. +- Added new methods in BetterPlayerController: enablePictureInPicture, disablePictureInPicture, isPictureInPictureSupported, + setBetterPlayerGlobalKey. +- Added Picture in Picture icon in player controls. +- Added Picture in Picture example. +- Updated ExoPlayer version. +- Added pipStart and pipStop events. +- [BREAKING_CHANGE] Removed skipsTimeInMilliseconds. Added forwardSkipTimeInMilliseconds and backwardSkipTimeInMilliseconds. +- Updated notification service in android example. +- Fixed event play/pause event not triggered when controlling video with PiP or remote notification. +- Fixed playerTheme not set correctly. +- Fixed progress bar able to drag over other buttons. +- Fixed iOS player last second issue (player did not complete on last second of resource). ## 0.0.44 -* Added placeholder until play example -* Added playback stalled feature in iOS. iOS version should behave same as Android once video failed to load. -* Added BetterPlayerTheme to controls configuration (added by https://github.com/maine98). -* [BREAKING_CHANGE] Changed custom controls builder in BetterPlayerControlsConfiguration. Now it accepts BetterPlayerController. -* Exposed BetterPlayerPlaylistState and betterPlayerController getter within. -* Added overriddenDuration to BetterPlayerDataSource. + +- Added placeholder until play example +- Added playback stalled feature in iOS. iOS version should behave same as Android once video failed to load. +- Added BetterPlayerTheme to controls configuration (added by https://github.com/maine98). +- [BREAKING_CHANGE] Changed custom controls builder in BetterPlayerControlsConfiguration. Now it accepts BetterPlayerController. +- Exposed BetterPlayerPlaylistState and betterPlayerController getter within. +- Added overriddenDuration to BetterPlayerDataSource. ## 0.0.43 -* Added autoDispose flag in BetterPlayerConfiguration -* Added removeEventsListener in BetterPlayerController -* Video list examples update -* Fixed Android native build warnings -* Fixed placeholder until play issues -* Added placeholderOnTop to the BetterPlayerConfiguration -* Lint fixes + +- Added autoDispose flag in BetterPlayerConfiguration +- Added removeEventsListener in BetterPlayerController +- Video list examples update +- Fixed Android native build warnings +- Fixed placeholder until play issues +- Added placeholderOnTop to the BetterPlayerConfiguration +- Lint fixes ## 0.0.42 -* Fixed resolution issue -* Fixed type of BetterPlayerDataSource for file type -* Added audio notify on dispose (iOS) (fixed by https://github.com/kingiol) + +- Fixed resolution issue +- Fixed type of BetterPlayerDataSource for file type +- Added audio notify on dispose (iOS) (fixed by https://github.com/kingiol) ## 0.0.41 -* Fixed loadingColor and loadingWidget for cupertino player -* Increased size of cupertino buttons -* Fixed setControlsEnabled in cupertino/material player -* [BREAKING_CHANGE] Removed startAt, looping, placeholder, overlay, fullScreenByDefault, - allowedScreenSleep, systemOverlaysAfterFullScreen, deviceOrientationsAfterFullScreen from BetterPlayerController + +- Fixed loadingColor and loadingWidget for cupertino player +- Increased size of cupertino buttons +- Fixed setControlsEnabled in cupertino/material player +- [BREAKING_CHANGE] Removed startAt, looping, placeholder, overlay, fullScreenByDefault, + allowedScreenSleep, systemOverlaysAfterFullScreen, deviceOrientationsAfterFullScreen from BetterPlayerController ## 0.0.40 -* Exposed VideoPlayerValue in export -* Fixed log issue -* Added loadingColor and loadingWidget in BetterPlayerControlsConfiguration + +- Exposed VideoPlayerValue in export +- Fixed log issue +- Added loadingColor and loadingWidget in BetterPlayerControlsConfiguration ## 0.0.39 -* Added lint library for dart analysis -* [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerDataSourceType -* [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerEventType -* [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerSubtitlesSourceType + +- Added lint library for dart analysis +- [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerDataSourceType +- [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerEventType +- [BREAKING_CHANGE] Changed constant names to lowerCamelCase in BetterPlayerSubtitlesSourceType ## 0.0.38 -* Added support for player notifications -* Added handleLifecycle to BetterPlayerConfiguration -* Added notificationConfiguration to BetterPlayerDataSource + +- Added support for player notifications +- Added handleLifecycle to BetterPlayerConfiguration +- Added notificationConfiguration to BetterPlayerDataSource ## 0.0.37 -* Added setControlsEnabled to BetterPlayerController -* Fixed example video list widget buttons not rendering correctly in small resolutions -* Added setOverriddenAspectRatio to BetterPlayerController -* Fixed crash connected with setSpeed in Android platform -* Fixed deviceOrientationsOnFullScreen for iOS -* Fixed CH translations (fixes by https://github.com/JarvanMo) -* Click to show/hide controls (fixed by https://github.com/mtAlves) -* [BREAKING_CHANGE] Removed future from isPlaying. Now it's sync method (https://github.com/hongfeiyang) + +- Added setControlsEnabled to BetterPlayerController +- Fixed example video list widget buttons not rendering correctly in small resolutions +- Added setOverriddenAspectRatio to BetterPlayerController +- Fixed crash connected with setSpeed in Android platform +- Fixed deviceOrientationsOnFullScreen for iOS +- Fixed CH translations (fixes by https://github.com/JarvanMo) +- Click to show/hide controls (fixed by https://github.com/mtAlves) +- [BREAKING_CHANGE] Removed future from isPlaying. Now it's sync method (https://github.com/hongfeiyang) ## 0.0.36 -* Added INITIALIZED event -* Added autoDetectFullscreenDeviceOrientation in BetterPlayerConfiguration -* Fixed autoPlay background issue -* Removed open_iconic_flutter icons used in Cupertino controls -* Added cupertino_icons for icons used Cupertiono controls -* Fixed progress bar not working correctly for iOS 12 with file datasource -* Removed yellow line below progress text (fixed by https://github.com/mtAlves) + +- Added INITIALIZED event +- Added autoDetectFullscreenDeviceOrientation in BetterPlayerConfiguration +- Fixed autoPlay background issue +- Removed open_iconic_flutter icons used in Cupertino controls +- Added cupertino_icons for icons used Cupertiono controls +- Fixed progress bar not working correctly for iOS 12 with file datasource +- Removed yellow line below progress text (fixed by https://github.com/mtAlves) ## 0.0.35 -* Fixed iOS black screen issue -* Fixed full screen placeholder issue -* Fixed event not firing in enterFullScreen and exitFullScreen -* Fixed subtitles parsing issues + +- Fixed iOS black screen issue +- Fixed full screen placeholder issue +- Fixed event not firing in enterFullScreen and exitFullScreen +- Fixed subtitles parsing issues ## 0.0.34 -* Added memory data source -* Added factories: network, file, memory for BetterPlayerDataSource -* Fixed missing useHlsTracks implementation -* Fixed placeholder showing after full screen when using showPlaceholderUntilPlay -* Added setControlsVisibility to BetterPlayerController -* [BREAKING_CHANGE] Removed showControlsOnInitialize from BetterPlayerConfiguration. Use BetterPlayerControlsConfiguration to set showControlsOnInitialize parameter. -* Fixed cupertino controls issue with hasError + +- Added memory data source +- Added factories: network, file, memory for BetterPlayerDataSource +- Fixed missing useHlsTracks implementation +- Fixed placeholder showing after full screen when using showPlaceholderUntilPlay +- Added setControlsVisibility to BetterPlayerController +- [BREAKING_CHANGE] Removed showControlsOnInitialize from BetterPlayerConfiguration. Use BetterPlayerControlsConfiguration to set showControlsOnInitialize parameter. +- Fixed cupertino controls issue with hasError ## 0.0.33 -* Fixed BetterPlayerEvent visibility -* Fixed lazy initialization, when first data source is passed after player finishes first render -* Added selectedByDefault to BetterPlayerSubtitlesConfiguration -* Fixed HLS tracks android native code -* Updated example + +- Fixed BetterPlayerEvent visibility +- Fixed lazy initialization, when first data source is passed after player finishes first render +- Added selectedByDefault to BetterPlayerSubtitlesConfiguration +- Fixed HLS tracks android native code +- Updated example ## 0.0.32 -* Fixed locale picking when context is not mounted anymore -* Added cache feature (based on https://github.com/sanekyy/plugins/tree/caching and https://github.com/vikram25897/flutter_cached_video_player solutions) -* Added BetterPlayerCacheConfiguration to BetterPlayerDataSource -* Refactored Android's native code + +- Fixed locale picking when context is not mounted anymore +- Added cache feature (based on https://github.com/sanekyy/plugins/tree/caching and https://github.com/vikram25897/flutter_cached_video_player solutions) +- Added BetterPlayerCacheConfiguration to BetterPlayerDataSource +- Refactored Android's native code ## 0.0.31 -* Added showPlaceholderUntilPlay in BetterPlayerConfiguration -* Fixed exception event not being triggered -* Fixed controls not displaying on video finished + +- Added showPlaceholderUntilPlay in BetterPlayerConfiguration +- Fixed exception event not being triggered +- Fixed controls not displaying on video finished ## 0.0.30 -* Fixed issue when full screen was triggered twice if autoPlay and fullScreenByDefault were enabled -* Removed flutter_widgets, since it's not maintained anymore. Added instead visibility_detector package (by https://github.com/espresso3389) -* Added rewind and forward buttons for android player. -* Fixed player UI's jank -* Added enableSkips and skipsTimeInMilliseconds in BetterPlayerControlsConfiguration -* Changed middle play button behavior (now it's only used for restart player). -* Updated BetterPlayerControllerProvider visibility. -* Override invalid dependency from wakelock library. + +- Fixed issue when full screen was triggered twice if autoPlay and fullScreenByDefault were enabled +- Removed flutter_widgets, since it's not maintained anymore. Added instead visibility_detector package (by https://github.com/espresso3389) +- Added rewind and forward buttons for android player. +- Fixed player UI's jank +- Added enableSkips and skipsTimeInMilliseconds in BetterPlayerControlsConfiguration +- Changed middle play button behavior (now it's only used for restart player). +- Updated BetterPlayerControllerProvider visibility. +- Override invalid dependency from wakelock library. ## 0.0.29+1 -* Updated readme + +- Updated readme ## 0.0.29 -* Fixed routePageBuilder usage from BetterPlayerConfiguration -* Added overflowMenuIcon, playbackSpeedIcon, qualitiesIcon, subtitlesIcon, overflowMenuIconsColor to BetterPlayerControlsConfiguration -* Added double tap to play/pause video (original idea by https://github.com/r6c) + +- Fixed routePageBuilder usage from BetterPlayerConfiguration +- Added overflowMenuIcon, playbackSpeedIcon, qualitiesIcon, subtitlesIcon, overflowMenuIconsColor to BetterPlayerControlsConfiguration +- Added double tap to play/pause video (original idea by https://github.com/r6c) ## 0.0.28 -* Fixed subtitles overflow issue when transitioning between fullscreen and normal state -* Added alignment and backgroundColor in BetterPlayerSubtitlesConfiguration + +- Fixed subtitles overflow issue when transitioning between fullscreen and normal state +- Added alignment and backgroundColor in BetterPlayerSubtitlesConfiguration ## 0.0.27 -* Added enableOverflowMenu option in BetterPlayerControlsConfiguration (enable/disable overflow menu) -* Added overflowMenuCustomItems in BetterPlayerControlsConfiguration (show custom menu items in overflow menu) -* [BREAKING_CHANGE] Removed defaultErrorText, loadingNextVideoText, liveText from BetterPlayerControlsConfiguration. To change these values, please use translations in BetterPlayerConfiguration. -* Added BetterPlayerTranslations in BetterPlayerConfiguration. You can use it to setup translations of the player. + +- Added enableOverflowMenu option in BetterPlayerControlsConfiguration (enable/disable overflow menu) +- Added overflowMenuCustomItems in BetterPlayerControlsConfiguration (show custom menu items in overflow menu) +- [BREAKING_CHANGE] Removed defaultErrorText, loadingNextVideoText, liveText from BetterPlayerControlsConfiguration. To change these values, please use translations in BetterPlayerConfiguration. +- Added BetterPlayerTranslations in BetterPlayerConfiguration. You can use it to setup translations of the player. ## 0.0.26 -* Added fullScreenAspectRatio and deviceOrientationsOnFullScreen to handle different full screen scenarios -* Updated wakelock version + +- Added fullScreenAspectRatio and deviceOrientationsOnFullScreen to handle different full screen scenarios +- Updated wakelock version ## 0.0.25 -* [BREAKING_CHANGE]: changed API in BetterPlayerControlsConfiguration: enableQualities replaces enableTracks. -* Added support for different video resolutions -* Fixed issue when full screen is being dismissed on changing subtitles -* Added CHANGED_RESOLUTION event + +- [BREAKING_CHANGE]: changed API in BetterPlayerControlsConfiguration: enableQualities replaces enableTracks. +- Added support for different video resolutions +- Fixed issue when full screen is being dismissed on changing subtitles +- Added CHANGED_RESOLUTION event ## 0.0.24 -* Added possibility to set multiple subtitles to video -* [BREAKING_CHANGE]: changed API in BetterPlayerDataSource. Instead of one subtitles object, list of subtitles is required. + +- Added possibility to set multiple subtitles to video +- [BREAKING_CHANGE]: changed API in BetterPlayerDataSource. Instead of one subtitles object, list of subtitles is required. ## 0.0.23 -* General bug fixes. -* Added playerVisibilityChangedBehavior in BetterPlayerConfiguration. -* Changed player behavior when player is not visible in viewport: if player was playing before leaving viewport it will be paused and if user views player again it will start playing automatically. -* Added BetterPlayer.network and BetterPlayer.file methods. -* Changed iOS & Android native classes name to prevent conflict issues with video_player. + +- General bug fixes. +- Added playerVisibilityChangedBehavior in BetterPlayerConfiguration. +- Changed player behavior when player is not visible in viewport: if player was playing before leaving viewport it will be paused and if user views player again it will start playing automatically. +- Added BetterPlayer.network and BetterPlayer.file methods. +- Changed iOS & Android native classes name to prevent conflict issues with video_player. ## 0.0.22 -* Added support for hls tracks (quality of the videos). -* Added useHlsTracks and hlsTrackName in BetterPlayerDataSource. -* Added CHANGED_TRACK event. -* You can choose track from overflow menu. When there's no tracks to select "Default" will be selected. + +- Added support for hls tracks (quality of the videos). +- Added useHlsTracks and hlsTrackName in BetterPlayerDataSource. +- Added CHANGED_TRACK event. +- You can choose track from overflow menu. When there's no tracks to select "Default" will be selected. ## 0.0.21 -* Added enableSubtitles parameter. + +- Added enableSubtitles parameter. ## 0.0.20 -* Added rotation parameter in BetterPlayerConfiguration. + +- Added rotation parameter in BetterPlayerConfiguration. ## 0.0.19 -* Added support for hls subtitles (BetterPlayer will handle them automatically). -* [BREAKING_CHANGE]: changed API in BetterPlayerSubtitlesSource. To use old API, please use factory: BetterPlayerSubtitlesSource.single. -* Added useHlsSubtitles parameter in BetterPlayerDataSource. -* Added CHANGED_SUBTITLES event. -* User can choose subtitles from overflow menu, when there's no subtitles selected, "none" options will be chosen. + +- Added support for hls subtitles (BetterPlayer will handle them automatically). +- [BREAKING_CHANGE]: changed API in BetterPlayerSubtitlesSource. To use old API, please use factory: BetterPlayerSubtitlesSource.single. +- Added useHlsSubtitles parameter in BetterPlayerDataSource. +- Added CHANGED_SUBTITLES event. +- User can choose subtitles from overflow menu, when there's no subtitles selected, "none" options will be chosen. ## 0.0.18: -* Fixed loading issue when auto play video feature is enabled in playlist. + +- Fixed loading issue when auto play video feature is enabled in playlist. ## 0.0.17 -* Fixed placeholder not following video fit options (fixed by https://github.com/nicholascioli). -* Updated dependencies. + +- Fixed placeholder not following video fit options (fixed by https://github.com/nicholascioli). +- Updated dependencies. ## 0.0.16 -* Added overflow menu. -* Added playback speed feature (based on https://github.com/shiyiya solution). -* User can choose playback speed from overflow menu. -* Added SET_SPEED event. + +- Added overflow menu. +- Added playback speed feature (based on https://github.com/shiyiya solution). +- User can choose playback speed from overflow menu. +- Added SET_SPEED event. ## 0.0.15 -* Added fit configuration option (based on https://github.com/shiyiya solution). + +- Added fit configuration option (based on https://github.com/shiyiya solution). ## 0.0.14 -* Better player list video player state is preserved on state changed. -* Fixed manual dispose issue. -* Fixed playlists video changing issue (fixed by https://github.com/sokolovstas). -* Added tap to hide feature for iOS player (by https://github.com/gazialankus). -* Fixed CONTROLS_VISIBLE and CONTROLS_HIDDEN events not triggered for ios player (fixed by https://github.com/gazialankus). -* Added seek method to BetterPlayerListVideoPlayerController. + +- Better player list video player state is preserved on state changed. +- Fixed manual dispose issue. +- Fixed playlists video changing issue (fixed by https://github.com/sokolovstas). +- Added tap to hide feature for iOS player (by https://github.com/gazialankus). +- Fixed CONTROLS_VISIBLE and CONTROLS_HIDDEN events not triggered for ios player (fixed by https://github.com/gazialankus). +- Added seek method to BetterPlayerListVideoPlayerController. ## 0.0.13 -* Changed channel name of video player plugin. -* Fixed dispose issue in cupertino player. + +- Changed channel name of video player plugin. +- Fixed dispose issue in cupertino player. ## 0.0.12 -* Fixed duration called on null (fixed by https://github.com/ganeshrvel). -* Added new control events (fixed by https://github.com/ganeshrvel). -* Fixed .m3u8 live stream issues in iOS. + +- Fixed duration called on null (fixed by https://github.com/ganeshrvel). +- Added new control events (fixed by https://github.com/ganeshrvel). +- Fixed .m3u8 live stream issues in iOS. ## 0.0.11 -* Fixed iOS crash on dispose. -* Added player headers support. -* Updated dependencies. -* Dart Analysis refactor. + +- Fixed iOS crash on dispose. +- Added player headers support. +- Updated dependencies. +- Dart Analysis refactor. ## 0.0.10 -* Added BetterPlayerListVideoPlayerController to control list video player. + +- Added BetterPlayerListVideoPlayerController to control list video player. ## 0.0.9 -* Fixed setState called after dispose. -* General bugfixes. + +- Fixed setState called after dispose. +- General bugfixes. ## 0.0.8 -* Fixed buffering indicator issue on Android. + +- Fixed buffering indicator issue on Android. ## 0.0.7 -* Fixed progress bar scroll lag. + +- Fixed progress bar scroll lag. ## 0.0.6 -* Fixed video duration issue. -* Added HTML subtitles. + +- Fixed video duration issue. +- Added HTML subtitles. ## 0.0.5 -* Added reusable video player. -* Bug fixes. + +- Added reusable video player. +- Bug fixes. ## 0.0.4 -* Changed 'settings' to 'configuration'. -* Removed unused parameters from configuration. -* Documentation update. + +- Changed 'settings' to 'configuration'. +- Removed unused parameters from configuration. +- Documentation update. ## 0.0.3 -* Updated documentation. + +- Updated documentation. ## 0.0.2 -* Moved example project from better_player_example to example. + +- Moved example project from better_player_example to example. ## 0.0.1 -* Initial release. + +- Initial release. diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 937b7e918..3c98b7f78 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -58,5 +58,7 @@ UIViewControllerBasedStatusBarAppearance + UIApplicationSupportsIndirectInputEvents + diff --git a/pubspec.yaml b/pubspec.yaml index 20c538d31..b9418159b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: better_player description: Advanced video player based on video_player and Chewie. It's solves many typical use cases and it's easy to run. -version: 0.0.83 +version: 0.0.84 # Disabled because of warning from analyzer # authors: -# - Jakub Homlala -homepage: https://github.com/jhomlala/betterplayer +# - Abhinav Kumar Sintoo +homepage: homepage: https://github.com/abhinav503/betterplayer documentation: https://jhomlala.github.io/betterplayer/ environment: